Creating RESTful APIs in Node.js using Express

Published: 19th August, 2024

Author: Sohan Shashikumar

Read Time: 12 minutes

Introduction

REST stands for Representational State Transfer. A REST API is an Application Programming Interface that adheres to the principles of REST architecture.

So what is an API?

In simple terms, an API acts as a bridge between a resource provider and a resource consumer. It is widely used in applications that follow a Client-Server Architecture.

Real World Example

Let's say you want to display information about a movie. You would need to use a service that maintains a record of every single movie and provides an interface for developers like us to interact with. This is the primary purpose of an API.

Depending on the type of request made, we can perform Create, Read, Update, and Delete (often referred to as CRUD) operations.

Our demo API will be built with this example in mind!

Flow Diagram

REST API architecture diagram

Prerequisites

  • Make sure you have Node.js installed. You can install it from the Official Documentation.
  • Node.js automatically includes npm, a package manager used to install third-party packagesβ€”in our case, Express.

Setting up the project

  • Create a new directory and open it in your favorite code editor. I'll be using VS Code, but feel free to use any code editor of your choice!
  • Run the following command to initialize a new project:
  • npm init -y
  • This will create a package.json file.
  • Next, run the following command to install Express:
  • npm install express
  • Create a src directory, and inside that, create a new file named index.js. This file will be the main entry point of our application.
  • Your project structure should look as follows:
  • project structure

Let's start coding

const express = require('express');

const port = 3000;
const app = express();
		
app.get('/', (req, res) => {
	res.status(200).send('Congrats, you just consumed data from a REST API!');
});
		
app.listen(port, () => {
	console.log(`πŸš€ Express app listening on http://localhost:${port}`);
});

Copy paste the above code in your index.js file and run the script using the following command:

node ./src/index.js

Upon running it, you'll see that our app has successfully started and is listening on localhost on the specified port. Open any web browser and type localhost:3000 You'll see the message we are sending back. Congrats, your REST API is functioning as intended!

So... how does it work?

  • We are requiring (importing) the Express package and creating a new app by invoking the function returned by require().
  • We call app.get() with a path, in this case, the root route, /. The second argument is a callback function which will be invoked whenever there's an incoming GET request to this route.
  • This function is called with the request and response object indicated by "req" and "res" respectively.
  • We are setting a HTTP status code of 200 which indicates that everything went well. Additionally, we are also sending a plain text response.
  • Lastly, we need to ensure our app listens for requests on a valid free port. app.listen() does this and calls the callback whenever the app starts successfully.
  • You see the message in your browser because, by default, while accessing any web page, browsers make a GET request!

Coming back to our example

Our app is very simple, it doesn't do anything except sending a plain text message. Let's change that by adding more functionality. Keeping the example I gave earlier in mind, we will build a REST API which users can use to perform the basic CRUD operations on movie data.

Create a new file, movies.js

const movies = [
	{
		name: 'Justice League',
		year: 2017,
		genres: ['Action', 'Superhero', 'Adventure'],
		boxOfficeHit: false
	},
	{
		name: 'Interstellar',
		year: 2014,
		genres: ['Mystery', 'Suspense', 'Sci-Fi'],
		boxOfficeHit: true
	},
	{
		name: 'Top Gun Maverick',
		year: 2022,
		genres: ['Action', 'Adventure', 'Drama'],
		boxOfficeHit: true
	},
	{
		name: 'Oppenheimer',
		year: 2023,
		genres: ['Thriller', 'Historical Drama'],
		boxOfficeHit: true
	}
];

module.exports = {
	movies
};

In a real world scenario, we would store movies in a database such as MongoDB, PostgreSQL or Cassandra. But for simplicity and to prevent diverting away from the topic of this blog, we will store it in runtime memory. We will now proceed to perform CRUD operations on this data.

Creating a new movie

const express = require('express');
const { movies } = require('./movies');
		
const port = 3000;
const app = express();
app.use(express.json());

app.post('/movies/create', (req, res) => {
	const { name, year, genres, boxOfficeHit } = req.body;

	if (!name || !year || !genres?.length || typeof boxOfficeHit === 'undefined') {
		return res.sendStatus(400);
	}

	movies.push({ name, year, genres, boxOfficeHit });
	return res.json(movies);
});

app.listen(port, () => {
	console.log(`πŸš€ Express app listening on http://localhost:${port}`);
});
  • We are requiring the movies.js file that stores all the movies that we created.
  • We are using the json() middleware function from express. This basically populates req.body for all requests having the application/json content type.
  • We create a new route using the app.post() method. The callback runs whenever a POST request is made to /movies/create. Notice how we are using app.post() and NOT app.get()
  • We get the details such as the name of the movie, year of release, genre and whether it was a box office hit from the incoming request body. If either one of them are not present, we send a status code of 400, which indicates a Bad Request.
  • We then push this object into our movies array and return the updated array as part of our response in json format.
  • If you are using VS Code, download this extension called Thunder Client. It is popularly used as a request client to make different type of requests to test endpoints at the comfort of your IDE. You can feel free to also use Postman.
  • Make a request to http://localhost:3000/movies/create and include the following json body.
  • We see that the movie that we sent in the request was added to our movies array (our database).
Request Demo

Reading a movie

const express = require('express');
const { movies } = require('./movies');
		
const port = 3000;
const app = express();
app.use(express.json());

app.get('/movie/:name', (req, res) => {
	const toSearch = req.params.name;
	const movie = movies.find((m) => m.name === toSearch);

	if (!movie) return res.sendStatus(404);
	return res.json(movie);
});

app.listen(port, () => {
	console.log(`πŸš€ Express app listening on http://localhost:${port}`);
});
  • We define a new route using app.get(). Notice the colon, : we are using. It is intentional. It is called a Route Parameter.
  • :name acts as a placeholder for accessing what movie we need to retrieve. We can call the endpoint like so, /movie/hello or /movie/world.
  • Whatever the case may be, in the callback, req.params.name will be equal to what was passed when we called the endpoint.
  • We find the movie based on the name that was passed. If a match was found, we return the movie. If not we send a 404 status of Not Found.
  • Make a GET request to http://localhost:3000/movie/Interstellar . The response body contains the particular movie we requested!
  • If you were to pass an invalid movie, you will get a 404 Not Found.

Updating a movie

const express = require('express');
const { movies } = require('./movies');
		
const port = 3000;
const app = express();
app.use(express.json());

app.patch('/movie/:name/update', (req, res) => {
	const toSearch = req.params.name;
	const movie = movies.find((m) => m.name === toSearch);

	if (!movie) return res.sendStatus(404);

	const { year, genres, boxOfficeHit } = req.body;
	if (year) movie.year = year;
	if (genres) movie.genres = genres;
	if (typeof boxOfficeHit !== 'undefined') movie.boxOfficeHit = boxOfficeHit;

	return res.json(movie);
});

app.listen(port, () => {
	console.log(`πŸš€ Express app listening on http://localhost:${port}`);
});
  • We create a new route using app.patch(). We get the name of the movie from the route parameter and access the fields that needs to be updated from the request body.
  • Based on what fields were passed, we update the movie object accordingly and return it.

There is one more operation left, that is deleting a movie. I won't be solving it here. You can take it up as a challenge and do it on your own.

Even if you're unable to, don't worry, all the code will be available on the GitHub Repository. Use it only if you get stuck though!

Conclusion

  • Congratulations πŸŽ‰ you now know how REST APIs work and you've implemented your very own in Node.js!
  • Your next steps would include exploring concepts such a Authentication, Authorization, Cookies, Sessions, Security and grouping similar routes to make your code very modular.
  • The Official Documentation is an excellent resource for gaining a more in-depth knowledge!

Feedback πŸ› οΈ

Your feedback is highly appreciated and will be used to improve the quality of existing & future posts!

What do you rate this post?

Anything else you wish to add?