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
2. Nowadays
Let’s now go to 2017 and use typescript. First let’s take advantage of two features here:
- typings
- async/await
the code can be rewritten like this:
Note how 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
apiMethod
decorator to reduce boilerplate. - First line tells it all : this
wrappedShowUser
function pretends that it returns an object that contains either a user of typeUser
, 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:
Conclusion
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 !
These people have absolutely no idea of what’s going on here, but they definitely look very happy about it.