In this post I’ll present a rather nice way of wrapping node-express controller methods in order to have a better self-describing codebase.
Let’s say you want to build an api endpoint that fetch some user, and returns it. Simple, yeah ?
1. A long time ago
A long time ago, people would write something that would read
Let’s now go to 2017 and use typescript. First let’s take advantage of two features here:
the code can be rewritten like this:
await user.findOne(...) allows us to code as if our flow were synchronous. Besides, any exception (or rejected promise) will be caught by the main
try/catch block. For more complex function, there is already a nice improvement. In case you don’t already know, I happen to be quite enthusiastic about async/await.
Also, try adding a typo, such as
res.seeendStatus(500) and the typescript compiler will warn you about an unknown method. Because
Result now has typings. Nice. Safe.
3. Even better ?!
I use the last example as a template for quite a while and it worked pretty well. Something would still bother me though.
By looking at this code, it is not obvious at first glance that this function returns a user. Adding some doc is an idea, but how can you be sure that the doc exactly describes what’s coded below (common answer: you don’t, thus you read the code anyways) ?
Of course this example is simple enough so you can read the method in no time. But why should you read a method when we are using typed functions everywhere ?
What we came up with at Hunteed is a wrapper that will allow us to declare the output of our express functions.
Before we dive into the details, here’s an example of what our final function should look like:
Here you can notice that:
- We use some magical
apiMethoddecorator to reduce boilerplate.
- First line tells it all : this
wrappedShowUserfunction pretends that it returns an object that contains either a user of type
User, or nothing at all.
- We don’t write no try/catch (there should still be a try catch somewhere, but we are not required to write it every single time, at the risk of forgetting it )
- The function actually returns the data we pretend to send. And typescript can check that we do. Yay !
Now for the implementation:
As promised, our final api method now reads
I really think this is some readable and self-describing piece of code. It takes advantages of typings, error handling, and guarantee that no-one will ever forget to use try/catch on an express api endpoint. Neat !