Software Development

Building REST APIs with Node.js: A Practical Guide for Beginners

Learn how to design, build, and deploy production-ready REST APIs using Node.js and Express — covering routing, middleware, authentication, error handling, and best practices.

Meritshot6 min read
Node.jsREST APIExpressBackendJavaScript
Back to Blog

Building REST APIs with Node.js: A Practical Guide for Beginners

If you are building a modern web or mobile application, chances are you need a backend API. REST APIs remain the most widely used approach for client-server communication, and Node.js with Express is one of the fastest ways to build one.

This guide walks you through everything you need to know to build a production-ready REST API from scratch.

What Is a REST API?

REST (Representational State Transfer) is an architectural style for designing networked applications. A REST API exposes data and functionality as resources, each identified by a URL, and uses standard HTTP methods to perform operations on them.

Key principles of REST:

  • Statelessness — Each request from the client contains all the information the server needs. No session state is stored on the server between requests.
  • Resource-based URLs — Endpoints represent resources (nouns), not actions (verbs). Use /users instead of /getUsers.
  • Standard HTTP methods — Use GET, POST, PUT, PATCH, and DELETE for CRUD operations.
  • JSON responses — Modern REST APIs almost always use JSON as the data format.

REST API architecture showing the request-response flow from client through Express server to route handler and back

HTTP Methods Explained

MethodPurposeExample EndpointBody?
GETRead/retrieve a resourceGET /api/usersNo
POSTCreate a new resourcePOST /api/usersYes
PUTReplace an entire resourcePUT /api/users/5Yes
PATCHUpdate part of a resourcePATCH /api/users/5Yes
DELETERemove a resourceDELETE /api/users/5No

Important: GET requests should never modify data. POST is for creation. PUT replaces the entire resource while PATCH updates only the specified fields.

Visual comparison of HTTP methods GET, POST, PUT, and DELETE with their purposes, example endpoints, and properties

Setting Up Your Project

Start by initializing a new Node.js project and installing Express:

mkdir my-api && cd my-api
npm init -y
npm install express dotenv cors helmet
npm install -D nodemon

Create a basic server in server.js:

const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
require('dotenv').config();

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

// Middleware
app.use(helmet());
app.use(cors());
app.use(express.json());

// Routes
app.get('/api/health', (req, res) => {
  res.json({ status: 'ok', timestamp: new Date().toISOString() });
});

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

Routing and Controllers

As your API grows, you should separate routes into their own files. A clean project structure looks like this:

my-api/
  src/
    controllers/
      userController.js
    middleware/
      auth.js
      errorHandler.js
    routes/
      userRoutes.js
    models/
      User.js
    utils/
      validators.js
  server.js
  .env
  package.json

Here is an example route file for users:

// src/routes/userRoutes.js
const express = require('express');
const router = express.Router();
const { getUsers, getUserById, createUser, updateUser, deleteUser }
  = require('../controllers/userController');

router.get('/', getUsers);
router.get('/:id', getUserById);
router.post('/', createUser);
router.put('/:id', updateUser);
router.delete('/:id', deleteUser);

module.exports = router;

Middleware: The Secret Weapon of Express

Middleware functions execute during the request-response cycle. They can modify the request, send a response, or pass control to the next middleware.

Common uses of middleware:

  • Authentication — Verify JWT tokens before allowing access to protected routes
  • Logging — Log every incoming request for debugging and monitoring
  • Validation — Check that request bodies contain required fields
  • Error handling — Catch and format errors consistently
// src/middleware/auth.js
const jwt = require('jsonwebtoken');

const authenticate = (req, res, next) => {
  const token = req.header('Authorization')?.replace('Bearer ', '');
  if (!token) {
    return res.status(401).json({ error: 'Access denied. No token provided.' });
  }
  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    req.user = decoded;
    next();
  } catch (err) {
    res.status(403).json({ error: 'Invalid or expired token.' });
  }
};

module.exports = authenticate;

Request Validation

Never trust incoming data. Always validate request bodies before processing them. A popular library for this is Joi:

const Joi = require('joi');

const userSchema = Joi.object({
  name: Joi.string().min(2).max(100).required(),
  email: Joi.string().email().required(),
  password: Joi.string().min(8).required(),
  role: Joi.string().valid('student', 'instructor', 'admin').default('student')
});

Validation prevents malformed data from reaching your database and protects against injection attacks.

Error Handling

A centralized error handler keeps your code clean and your API responses consistent:

// src/middleware/errorHandler.js
const errorHandler = (err, req, res, next) => {
  console.error(err.stack);
  const status = err.statusCode || 500;
  res.status(status).json({
    error: {
      message: err.message || 'Internal Server Error',
      status,
    }
  });
};

module.exports = errorHandler;

Always return proper HTTP status codes: 200 for success, 201 for created, 400 for bad request, 401 for unauthorized, 404 for not found, and 500 for server errors.

Authentication with JWT

JSON Web Tokens are the standard for stateless API authentication. The flow works like this:

  1. User sends login credentials (email and password) to POST /api/auth/login
  2. Server verifies credentials and returns a signed JWT
  3. Client stores the token and sends it in the Authorization header with every subsequent request
  4. Server middleware verifies the token on protected routes

Security tip: Store JWTs in httpOnly cookies for web applications, not in localStorage. Set short expiration times (15-30 minutes) and use refresh tokens for longer sessions.

Best Practices for Production APIs

  • Use environment variables for secrets, database URLs, and configuration. Never hardcode them.
  • Implement rate limiting using express-rate-limit to prevent abuse.
  • Add request logging with libraries like morgan or winston.
  • Version your API — prefix all routes with /api/v1/ so you can introduce breaking changes safely.
  • Use HTTPS in production. Tools like Let's Encrypt make this free.
  • Write tests — Use Jest and Supertest to test your endpoints automatically.
  • Document your API — Swagger (OpenAPI) generates interactive documentation from your code.

What to Build Next

The best way to learn is to build a real project. Here are some ideas:

  • Task Manager API — CRUD for tasks with user authentication and categories
  • Blog API — Posts, comments, and user roles (author, editor, admin)
  • E-commerce API — Products, cart, orders, and payment integration

Each of these will force you to deal with relationships between resources, authentication, pagination, and error handling — all skills that are essential for backend roles.

Final Thoughts

Node.js and Express give you a fast, flexible, and well-supported foundation for building REST APIs. The ecosystem is mature, the community is massive, and the job market for Node.js developers in India continues to grow.

Start with the basics, follow the project structure outlined here, and gradually add complexity. Every production API you encounter in your career will use these same patterns.