🚂 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())andapp.use(cors()). - Conceptually group your routes using the correct HTTP methods (
GET,POST,PUT,DELETE). - Understand that
req.params,req.query, andreq.bodyextract different types of data. - Protect sensitive routes using a custom Middleware function that conditionally calls
next().