This article is part of the Nest.js Deep Dive Series. I recommend reading the previous article first to make following along easier.
You can find the code described in the article HERE
In the previous article, we introduced Nest.js, explained what it is, and created a simple Nest application in JavaScript.
Here is the code we currently have
import { NestFactory } from "@nestjs/core";
class AppModule {} // Just an empty class for now
const bootstrapp = async () => {
const app = await NestFactory.create(AppModule); // we create the app with our module
await app.listen(3000);
};
bootstrapp();
At this point, we are calling await NestFactory.create() and passing in AppModule to create our Nest.js application. The AppModule is currently just an empty class, so the code runs but doesn’t actually do anything.
In this article, we’ll take a closer look at what the AppModule is and how it’s supposed to work.
Nest.js Modules
According to the official Nest documentation
A module is a class that is annotated with the @Module() decorator. This decorator provides metadata that Nest uses to efficiently organize and manage the application structure.
But what does this really mean? Let’s break it down into simpler terms.
First, a module is a class
In the previous article, we initialized a Nest application by calling the NestFactory.create() function. This function expects a Nest.js module, which is why we were able to pass an empty class—it worked because, at its core, a module is a class.
However, a module is not just any class. The definition says, “A module is a class that is annotated with the @Module() decorator.”
This a@Module() decorator is what makes it special. It adds extra metadata that Nest internally uses to organize and manage the application structure.
To really understand this, we first need to understand what a decorator is.
What is a decorator?
At a high level, a decorator is a function that wraps another function, class, or method to add extra behavior to it, without modifying its original code.

source: https://www.telerik.com/blogs/decorators-in-javascript
Let's take a real-life example
We decorate things because we want them to look or behave differently. For example, you might take a plain box and wrap it in gift paper to turn it into a Christmas present. 🎁
In this case, the box hasn’t changed; it’s just been decorated to serve a new purpose as a gift container.
Another example is when someone uses lipstick for makeup; they are decorating their lips to make them have a different look
Notice that by decorating something, we don’t change its original structure. This gives us flexibility—we can apply or remove decorations as we like. Lipstick, for example, doesn’t permanently alter someone’s lips (it would be weird if they did 😁); they can always apply a different makeup style later.
Now let’s see this concept in code
class Box {
constructor() {
this.color = "white";
}
}
We define a box that has a white color
function GiftContainer(color){
return function (target){
target.coverColor = color
}
}
We define a decorator GiftContainer that takes a cover color and applies it to the given "target" class
Here is the full code example
function GiftContainer(color) {
return function (target) {
target.coverColor = color;
};
}
@GiftContainer("red")
class Box {
constructor() {
this.color = "white";
}
}
const birthdayBox = new Box();
console.log(birthdayBox.coverColor); // Outputs "red"
We use the @ syntax to apply the decorator. It calls the GiftContainer function and passes the Box class as its target.
The real power of decorators comes from separating enhancement logic from the original implementation.
In Nest.js, for example, you might have the following code:
@Controller('')
class UserController(...)
Depending on how @Controller is implemented, applying it to UserController adds extra information and behavior that Nest can recognize internally to make your application work.
Using Decorators in our JavaScript Nest.js application
Now that we have a basic understanding of how decorator works, let’s integrate them into our Nest.js app from the previous article.
Note:
Currently, JavaScript doesn’t officially support decorator syntax—it’s still considered experimental. However, we can use Babel, a JavaScript transpiler, to enable decorators even before they become a standard feature.
Let's install the necessary Babel dependencies
npm install @babel/core @babel/cli @babel/plugin-proposal-decorators
@babel/coreincludes the base functionality that Babel needs to work@babel/cli: Allow us to use Babel via command line (terminal)@babel/plugin-proposal-decorators: Now the package that will allow us to use decorators.
After installing, we need to configure Babel so it recognizes our setup.
- Create a file called
babel.config.jsonpaste the following configuration
// babel.config.json
{
"plugins": [["@babel/plugin-proposal-decorators", { "version": "2023-05" }]]
}
This tells Babel that we’re using the decorators plugin (the one we just installed) and specifies the version we’re targeting (the latest one as of now)
Next, we need to update or start the script so Babel compiles our code before Node runs it:
// package.json
"scripts": {
"start": "babel index.js -d dist && node dist/index.js"
}
Now our start script first runs Babel to compile index.js and output the result in a dist folder. Once that’s done, Node runs the compiled version (dist/index.js).
Now if you run npm start, our app should work as before, but now with Babel in place to support decorators.
Back to Our Module
Let’s revisit the definition of a module:
A module is a class annotated with a
@Module()decorator. The@Module()decorator provides metadata that Nest makes use of to organize the application structure.
With our new understanding of decorators, we can guess what @Module() is doing; it wraps a class and attaches metadata that Nest uses internally to link different parts of the app together.
With our new knowledge of decorator, we can have an idea of what @Module() must be doing, it will wrap a class and provide some metadata that Nest will use internally to know which parts of the application are related. Let’s see this in action
We updated our application to be like the following
import { Module } from "@nestjs/common";
import { NestFactory } from "@nestjs/core";
@Module({})
class AppModule {}
const bootstrapp = async () => {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
};
bootstrapp();
Here, we imported the @Module decorator and applied it to our AppModule class.
We’re passing an empty object for now, since @Module() expects metadata—such as which controllers or providers belong to this module—but we haven’t added any yet.
You can have multiple modules in a Nest.js application, but you need at least one root module to initialize the app. The module passed into NestFactory.create() is known as the root module.
Now, if you run npm start again, your Nest application should start up just as before—only this time, it’s using proper module structure.
Top comments (0)