Request Context with TypeScript and Express

Express with TypeScript can be confusing to start with!

If you’ve ever used Express with TypeScript, you know first hand how confusing it can sometimes be. Traditional Express conventions involve the mutation of the Request object as a sort of “thread local” or “context” for the life of the request. This approach unfortunately relies on the loosely typed nature and conventions surrounding vanilla JavaScript, and does not scale well in a TypeScript project.

When building APIs with Express + Javascript, you’ll usually see something like this via Express middleware to decorate values and objects to the req object for later middleware / route controllers to consume:

With TypeScript, this has an obvious pitfall: the request object is strongly typed in most projects, so you now have to either create a custom request type or extend the default request type for every property you plan on decorating the request object with. I’ve found a much better approach is avoid decorating the request object all together, and instead rely on middleware to bind a Request Context object that can contain all metadata you wish to associate with a given Request.

To do this is dead simple using WeakMaps. The reason we want to use WeakMaps for something like this is that we want the Request Context object to be garbage collected as soon as the Request itself is garbage collected, to avoid memory leaks. A simple implementation involves a piece of middleware along with a Context class that is strongly typed.

To begin with, let’s implement a simple class that weakly bind itself to a number of incoming Request objects:

Next, we need a simple piece of Middleware to bind each Context to the Request object:

Finally, we can access our Context within route controllers:

… and there you have it! You can now “attach” objects to the Request without actually decorating it and requiring annoying type annotation additions, all with the JavaScript primitive WeakMap.