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

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