What You'll Build
By the end of this tutorial, you'll have a working REST API built with Node.js and Express that handles basic CRUD-style routes and returns JSON. This is the foundation for most backend web services and a critical skill for any full-stack developer.
Prerequisites
- Node.js installed (v18 or later recommended)
- Basic familiarity with JavaScript
- A terminal and a code editor (VS Code works great)
Step 1: Initialize Your Project
Create a new directory and initialize a Node project:
mkdir my-api
cd my-api
npm init -y
Then install Express:
npm install express
Step 2: Create Your Entry Point
Create a file called index.js in your project root:
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 running on http://localhost:${PORT}`);
});
Run it with node index.js and visit http://localhost:3000 in your browser or a tool like Postman. You should see your JSON response.
Step 3: Add Resource Routes
Let's build routes for a simple tasks resource. Add this to your index.js:
let tasks = [
{ id: 1, title: 'Write documentation', done: false },
{ id: 2, title: 'Review pull request', done: true },
];
// GET all tasks
app.get('/tasks', (req, res) => {
res.json(tasks);
});
// GET a single task
app.get('/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' });
res.json(task);
});
// POST a new task
app.post('/tasks', (req, res) => {
const newTask = {
id: tasks.length + 1,
title: req.body.title,
done: false,
};
tasks.push(newTask);
res.status(201).json(newTask);
});
// DELETE a task
app.delete('/tasks/:id', (req, res) => {
tasks = tasks.filter(t => t.id !== parseInt(req.params.id));
res.status(204).send();
});
Step 4: Test Your API
Use a tool like Postman, Insomnia, or the curl command to test each route:
GET /tasks— returns all tasksGET /tasks/1— returns task with ID 1POST /taskswith body{"title": "New task"}— creates a taskDELETE /tasks/1— removes task with ID 1
Next Steps
This in-memory implementation is a great starting point, but a real API needs persistence. From here, consider:
- Connecting to a database (MongoDB with Mongoose, or PostgreSQL with Prisma)
- Adding input validation with a library like
zodorjoi - Implementing authentication with JWT
- Structuring your app with separate route and controller files
Every production API started as something this simple. Build it, break it, and improve it step by step.