Rest API with Express router, Jest and Supertest

Tom Lamb
5 min readSep 9, 2021

Creating your own portfolio and want to showcase a backend? Maybe you’re creating an app for a friend and need to deal with content, users or products? Or maybe just trying to understand how to setup a REST API using Node.js? This tutorial will guide you through setting up a very simple backend using Express and vanilla javascript.

I have also added Jest and Supertest to allow you to quickly start to unit test your utility functions or maybe test what is returned from an endpoint.

Introduction

I have often found myself looking on search engines for tutorials on how to setup an express server with testing using vanilla javascript and often found that there are a lot of them out there, a lot of very different solutions but none that I found very simple. This is my attempt to bring some simplicity to a subject that confused me quite a bit in the beginning.

Setting up our repository

Before diving into loads of code, let’s start with setting up our repository, installing all the necessary dependencies and adding a few commands to our app.

First open up your terminal and create a directory called express-rest-api by typing in the following code:

mkdir express-rest-api && cd express-rest-api

Now that we have created the directory we can initialise a git repository:

git init

Next we initialise our project using npm init:

npm init -y

This should automatically create a package.json file which should look like this:

{
"name": "express-rest-api",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}

Now it is time to install all our dependencies

# install express
npm install express --save
# install jest and supertest
npm install --save-dev jest supertest
# install dotenv
npm install --save dotenv
# install nodemon
npm install --save-dev nodemon

And finally, we need to make a few changes to our package.json by adding our test script jest — watch — verbose and the script that will run our application nodemon index.js.

Your package.json should look like this now…

{
"name": "express-rest-api",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "jest --watch --verbose",
"dev": "nodemon index.js"
},
"jest": {
"testEnvironment": "node",
"coveragePathIgnorePatterns": [
"/node_modules/"
]
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"jest": "^27.1.0",
"nodemon": "^2.0.12",
"supertest": "^6.1.6"
},
"dependencies": {
"dotenv": "^10.0.0",
"express": "^4.17.1"
}
}

Great! Now we are ready to start coding!

Coding our app

index.js

Let’s create a file called index.js in the root of our repository and insert the following code:

require('dotenv').config();const app = require('./app');const port = process.env.PORT || 8080;app.listen(port, () => console.log(`App is listening on http://localhost:${port}`));

app.js

Now we need to create a file called app.js and insert the following code:

const express = require('express');
const api = require('./src/routes');
const app = express();app.use(express.json());
app.use('/api', api);
// High level error handling
app.use((req, res, next) => {
const error = new Error('Not found');
error.statusCode = 404;
next(error);
});
app.use((err, req, res, next) => {
const statusCode = err.statusCode || 500;
const name = err.name || 'Error';
res
.status(statusCode)
.json({ name, message: err.message });
});
module.exports = app;

These two files allow us to create and define an app in app.js and then initialise our server in index.js.

At this point you might be wondering what const api = require(‘./src/routes’); and app.use(‘/api’, api); are doing here? They might even be showing you an error in your code editor?

These two lines of code are going to help us create a router for our application. Basically we are saying here that any request that is sent to http://localhost:8080/api will be redirected to code we write in src/routes.

So let’s write that code !

src/routes/index.js

Create a src folder, inside create a routes folder and finally inside create an index.js file where you copy the following code.

const router = require("express").Router();
const photos = require("./photos");
router.use("/photos", photos);module.exports = router;

This code will act as a switchboard for our api and we can define all our different routes here.

For the moment our API will only accept requests to /api/photos and any request made to that endpoint will routed to src/routes/photos/index.js.

src/routes/photos/index.js

This is finally where the user will be sent when they make their request. As you can see here we have defined a GET request and are returning a JSON object.

const router = require("express").Router();router.get("", (req, res) => {
res.json({
message: "Hello World"
})
})
module.exports = router;

In app.js we added /api to our url, then in our router we added /photos to our url, therefore in order to make a GET request to our API and receive back the string “Hello World” we need to use http://localhost:8080/api/photos.

Now let’s see if this works!

To test this we first run npm run dev, then we can either download postman from here. Or seeing as we are making a GET request we can just use our browser and type in our url.

Yay we did it!!

Testing our API

Now this is all very good but do we really need to use postman or our browser every single time we need to test an api endpoint or is there a better way of doing this?

Let’s create automated testing for our API!

Let’s create the subfolder src/test and inside create a file called index.test.js

describe('Sanity test', () => {
test('1 should equal 1', () => {
expect(1).toBe(1)
})
})

In this simple test we are essentially making sure that 1 is equal to 1 this should be true right? let’s see by running npm run test

Nice!

Now let’s actually test our api endpoint and this is where supertest comes into its own.

Inside our test file let’s add the following code at the top:

const request = require('supertest')
const app = require('../../app');

and then add the following code after our first test.

describe('Photos endpoint', () => {
test('should return hello world object', async () => {
const res = await request(app)
.get('/api/photos')
expect(res.statusCode).toEqual(200)
expect(res.body).toEqual({
message: "Hello World"
})
})
})

Conclusion

We now have a server that will serve whatever we define in our routes, tests that will run when we call them and a nicely organised repository.

You can find the boilerplate on GitHub here.

To add Typescript to this repository, follow the next article in the series here.

I hope you liked this tutorial! Please don’t hesitate to leave any comments you might have!

--

--

Tom Lamb
Tom Lamb

Written by Tom Lamb

Full Stack Javascript developer, skateboarder, skier and tech enthusiast! Visit my website at https://tom-lamb.com