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!