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 tasks
  • GET /tasks/1 — returns task with ID 1
  • POST /tasks with body {"title": "New task"} — creates a task
  • DELETE /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:

  1. Connecting to a database (MongoDB with Mongoose, or PostgreSQL with Prisma)
  2. Adding input validation with a library like zod or joi
  3. Implementing authentication with JWT
  4. 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.