Back

Backend API Engineering

3 BACKEND API

Express Architecture

๐Ÿš‚ Express.js Architecture: Building the Backend

Node.js's native http module is incredibly powerful, but writing a raw HTTP server from scratch is tedious. You would have to manually parse URL strings, manually slice TCP byte streams to read JSON bodies, and write 50-line if/else ladders to route traffic.

Express.js abstracts all of this pain away. It is a minimalist web framework for Node.js. It remains the absolute industry standard for building REST APIs.


1๏ธโƒฃ Setting Up an Express Server

Building a web server capable of handling millions of requests takes about 7 lines of code in Express.

npm install express
import express from 'interface';

// 1. Instantiate the Express application
const app = express();
const PORT = 3000;

// 2. Define a Route (When a GET request hits the root '/')
app.get('/', (req, res) => {
    // 3. Send a response back to the client
    res.send('Welcome to the Mwenaro API');
});

// 4. Open the port and start listening
app.listen(PORT, () => {
    console.log(`๐Ÿš€ Server running on http://localhost:${PORT}`);
});

2๏ธโƒฃ Routing and HTTP Methods

REST APIs map operations (Create, Read, Update, Delete) directly to HTTP Methods. Express makes this beautifully semantical.

// ๐ŸŸข READ (Fetch a list of users)
app.get('/api/users', (req, res) => {
    res.json({ status: "success", data: [{ id: 1, name: "Mwero" }] });
});

// ๐ŸŸก CREATE (Add a new user to the database)
app.post('/api/users', (req, res) => {
    res.status(201).json({ status: "created" });
});

// ๐Ÿ”ต UPDATE (Change an existing user's data)
app.put('/api/users/1', (req, res) => {
    res.json({ status: "updated" });
});

// ๐Ÿ”ด DELETE (Remove a user)
app.delete('/api/users/1', (req, res) => {
    // 204 means successful deletion, no content to return
    res.status(204).send(); 
});

3๏ธโƒฃ The Middleware Architecture (The Onion Pattern)

This is the most critical concept in Express. Middleware functions are layers of an onion.

When a request hits your server, it doesn't instantly go to the final route. It travels linearly through a chain of Middleware functions. Each middleware can inspect the request, modify it, or reject it entirely.

Writing Custom Middleware

A middleware function takes three arguments: req (Request), res (Response), and next. If you do not call next(), the request freezes forever and the client times out.

// 1. Defining the Middleware
const loggerMiddleware = (req, res, next) => {
    console.log(`[${new Date().toISOString()}] ${req.method} request to ${req.url}`);
    
    // Pass control to the NEXT layer of the onion
    next(); 
};

// 2. Applying it globally (Affects every single route)
app.use(loggerMiddleware);

Route-Specific Middleware

Sometimes you only want a middleware to protect one specific route.

const requireAdmin = (req, res, next) => {
    if (req.headers.authorization === 'SecretAdminCode') {
        next(); // Proceed to the route
    } else {
        res.status(403).json({ error: "Access Denied" }); // Reject instantly
    }
};

// The 'requireAdmin' shield is placed squarely in front of the final callback
app.delete('/api/database', requireAdmin, (req, res) => {
    res.send("Database wiped.");
});

4๏ธโƒฃ Essential Built-in Third-Party Middlewares

Out of the box, Express is completely naked. It doesn't even know how to read JSON data sent by a React frontend. You must plug these tools in globally:

import express from 'express';
import cors from 'cors';

const app = express();

// 1. JSON Body Parser (MANDATORY)
// Without this, req.body will be undefined. This parses incoming JSON payload streams.
app.use(express.json()); 

// 2. URL-Encoded Parser
// Allows Express to read data sent from old-school HTML <form> tags.
app.use(express.urlencoded({ extended: true }));

// 3. CORS (Cross-Origin Resource Sharing)
// Browsers block React (running on port 3000) from talking to Express (port 5000) for security.
// This middleware mathematically tells the browser: "It's fine, I allow port 3000 to talk to me."
app.use(cors({ origin: 'http://localhost:3000' }));

5๏ธโƒฃ The Request (req) and Response (res) Objects

Express provides powerful utility methods on these objects.

Extracting Data from the Request (req)

app.post('/api/users/:userId', (req, res) => {
    // 1. req.params: Extracts URL Path variables (e.g. /users/99)
    const id = req.params.userId; // "99"

    // 2. req.query: Extracts URL Query Strings (e.g. ?sort=asc&limit=10)
    const limit = req.query.limit; // "10"

    // 3. req.body: Extracts the actual JSON payload attached by React 
    // (Requires app.use(express.json()) to work!)
    const payload = req.body; // { name: "Mwero", role: "Dev" }
});

Sending Data via the Response (res)

app.get('/api/data', (req, res) => {
    // Send a string (Content-Type: text/html)
    res.send("<html>Hello</html>");

    // Send a precise JSON object (Content-Type: application/json)
    res.json({ success: true, count: 5 });

    // Chain status codes
    res.status(404).json({ error: "File not found" });

    // Send a physical file download to the user
    res.download('/path/to/invoice.pdf');
});

๐Ÿ’ก Summary Checklist for Express Services

  • Initialize const app = express().
  • Globally attach app.use(express.json()) and app.use(cors()).
  • Conceptually group your routes using the correct HTTP methods (GET, POST, PUT, DELETE).
  • Understand that req.params, req.query, and req.body extract different types of data.
  • Protect sensitive routes using a custom Middleware function that conditionally calls next().

Knowledge Check

0 of 3 Attempts Used

Complete this quick quiz to verify your understanding and unlock the next module.

Max attempts (3) reached. You have been allowed to proceed.

Ready to master Backend API Engineering?

Enroll now to get full access to all 7 modules, guided project reviews, and a 3-month free internship at Mwenaro Labs.