Heads up! This article is part two of the series: Exploring NestJS 🙌🏻.
What’s the module system, anyways?
NestJS chose to work with so-called “modules”.
A module is an isolated part of an application, which encapsulates multiple functionalities that belong together.
Usually, one module includes all the functionality needed for a business domain.
How does the module system work?
Let’s say you are building an application which has multiple users, maybe somewhere saved in the database.
Clearly, you have to access the data itself (the user data) and for this you need some files, let’s say User.entity.ts (which describes the structure of a user entity) and a service through which you do CRUD operations (create, read, update, delete): Users.service.ts.
We have to “put” both of these files in the same module: Users.module.ts, so that it’s clear they belong together.
When another part of the application (another module) has to do with anything user-related, it knows that the Users module is the point of access.
The root application module
Every NestJS app has a root module, called the Application module.
All the other modules are “below” this in the dependency graph of the modules.
If you don’t create any other modules in your Nest app, you will still have the default “AppModule”, which looks like this:
As you can see, a module is defined by decorating a TypeScript class with the @Module decorator.
A module is defined by its controllers, providers, exports and imports.
Let’s demystify what these mean.
A controller is a simple class annotated with the @Controller decorator.
It can respond to HTTP requests of type GET, POST, PUT etc.
We will not deep dive into them here (only in the following article), but this is how one looks:
Okay, presumed that we have this controller defined, we must now link it to the module we want to have it in.
How? We simply add the controller to the “controllers” array of the module:
Voila! We now have a controller in our module.
A really important decorator that you’ll work with in NestJS is @Injectable.
You put the code that handles business logic (all the logic of your app, accessing the database, accessing external services etc.) into Service classes.
A service class looks like this:
The @Injectable decorator is used on these service classes. It enables injecting these services into other places, for example other services that might need to communicate with it.
Coming back to our modules, we put the name of the service into our providers array. Now our service belongs to our beloved module. Yay!
It’s important to note that providers are not only for services.
They can be used in many other cases (related to Dependency Injection), like swapping out implementations (for testing) and so on. We will not go into details as it is not crucial to have that know-how in the beginning.
Remember our diagram from above, one module using another one?
If you’d like to expose one of the services from a module, you do this via the “exports” array of the module definition.
Example: consider a module which encapsulates all users related functionalities, let’s just call it UsersModule.
This module contains the UsersService.
If we’d like other parts of our application to access UsersService and retrieve some user data, we must export it from UsersModule.
We do it this way:
Basically, the exports array is a subset of our providers array, and specifies which providers (like Services) can be accessed and used in other modules.
Now, we have only one thing to clarify: how do we use the UsersService from within another module? This is what “imports” does.
You use the imports array to specify other modules from which you’d like to use services (or other providers).
In the above example, we import the UsersModule in AuthModule (a module which handles authentication/ authorization — maybe we need to get some user details to see if the user has access to certain resources).
Great, but how do we actually use the services exported from UsersModule here? The answer is dependency injection:
We put a new field, of type UsersService, in the constructor of AuthService (part of AuthModule). This will automatically be wired via the Dependency Injection of NestJS. Elegant!
After this, we can use the functions exposed by UsersService:
How do I create my own module?
You saw in Chapter 1 how to bootstrap a NestJS project.
It is really easy to create another module in your Nest app, using the Nest CLI.
Of course, you can do all the coding and wiring manually, but the CLI eases our lives.
Open a terminal window (or command prompt window) in your existing NestJS project directory and write the following:
nest generate module my-first-module
You should see the following dialog:
What did just happen?
Nest’s CLI created a new folder with the new module’s name (“my-first-module”) with the module file itself:
As you can observe, this module is quite empty, but we’ll add some controllers and services in the next chapter of this series.
It’s also important to note that the new module was registered in the AppModule :
Looks familiar 🤔? Registration of a new module is done my importing it in the AppModule.
Congratulations 🎆! Now you should have a clear understanding of how things are structured in NestJS.