How Express Routing Works

Shay Pepper
4 min readNov 12, 2022
Image by tawatchai07 on Freepik

Express is a very popular Node.js application framework that makes developing and maintaining applications a way easier task. Express lets developers quickly create route handlers based on HTTP methods and URLs. This article describes how express routing works behind the scenes.

Hello World Example

Let’s look at a basic example of an express app to get started.

The following code will create a new HTTP server that listens to requests on port 3000 and when the server gets a GET request to the “/” URL it will return a “Hello World!” response.

The Express App

When we call to express() behind the scenes we evaluate the createApplication() function that creates a new app object. Let’s take a look at that function.

In line 2 we define the app as an anonymous function that wraps the handle(req, res, next) — this function will invoke on each request that the server will get.

At line 7 we mixing the app object with the express methods such as use(), listen(), get() and etc.

An important property of the app object is the “Router” property which is responsible for what methods will be invoked when the server receives a new request.

Listen to requests

If we take a deeper look, Express basically uses the Node.js http module.

When we call the app.listen() we create a new http server, “this” argument is a function that will invoke on each new http request. It’s the same anonymous function we created in createApplication() that just wraps the app.handle() method.

Inside the Router

The Router object has a stack of “layers”, each layer has an “handle” method and a “path” property.

A layer is one of the following:

  1. Middleware — A function that has the following signature: function(req, res, next), the function can have some logic that can modify the “req” and the “res” and eventually invoke next() — which calls the next layer or use send() to return a response.
    In case of passing an array of functions, each function will be a separate layer.
  2. Route — An HTTP method (GET, POST and etc.) and a function with the same signature as the middleware, the function will trigger only for request with the same HTTP method, It’s also possible to define a route that handle all kind of HTTP methods with the .all().
    In case of passing an array of functions, the route store the given functions as a stack and each call to next() invoke the next one.
  3. Router — we can create a new router (that also contains his own stack of layers). You can think of Router as a “mini-application” that can eventually trigger some middlewares and routes functions or even another router.

Matching the relevant path

How does Express determine which layer is relevant to a particular request?

Express uses a library called “path-to-regexp” to match a request routes to the defined path. The path can be a string, string patterns or regular expressions.

Here are a few examples for path matching:

  1. The following path will match request to “/index.html”
app.get('/index.html', (req, res) => {
res.send('index');
});

2. The following path will match the requests for “/abc” or “/ac”

app.get('/ab?c', (req, res) => {
res.send('ab?c');
});

3. The following path will match the requests for “abc” or “abbbc” and so on.

app.get('/ab+c', (req, res) => {
res.send('ab+c');
});

4. The following path will match requests for “axxxb” or “`aRANDOMb” and so on.

app.get('/a*b', (req, res) => {
res.send('a*b');
});

You can find more details and examples at the “path-to-regexp” documentation.

Handle a request

After we learn about the basic structure of the express router let’s now understand what happens once the server receives a request.

As we discussed before, when we create a new express app and call the app.listen() method we create an http server that will invoke app.handle() for each new http request.

  1. A new request is received and the app.handle() method invoked.
  2. The app.handle() execute the root router handle method.
  3. The handle method will loop through the router layers till it find layer with path that match and invokes the handle method of the given layer.
    a) If the layer is a router the handle function will loop through this router.
  4. If the next() callback is called, then it will search for the next matching layer and also handle that layer, if a response is sent back to the client it will stop.

We can see the following example for Routes, Routers and middlewars:

Conclusion

This was a brief review of how express know what function should handle what request, you are very welcome to read more in express documentation and take a deeper look at the source code.

--

--