NestJS is a progressive Node.js framework for building efficient, reliable, and scalable server-side applications. Combining the best ideas from OOP (Object-Oriented Programming), FP (Functional Programming), and FRP (Functional Reactive Programming), it gives you a fully-architected, batteries-included platform on top of Express (or Fastify).
If you’re coming from Angular, you’ll feel right at home with its module/controller/service structure and powerful dependency-injection system.
1. What is NestJS?
NestJS is a framework for building server-side applications in Node.js. It’s written in TypeScript (but supports plain JavaScript as well). At its core, it:
- Wraps a mature HTTP server library (Express or Fastify)
- Standardizes application architecture around modules, controllers, and providers
- Leverages TypeScript’s type system for compile-time safety and clear APIs
- Offers built-in support for things like validation, configuration, and testing
Install the Nest CLI globally
npm install -g @nestjs/cli
Create a new project called 'my-app'
nest new my-app
cd my-app
npm run start:dev
Modules
A simple module example:
@Module({
controllers: [ItemController],
providers: [ItemService],
})
export class ItemModule {}
Controllers
- @get() — Defines a get request
- @post() — Defines a post request
- @Delete() — Delete request
- @Put() — Put request
Here is an example of a simple controller with one get route:
@Controller('item')
export class ItemController {
@get()
findAll(): string {
return 'Returns all items';
}
}
Providers
For example, we can easily create a service which fetches all our items and use it in our ItemController.
@Injectable()
export class ItemService {
private readonly items: Item[] = [{ title: 'Great item', price: 10 }];
create(item: Item) {
this.items.push(item);
}
findAll(): Item[] {
return this.items;
}
}
Now that we have defined our service let’s use it in our controller:
@Controller('item')
export class ItemController {
constructor(private readonly itemService: ItemService) {}
@get()
async findAll(): Promise {
return this.itemService.findAll();
}
}
Usage
Each of these four lifecycle hooks is represented by an interface. That means that we just need to implement the interface in our component (class) and override the function.
Here is a simple example of the OnModuleInit interface:
import { Injectable, OnModuleInit } from '@nestjs/common';
@Injectable()
export class ItemService implements OnModuleInit {
onModuleInit() {
console.log(The module has been initialized.
);
}
}
Pipes
Pipes in Nestjs are used to operate on the arguments of the controller route handler. This gives them two typical use cases:
transformation — transform input data to the desired output
validation — evaluate if the input data is valid
Usage:
Pipes can be created by implementing the PipeTransform interface on our class and overriding the transform function. Let’s look at a simple example of a custom ValidationPipe:
import { PipeTransform, Injectable, ArgumentMetadata } from '@nestjs/common';
@Injectable()
export class CustomValidationPipe implements PipeTransform {
transform(value: any, metadata: ArgumentMetadata) {
const { metatype } = metadata;
if (!metatype) {
return value;
}
const convertedValue = plainToClass(metatype, value);
return convertedValue;
}
}
In this example we check if the metatag we provided isn’t empty and if so we converted the received data to the metatype we defined.
Unit tests
Now let’s look at a simple unit test for the ItemService we defined above.
import { Test } from '@nestjs/testing';
import { ItemService } from './item.service';
describe('ItemService', () => {
let service: ItemService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [ItemService],
}).compile();
service = module.get<ItemService>(ItemService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
Top comments (0)