Javascript

Nest RELEASE canditate is here! Introduction to modern Node.js framework

26 marca 2017

Nest is a powerful web framework for Node.js, which helps you effortlessly build efficient, scalable applications. It uses modern JavaScript, is built with TypeScript and combines best concepts of both OOP (Object Oriented Progamming) and FP (Functional Programming).

It is not just another framework. You do not have to wait for a large community, because Nest is built with awesome, popular well-known libraries – Express and socket.io! It means, that you can quickly start using framework without worrying about a third party plugins.

Core Concept

The core concept of Nest is to provide an architecture, which helps developers to accomplish maximum separation of layers and increase abstraction in their applications.

Setup Application

Nest is built with features from both ES6 and ES7 (decorators, async / await). It means, that the easiest way to start adventure with it is to use Babel or TypeScript.

In this article I will use TypeScript (it is not required!) and I recommend everyone to choose this way too. Sample tsconfig.json file:

As you can see, methods in Nest controllers have the same list of arguments and behaviour as a simple routes in Express.

If you want to learn more about req (request), res (response) and next you should read short Routing Documentation. In Nest, they work equivalently. In fact, Nest controller methods are some kind of encapsulated express routes.

UsersController is ready to use, but our module doesn’t know about it yet. Let’s open ApplicationModule and add some metadata.

As you can see – we only have to insert our controller into controllers array. It’s everything.

Components

Almost everything is a component – Service, Repository, Provider etc. and they might be injected to controllers or to another component by constructor.

In previous section, we built a simple controller – UsersController. This controller has an access to our data (I know, it’s a fake data, but it doesn’t really matter here). It’s not a good solution. Our controllers should only handle HTTP requests and delegate more complex tasks to services. This is why we are going to create UsersService component.

In real world, UsersService should call appropriate method from persistence layer e.g. UsersRepository component. We don’t have any kind of database, so again – we will use fake data.

As might be seen in getUser() method we used HttpException. It is a Nest built-in Exception, which takes two parameters – error message and status code. It is a good practice to create domain exceptions, which should extend HttpException (more about it in „Error Handling” section).

That’s it! Now, our application will run, but still one of routes doesn’t work properly – addUser. Why? Because we are trying to extract request body (req.body.user) without body-parser express middleware. As you should already know, it is possible to pass express instance as a second argument of NestFactory.create() method.

As might be seen, with Nest you can naturally split your code into separated and reusable modules!

Middlewares

Middleware is a function, which is called before route handler. Middleware functions have access to request and response objects, so they can modify them. They can also be something like a barrier – if middleware function does not call next(), the request will never be handled by route handler.

Let’s build a dummy authorization middleware (for explanation purposes – just by username). We will use X-Access-Token HTTP header to provide username (weird idea, but it doesn’t matter here).

you should use @Middleware() annotation to tell Nest, that this class is a middleware,

you can use NestMiddleware interface, which forces on you to implement resolve() method,

middlewares (same as components) can inject dependencies through their constructor (dependencies have to be a part of module),

middlewares must have resolve() method, which must return another function (higher order function). Why? Because there is a lot of third-party, ready to use express middlewares, which you could simply – thanks to this solution – use in Nest.

As shown, Modules can have additional method – configure(). This method receives as a parameter MiddlewareBuilder, an object, which helps us to configure middlewares.

This object has apply() method, which receives infinite count of middlewares (this method uses spread operator, so it is possible to pass multiple classes separated by comma). apply() method returns object, which has two methods:

forRoutes() – we use this method to pass infinite count of Controllers or objects (with path and method properties) separated by comma,

with – we use this method to pass custom arguments to resolve()method of middleware.

How does it works?

When you pass UsersController in forRoutes method, Nest will setup middleware for each route in controller:

GET: users
GET: users/:id
POST: users

But it is also possible to directly define for which path middleware should be used, just like that:

Communication via TCP

By default Nest microservice is listening for messages via TCP protocol. It means that right now @RequestMapping() (and @Post(), @Get() etc. too) will not be useful, because it is mapping HTTP requests. So, how microservice will recognize messages? Just by patterns.

What is pattern? It is nothing special. It could be an object, string or even number (but it is not a good idea).

Then, we only have to import this module into another modules, which should share component instance:

@Module({
modules: [ SharedModule ]
})
export class FeatureModule {}

That’s all.

Dependency Injection

Dependency Injection is a strong mechanism, which helps us easily manage dependencies of our classes. It is very popular pattern in strongly typed languages like C# and Java.

In Node.js it is not such important to use those kind of features, because we already have amazing module loading system and e.g. sharing instance between files is effortless.

The module loading system is sufficient for small and medium size applications. When amount of code grows, it is harder and harder to smoothly organize dependencies between layers. Someday everything may just blow up.

It is also less intuitive than DI by constructor.

This is the reason, why Nest has its own DI system.

Custom components

You have already learnt, that it is incredibly easy to add component to chosen module:

In addition to this, ModuleRef is combined with real instance of your module. What it means? If your UsersModule looks like that:

export class UsersModule {
getContext() {
return 'Test';
}
}

Then, you can simply call this method from moduleRef:

moduleRef.getContext() === 'Test' // true

Of course – if you are using TypeScript – you have to cast instance to appropriate type.

Testing

Nest gives you a set of test utilities, which boost application testing process. There are two different approaches to test your components and controllers – isolated tests or with dedicated Nest test utilities.

Isolated Tests

Both Nest controllers and components are a simple JavaScript classes. Itmeans, that you could easily create them by yourself:

const instance = new SimpleComponent();

If your class has any dependency, you could use test doubles, for example from such libraries as Jasmine or Sinon:

createTestingModule(metadata: ModuleMetadata), which receives as an parameter simple module metadata (the same as Module() class). This method creates a Test Module (the same as in real Nest Application) and stores it in memory.

get<T>(metatype: Metatype<T>), which returns instance of chosen (metatype passed as parameter) controller / component (or null if it is not a part of module).