Nest.js is a progressive Node.js framework that combines the efficiency of Node.js with the power of TypeScript (or JavaScript) to build reliable and scalable server-side applications. Drawing inspiration from Angular, Nest.js embraces modularity, dependency injection, and decorators, fostering testable and loosely coupled systems. This topic will guide you through the core concepts of Nest.js and demonstrate how to get started with building a simple REST API.
Core principles of nest.js
Nest.js introduces a modular architecture, where applications organize into modules, each acting as an individual software unit that encapsulates a related set of capabilities. Nest.js adheres to several core principles:
- Modularity: Nest.js applications are consist of modules that encapsulate providers, controllers, and other elements.
- Services and Dependency Injection: Nest.js uses services to encapsulate business logic, which can be injected into controllers or other services using dependency injection.
- Decorators: Decorators, a core feature of TypeScript, are prevalent in Nest.js. They annotate classes and their members to define metadata and behavior, enabling the creation of annotations and class-field decorators.
Setting up a nest.js project
To begin with Nest.js, ensure Node.js is installed on your system. After installing Node.js, use npm to install the Nest CLI:
npm i -g @nestjs/cli
After installing the Nest CLI, create a new Nest.js project by executing the following command:
nest new project-name
Navigate into the newly created project directory and open up the main file, usually named main.ts. Here's what it typically looks like:
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
bootstrap();
This file sets up a new Nest.js application using the AppModule and starts the server on port 3000.
The @module() decorator
In Nest.js, a module is a class annotated with a @Module() decorator. Nest.js uses the metadata from the @Module() decorator to organize the application's dependency injection system. The framework ensures that each controller and provider has the dependencies it needs to operate, as declared in the module.
As you launch your Nest.js application, the root module initiates the application graph construction. Nest.js recursively resolves the dependencies for each imported module, controller, and provider, creating a complete map of how different parts of your application fit together.
Here's a closer look at the GreetingsModule example:
import { Module } from '@nestjs/common';
import { GreetingsController } from './greetings.controller';
@Module({
controllers: [GreetingsController],
})
export class GreetingsModule {}
In this example, the GreetingsModule is declares that GreetingsController is part of this module. Nest.js will handle the instantiation of this controller and integrate into the application's request-handling pipeline.
The @controller() decorator
Controllers are defined using classes in TypeScript, with decorators to provide metadata and establish routing rules. These decorators simplify the process of linking controller methods with specific HTTP verbs and paths, fostering a clear and maintainable routing structure. The decorator takes an optional argument that specifies the base path for any routes defined within the class. For instance, by setting @Controller('greetings'), all routes in this controller will have the prefix /greetings.
Here's a breakdown of how it works:
import { Controller, Get } from '@nestjs/common';
@Controller('greetings')
export class GreetingsController {
@Get()
getHello(): string {
return 'Hello, World!';
}
}
@Controller('greetings'): This decorator associates theGreetingsControllerclass with the base path/greetings.export class GreetingsController: Defines a new class calledGreetingsController.@Get(): This method-level decorator tells Nest.js to create a route for an HTTP GET request. By default, the path for this route is the root of the controller's base path (/greetingsin this case).getHello(): This is a method within the controller class that will be called when an HTTP GET request is made to the/greetingspath.
Route decorators
Nest.js provides several decorators to associate controller methods with specific HTTP request methods and paths. Some of the commonly used route decorators include:
@Get(): Maps to HTTP GET requests.@Post(): Maps to HTTP POST requests.@Put(): Maps to HTTP PUT requests.@Delete(): Maps to HTTP DELETE requests.@Patch(): Maps to HTTP PATCH requests.
Each of these decorators can take a path argument to further refine the endpoint. For instance, @Get('hello') within the GreetingsController would map to the /greetings/hello path.
Generating controllers with the Nest CLI
An alternative way to create controllers is by using the Nest Command Line Interface (CLI). The CLI provides a command that generates the controller file, test file, and updates the module file to include the controller. To generate a controller using the CLI, execute the following command in your terminal:
nest generate controller greetings
This command creates a new controller with the name greetings and sets up a basic controller structure for you to start adding your routes and logic.
Providers in nest.js
Providers in Nest.js play are essential to the framework's dependency injection system. They are used to abstract logic and functionalities into reusable and testable parts. Providers can be services, repositories, factories, helpers, or any construct that is injectable into a constructor.
Providers are responsible for encapsulating application business logic, which can include data retrieval, validation, logging, and any other operation that is not directly tied to taking an HTTP request and sending a response. They are designed to be independent of the context in which they are used, making them easily injectable into controllers, other providers, or even modules
To define a provider, you decorate a class with the @Injectable() decorator. This marks the class as a provider that can be managed by Nest's dependency injection system. Here's a simple example of a service provider:
import { Injectable } from '@nestjs/common';
@Injectable()
export class GreetingsService {
getGreeting(): string {
return 'Hello, World!';
}
}
In the above code, GreetingsService is a provider that has a single method getGreeting, which returns a greeting message.
Registering providers
Providers are usually registered in the module's providers array within the @Module() decorator. Once registered, they become part of the module's context, allowing injection into other components such as controllers and services.
Here's how you would register the GreetingsService within a module:
import { Module } from '@nestjs/common';
import { GreetingsService } from './greetings.service';
@Module({
controllers: [GreetingsController],
providers: [GreetingsService],
})
export class AppModule {}
Injecting providers
After registering a provider, you can inject it into controllers or other providers using constructor injection. Here's the process for injecting the GreetingsService into a controller:
import { Controller, Get } from '@nestjs/common';
import { GreetingsService } from './greetings.service';
@Controller('greetings')
export class GreetingsController {
constructor(private greetingsService: GreetingsService) {}
@Get()
getHello(): string {
return this.greetingsService.getGreeting();
}
}
In this example, GreetingsController injects GreetingsService through its constructor. The private keyword in the constructor argument list both declares and initializes greetingsService as a member of the class. The @Get() route handler uses the service to get the greeting message.
Custom providers in nest.js
While Nest.js offers a range of built-in provider types, sometimes your application may require a more granular level of control over the instantiation and behavior of your providers. Custom providers provide this flexibility, allowing you to customize the creation and management of providers to meet specific requirements. They are especially valuable for integrating third-party services or implementing complex logic beyond the scope of built-in providers.
Creating a custom provider
To create a custom provider in Nest.js, define an object that adheres to the Provider interface. This object must include a provide property, a token for injecting the provider, and a useFactory property, which is a factory function that returns the instance of the provider.
Here's a simple example of a custom provider that integrates a hypothetical logging service:
import { LoggerService } from './logger.service';
export const customLoggerProvider = {
provide: 'CustomLogger',
useFactory: () => {
const loggerService = new LoggerService();
loggerService.initialize('Custom Initialization Parameters');
return loggerService;
},
};
In this example, you define a custom provider named CustomLogger. The useFactory function creates an instance of LoggerService, initializes it with custom parameters, and returns the instance.
Registering a custom provider
After defining your custom provider, register it within a module for Nest.js to acknowledge it and make it available for injection. Do this in the providers array of the module's metadata:
import { Module } from '@nestjs/common';
import { customLoggerProvider } from './custom-logger.provider';
@Module({
providers: [customLoggerProvider],
})
export class AppModule {}
With the custom provider registered, you can now inject it into other components using the @Inject decorator and the token provided:
import { Inject, Injectable } from '@nestjs/common';
@Injectable()
export class SomeService {
constructor(@Inject('CustomLogger') private customLogger: LoggerService) {}
doSomething() {
this.customLogger.log('Doing something...');
}
}
In the SomeService class, you inject the custom logger using the @Inject decorator and the 'CustomLogger' token. Now, the SomeService class has access to the LoggerService instance created by the custom provider.
Conclusion
In this topic, you've been introduced to Nest.js, a robust framework for developing server-side applications with Node.js. You've explored the framework's core principles, including modularity, services, dependency injection, and decorators. You also learned how to set up a new Nest.js project, create a basic controller to handle HTTP requests, and structure your application using modules.
Nest.js's architecture is designed to support the development of scalable and maintainable applications, making it an excellent choice for enterprise-level projects. Now that you have a foundational understanding of Nest.js, you can delve deeper into its features and begin practicing by building applications.