ExpressJS with TypeScript
It’s been a while since I wrote the last post.
This post covers my recent venture into TypeScript
, yet another Javascript transcompiling language.
My primal motivation for TypeScript
was from my recent startup experience, Globl. It currently runs an API server built with Node.JS
.
When I first started, I used CoffeeScript
since its terse syntax allowed me to do a faster iteration. However, as the codebase gets bigger and API becomes more complex, I quickly found myself in the depth of never ending regression bugs.
These bugs were mostly stupid like function calls with wrong arguments and unmatching variables names etc. These kinds of bugs would have been easily solved with any statically typed language.
Long story short, I’ve delved into TypeScript
and have never been happier. (No more Find All commands!)
However, this post won’t give an introduction to TypeScript
as there are many good sources out there already, especially this handbook by Microsoft.
For any seasoned Javascript
programmers with decent amount of statically typed language experience will have no problem learning the entirety of the language within a day or two.
Instead, I’d like to talk about this little code snippet that I wrote with Express.JS
export function GET<T extends typeof APIController>(path:string, paramsTypesInString: TypesArrayMap<string>) {
return function<A>(target:T, methodString:string, descriptor:TypedPropertyDescriptor<A>) {
app.get(path, (req: _ExpressRequest, res: _ExpressResponse) => {
const [typesArrayMap, paramsMap] = extractParams(paramsTypesInString, req, MethodType.GET);
if (target.handleInvalidParamTypes(typesArrayMap, res)) { return; }
(<RequestHandlerWithParams> (<any> target)[methodString]).call(target, req, res, paramsMap);
});
}
}
This snippet utilizes the power of decorator
in TypeScript (a.k.a Annotation
in Java, Python …) For detailed discussion with decorator
, refer to this and this.
It enables a new API Design Pattern for Express.JS
like the following.
class UserController extends APIController {
@GET("/user", { string: ["user_id"] })
public static fetchUserData(req:_ExpressRequest, res:_ExpressResponse, params:ParamsType) {
const {user_id} = params;
res.send(user_id);
}
}
There are several things to note here.
First, we take full advantage of the nice class inheritance provided by TypeScript
. APIController
is so-called abstract class
, which has all the methods that are shared between its subclasses like handling client errors etc.
Second, we are using the decorator
function that we’ve declared previously. First parameter to @GET
is the path
of this API method while the second parameter is the querystring
that this API expects to receive.
Third, by using the decorator
to associate API methods with paths, it enables us to write more declaratively. The typical API design by Express.JS
usually involves association by imperative execution.
The end result looks quite similar to that of Ruby on Rails
. By having its relevant data (path, querystring) close to the controller itself, it makes for both more readable and maintainable code since now there is no need for jumping back and forth between the API path declaration file and Controller.
Lastly, using decorator
in TypeScript
has no performance downside added since these decorators
are executed at the design level, which means it executes declaratively once your server starts running not every time a new request comes.