Components
Creating a Component
Almost everything is a component – Service, Repository, Factory, Helper [...] and they can be injected into controllers or to other components through constructor
.
In the previous chapter, we have built a simple CatsController
.
The controllers should only handle HTTP requests and delegate more complex tasks to the components. The components are a plain TypeScript classes with @Component()
decorator.
Hint
Since Nest enables the possibility to design, organize the dependencies in more OO-way, we strongly recommend to follow the SOLID principles.
Let's create a CatsService
component:
TypeScript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | import { Component } from '@nestjs/common'; import { Cat } from './interfaces/cat.interface'; @Component() export class CatsService { private readonly cats: Cat[] = []; create(cat: Cat) { this.cats.push(cat); } findAll(): Cat[] { return this.cats; } } |
JavaScript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | import { Component } from '@nestjs/common'; @Component() export class CatsService { constructor() { this.cats = []; } create(cat) { this.cats.push(cat); } findAll() { return this.cats; } } |
There's nothing specifically about components. Here's a CatsService
, a basic class with one property and two methods. The only difference is that it has the @Component()
decorator. The @Component()
attaches the metadata, thereby Nest knows that this class is a Nest component.
Note
There's a Cat
interface here. I didn't mention it because the schema is exactly same as in the CreateCatDto
class which we have created in the previous chapter.
Dependency Injection
Since we have the service class already done, let's use it inside the CatsController
:
TypeScript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | import { Controller, Get, Post, Body } from '@nestjs/common'; import { CreateCatDto } from './dto/create-cat.dto'; import { CatsService } from './cats.service'; import { Cat } from './interfaces/cat.interface'; @Controller('cats') export class CatsController { constructor(private readonly catsService: CatsService) {} @Post() async create(@Body() createCatDto: CreateCatDto) { this.catsService.create(createCatDto); } @Get() async findAll(): Promise<Cat[]> { return this.catsService.findAll(); } } |
JavaScript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | import { Controller, Get, Post, Body, Bind, Dependencies } from '@nestjs/common'; import { CatsService } from './cats.service'; @Controller('cats') @Dependencies(CatsService) export class CatsController { constructor(catsService) { this.catsService = catsService; } @Post() @Bind(Body()) async create(createCatDto) { this.catsService.create(createCatDto); } @Get() async findAll() { return this.catsService.findAll(); } } |
The CatsService
is injected through the class constructor. Don't be afraid of the private readonly
shortened syntax. It means that we've created and initialized the catsService
member in the same location.
Constructor Syntax
Nest is built around the strong design pattern, which is commonly known as a Dependency Injection. There's a great article about this concept in the official Angular documentation.
Hint
Learn more about the Dependency Injection in Nest here.
It's extremely easy to manage dependencies with TypeScript because Nest will recognize your dependencies just by type. This single line:
1 | constructor(private readonly catsService: CatsService) {}` |
Warning
Be sure to set the emitDecoratorMetadata
and experimentalDecorators
flags to true in your tsconfig.json. This tells the compiler to attatch the decorators we use for components to the classes they decorate. Without these flags, you will get a run-time error.
Component Instantiation
The last thing we need to do is tell the module that CatsService
exists in our codebase. The way to go about notifying Nest of your components is by editing your app.module.ts
file, and putting the service into the components
array of the @Module()
decorator metadata.
1 2 3 4 5 6 7 8 9 | import { Module } from '@nestjs/common'; import { CatsController } from './cats/cats.controller'; import { CatsService } from './cats/cats.service'; @Module({ controllers: [CatsController], components: [CatsService], }) export class ApplicationModule { } |
Now Nest will smoothly resolve the dependencies of the CatsController
class.
That's how our directories structure looks right now:
src └── server.ts └── modules └── app.controller.ts └── app.module.ts └── cats └── cats.service.ts └── cats.controller.ts └── dto └── create-cat.dto.ts └── interfaces └── cat.interface.ts