Camkode
Camkode

Building RESTful APIs with Node.js and Express in 2025

Posted by Kosal

Building RESTful APIs with Node.js and Express in 2025

In 2025, building efficient and scalable RESTful APIs remains a cornerstone of modern web and mobile application development. Among the many tools available, Node.js and Express continue to stand out for their simplicity, speed, and vast ecosystem. Whether you're a seasoned backend engineer or just stepping into API development, this guide will walk you through creating a robust RESTful API using the latest patterns and best practices.

Why Node.js and Express in 2025?

Node.js, powered by V8, is still one of the fastest JavaScript runtimes available. Its event-driven, non-blocking I/O model makes it ideal for building APIs that can handle thousands of simultaneous connections.

Express remains the most popular framework for creating web APIs with Node.js. It offers:

  • Minimal setup
  • Middleware support
  • A huge ecosystem
  • Integration with modern tools (TypeScript, ESM, Docker, etc.)

Step 1: Initialize Your Project

mkdir node-api-2025
cd node-api-2025
npm init -y

Install dependencies:

npm install express
npm install --save-dev nodemon

Update your package.json to include a start script:

"scripts": {
  "start": "nodemon index.js"
}

Step 2: Create a Basic Server

Create index.js:

const express = require('express');
const app = express();

app.use(express.json()); // To parse JSON bodies

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

app.get('/', (req, res) => {
  res.send('Welcome to the User API!');
});

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

Run the server:

npm start

When you run npm start, which launches the Express server, navigating to http://localhost:3000 will respond with:

Welcome to the User API!

Step 3: Create User Resource with Full CRUD

We'll now implement full CRUD (Create, Read, Update, Delete) operations for managing users. For simplicity, this example uses an in-memory array instead of a real database.

Here’s the sample user object structure:

{
  id: 1,
  name: "Kosal Ang",
  email: "angkosal@example.com"
}

Start by defining a mock database:

let users = [
  { id: 1, name: 'Bora Ang', email: 'bora@example.com' },
  { id: 2, name: 'Sakni Ang', email: 'sakni@example.com' }
];

3.1 Get All Users

Purpose: Retrieve a list of all users.

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

This endpoint returns the entire list of users. Useful for dashboards, admin panels, or lists in your frontend application.

3.2 Get Single User by ID

Purpose: Retrieve a specific user by their id.

app.get('/api/users/:id', (req, res) => {
  const user = users.find(u => u.id == req.params.id);
  if (!user) return res.status(404).json({ message: 'User not found' });
  res.json(user);
});

This endpoint is useful when viewing a specific user's profile or editing their details. It checks whether a user with the provided id exists and returns a 404 error if not.

3.3 Create a New User

Purpose: Add a new user to the system.

app.post('/api/users', (req, res) => {
  const { name, email } = req.body;

  if (!name || !email) {
    return res.status(400).json({ message: 'Name and email are required' });
  }

  const newUser = {
    id: users.length ? users[users.length - 1].id + 1 : 1,
    name,
    email
  };

  users.push(newUser);
  res.status(201).json(newUser);
});

This endpoint handles user creation. It validates input and assigns a unique id to each new user.

3.4 Update an Existing User

Purpose: Modify the name or email of an existing user.

app.put('/api/users/:id', (req, res) => {
  const user = users.find(u => u.id == req.params.id);
  if (!user) return res.status(404).json({ message: 'User not found' });

  const { name, email } = req.body;
  user.name = name ?? user.name;
  user.email = email ?? user.email;

  res.json(user);
});

Use this endpoint when users need to update their profile information. If the user doesn’t exist, it returns a 404. Only updates fields that are provided.

3.5 Delete a User

Purpose: Permanently remove a user from the system.

app.delete('/api/users/:id', (req, res) => {
  const userIndex = users.findIndex(u => u.id == req.params.id);
  if (userIndex === -1) return res.status(404).json({ message: 'User not found' });

  users.splice(userIndex, 1);
  res.status(204).send();
});

This endpoint is used to delete users who are no longer needed in the system. It ensures that the user exists before attempting to remove them.

Step 4: Test Your API

Use Postman or curl to test your endpoints:

http://localhost:3000/api/users

You should get the following JSON response:

[
    {
        "id": 1,
        "name": "Bora Ang",
        "email": "bora@example.com"
    },
    {
        "id": 2,
        "name": "Sakni Ang",
        "email": "sakni@example.com"
    }
]

Or test each CRUD operation using appropriate HTTP methods like GET, POST, PUT, and DELETE.

Step 5: Add Middleware and Error Handling

Add centralized error handling middleware at the bottom of index.js:

app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).json({ message: 'Internal Server Error' });
});

Logging middleware:

app.use((req, res, next) => {
  console.log(`${req.method} ${req.path}`);
  next();
});

Step 6: Optional Improvements

Enhance your API further with:

  • TypeScript integration
  • Connecting to a real database (MongoDB, PostgreSQL)
  • JWT-based Authentication
  • Docker containerization
  • Swagger (OpenAPI) docs

Final Thoughts

Building RESTful APIs with Node.js and Express in 2025 is as practical and powerful as ever. With minimal setup and rich extensibility, Express is a solid choice for both quick prototypes and production-grade APIs.