How to build and test a Node.js REST API with Jest and Supertest
Build a small Express REST API and test its endpoints with Jest and Supertest: separate the app from the server, send in-memory requests, isolate tests with setup and teardown, and measure coverage.
What you will build
You will build a small REST API with Express and test its HTTP endpoints. Jest is a popular JavaScript test runner with assertions and mocking. Supertest sends real HTTP requests to your app in memory, so you can assert on status codes and response bodies without starting a network server. The key practice is separating the app from the server so tests can import the app directly.
Prerequisites
- Node.js 18 or later
- Basic JavaScript and HTTP knowledge
Steps
1. Create the project
mkdir todo-api && cd todo-api
npm init -y
npm install express
2. Build a testable Express app
Export the app separately from the code that calls listen, so tests import the app without binding a port.
// app.js
const express = require("express");
const app = express();
app.use(express.json());
let todos = [];
app.get("/todos", (req, res) => res.json(todos));
app.post("/todos", (req, res) => {
const todo = { id: todos.length + 1, title: req.body.title };
todos.push(todo);
res.status(201).json(todo);
});
module.exports = app;
3. Install Jest and Supertest
npm install -D jest supertest
4. Write endpoint tests
// app.test.js
const request = require("supertest");
const app = require("./app");
test("creates and lists todos", async () => {
const created = await request(app).post("/todos").send({ title: "buy milk" });
expect(created.status).toBe(201);
expect(created.body.title).toBe("buy milk");
const list = await request(app).get("/todos");
expect(list.body).toHaveLength(1);
});
5. Add setup and teardown
Use beforeEach to reset shared state so tests do not interfere with each other.
beforeEach(() => { /* reset database or in-memory store */ });
6. Run tests and coverage
Add a script and run it.
{ "scripts": { "test": "jest --coverage" } }
npm test
Verification
Run npm test and confirm both the create and list assertions pass. Add a test that posts without a title and assert on the resulting behavior. Break the route to return the wrong status and confirm the test catches it.
Next Steps
Mock external services, test error paths and validation, use a separate test database, and run the suite in continuous integration.