Tutorials

How to Build Your First REST API with Node.js in 2026 (Complete Guide)

Building a REST API sounds intimidating. It's not. This step-by-step guide will take you from zero to a working API with Node.js, Express, and real endpoints in under an hour.

S
Stackpulse Team
··...·6 min read
API documentation and Node.js code on a laptop screen

Every backend developer remembers their first REST API.

Mine was terrible. It worked, sort of, in the same way that a car with no brakes technically gets you moving.

This guide will help you build one that actually holds up — with proper error handling, a clean structure, and code you'll understand when you look at it a week later.

Let's go from zero to working API in about an hour.

What We're Building

A simple but complete REST API for a blog — posts you can create, read, update, and delete (CRUD). By the end, you'll have:

  • Working endpoints: GET /posts, GET /posts/:id, POST /posts, PUT /posts/:id, DELETE /posts/:id
  • Proper error handling
  • Request validation
  • Clean, reusable code structure

Prerequisites

  • Node.js installed (v18+) — download from nodejs.org
  • Basic JavaScript knowledge (you know what a function and object are)
  • A terminal and a code editor

That's genuinely all you need.

Step 1: Set Up the Project

Open your terminal and run:

mkdir my-first-api
cd my-first-api
npm init -y
npm install express
npm install --save-dev nodemon

Now open package.json and add this to the scripts section:

"scripts": {
  "start": "node server.js",
  "dev": "nodemon server.js"
}

nodemon automatically restarts your server when you change a file — essential for development.

Step 2: Create the Server

Create a file called server.js in your project folder:

const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;

// Middleware — tells Express to parse JSON request bodies
app.use(express.json());

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

app.listen(PORT, () => {
  console.log(`Server running on http://localhost:${PORT}`);
});

Run it:

npm run dev

Open your browser and go to http://localhost:3000. You should see:

{ "message": "API is running! 🚀" }

Congratulations. You just built your first API endpoint.

Step 3: Set Up Our Data (In-Memory for Now)

In a real app you'd use a database. For this tutorial, we'll use a JavaScript array — this lets us focus on the API structure without database setup getting in the way.

Add this above your routes in server.js:

// Our "database" — an array of post objects
let posts = [
  {
    id: 1,
    title: "My First Blog Post",
    content: "This is the content of my first post.",
    author: "Stackpulse",
    createdAt: new Date().toISOString()
  },
  {
    id: 2,
    title: "How AI is Changing Development",
    content: "AI tools are reshaping how we write code...",
    author: "Stackpulse",
    createdAt: new Date().toISOString()
  }
];

let nextId = 3; // Simple auto-increment

Step 4: GET All Posts

// GET /posts — return all posts
app.get('/posts', (req, res) => {
  res.json({
    success: true,
    count: posts.length,
    data: posts
  });
});

Test it: visit http://localhost:3000/posts. You'll see both posts returned as JSON.

Step 5: GET a Single Post

// GET /posts/:id — return one post by ID
app.get('/posts/:id', (req, res) => {
  const id = parseInt(req.params.id);
  const post = posts.find(p => p.id === id);

  if (!post) {
    return res.status(404).json({
      success: false,
      error: `Post with id ${id} not found`
    });
  }

  res.json({ success: true, data: post });
});

Try http://localhost:3000/posts/1 and http://localhost:3000/posts/999. See the difference in the response.

Step 6: POST — Create a New Post

This is where it gets more interesting. To test this, you need a tool that can send POST requests — use Postman, Insomnia, or the curl command.

// POST /posts — create a new post
app.post('/posts', (req, res) => {
  const { title, content, author } = req.body;

  // Basic validation
  if (!title || !content) {
    return res.status(400).json({
      success: false,
      error: 'Title and content are required'
    });
  }

  const newPost = {
    id: nextId++,
    title,
    content,
    author: author || 'Anonymous',
    createdAt: new Date().toISOString()
  };

  posts.push(newPost);

  res.status(201).json({
    success: true,
    data: newPost
  });
});

Test with curl:

curl -X POST http://localhost:3000/posts \
  -H "Content-Type: application/json" \
  -d '{"title": "New Post", "content": "Hello world!"}'

Step 7: PUT — Update a Post

// PUT /posts/:id — update an existing post
app.put('/posts/:id', (req, res) => {
  const id = parseInt(req.params.id);
  const postIndex = posts.findIndex(p => p.id === id);

  if (postIndex === -1) {
    return res.status(404).json({
      success: false,
      error: `Post with id ${id} not found`
    });
  }

  const { title, content, author } = req.body;

  // Update only the fields that were sent
  posts[postIndex] = {
    ...posts[postIndex],
    ...(title && { title }),
    ...(content && { content }),
    ...(author && { author }),
    updatedAt: new Date().toISOString()
  };

  res.json({ success: true, data: posts[postIndex] });
});

Step 8: DELETE — Remove a Post

// DELETE /posts/:id — delete a post
app.delete('/posts/:id', (req, res) => {
  const id = parseInt(req.params.id);
  const postIndex = posts.findIndex(p => p.id === id);

  if (postIndex === -1) {
    return res.status(404).json({
      success: false,
      error: `Post with id ${id} not found`
    });
  }

  posts.splice(postIndex, 1);

  res.json({
    success: true,
    message: `Post ${id} deleted successfully`
  });
});

Step 9: Handle Unknown Routes

Add this at the bottom, before app.listen:

// Handle routes that don't exist
app.use((req, res) => {
  res.status(404).json({
    success: false,
    error: `Route ${req.method} ${req.url} not found`
  });
});

Your Complete API — What You've Built

You now have a fully working REST API with:

  • ✅ 5 endpoints covering all CRUD operations
  • ✅ Proper HTTP status codes (200, 201, 400, 404)
  • ✅ Consistent JSON response format
  • ✅ Input validation
  • ✅ Useful error messages

What's Next?

This API works, but it's not production-ready. Here's what to add next:

1. A real database — Replace the array with MongoDB (using Mongoose) or PostgreSQL (using Prisma). Both have great Node.js libraries.

2. Authentication — Add JWT (JSON Web Tokens) so only logged-in users can create, update, or delete posts.

3. Environment variables — Never hardcode secrets. Use dotenv to manage your PORT, database URL, and API keys.

4. Rate limiting — Protect your API from abuse with express-rate-limit.

5. Deployment — Deploy to Railway, Render, or Fly.io — all have free tiers and take under 10 minutes to set up.

The hardest part of building your first API isn't the code — it's getting over the idea that it's complicated. It's not. You just built one.

What are you going to build with it? Let me know in the comments.

Related Articles