-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' of https://github.com/acmucsd/hack-website into a…
…lexis-nikhil/deployment
- Loading branch information
Showing
26 changed files
with
584 additions
and
53 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,211 @@ | ||
# Week 4: Building APIs with Express.js | ||
|
||
## What is Express? | ||
[Express](https://expressjs.com) is a web application framework for Node.js that enables developers to start a local server and develop API routes. Express does | ||
a good job of abstracting away the complexity of serving a web application. Before we explore Express, some background on the Internet and APIs is helpful! | ||
|
||
## Background on the Internet | ||
|
||
Before we explore Express.js, some background information on how servers work is necessary. | ||
|
||
### How the Internet works | ||
|
||
The internet can be thought of as a really, really long wire. Computers all over the world can interact with one another via this wire. | ||
In prior chapters, we mentioned the idea of **clients**. Web **clients** are the users (technically, web browsers, like Chrome or Safari). | ||
These clients request data through this long wire from web **servers**, which are simply any computers that serve data to client. | ||
|
||
The standard of this client/server communication is a protocol known as HTTPS (Hypertext Transfer Protocol). This is a "language" that both | ||
clients and servers can speak. | ||
|
||
### HTTP Requests | ||
|
||
We mentioned earlier that clients can request data from servers across the internet. This is accomplished with an HTTP Request, which is just | ||
a way of telling the server what the client would like to do. There are four common HTTP methods: | ||
|
||
- `GET`, for retrieving a resource from the server | ||
- `POST`, for posting or adding a resource to the server | ||
- `PUT`, for editing or modifying an existing resource on the server | ||
- `DELETE`, for deleting an existing resource on the server | ||
|
||
Together, these four methods give any application **CRUD functionality**–Create, Read, Update, Delete. Take a look at your favorite applications, and you'll be structured | ||
to notice that all of them support some form of CRUD. | ||
|
||
There are more [HTTP methods](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods), but the above four are the most common ones you'll see. | ||
|
||
### HTTP Responses | ||
For every request, there must be a response. Servers can respond to requests through an HTTP response. These responses have status codes tied to them, to indicate | ||
to the client whether the request was successful, and any specific information if it was unsuccessful. You've definitely seen these before! Have you ever tried to buy tickets to an event, | ||
but encountered a `500` (internal server error), or clicked on a link to be met with a `404` (resource not found error)? | ||
|
||
Status codes are 3 digit numbers that are classified according to success level, client errors, server errors, etc. You can see a comprehensive list | ||
of them [here](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status)! | ||
|
||
### Web application architecture | ||
|
||
Putting our clients, servers, requests, and responses together, this is a standard web application architecture: | ||
|
||
![image](pages/hack-school/images/webapparch.png) | ||
|
||
To summarize, clients communicate with servers via HTTP requests and responses. When those requests and recieved and processed, | ||
the server may make database queries to provide actual data for the client to use. | ||
|
||
## APIs | ||
Although there exists a standard for communication (HTTP), every server interacts differently. Imagine trying to `GET` a song from Spotify's servers, and then | ||
attemping to `GET` a movie from Netflix's servers. While both requests are attempting to retrieve a resource, the way that one might ask for each differs. This is where | ||
**APIs** come into play. | ||
|
||
API stands for **A**pplication **P**rogramming **I**nterface. An API is simply a toolkit for a client to interact with. Every server is different and accomplishes different goals | ||
through the use of this toolkit. APIs provide a framework and standard for how clients can interact with any given server. | ||
|
||
A good analogy for APIs is that of a waiter in a restaurant. The tables and dining area is the client, where the customers send requests for food to the kitchen (Requests), which is the backend. The kitchen is responsible for creating | ||
the food and sending it back to the dining area (Responses). How is this communication done? Through the waiters and waitresses! APIs are similar to waiters and waitresses, since they're the middlemen responsible for requests making it to the back-end, and | ||
responses making it back to the client. | ||
|
||
![image here](pages/hack-school/images/apiwaiter.png) | ||
Source: monosolutions.com | ||
|
||
Generally, APIs provide **API routes**, which are specific routes on a server that HTTP requests can be sent to. This allows clients to figure out how to interact with a particular server and request | ||
a certain set of resources. API routes have the following syntax: | ||
|
||
``` | ||
http://localhost:5000/api/purchases | ||
``` | ||
|
||
where `/api/purchases` is the route, and `localhost:5000` is the server. | ||
|
||
The same API route can be used for different HTTP methods. For example, a GET request sent to this route might return all purchases, while a POST | ||
request sent to this route (with a request body) would post a new purchase. | ||
|
||
You can see this in action with the [Spotify API](https://developer.spotify.com/documentation/web-api)! Take a look at the endpoints for various routes, possible responses based on status code, and parameters for [GET Album](https://developer.spotify.com/documentation/web-api/reference/get-an-album). | ||
|
||
### Further API functionality | ||
|
||
Routes aren't the only part of APIs. Production APIs especially can be far more complex with many moving parts. You might have heard of **rate-limiting**, or limiting the number of API requests or calls made by a certain | ||
user within a time period. APIs may also need to deal with **authentication/authorization** (this is the case for routes involving user profiles for Spotify, since it handles user account information). In both of these cases, **middleware** is important. Like the name | ||
suggests, middleware is a blanket term for any technology or processes that sit in the "middle" of an API. Middleware often intercepts requests and can process them in a certain way, *before* the server handles them. For example, | ||
a certain request's headers can be validated for the necessary authorization to access a route, or the signature can be checked to ensure the same user isn't breaking the rate-limiting threshold. You can read about Express middleware [here](https://expressjs.com/en/guide/using-middleware.html). | ||
|
||
|
||
## Building our own APIs with Express | ||
|
||
Now that we have a background on what APIs are and how we interact with them, we can discuss how Express enables developers to build their own APIs. Express abstracts away | ||
the complexity in serving a web application, allowing developers to quickly set up and customize their application. | ||
|
||
### Server setup | ||
To get started, create a directory called `server` within your application. | ||
|
||
Next, [initialize a Node.js application](https://docs.npmjs.com/cli/v10/commands/npm-init). | ||
This is achieved by executing `npm init -y` in your terminal. The `-y` flag is a shortcut to allow default configurations. | ||
|
||
```bash copy showLineNumbers | ||
npm init -y | ||
``` | ||
|
||
Now, we need to install Express as a package. [NPM (Node Package Manager)](https://www.npmjs.com) has hundreds of packages for developing applications, which you can see here. | ||
We just need Express for now, so you can execute | ||
|
||
```bash copy showLineNumbers | ||
npm i express | ||
``` | ||
|
||
which will install Express. You should notice a `package.json` file in your directory | ||
|
||
Then, navigate inside this directory with `cd server` and create a file called | ||
`index.js`. You can populate it with the following three lines: | ||
|
||
```javascript copy showLineNumbers | ||
const express = require('express'); | ||
const server = express(); | ||
server.listen(3000); | ||
``` | ||
|
||
These three lines import Express, instantiates a server, and has it listening on your computer's `PORT 3000`. If you open your web browser and | ||
go to `localhost:3000`, you can see the result of this API! | ||
|
||
### Creating an API route | ||
|
||
Now that Express is instantiated, we can add API routes. The following lines define a `GET` route: | ||
|
||
```javascript copy showLineNumbers | ||
const router = express.Router(); | ||
router.get('/purchases', async (req, res) => { | ||
// fetch purchases from database | ||
res.status(200).json({ purchase }); | ||
}); | ||
``` | ||
|
||
We begin by instantiating an Express router, which enables us to attach routes to our API. | ||
Then, we define a `GET` route with arrow function syntax. This route is now live on `localhost:3000/purchases`, so sending a `GET` request | ||
would return the `json` representation of `purchase`. | ||
|
||
You'll notice that we have a callback function here too; an asynchronous function with `(req, res)` as parameters. If you guessed that these are | ||
our **request** and **response** objects, you'd be correct! This is how our `GET` route can access the request and attach a status code to the response. | ||
|
||
What if we want to get a specific purchase? This is where route parameters come into play. We can slightly adjust our route to take in a specific ID, and use it | ||
to query from our database | ||
|
||
```javascript copy showLineNumbers | ||
router.get('/purchases/:id', async (req, res) => { | ||
|
||
const purchaseId = req.params.id; | ||
|
||
// fetch purchases from database | ||
res.status(200).json({ purchase }); | ||
}); | ||
``` | ||
|
||
The resulting response might look something like this (note that we sent the output as JSON above; requests are sent in the same manner): | ||
|
||
```json copy showLineNumbers | ||
{ | ||
"purchase": { | ||
"id": 123, | ||
"product": "ACM Hack Hoodie", | ||
"price": 30.99, | ||
"quantity": 2, | ||
"timestamp": "2023-09-24T10:00:00Z" | ||
} | ||
} | ||
``` | ||
|
||
We can do something similar with `POST` requests—we can utilize the `req` parameter to access details of our request body. | ||
|
||
Sample request: | ||
|
||
```json copy showLineNumbers | ||
{ | ||
"purchase": { | ||
"name": "ACM Bucket Hat", | ||
"description": "ACM's very first bucket hat, in 3 stunning colors", | ||
"color": "Blue", | ||
"price": 25.99, | ||
"quantity": 3, | ||
"timestamp": "2023-09-24T14:30:00Z", | ||
} | ||
} | ||
``` | ||
|
||
|
||
```javascript copy showLineNumbers | ||
router.post('/purchases', async (req, res) => { | ||
|
||
const purchase = req.body.purchase; | ||
|
||
const name = purchase.name; | ||
|
||
const {description} = purchase; // object destructuring | ||
|
||
// log purchases to database | ||
res.status(200).json({ purchase }); | ||
}); | ||
``` | ||
> Don't forget that a robust API can include some middleware. [Check out how to add functionality to your Express API's middleware](https://expressjs.com/en/guide/using-middleware.html). | ||
## Testing our API with Postman | ||
|
||
It's always important to test APIs, just as you would test any software you build. Check out our section on testing with Postman! | ||
|
||
|
||
|
||
|
||
|
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,22 @@ | ||
import HomePage from '../src/homepage'; | ||
export default HomePage; | ||
import { useSSG } from 'nextra/ssg'; | ||
import { getAllHackEvents, getHackEvent } from '../src/api/events_api'; | ||
|
||
export async function getStaticProps() { | ||
const uuids = ["1668587f-5816-45cc-8b5e-1a4825dff9e4", "2ba9e5ed-614e-4c19-9d7c-ab2cfd57e140", "73fc61ef-b3db-46c4-a5bf-71aba152e2f6", "3b922d84-9c57-4f2a-9e76-19e4e9130fe2", "6124d23b-ab77-481f-9248-920565ac07f3", "fdd1c33f-f31b-4421-abc9-0887158147cc"] | ||
const promises = uuids.map(uuid => getHackEvent(uuid)) | ||
const past_events = await Promise.all(promises); | ||
const future_events = await getAllHackEvents('future'); | ||
|
||
return { | ||
props: { | ||
ssg: { | ||
past_events: past_events || [], | ||
future_events: future_events || [] | ||
} | ||
}, | ||
revalidate: 1 * 60 * 60, // once every hour (in seconds) | ||
}; | ||
} | ||
|
||
<HomePage past_events={useSSG().past_events} future_Events={useSSG().future_events} /> |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Oops, something went wrong.