Back to roadmaps nodejs Course

Custom Routing and Middlewares

In a production web application, handling all server logic inside a single monolithic callback quickly becomes unmaintainable. To resolve this, we can implement custom routers to isolate paths and middleware pipelines to handle cross-cutting concerns (like logging or authentication).


1. Modular Routing Architecture

Let us design a router container that matches incoming request paths and HTTP methods against registered handler functions:

import http from "http";

class SimpleRouter {
  constructor() {
    this.routes = {
      GET: {},
      POST: {}
    };
  }

  get(path, handler) {
    this.routes.GET[path] = handler;
  }

  post(path, handler) {
    this.routes.POST[path] = handler;
  }

  handle(req, res) {
    const method = req.method;
    const path = req.url;
    const handler = this.routes[method] ? this.routes[method][path] : null;

    if (handler) {
      handler(req, res);
    } else {
      res.writeHead(404, { "Content-Type": "text/plain" });
      res.end("Route not found");
    }
  }
}

// Setup and register routes
const router = new SimpleRouter();

router.get("/api/health", (req, res) => {
  res.writeHead(200, { "Content-Type": "application/json" });
  res.end(JSON.stringify({ status: "alive" }));
});

const server = http.createServer((req, res) => router.handle(req, res));
server.listen(3000);

2. Understanding Middleware (The Onion Model)

Middlewares are callback functions that sit between the server receiving a request and sending a response. Each middleware has access to the request object, response object, and a next callback to hand over control to the subsequent handler in line.


3. Implementing a Middleware Engine

Let us write a miniature middleware runner that runs registered tasks sequentially before executing the primary route handler:

import http from "http";

class AppServer {
  constructor() {
    this.middlewares = [];
  }

  // Register middleware
  use(middleware) {
    this.middlewares.push(middleware);
  }

  handle(req, res) {
    let index = 0;

    // Define next function runner
    const next = () => {
      if (index < this.middlewares.length) {
        const middleware = this.middlewares[index];
        index += 1;
        middleware(req, res, next);
      } else {
        // Fallback main route handler
        res.writeHead(200, { "Content-Type": "text/plain" });
        res.end("Final handler reached");
      }
    };

    // Start execution chain
    next();
  }
}

const app = new AppServer();

// Middleware 1: Logger
app.use((req, res, next) => {
  console.log(`[LOG] Request received: ${req.method} ${req.url}`);
  next(); // Pass control to the next middleware
});

// Middleware 2: Basic Auth check
app.use((req, res, next) => {
  const token = req.headers["authorization"];
  if (token === "mysecret") {
    next();
  } else {
    res.writeHead(401, { "Content-Type": "text/plain" });
    res.end("Unauthorized request token");
  }
});

const server = http.createServer((req, res) => app.handle(req, res));
server.listen(3000);
Published on Last updated: