Skip to main content

Command Palette

Search for a command to run...

Building a Mini Express from Scratch

Updated
3 min read

1. Why Build Express From Scratch?

Ever wondered what's actually happening under the hood when you type app.get()? Express feels like magic because it is abstraction done right. It hides the messy parts of handling HTTP requests so we can focus on shipping.

Building a mini Express teaches what frameworks actually do. You'll understand routing, middleware chains, and why abstraction layers matter. Think of it this way a framework is just structured orchestration sitting on top of http.createServer.

2. Designing the Mini Framework API

const app = miniExpress();

app.get("/users", (req, res) => { res.send("Users"); });

app.listen(3000);

We're building an abstraction layer that feels like Express, our own mini express.

3. Routing System

The router is just a registry that matches incoming requests to handlers -

const routes = [];

app.get = (path, handler) => {
  routes.push({ method: "GET", path, handler });
};

// when the request comes in 

const match = routes.find(
  r => r.method === req.method && r.path === req.url
);

Simple O(n) lookup. Express uses fancy trie structures for speed. For learning purposes, a loop works fine. The concept match method and path, then execute is what matters.

4. Middleware System

This is where things get interesting. Middleware is just a function that runs before your route handler, with the power to continue or stop the chain.

const middlewares = [];

app.use = (fn) => {
  middlewares.push(fn);
};

//
//
//

let i = 0;

function next() {
  const middleware = middlewares[i++];
  if (middleware) {
    middleware(req, res, next);
  }
}

Each middleware calls next() to pass control to the next one. This creates a linear execution chain where any middleware can modify the request, send a response early, or pass it along. It's control flow inversion, instead of you calling functions, the framework calls you and asks "what next?"

5. Enhancing responses (res)

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

Frameworks enhance primitives. We can add anything in our mini express. i.e. res.json, res.status, whatever solves our purpose.

6. Order of Execution

app.use(authMiddleware);
app.get("/profile", handler);

The auth check runs first. If it fails, the profile handler never sees the request. Route matching typically happens after global middleware. That's a design decision Express made, and it affects how you structure apps. Order is architecture.

7. Handling 404 and Fallback

What happens when no route matches? Lets see what can we do here

if (!match) {
  res.statusCode = 404;
  res.end("Not Found");
}

Express has a default 404 handler, but in production we usually override it. Defining fallback behavior is part of framework design.

8. Handling Errors

if (err) {
  errorHandler(err, req, res);
}

Error middleware takes four arguments (err, req, res, next) and centralizes failure handling. Instead of try-catch blocks scattered everywhere, you bring all errors to one place.

9. Comparing to Real Express Internals

Oue mini express works, but real Express has more moving parts. It uses the connect middleware engine internally, with a Layer abstraction that wraps each middleware. The Router object handles nested routes and params.

10. Production Limitations of Your Mini Framework

Here are somethings that we did’nt really touch upon in this one, you try some of them on your own, it would be a fun ride

  • No async error handling try-catch won't catch errors in async middleware

  • No route params /users/:id won't work, just exact matches

  • No body parsing req.body is undefined; you'd need to stream and parse

  • No query parsing ?foo=bar stays a string

  • No performance optimization that O(n) route lookup breaks at scale

  • No security hardening no helmet, no cors, no protection

My only goal with this one was to convey that frameworks aren’t magic, Its just someone’s organized code, and express has really organised our way of writing code.

T

Building frameworks from scratch is the best way to truly understand them. Once you see how middleware chaining and routing work under the hood, you never look at Express the same way again. Great exercise!