Hack School
Week 4: Express

Week 4: Building APIs with Express.js

What is Express?

Express (opens in a new tab) 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 (opens in a new tab), 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 (opens in a new tab)!

Web application architecture

Putting our clients, servers, requests, and responses together, this is a standard web application architecture:

image

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 Application Programming Interface. 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 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 (opens in a new tab)! Take a look at the endpoints for various routes, possible responses based on status code, and parameters for GET Album (opens in a new tab).

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 (opens in a new tab).

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 (opens in a new tab). This is achieved by executing npm init -y in your terminal. The -y flag is a shortcut to allow default configurations.

npm init -y

Now, we need to install Express as a package. NPM (Node Package Manager) (opens in a new tab) has hundreds of packages for developing applications, which you can see here. We just need Express for now, so you can execute

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:

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:

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

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):

{
  "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:

{
  "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",
  }
}
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 (opens in a new tab).

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!