Unleashing the Power of Nest JS: A Simple Guide into Effortless Backend Development

Yaman Omar Alashqar
16 min readMay 25, 2023

--

Welcome to the world of effortless backend development with Nest JS! In this exciting journey, we’ll explore how Nest JS revolutionizes the creation of backend projects, making it easier than ever before.

Unlike other frameworks that drown you in boilerplate code even for a simple “Hello World” example, Nest JS streamlines the process by automating routine tasks, allowing you to focus on the essence of your application.

With its comprehensive solutions for essential backend components, such as authorization, authentication, controllers, and GraphQL, Nest JS empowers developers to build robust and scalable applications with ease.

Make sure to check out my previous introductory article for a comprehensive overview!

Outline:

I. Technical Requirements

  • Tools
  • Concepts

II. Getting Started with Nest JS

  • Installing the Nest CLI & Creating a new Nest JS project
  • Overview of the generated project structure and files
  • Overview of the installed packages and dependencies

III. Running Your Nest JS Application

  • Starting the server using the Nest CLI
  • Understanding how the server handles incoming requests

IV. Exploring src/main.ts

  • A. Overview of the main.ts file as the entry point of the application
  • Understanding the create method and its parameters
  • Exploring the app.listen method and specifying the port number

V. Exploring src/app.module.ts

  • Overview of modules in Nest JS
  • Importing and configuring controllers and providers

VI. Exploring src/app.controller.ts

  • Understanding the role of controllers in handling incoming requests
  • Analyzing the AppController class and its dependencies
  • Exploring the Controller class decorator and its usage

VII. Conclusion

Technical requirements

To embark on this Nest JS journey, here’s what you’ll need:

  1. Node JS and NPM: Ensure that you have Node JS and NPM installed on your machine. These tools will serve as the foundation for running Nest JS applications.
  2. Nest CLI: Install the Nest CLI globally using NPM. The Nest CLI provides a command-line interface that simplifies the creation and management of Nest JS projects.
  3. TypeScript language basics: Familiarize yourself with the basics of TypeScript, as Nest JS leverages this superset of JavaScript for building robust and statically-typed applications.
  4. Your favorite IDE: Choose your preferred Integrated Development Environment (IDE) for writing code. Visual Studio Code (VS Code) is a popular choice among developers due to its extensive features and ecosystem.
  5. Understanding API concepts: Gain a general understanding of Application Programming Interfaces (APIs) and how they serve as endpoints for communication between different software components.

With these prerequisites in place, you’re all set to dive into the world of Nest JS and unlock the full potential of backend development. So let’s get started and embark on an exciting journey of building powerful and scalable applications with Nest JS!

Creating a Hello Nest App Using the CLI

Creating backend projects has never been this easy as is the case with Nest JS.

The command-line interface (CLI): is a command-line interface tool which assists the developers while building Nest applications, from the first step of initializing your project using a solid architectural pattern to building and bundling the application for distribution and deployment.

We will install the CLI using NPM (note that other package managers may be used to install the CLI). So, use the following command to install the CLI globally:

npm install -g @nestjs/cli

You have the choice to install the CLI globally (using the -g option), but note that if you are working on multiple projects, all of them will be served using the same version of the globally installed CLI.

Use Nest’s CLI to quickly generate your project by running the following command:

nest new <project-name>

You can choose any valid name for your project (just remember that project names must start with a letter, and must contain only alphanumeric characters or dashes).

You will then be prompted to choose a package manager for the project; I will select npm.

The following figure shows the installation process in action from the terminal:

Figure 1: The process of creating a new project.
Figure 1: The process of creating a new project.

When the installation is successfully finished, you will end up with a folder that includes node modules, a few other boilerplate files, and a src folder populated with several core files. The core files are:

  • app.controller.ts: Handles incoming requests to determine which function to execute.
  • app.module.ts: Group related services, controllers, and entities to organize code.
  • app.service.ts: Contains all the business logic.
  • main.ts: Serves as the entry point for our Nest JS application. It sets up the Nest application instance and starts the server.

We will explore these in more detail in just a while.

Also, some packages and dependencies were installed with the project:

  • @nestjs/common: Contains the vast majority of functions, classes, etc. that we need to start working with Nest.
  • @nestjs/platform-express: Since Nest itself cannot handle HTTP requests, it relies upon outside implementation such as Fastify or Express JS (the latter we are using here).
  • reflect-metadata: Helps make decorators work.
  • typescript: Enabled as our Nest apps will be developed using Typescript (although it’s not strictly required, since you could use JavaScript too).

The actual packages live in the nodeModules directory, while the versions and dependencies of each package can be found within the package.json file.

Now that we know some of the files and folders that were generated in our project, and some of the most important packages and dependencies that Nest JS uses to run and build your application, we will learn how to run our first application on a specific port number.

Running Your Application

Once the project is set up, and all the packages and dependencies are installed, we can now run our server to start listening to incoming HTTP requests.

Using the Nest CLI, you can start your application by entering the following command in your terminal:

nest start

This command will trigger the corresponding command is found in the package.json file in the scripts section, which is:

npm run start

Once run, your Nest application will start listening to incoming HTTP requests on a specified port number. Here is a screenshot of the terminal after running nest start:

Figure 2 — A terminal showing that the Nest server is running.

As we can see in Figure 2, the Nest server is running and ready to handle our requests; most of the events happening on our server are logged in the terminal with a date/time stamp as shown.

Now, you may be wondering about the specific port number your application is served on and how it handles those incoming requests. Additionally, you might be curious if any endpoints have been defined yet. To address these questions, let’s explore the files that the Nest CLI has already generated for us.

Stay tuned as we dive into the next section and unravel the mysteries of the generated files.

Exploring: src\main.ts

main.ts is the entry class of the application, used to create a Nest application instance (through the create method). It contains a bootstrap function which runs the NestFactory.create(AppModule) method. This method takes in the following parameters:

  • The entry (root) application module class.
  • A list of options to initialize Nest application (as an optional parameter), which will create an instance of your Nest application.

Note that you could create the instance with a specified HTTP adapter (Express JS, Fastify or any other HTTP adapter to handle the request/response cycle).

This method returns a promise that, when resolved, contains a reference to the Nest application instance. When the create method returns a promise, you can call the .listen(portNumber) method, which will start listening to incoming HTTP requests. This method takes in a port number as a parameter, and you can optionally pass in a callback function that will execute once the server is up and running.

Since applications often run in different environments with different values, usually the port number is stored in the .env file (environment variables file). The .env file holds information that could change from one environment to another, for example, the username and password for a database, or for a mail server, certain flags to disable authentication during development, the directory to store uploaded files, and endless variables. Read more here about environment files!

Also, this method is overloaded (which means there is another version of the same method with different parameters) — the first parameter is the port number, the second parameter (optional) is the host (IP address) that the application will listen on, and the third parameter (optional) is the callback function that will be executed when the Nest application starts listening.

This is the answer to one of our questions: “Which port is your application served on?” Now we know it’s specified in the src/main.ts, specifically in the app.listen(portNumber) method.

Now let’s explore the main.ts file line by line. Have a look at Figure 3 which represents the base code of the main.ts auto generated file:

Figure 3— ‘main.ts’ auto generated file using the CLI

Let’s break it down:

  • Line #1 imports the NestFactory class which contains the .create(rootModule) method. This is used to pass the root module (entry point module) for the current application from @nestjs/core package.
  • On Line #2, we are importing the AppModule (the root module) which is auto generated from the CLI when we created the app earlier.
  • In Line #4, the bootstrap function is marked as async (note that the keyword async makes the function return a promise; the keyword await is used inside async functions, which makes the program wait until the promise resolves before executing the next line). The main purpose of the bootstrap function is to call it to start the application, and it’s worth noting that it can be named anything.
  • In Line #5, the execution of the app starts where the NestFactory.create() method is called and the AppModule is passed as an argument to represent the entry point module (the root module). The instance of the Nest application is created and stored in the app constant.
  • In Line #6, the app constant is used to start the HTTP server by using the event listener method on the webserver with an available port (by default, 3000 is passed as the parameter). The .listen() method will return a promise, indicating that the server has been successfully started embarking on the await key.

There is no need to use the optional callback function in the .listen() method. Since the method uses the await key, this means that any following code is guaranteed to be executed after the server starts listening.

  • Line #8 just executes (calls) the bootstrap function.

Now that every line is clear in this file and we answered the first question “Which port is your application served on?”, you might be wondering “What is a module?” and “What does the AppModule that we imported on Line #2 and passed to the .create() method on Line #5 include?” Let’s find out.

Exploring: src\app.module.ts

In my previous article, I mentioned that modules are logical units of code organized together. A single module will group related services, controllers, and entities or as it is mentioned in the official documentation of nest: “Modules are used by Nest to organize the application structure into scopes.”

Although you could throw all of your code into a single module, to make use of Nest’s capabilities, it is strongly recommended to use modules as an efficient way to organize your components and other parts that are related to each other. The architecture of any application will employ multiple modules, each encapsulating a closely related set of capabilities.

The AppModule is the root module and the starting point of the application (generated by the CLI) that imports the whole application. In Figure 4, you can see the base code of the app.module.ts auto generated file.

Figure 4 — ‘app.module.ts’ auto generated file using the CLI

Let’s break this file down line by line:

  • Line #1 imports the Module class decorator from @nestjs/common. The Module class decorator holds the following properties: imports, controllers, and providers. We will explore them in the next few lines.
  • Line #2 imports the AppController class, which is the controller that was generated using the CLI. Controllers are used to handle incoming requests from the client and execute the related logic as requested.
  • Line #3 imports the AppService class which is also generated by the CLI. A service is a type of provider used to abstract complex pieces of code that can be injected as dependencies.
  • Line #5 through Line #9 shows the Module class decorator (used to tell Nest JS that this class will act as a module for our application), holding the three properties that we mentioned in the explanation of Line #1

The first property (imports) holds an optional list of imported modules that export the providers required in the AppModule.

The second property (controllers) holds an optional list of controllers that Nest JS will use to create an instance of each controller when the app starts.

The third property (providers) holds an optional list of providers that will be instantiated by the Nest injector and that may be shared across the module.

There is a fourth property (exports) which is not used in this module — it holds an optional list of providers that are provided by this module and that should be available in other modules which import this module.

In Line #10, the AppModule class is exported so that it can be imported in the main.ts file.

Now that we have broken down every line, you should be clear on what each property in the decorator is used for. Next, we will understand the code that builds the controller, where we will answer the remaining two questions that we mentioned at the beginning of this section: “How will we handle these requests?” and “Are there any endpoints yet?”

Exploring: src\app.controller.ts

Controllers handle incoming requests from the client to perform application logic and return a response. So basically, in the controller we will define the endpoints that a client could request and define what logic each endpoint should execute. Inside the controller class, we will add a variety of different methods (routes/endpoints handlers) where each method is designed to handle a single type of HTTP requests.

Let’s explore the app.controller.ts file line by line, using Figure 5 which shows the base code of the auto generated file.

Figure 5 — ‘app.controller.ts’ auto generated file using the CLI

Breaking the code down:

  • Line #1 imports the Controller class decorator from @nestjs/common. The Controller class decorator is required for Nest to understand that the class is acting as a controller, which gives the class the ability to receive requests and produce responses. A Controller class contains a variety of related routes (endpoints), where each handles a single path of a single HTTP request type. Also, we can see that the Get method decorator is imported from @nestjs/common.
  • Line #2 imports the AppService class. As mentioned, services are injectable classes (providers) that contain the business logic for a specific module; usually a controller will execute an action or group of actions (methods) that are implemented in a particular service.
  • Line #4 makes use of the Controller class decorator, to tell Nest JS that this class will act as a controller for our application. This decorator takes an optional parameter called the prefix (string), that defines a route path prefix. The prefix is prepended to the path specified in any request decorator in the class (which means it will be prepended to the paths of the methods that we will define within the class). I like this architecture, since it avoids duplicating code, and makes the path more readable. For example, the following controller decorator, @Controller(‘/books’), will pre-pend the path /books to each and every method inside this controller class.
  • In Line #5, the AppController class is exported so that it can be imported in other modules.
  • Line #6 is the class constructor, and it is used to receive the injected providers for the current controller (in our case, the AppService which contains the business logic implantation for the AppController routes). Nest will resolve the AppService by creating and returning an instance of AppService (in a form of a singleton, which means that if the instance exists somewhere else it will just return it, else it will be initialized).

Note: The instance of the AppService is ‘readonly’ and ‘private’. Here, ‘readonly’ means that this instance is accessible only in the current class, and the class property is marked as immutable (it just protects the reference of the injected service but none of the properties). It is not something that you have to do, it’s just a good practice and the philosophy behind this practice is that you probably don’t want to reassign the instance again. The ‘private’ keyword is an access modifier (who is allowed to access this property or method), other access modifiers are public and protected.

  • Line #8 to Line #11 is the endpoint handler method which is used to handle a single path of a single HTTP request type. Moreover, Line #8 is the Get method decorator which is imported from the @nestjs/common package. The Get method decorator is used to route HTTP GET requests to a specified path (endpoint).

Note: Other decorators are available for other HTTP requests such as Post, Delete, Put, and Patch. For now, you just need to understand that the Get decorator is a handler for a specific endpoint for HTTP requests.

You might be wondering what is the path or endpoint to reach this method? We mentioned that the @Controller() class decorator takes an optional parameter called the prefix, that defines a route path prefix to reach each and every endpoint within the decorated class. The @Get() method decorator can also take an optional parameter that acts as the endpoint, so the final route path for any handler is determined by concatenating both the optional prefix declared for the controller and the path specified in the method’s decorator. Let’s consider an example: if a prefix of prefix @Controller(‘books’) is combined with a method decorator @Get(‘audio’), this would produce a route mapping for requests like GET ‘/books/audio’. In our example, both the prefix and the route path are empty, which means that any Get request to our app will trigger the getHello() method.

  • Line #9 is the getHello() method that will be executed when the route associated to the method decorator is requested.
  • In Line #10, the method will return a 200 status code and the associated response, which in this case is just a string that is returned from the service. The string is returned from the getHello() method in the AppService class; this is because the controller is responsible for handling requests only while the business logic lives in the service.

Note: status code 200 means that the request has succeeded; it is default status code for all responses except Post responses, which are 201 by default. You can change the status code using the @HttpCode() method decorator which is imported from @nestjs/common package.

Exploring: src\app.service.ts

Services contain the implementation of the business logic for a specific module; usually a controller will execute and action or group of actions (methods) in a particular service class, for example a @Get(‘/books’) controller will call a particular method in a service (for example this.bookService.getBooks()) which will retrieve the data, execute some logic, and return the result to the controller.

In our first Nest JS application we have a getHello() controller that calls then returns a method that is also called getHello() in the appService (note that the methods names could be anything, they do not have to be identical).

Let’s explore the app.service.ts file line by line, using Figure 6 which shows the base code of the auto generated file.

Figure 6 — ‘app.service.ts’ auto generated file using the CLI

Breaking the code down:

Our AppService is a basic class with one property and two methods. The only new feature is that it uses the @Injectable() decorator.

  • Line #1 imports the Injectable class decorator from @nestjs/common. The Injectable class decorator is required for Nest to understand that the class is acting as an injectable, which gives the class the ability to receive
  • A service is usually responsible to handle data storage and retrieval and execute business logic, it is designed to be executed by the controller (in our case the @Get() controller).

After looking at all of those files, you might be wondering why your app has so many bits and pieces, and why, although it’s only a simple “Hello” app, is everything separated? What does this achieve? Well, Nest JS offers multiple layers that leads to an independent framework in each layer, making your application testable, open for extension, and gives you the ability to replace any of the layers with minimum fuss.

Now that we generated our first application using the Nest CLI we are ready to run our application, if it’s not already running, use the following command in the command line: nest start.

Understanding the Nest JS Lifecycle

Once we start our Nest application, it enters a vibrant lifecycle where the magic happens. Nest’s powerful dependency injection system kicks in, modules are initialized, and controllers are ready to handle incoming requests. It’s important to grasp this lifecycle to effectively develop and troubleshoot your application.

To end this article, we could start viewing the output of this app using the browser or any API testing tool such as Postman which is an excellent tool for API testing, providing a user-friendly interface to send requests and observe responses.

We can easily configure Postman to make requests to our Nest JS application, ensuring that our endpoints are functioning as expected. Now open Postman and create a new request, then enter the URL http://localhost:3000/ (3000 is the port number we configured earlier) and select the HTTP method as GET. Click on the "Send" button, and you should see the response "Hello World!" in the response body.

Note: You can simply view the same results by accessing the same URL from the browser.

Congratulations! You have successfully started your first Nest JS application and tested the endpoint using Postman.

Let me know what you would like to read about in the next article, and I will be happy to share my knowledge.

My LinkedIn Account:

Contact me for any help, suggestions, or even to say hi! 😎

--

--