It is a framework for building efficient, scalable server-side applications.
It provides the ecosystem of library, such as mongoose, ...
It provides an out-of-the-box application architecture which allows developers and teams to create highly testable, scalable, loosely coupled, and easily maintainable applications. The architecture is heavily inspired by Angular. It effectively solve the main problem of - Architecture.
It is built around the strong design pattern commonly known as Dependency injection.
The dependencies injected can be used within the module
Applied AOP design pattern into it, which is similar with spring boot
constructor(private catsService: CatsService) {}
Nest is able to work with any Node HTTP framework once an adapter is created. There are two HTTP platforms supported out-of-the-box: and .
Whichever platform is used, it exposes its own application interface. These are seen respectively as NestExpressApplication and NestFastifyApplication.
When you pass a type to the NestFactory.create() method, as in the example below, the app object will have methods available exclusively for that specific platform.
Each application has at least one module, a app module.
The AppModule is the entry point for the application. When the NestJS application starts, it looks for the AppModule to initialize and configure the application. Nest uses to build the application graph
For the module having controller should be imported into app module to ensures that the controllers are registered with the NestJS application and that their routes are accessible.
main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// Apply global middleware
app.use(someMiddleware());
await app.listen(3000);
}
bootstrap();
app.module.ts
import { Module } from '@nestjs/common';
import { CatsModule } from './cats/cats.module';
@Module({
imports: [CatsModule],
})
export class AppModule {}
It can be used to provide global guard, filter, interceptor
There are 2 methods to define intercepter, guard globally
one is to apply in main.ts directly , the guard , intercepter class cannot inject other dependencies into it , since it is registered outside of any module
main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { RolesGuard } from './guards/roles.guard';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalGuards(new RolesGuard()); // Apply the guard globally
await app.listen(3000);
}
bootstrap();
Controller
To handle and receive incoming HTTP requests and send responses to the client.
Using this built-in method, when a request handler returns a JavaScript object or array, it will automatically be serialized to JSON. When it returns a JavaScript primitive type (e.g., string, number, boolean), however, Nest will send just the value without attempting to serialize it. This makes response handling simple: just return the value, and Nest takes care of the rest.
Provider
Many of the basic Nest classes may be treated as a provider – services, repositories, factories, helpers, and so on. The main idea of a provider is that the class that can be injected as a dependency
If A service want to get B Service from provider, and B service is also have the relationship with C Service, C must be injected to A Service
In the above example. the class included in dragon module and dog service can be injected into cat controller class, as dog module exported part included it and be imported
However, the circular dependencies relationship should be prohibited, here is an example:
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { LoggerMiddleware } from './common/middleware/logger.middleware';
import { CatsModule } from './cats/cats.module';
@Module({
imports: [CatsModule],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.forRoutes('cats');
}
}
Exception Filter
Nest comes with a built-in exceptions layer which is responsible for processing all unhandled exceptions across an application. When an exception is not handled by your application code,
It can help to customize the error response depend on the kind of exception thrown
export class OptionalIntPipe implements PipeTransform {
transform(value: string, metadata: ArgumentMetadata): number | undefined {
if (!value) return undefined;
const num = Number(value);
if (isNaN(num))
throw new BadRequestException(
OPTIONAL_INT_PIPE_NUMBER.replace('$key', metadata.data),
);
return num;
}
}
Guard
Guards have a single responsibility. They determine whether a given request will be handled by the route handler or not, depending on certain conditions (like permissions, roles, ACLs, etc.) present at run-time. This is often referred to as authorization
Every guard must implement a canActivate() function. This function should return a boolean, indicating whether the current request is allowed or not.
The canActivate() function takes a single argument, the ExecutionContext instance. The ExecutionContext inherits from ArgumentsHost. ArgumentsHost simply acts as an abstraction over a handler's arguments. For example, for HTTP server applications (when @nestjs/platform-express is being used), the host object encapsulates Express's [request, response, next] array
Guards have access to the ExecutionContext instance, and thus know exactly what's going to be executed next.
import { Test } from '@nestjs/testing';
import { ModuleMocker, MockFunctionMetadata } from 'jest-mock';
import { DogService } from './dog.service';
import { mockDeep, DeepMockProxy, any } from 'jest-mock-extended';
import { MouseService } from 'src/mouse/mouse.service';
import { PrismaService } from 'src/prisma/prisma.service';
import { User } from '@prisma/client';
const moduleMocker = new ModuleMocker(global);
let dogService: DogService;
describe('DogService', () => {
let mouseService: DeepMockProxy<MouseService>;
let prismaService: DeepMockProxy<PrismaService>;
beforeEach(async () => {
mouseService = mockDeep<MouseService>();
prismaService = mockDeep<PrismaService>();
const moduleRef = await Test.createTestingModule({
providers: [
DogService,
{ provide: MouseService, useValue: mouseService },
{ provide: PrismaService, useValue: prismaService },
],
})
.useMocker((token) => {
if (typeof token === 'function') {
const mockMetadata = moduleMocker.getMetadata(
token,
) as MockFunctionMetadata<any, any>;
const Mock = moduleMocker.generateFromMetadata(mockMetadata);
return new Mock();
}
})
.compile();
dogService = moduleRef.get(DogService);
});
describe('test', () => {
it('/test', () => {
mouseService.getNumber.calledWith(1).mockResolvedValue(1);
expect(dogService.test(1)).resolves.toBe(1);
});
});
describe('prismaTest', () => {
it('/test', () => {
const deletedUser = { id: 1 };
prismaService.user.delete
.calledWith(any())
.mockResolvedValue(deletedUser as User);
expect(prismaService.user.delete({ where: { id: 1 } })).resolves.toEqual(
deletedUser,
);
expect(dogService.prismaTest(1)).resolves.toBe(deletedUser);
});
});
});
The Test class is useful for providing an application execution context that essentially mocks the full Nest runtime, but gives you hooks that make it easy to manage class instances, including mocking and overriding.
Nest also allows you to define a mock factory to apply to all of your missing dependencies. This is useful for cases where you have a large number of dependencies in a class and mocking all of them will take a long time and a lot of setup. To make use of this feature, the createTestingModule() will need to be chained up with the useMocker() method, passing a factory for your dependency mocks.
Jest-mock-extended provides complete Typescript type safety for interfaces, argument types and return types. If your class has objects returns from methods that you would also like to mock, you can use mockDeep in replacement for mock.
We can use the library-specific (e.g., Express) , which can be injected using the @Res() decorator in the method handler signature (e.g., findAll(@Res() response)). With this approach, you have the ability to use the native response handling methods exposed by that object. For example, with Express, you can construct responses using code like response.status(200).send().
Middleware is a function which is called before the route handler. Middleware functions have access to the and objects, and the next() middleware function in the application’s request-response cycle. The next middleware function is commonly denoted by a variable named next.