>_>_
Creating Your Own ExpressJS from Scratch (Part 3) - Handling Request and Response Objects

Creating Your Own ExpressJS from Scratch (Part 3) - Handling Request and Response Objects

In Part 2, we implemented middleware chaining to allow multiple functions to be executed per route. Now, in Part 3, we’ll improve the developer experience by decorating the Request and Response objects.

We'll extract route parameters and query strings, and define helper methods like res.status() and res.json() — just like in ExpressJS.


🧪 What You Will Learn

  • Use decorators to enhance the req and res objects
  • Extract route parameters and query strings from incoming requests
  • Create convenient response helpers like .status() and .json()

🏗️ Request Decorator

The request decorator adds params and query properties to the request object.

src/request.js

const { match } = require('path-to-regexp');

const RequestDecorator = (routes, req, res, next) => {
  const getParams = () => {
    const urlParts = req.url.split('/').slice(1);
    const [lastPart] = urlParts[urlParts.length - 1].split('?');
    urlParts.splice(urlParts.length - 1, 1);
    const fullPath = [...urlParts, lastPart].join('/');
    const fullUrl = `/${fullPath}/${req.method.toUpperCase()}`;

    for (const path of routes) {
      const urlMatch = match(path, { decode: decodeURIComponent });
      const found = urlMatch(fullUrl);
      if (found) {
        req.params = { ...req.params, ...found.params };
        break;
      }
    }
  };

  const getQuery = () => {
    const [_, query] = req.url.split('?');
    if (query) {
      const params = new URLSearchParams(query);
      req.query = Object.fromEntries(params.entries());
    }
  };

  getParams();
  getQuery();
  next();
};

module.exports = RequestDecorator;

✅ What This Does:

  • req.params: Extracted from route pattern like /users/:id
  • req.query: Extracted from URL query string like ?sort=desc

🚀 Response Decorator

Let’s define some Express-style helpers on the response object.

src/response.js

const ResponseDecorator = (req, res, next) => {
  res.status = (code) => {
    res.statusCode = code;
    return res;
  };

  res.json = (data) => {
    res.setHeader('Content-type', 'application/json');
    res.end(JSON.stringify(data));
  };

  res.send = (data) => {
    res.end(data);
  };

  res.render = (templatePath, data) => {
    // Future implementation
  };

  next();
};

module.exports = ResponseDecorator;

✅ What This Enables:

  • res.status(200).json({ msg: 'ok' })
  • res.send('plain text response')

🔌 Registering Decorators in the App

Update your src/app.js to apply these decorators automatically.

const requestDecorator = require('./request');
const responseDecorator = require('./response');

Modify the serverHandler:

const serverHandler = async (req, res) => {
  const sanitizedUrl = sanitizeUrl(req.url, req.method);
  const match = matchUrl(sanitizedUrl);

  if (match) {
    const handlers = routes.get(match);
    await dispatchChain(
      req,
      res,
      [
        requestDecorator.bind(null, routes.keys()),
        responseDecorator,
        ...handlers,
      ]
    );
  } else {
    res.statusCode = 404;
    res.end('Not found');
  }
};

Now, every request will be decorated with .params, .query, .json(), and more.


🧪 Testing Parameter and Query Extraction

index.js

app.get('/params/:id/:name', (req, res) => {
  res.json({ params: req.params, query: req.query });
});

app.get('/response/:id', (req, res) => {
  if (req.params.id === '123') {
    return res.status(200).json({ message: 'Valid ID' });
  }
  res.status(400).json({ message: 'Invalid ID' });
});

Test these routes:

GET http://localhost:3000/params/42/john?sort=desc
GET http://localhost:3000/response/123

📚 Summary

  • You created request and response decorators
  • You now support req.params, req.query, and helpers like res.json()
  • These enhancements make your framework feel much more like Express

▶️ Coming Up Next

In Part 4, we’ll add support for dynamic route matching with path variables, and begin thinking about response rendering.

Follow along for the next steps in building your own web framework! 🚀