Розробка веб-бекенду на Node.js (NestJS)
NestJS—не просто фреймворк, це архітектурне рішення. Нав'язує структуру, яку команда інакше винаходила б сама: модулі, провайдери, dependency injection, декоратори. Для складних проектів з кількома розробниками це цінність. Для малого API в одиночку—надмірність.
NestJS оправданий при: monorepo з кількома додатками, мікросервісній архітектурі, команді від трьох розробників, довгостроковому проекті з мінливою командою.
Структура модулів
Кожен функціональний блок—окремий модуль. Модуль оголошує, що експортує й що імпортує:
// users/users.module.ts
@Module({
imports: [TypeOrmModule.forFeature([User]), JwtModule],
controllers: [UsersController],
providers: [UsersService, UsersRepository],
exports: [UsersService]
})
export class UsersModule {}
// app.module.ts
@Module({
imports: [
ConfigModule.forRoot({ isGlobal: true }),
TypeOrmModule.forRootAsync({
useFactory: (config: ConfigService) => ({
type: 'postgres',
url: config.get('DATABASE_URL'),
entities: [__dirname + '/**/*.entity{.ts,.js}']
}),
inject: [ConfigService]
}),
UsersModule,
ProductsModule,
AuthModule,
OrdersModule,
]
})
export class AppModule {}
Контролери й валідація
Контролер обробляє тільки HTTP-шар: маршрути, заголовки, коди статусу. Логіка—у сервісах.
@Controller('users')
@UseGuards(JwtAuthGuard)
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@Get()
@Roles('admin')
findAll(@Query() paginationDto: PaginationDto): Promise<PaginatedResult<User>> {
return this.usersService.findAll(paginationDto)
}
@Get(':id')
async findOne(@Param('id', ParseIntPipe) id: number): Promise<User> {
const user = await this.usersService.findById(id)
if (!user) throw new NotFoundException(`User ${id} not found`)
return user
}
@Post()
@HttpCode(HttpStatus.CREATED)
create(@Body() createUserDto: CreateUserDto): Promise<User> {
return this.usersService.create(createUserDto)
}
}
Вбудовані pipes валідують і трансформують DTO автоматично. Декоратори @Roles, @UseGuards застосовують авторизацію перед контролером.
Сервіси й Dependency Injection
// users.service.ts
@Injectable()
export class UsersService {
constructor(
private readonly usersRepository: UsersRepository,
private readonly jwtService: JwtService,
private readonly config: ConfigService
) {}
async create(createUserDto: CreateUserDto): Promise<User> {
const existing = await this.usersRepository.findByEmail(createUserDto.email)
if (existing) throw new ConflictException('Email already exists')
const user = await this.usersRepository.create({
...createUserDto,
password: await this.hashPassword(createUserDto.password)
})
return user
}
private async hashPassword(password: string): Promise<string> {
return bcrypt.hash(password, 10)
}
}
Сервіси інжектабельні—без ручної інстанціації. Контейнер управляє lifetime (singleton, request-scoped, transient).
База даних з TypeORM
// user.entity.ts
@Entity('users')
export class User {
@PrimaryGeneratedColumn()
id: number
@Column({ unique: true })
email: string
@Column()
passwordHash: string
@Column({ type: 'enum', enum: UserRole, default: UserRole.USER })
role: UserRole
@CreateDateColumn()
createdAt: Date
}
OpenAPI документація
NestJS генерує Swagger docs автоматично:
npm install @nestjs/swagger swagger-ui-express
Тестування
Вбудований тестовий модуль NestJS:
// users.service.spec.ts
describe('UsersService', () => {
let service: UsersService
let repo: UsersRepository
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
UsersService,
{ provide: UsersRepository, useValue: mockRepository }
]
}).compile()
service = module.get<UsersService>(UsersService)
})
it('should create user', async () => {
const result = await service.create({ email: '[email protected]' })
expect(result).toBeDefined()
})
})
Таймлайн
Малий REST API з NestJS—1–2 тижні: модулі, аутентифікація, базовий CRUD. Середній monorepo (3 сервіси)—4–6 тижнів. Складна система—залежить від фіч, типово 8+ тижнів.
NestJS інвестиція окупається в команді від 3+ й довгострокових проектах—boilerplate амортизується.







