URL Routing

Pattern matching and route parameters in Node.js

URL routing maps incoming requests to handler functions based on the URL path and HTTP method. This is a fundamental pattern in web servers and APIs.

This example shows a minimal router implementation using vanilla Node.js, without any framework dependencies.

Basic Router Class

// router.js - Minimal router implementation export class Router { constructor() { this.routes = []; } // Register route methods get(path, handler) { this.routes.push({ method: 'GET', path, handler }); } post(path, handler) { this.routes.push({ method: 'POST', path, handler }); } put(path, handler) { this.routes.push({ method: 'PUT', path, handler }); } delete(path, handler) { this.routes.push({ method: 'DELETE', path, handler }); } // Convert route pattern to regex pathToRegex(path) { // Convert :param to named capture groups const pattern = path .replace(/:\w+/g, '([^/]+)') // :id → capture group .replace(/\//g, '\\/'); // escape slashes return new RegExp(`^${pattern}$`); } // Extract param names from path getParamNames(path) { const matches = path.match(/:\w+/g) || []; return matches.map(m => m.slice(1)); // Remove : } // Find matching route and extract params match(method, url) { for (const route of this.routes) { if (route.method !== method) continue; const regex = this.pathToRegex(route.path); const match = url.match(regex); if (match) { const paramNames = this.getParamNames(route.path); const params = {}; paramNames.forEach((name, i) => { params[name] = match[i + 1]; }); return { handler: route.handler, params }; } } return null; } }

Using the Router

import { createServer } from 'node:http'; import { Router } from './router.js'; const router = new Router(); // Define routes router.get('/', (req, res) => { res.json({ message: 'Welcome to the API' }); }); router.get('/users', (req, res) => { res.json([ { id: 1, name: 'Alice' }, { id: 2, name: 'Bob' } ]); }); router.get('/users/:id', (req, res, params) => { const user = findUser(params.id); if (!user) { res.statusCode = 404; res.json({ error: 'User not found' }); return; } res.json(user); }); router.get('/users/:userId/posts/:postId', (req, res, params) => { // params = { userId: '123', postId: '456' } res.json({ userId: params.userId, postId: params.postId }); }); router.post('/users', async (req, res) => { const body = await parseJSON(req); const user = createUser(body); res.statusCode = 201; res.json(user); }); router.delete('/users/:id', (req, res, params) => { deleteUser(params.id); res.statusCode = 204; res.end(); });

Server Setup with Helpers

// Helper: Parse JSON body function parseJSON(req) { return new Promise((resolve, reject) => { let body = ''; req.on('data', chunk => body += chunk); req.on('end', () => { try { resolve(body ? JSON.parse(body) : {}); } catch (e) { reject(new Error('Invalid JSON')); } }); }); } // Helper: Parse query string function parseQuery(url) { const queryString = url.split('?')[1] || ''; return Object.fromEntries(new URLSearchParams(queryString)); } // Create server const server = createServer(async (req, res) => { // Add json helper to response res.json = (data) => { res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify(data)); }; // Parse URL const [pathname] = req.url.split('?'); req.query = parseQuery(req.url); // Find matching route const match = router.match(req.method, pathname); if (match) { try { await match.handler(req, res, match.params); } catch (error) { res.statusCode = 500; res.json({ error: error.message }); } } else { // 404 Not Found res.statusCode = 404; res.json({ error: 'Not Found' }); } }); server.listen(3000, () => { console.log('Server running on http://localhost:3000'); });

Route patterns supported:

  • /users - Exact match
  • /users/:id - Single parameter
  • /users/:userId/posts/:postId - Multiple parameters

Query String Parameters

// URL: /users?page=2&limit=10&sort=name router.get('/users', (req, res) => { const { page = 1, limit = 10, sort } = req.query; const users = getUsers({ offset: (page - 1) * limit, limit: parseInt(limit), sortBy: sort }); res.json({ data: users, pagination: { page: parseInt(page), limit: parseInt(limit), total: getTotalUsers() } }); });