If you’re a TypeScript user looking to take full advantage of the type system in your Node web servers, I’ve been working on something that may be of interest to you: express-fp, a wrapper for the Express web server library to provide more type safety in our request handler functions. By ensuring type safety of the request and response, we can avoid many common bugs.
Although I am actively using this on personal projects, this is very much still WIP. There is no real documentation, although if you’re tempted to give it a go, the static types and the example should be enough to give you the gist and get you started.
The current API very closely mirrors the Play framework (specifically Scala Play), because Play is a good example of a type safe web server, and it’s the only well-typed web server I’m currently familiar with.
With that out of the way, let’s look at a few of the things express-fp attempts to improve.
Instead of mutating responses, return responses as data
In Express, responses are constructed by mutating the response object, and calling
res.send when you're done. If you forget to call
res.send, your request will hang until it timeouts.
In express-fp, responses must be returned as object (using express-result-types). If you forget to return, or if you return the wrong type, the type system will complain.
Instead of middleware and mutation, compose pure functions
In Express, requests are handled through a series of user-defined “middlewares”, which traditionally mutate the request and response objects. A common source of bugs is when middleware appear in the wrong order. For example, B assumes that A is called first and that the response has already had its response headers set. However, we can easily break this ordering, and we will get no warning from the type system about this.
Common use cases for middleware include:
- parsing of request body and query
- exception handling
In express-fp, middleware are replaced with function composition. Requests never fall through multiple handlers, and there is no
By composing pure functions, the type system is fully aware of all inputs and outputs, guaranteeing correct order.
Parsing is optionally handled within request handlers, as and when needed.
Thanks to pure functions, we don’t need exception handlers. Where errors are needed, we can use
Either to return them as values.
Validation of incoming values for type safety (request body, query)
In Express, the request body and query can be anything. We can easily assert the type, e.g.
req.body as MyBody, but that doesn't help validate the type at runtime—
req.body can truly be anything at runtime.
express-fp uses the fantastic io-ts to validate values coming into your application from outside. If you’ve used Joi before, io-ts is similar, except it works out of the box with TypeScript. Once a value is validated, io-ts will automatically annotate the value with its static type.
In express-fp, we can only access the request body by defining a “runtime type” for validation. In return, we get back an
Either type—either containing validation errors or the validated body. If the body is valid, the type system will already know the shape of the body, without having to repeat it in a separate type interface.
In express-fp, the request query is exposed as
Map<string, string | string>, which matches the well-defined shape of URL query parameters. It is up to you to extract and validate query parameters.
My primary motivation for this blog post is to get some feedback, so if you’re tempted, please give it a try and let me know what you think. If you do have any thoughts, please reach out to me in the comments, on Twitter, or via GitHub issues. Further down the road I hope to write a more in-depth article with inline examples.