Node.js allows you to run JavaScript on the server. Express is the most popular, minimal web framework for Node.js. Together, they make it incredibly fast to build robust REST APIs.


Step 1 — Initializing a Node.js Project

First, you need to create a project directory and initialize it with npm (Node Package Manager).

Terminalbash
mkdir my-express-api
cd my-express-api

# Initialize package.json
npm init -y

# Install Express and a development dependency (nodemon) for auto-restarts
npm install express
npm install --save-dev nodemon

Step 2 — Building Your First Server

Let's create a server.js file and set up a basic Express app that listens on a port.

server.jsjavascript
const express = require('express');
const app = express();

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

// Basic GET route
app.get('/', (req, res) => {
  res.send('Hello, World! Express server is running.');
});

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

Step 3 — Express Middleware

Middleware functions have access to the request object (req), the response object (res), and the next middleware function (next). They form the core of Express architecture.

middleware.jsjavascript
// Built-in middleware to parse incoming JSON payloads (e.g. POST request bodies)
app.use(express.json());

// Custom logging middleware
app.use((req, res, next) => {
  console.log(`${req.method} request to ${req.url}`);
  next(); // Pass control to the next middleware/route handler
});

Types of Middleware

  • Application-level: Runs for every request (e.g., logging, CORS).
  • Router-level: Bound to specific routes (e.g., authentication checks).
  • Error-handling: Has 4 arguments (err, req, res, next) and catches unhandled exceptions.

Step 4 — Routing & Request Parameters

Express routing maps HTTP methods and URLs to specific handler functions.

routes.jsjavascript
const users = [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }];

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

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

// Query Parameters
// Example: /api/search?q=express
app.get('/api/search', (req, res) => {
  const query = req.query.q;
  res.json({ searchResultFor: query });
});

Step 5 — Handling POST Requests

To accept data from clients (like forms or JSON payloads), you handle POST requests. Remember to use express.json() middleware first!

post.jsjavascript
app.post('/api/users', (req, res) => {
  const newUser = req.body;
  
  // Basic validation
  if (!newUser.name) {
    return res.status(400).json({ error: 'Name is required' });
  }
  
  newUser.id = users.length + 1;
  users.push(newUser);
  
  // Return 201 Created and the new object
  res.status(201).json(newUser);
});

Step 6 — Centralized Error Handling

Instead of returning errors manually everywhere, use Express's centralized error-handling middleware. This must be the very last app.use() in your file.

error-handler.jsjavascript
// A route that throws an error
app.get('/api/broken', (req, res, next) => {
  const error = new Error('Database connection failed');
  error.status = 500;
  next(error); // Passes the error to the error handler
});

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