Build a REST API with Node.js and Express from Scratch

REST APIs are the backbone of modern web applications. Whether you're powering a mobile app, a single-page application, or a microservice, knowing how to build a clean and reliable API is an essential skill for any developer. In this tutorial, you'll build a fully functional REST API using Node.js and Express — step by step.

What You'll Need

  • Node.js (v18 or later) installed on your machine
  • A terminal or command prompt
  • A code editor (VS Code recommended)
  • Basic familiarity with JavaScript

Step 1: Initialize Your Project

Start by creating a new project directory and initializing it with npm:

mkdir my-api
cd my-api
npm init -y

Next, install Express:

npm install express

Step 2: Create Your Entry Point

Create a file called index.js in the root of your project. This will be the main entry point for your server:

const express = require('express');
const app = express();
const PORT = 3000;

app.use(express.json());

app.get('/', (req, res) => {
  res.json({ message: 'API is running!' });
});

app.listen(PORT, () => {
  console.log(`Server listening on port ${PORT}`);
});

Run the server with node index.js and visit http://localhost:3000 in your browser or a tool like Postman.

Step 3: Define Your Routes

A REST API is organized around resources. For this example, we'll build a simple API for managing a list of tasks. Create a new file called routes/tasks.js:

const express = require('express');
const router = express.Router();

let tasks = [
  { id: 1, title: 'Learn Express', done: false },
  { id: 2, title: 'Build an API', done: false }
];

// GET all tasks
router.get('/', (req, res) => res.json(tasks));

// GET a single task
router.get('/:id', (req, res) => {
  const task = tasks.find(t => t.id === parseInt(req.params.id));
  if (!task) return res.status(404).json({ error: 'Task not found' });
  res.json(task);
});

// POST create a task
router.post('/', (req, res) => {
  const task = { id: Date.now(), title: req.body.title, done: false };
  tasks.push(task);
  res.status(201).json(task);
});

// DELETE a task
router.delete('/:id', (req, res) => {
  tasks = tasks.filter(t => t.id !== parseInt(req.params.id));
  res.status(204).send();
});

module.exports = router;

Step 4: Register Your Router

Back in index.js, import and use the tasks router:

const tasksRouter = require('./routes/tasks');
app.use('/api/tasks', tasksRouter);

Step 5: Add Basic Error Handling

Good APIs handle errors gracefully. Add a catch-all error handler at the bottom of index.js:

app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).json({ error: 'Something went wrong' });
});

HTTP Method Summary

MethodEndpointAction
GET/api/tasksRetrieve all tasks
GET/api/tasks/:idRetrieve a single task
POST/api/tasksCreate a new task
DELETE/api/tasks/:idDelete a task

Next Steps

Now that your API is running, here are some natural next steps to level it up:

  1. Connect a real database using MongoDB (with Mongoose) or PostgreSQL (with Prisma)
  2. Add authentication using JWT tokens
  3. Validate incoming request data with a library like Zod or Joi
  4. Document your API with Swagger/OpenAPI

Building APIs becomes intuitive quickly once you understand the fundamentals of routing, middleware, and HTTP semantics. Keep experimenting!