TypeScript brought a new wave into web development.

It seems like before TypeScript, developers wrote “code sections” instead of a program with a real design behind it. The reason for this change are the abilities which TypeScript brought us:

Types — Enforce the developers to think about what they store and where they store it.

OOP principles — TypeScript brought inheritance & interfaces to the web. These principles help to manage software architectures with the ability to scale, while encapsulating complex and reusable logic.

Decorators (annotations) — Decorators are meta-functions that are applied during the class initialization process (while loading the JS file); those meta-functions can affect the class properties, functions and meta-data.

Types and OOP principles are quite common and familiar to most of us. In this article I would like to focus on the third of the previously described abilities offered by TypeScript, decorators.

Decorators

A decorator is a unique annotation that can be attached to a class declaration, method, accessor, property, or class parameter.

Decorators can be used to observe, modify and replace subject (method\property\class) definition.

Most languages, including typescript, implement decorators by using the ‘@’ sign prefix followed by the name of the applied function which should be emitted

Decorator types

Class decorators

Decorators of this type are called before the class declaration with the class constructor as an argument

Example

Usage

Method & accessor (get,set) decorators

Decorators of this type are called before the method/accessor declaration with three arguments:

target — In the case of a static method - the constructor function. In the case of an instance - the instance prototype.

name — The method name.

descriptor — the method descriptor (contains the function value and meta-data — more info)

Example

Usage

Property Decorators

Property decorators are called before the property declaration with two argument:

target — In the case of a static method - the constructor function. In case of an instance - the instance prototype.

name — The property name.

Example

Usage

Check out this Fiddle

Real life usage

To better understand the benefits derived from decorators I would like to provide examples from real life use cases by a package that I use, SugoiJS (more specifically @sugoi/server).

SugoiJS is a modular framework which was developed in order to reduce boilerplates, optimize and enhance the development process while making your application more reliable.

The SugoiJS framework contains standalone packages for ORM, MongoDB, Web server and Sockets.

I’ll demonstrate with some of the @sugoi/server package features.

Notice: SugoiJS server module relies on the express.js package.

Route mapping and handling

SugoiJS provides the ‘@HttpX’ decorator which has come to replace the express route handling boilerplate

Pure Express.js

routes.js

SugoiJS

In this case decorators help us organize our code better and reduce the amount of code needed for setting the routes and pluck the needed values from the request object.

Instead of setting routes on a separate file we define the routes near the bond functions.

Request parameters validation

SugoiJS provides an ability named policies, which provide us the ability to run a verification function before applying the decorated function.

Since parameter validation is pretty much a straight forward task (check if the property exists, if so check its’ type, regex\limits validation) we are able to decorate it as well.

Let’s use the ‘RequestSchemaPolicy’ — @RequestSchemaPolicy(paramSchema, queryParamSchema, bodySchema, headersSchema):

Our schema

Before

After

In this case we can see we shouldn’t re-implement validation logic for each request and maintain it for each change, instead we can use the SugoiJS RequestSchemaPolicy with the ComparableSchema object.

Authorization

User authorization by role type and\or permissions is pretty much a “Must have” feature on a web server.

The implementation of the authorization is most of the time the same (different between servers but the same across a given server and its different endpoints).

So, let’s decorate it!

We will use another policy which SugoiJS provides us, the authorized policy.

This policy allows us to verify if the user is authorized, in the right role and\or with the right permissions.

The @Authorized interface is used as following:

Usage

But wait, how has the validation been done?

I’ll bet you have many roles and permissions I’ve never heard of.

Is this kind of black magic? Machine learning? Infinity stone?

Well, you got me on this one.

We will have to set another small thing:

authorization.service.ts

And setting this class as our official authorization service

app.ts

That’s all. The entire authorization process will take place inside the authorization service with your logic.

With these decorators we can forget about re-implementation of trivial server logic.

Final result

index.controller.ts

Just for comparison:

routes.js

index.controller.js

Hope you enjoyed this article!

I welcome you to save a lot of your time by checking more about SugoiJS.

Cheers!