Services and dependency injection
A service is a class that holds the application logic: storing, finding and changing data. The controller only accepts the request and calls the service. NestJS calls such classes providers: it creates their objects itself and hands them to whoever needs them. This mechanism is called dependency injection (DI).
The post class
First describe what a post looks like. The file src/posts/entities/post.entity.ts was created by nest g resource:
// src/posts/entities/post.entity.ts
export class Post {
id: number;
title: string;
content: string;
}Later this class becomes a database table; for now posts are kept in an array.
The service
nest g service posts// src/posts/posts.service.ts
import { Injectable } from '@nestjs/common';
import { CreatePostDto } from './dto/create-post.dto';
import { Post } from './entities/post.entity';
@Injectable()
export class PostsService {
private posts: Post[] = [];
private nextId = 1;
findAll(): Post[] {
return this.posts;
}
findOne(id: number): Post | undefined {
return this.posts.find((post) => post.id === id);
}
create(dto: CreatePostDto): Post {
const post = { id: this.nextId++, ...dto };
this.posts.push(post);
return post;
}
}The @Injectable() decorator marks the class as a provider that NestJS can manage. The CLI has already added PostsService to the providers of PostsModule.
Injecting into the controller
The controller does not create the service with new; it asks for it in the constructor:
// src/posts/posts.controller.ts
import { Body, Controller, Get, Param, Post } from '@nestjs/common';
import { CreatePostDto } from './dto/create-post.dto';
import { PostsService } from './posts.service';
@Controller('posts')
export class PostsController {
constructor(private readonly postsService: PostsService) {}
@Get()
findAll() {
return this.postsService.findAll();
}
@Get(':id')
findOne(@Param('id') id: string) {
return this.postsService.findOne(Number(id));
}
@Post()
create(@Body() dto: CreatePostDto) {
return this.postsService.create(dto);
}
}private readonly postsService in the constructor is a TypeScript shorthand: it declares a property and assigns the argument to it. NestJS looks at the PostsService type, finds that provider in the module and passes its object.
Create a post with a POST request, then open http://localhost:3000/posts — it is in the list. After a server restart the array is empty again: the database arrives in chapter four.
One object for the whole application
NestJS creates a service once and hands the same object to everyone who asks for it. That is how CommentsService from the modules lesson sees the same posts as the controller:
// src/comments/comments.service.ts
import { Injectable } from '@nestjs/common';
import { PostsService } from '../posts/posts.service';
@Injectable()
export class CommentsService {
constructor(private readonly postsService: PostsService) {}
canComment(postId: number): boolean {
return this.postsService.findOne(postId) !== undefined;
}
}Common mistake
If the service is not listed in the module's providers, NestJS fails at startup with Nest can't resolve dependencies of the PostsController (?). The question mark stands in place of the argument it could not find. Check that the service has the decorator and is listed in its module's providers, and if it comes from another module, that the module exports it.