Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/acmucsd/hack-website into a…
Browse files Browse the repository at this point in the history
…lexis-nikhil/deployment
  • Loading branch information
nik-dange committed Sep 30, 2023
2 parents 023ac9f + 9908b04 commit 2e7db81
Show file tree
Hide file tree
Showing 26 changed files with 584 additions and 53 deletions.
3 changes: 2 additions & 1 deletion pages/hack-school/_meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"css": "Week 1: CSS",
"js": "Week 2: JavaScript",
"react": "Week 3: React",
"express": "Week 4: Building APIs with Express",
"mongodb": "Week 5: Databases & MongoDB",
"deployment": "Week 6: Deployment"
}
}
211 changes: 211 additions & 0 deletions pages/hack-school/express.mdx
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!





Binary file added pages/hack-school/images/apiwaiter.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added pages/hack-school/images/props.PNG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 21 additions & 1 deletion pages/index.mdx
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} />
Binary file added public/assets/alexis.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/angela.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/charvi.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/khushi.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion public/assets/left_diamonds.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/nikhil.JPG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 2e7db81

Please sign in to comment.