# How to Build a REST API with Node.js

Building a REST API is one of the most practical skills a backend developer can learn, and Node.js remains one of the most popular tools for the job. Its non-blocking, event-driven architecture makes it well suited for handling many simultaneous requests, which is exactly what a REST API needs to do. This guide walks through the process step by step, from setup to deployment-ready structure, so you can build a working API even if you're doing it for the first time.

## What Is a REST API?

REST (Representational State Transfer) is an architectural style for designing networked applications. A REST API exposes resources — like users, products, or orders — through predictable URLs, and lets clients interact with those resources using standard HTTP methods: GET (read), POST (create), PUT/PATCH (update), and DELETE (remove). Because REST relies on stateless requests and standard HTTP verbs, it's easy to consume from web apps, mobile apps, or other services.

## Step 1: Set Up Your Project

Start by creating a project folder and initializing it with npm:

bash

```bash
mkdir node-rest-api
cd node-rest-api
npm init -y
```

This generates a `package.json` file that tracks your dependencies and scripts.

## Step 2: Install Express

While you can build an API with Node's built-in `http` module, most developers use **Express**, a minimal and flexible framework that simplifies routing, middleware, and request handling.

bash

```bash
npm install express
```

For a more complete setup, also install `cors` (to allow cross-origin requests), `dotenv` (for environment variables), and `nodemon` (to auto-restart the server during development):

bash

```bash
npm install cors dotenv
npm install --save-dev nodemon
```

## Step 3: Create the Server

Create a file called `server.js` and set up a basic Express server:

javascript

```javascript
const express = require('express');
const cors = require('cors');

const app = express();
app.use(cors());
app.use(express.json());

const PORT = process.env.PORT || 3000;

app.get('/', (req, res) => {
  res.send('API is running');
});

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

Run it with `node server.js`, or add a script to `package.json` so `nodemon` handles restarts automatically:

json

```json
"scripts": {
  "dev": "nodemon server.js"
}
```

## Step 4: Design Your Routes

Good REST APIs follow consistent, predictable URL patterns. For a simple "tasks" resource, routes typically look like this:

*   `GET /tasks` – retrieve all tasks
    
*   `GET /tasks/:id` – retrieve a single task
    
*   `POST /tasks` – create a new task
    
*   `PUT /tasks/:id` – update a task
    
*   `DELETE /tasks/:id` – remove a task
    

Here's a basic in-memory implementation to illustrate the pattern:

javascript

```javascript
let tasks = [];
let nextId = 1;

app.get('/tasks', (req, res) => {
  res.json(tasks);
});

app.post('/tasks', (req, res) => {
  const { title } = req.body;
  if (!title) return res.status(400).json({ error: 'Title is required' });

  const task = { id: nextId++, title, completed: false };
  tasks.push(task);
  res.status(201).json(task);
});

app.put('/tasks/: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' });

  Object.assign(task, req.body);
  res.json(task);
});

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

## Step 5: Connect a Database

In-memory data disappears every time the server restarts, so real APIs need persistent storage. Popular choices include:

*   **MongoDB** with Mongoose (flexible, document-based, great for rapid development)
    
*   **PostgreSQL** or **MySQL** with an ORM like Sequelize or Prisma (structured, relational data)
    

For example, connecting Mongoose to MongoDB:

javascript

```javascript
const mongoose = require('mongoose');
mongoose.connect(process.env.MONGO_URI);

const taskSchema = new mongoose.Schema({
  title: String,
  completed: { type: Boolean, default: false }
});

const Task = mongoose.model('Task', taskSchema);
```

You would then replace the in-memory array logic with Mongoose queries like `Task.find()`, `Task.create()`, and `Task.findByIdAndUpdate()`.

## Step 6: Add Validation, Error Handling, and Middleware

Production-ready APIs need more than working routes. Add:

*   **Input validation** using a library like `joi` or `express-validator` to catch bad requests early
    
*   **Centralized error handling** with an Express error-handling middleware so failures return consistent JSON responses
    
*   **Authentication** using JWTs or sessions if the API needs protected routes
    
*   **Rate limiting** with `express-rate-limit` to prevent abuse
    

## Step 7: Test and Document the API

<p>
Use tools like <strong>Postman</strong>, <strong>Insomnia</strong>, or <code>curl</code> to manually test your API endpoints. For automated testing, <code>Jest</code> combined with <code>supertest</code> allows you to write test cases that start your Express application and verify responses. You can also document your API using <strong>Swagger/OpenAPI</strong>, making it easier for other <a href="https://themanhattanweekly.com/how-to-turn-on-chrome-os-developer-mode/" title="How to Turn on Chrome OS Developer Mode" rel="dofollow">developers</a> (or your future self) to understand, test, and integrate with your REST API.
</p>

## Final Thoughts

Building a REST API with Node.js comes down to a repeatable pattern: set up Express, define resource-based routes, connect a database, and layer in validation, error handling, and security. Once you're comfortable with this flow, you can extend it with authentication, pagination, filtering, and versioning to support production-grade applications.
