NestJS
本文缩写(英文全称与中文含义)
| 缩写 | 英文全称/名称 | 中文含义 |
|---|---|---|
| API | Application Programming Interface | 应用程序编程接口 |
| BFF | Backend For Frontend | 为特定前端提供的后端层 |
| CI | Continuous Integration | 持续集成 |
| CORS | Cross-Origin Resource Sharing | 跨源资源共享 |
| CPU | Central Processing Unit | 中央处理器 |
| DI | Dependency Injection | 依赖注入 |
| DTO | Data Transfer Object | 数据传输对象 |
| HTTP | Hypertext Transfer Protocol | 超文本传输协议 |
| JSON | JavaScript Object Notation | 一种结构化数据格式 |
| ORM | Object-Relational Mapping | 对象关系映射 |
| SQL | Structured Query Language | 结构化查询语言 |
| WAF | Web Application Firewall | Web 应用防火墙 |
Links#
https://docs.nestjs.com/
https://docs.nestjs.com/first-steps
https://docs.nestjs.com/modules
https://docs.nestjs.com/controllers
https://docs.nestjs.com/providers
https://docs.nestjs.com/techniques/validation
https://docs.nestjs.com/techniques/configuration
https://docs.nestjs.com/guards
https://docs.nestjs.com/interceptors
https://docs.nestjs.com/exception-filters
https://docs.nestjs.com/fundamentals/testing
https://docs.nestjs.com/openapi/introduction1. Important Points#
NestJS 是 Node.js server-side application framework:
TypeScript first
built around module / controller / provider
default HTTP platform can use Express
supports Fastify adapter
supports DI / decorator / metadata
good for API / BFF / worker / microservice gateway
适合:
structured backend API
medium / large TypeScript service
team needs clear module boundary
validation / guard / interceptor / OpenAPI are required
不适合:
very small script
simple one-file HTTP service
team dislikes decorator-heavy style
CPU-heavy workload without worker / queue separation核心原则:
keep controller thin
put business logic in service
use DTO for request boundary
enable ValidationPipe globally
validate config at startup
module should own one business capability
database client should be one shared provider
do not leak ORM query shape into controller2. Service Configuration#
create project#
npm i -g @nestjs/cli
nest new order-api
cd order-api
npm install @nestjs/config zod
npm install drizzle-orm postgres
npm install -D drizzle-kitnotes:
@nestjs/cli:
scaffolds NestJS project
@nestjs/config:
loads environment config
zod:
validates config / request body when needed
drizzle-orm / drizzle-kit:
type-safe SQL ORM and migration toolpackage scripts#
{
"scripts": {
"start:dev": "nest start --watch",
"build": "nest build",
"start:prod": "node dist/main.js",
"test": "jest",
"test:e2e": "jest --config ./test/jest-e2e.json",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"db:generate": "drizzle-kit generate",
"db:migrate": "drizzle-kit migrate",
"db:studio": "drizzle-kit studio"
}
}project structure#
order-api
├── src
│ ├── main.ts
│ ├── app.module.ts
│ ├── config
│ │ ├── app-config.ts
│ │ └── config.schema.ts
│ ├── database
│ │ ├── database.module.ts
│ │ ├── database.provider.ts
│ │ └── schema.ts
│ └── orders
│ ├── dto
│ │ ├── create-order.dto.ts
│ │ └── update-order-status.dto.ts
│ ├── orders.controller.ts
│ ├── orders.module.ts
│ ├── orders.repository.ts
│ └── orders.service.ts
├── drizzle.config.ts
└── test
└── orders.e2e-spec.tsstructure rules:
app.module.ts:
root composition only
main.ts:
global pipes / filters / swagger / app listen
database/:
connection provider and schema
feature module:
controller + service + repository + dto3. Core Concepts#
module#
import { Module } from '@nestjs/common';
import { OrdersController } from './orders.controller';
import { OrdersService } from './orders.service';
import { OrdersRepository } from './orders.repository';
@Module({
controllers: [OrdersController],
providers: [OrdersService, OrdersRepository],
exports: [OrdersService],
})
export class OrdersModule {}module 用来组织 dependency graph:
controllers:
HTTP entrypoints
providers:
service / repository / client / factory
exports:
what other modules can injectcontroller#
import { Body, Controller, Get, Param, Post } from '@nestjs/common';
import { CreateOrderDto } from './dto/create-order.dto';
import { OrdersService } from './orders.service';
@Controller('orders')
export class OrdersController {
constructor(private readonly ordersService: OrdersService) {}
@Post()
create(@Body() dto: CreateOrderDto) {
return this.ordersService.create(dto);
}
@Get(':id')
findById(@Param('id') id: string) {
return this.ordersService.findById(id);
}
}controller should:
parse route / body / query
call service
return response DTO
controller should not:
open database connection
contain business workflow
know SQL detailsprovider / service#
import { Injectable, NotFoundException } from '@nestjs/common';
import { CreateOrderDto } from './dto/create-order.dto';
import { OrdersRepository } from './orders.repository';
@Injectable()
export class OrdersService {
constructor(private readonly ordersRepository: OrdersRepository) {}
async create(dto: CreateOrderDto) {
return this.ordersRepository.create(dto);
}
async findById(id: string) {
const order = await this.ordersRepository.findById(id);
if (!order) {
throw new NotFoundException('order not found');
}
return order;
}
}service should:
coordinate business workflow
enforce business rule
call repository / external client
repository should:
own database query
hide ORM details from service4. Request / Runtime Best Practices#
validation#
import { NestFactory } from '@nestjs/core';
import { ValidationPipe } from '@nestjs/common';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(
new ValidationPipe({
whitelist: true,
forbidNonWhitelisted: true,
transform: true,
}),
);
await app.listen(process.env.PORT ?? 3000);
}
bootstrap();import { IsInt, IsString, Min } from 'class-validator';
export class CreateOrderDto {
@IsString()
userId!: string;
@IsInt()
@Min(1)
amountCents!: number;
}validation baseline:
whitelist:
remove properties without decorators
forbidNonWhitelisted:
reject unexpected properties
transform:
transform plain request object into DTO classconfiguration#
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { z } from 'zod';
const envSchema = z.object({
NODE_ENV: z.enum(['development', 'test', 'staging', 'production']),
PORT: z.coerce.number().default(3000),
DATABASE_URL: z.string().url(),
});
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
validate: (config) => envSchema.parse(config),
}),
],
})
export class AppModule {}config rules:
fail fast at startup
never read process.env everywhere
keep .env.example updated
production secrets should come from secret manager / mounted secret / env injectionexception / response#
use NestJS built-in HTTP exceptions:
BadRequestException
UnauthorizedException
ForbiddenException
NotFoundException
ConflictException
InternalServerErrorException
avoid:
throw new Error() from controller for client errors
return null for missing resource without explicit behavior5. Security Best Practices#
Controller 负责接口边界,不把 ORM 查询形态泄漏给调用者;请求日志要移除 token、密码和敏感正文。
security defaults:
enable validation pipe globally
reject unknown request properties
use helmet in public HTTP API
configure CORS explicitly
do not log request body with secrets
use guard for authn/authz
keep rate limit at gateway / app layer
expose Swagger only in controlled environmentguard#
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
@Injectable()
export class ApiKeyGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
const request = context.switchToHttp().getRequest();
return request.headers['x-api-key'] === process.env.INTERNAL_API_KEY;
}
}guard 用于:
authentication
authorization
route access policy
不要把业务校验全部塞进 guard。6. Reliability / Testing#
unit test#
import { Test } from '@nestjs/testing';
import { OrdersService } from './orders.service';
import { OrdersRepository } from './orders.repository';
describe('OrdersService', () => {
it('returns order by id', async () => {
const moduleRef = await Test.createTestingModule({
providers: [
OrdersService,
{
provide: OrdersRepository,
useValue: {
findById: jest.fn().mockResolvedValue({ id: 'ord_1' }),
},
},
],
}).compile();
const service = moduleRef.get(OrdersService);
await expect(service.findById('ord_1')).resolves.toEqual({ id: 'ord_1' });
});
});e2e test#
e2e test should cover:
route path
validation failure
auth failure
success response
database transaction behavior if applicable7. Monitoring#
logs:
request_id
method
path
status_code
duration_ms
user_id / tenant_id when safe
error_name
metrics:
http_request_duration_seconds
http_requests_total
validation_error_total
database_query_duration_seconds
unhandled_exception_total
alerts:
5xx rate high
p95 / p99 latency high
database connection pool exhausted
error log spike8. Hands-on#
install validation packages#
npm install class-validator class-transformercreate resource#
nest g module orders
nest g controller orders
nest g service ordersrun#
npm run start:dev
curl -X POST http://localhost:3000/orders -H 'content-type: application/json' -d '{"userId":"user_1","amountCents":1000}'swagger#
npm install @nestjs/swagger swagger-ui-expressimport { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
const config = new DocumentBuilder()
.setTitle('order-api')
.setVersion('1.0.0')
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('docs', app, document);9. Production Checklist#
application:
global ValidationPipe enabled
config validation enabled
CORS configured explicitly
Swagger controlled by environment
graceful shutdown enabled
health endpoint available
architecture:
controller is thin
service owns business rules
repository owns database details
modules have clear boundaries
security:
auth guard applied
secrets not logged
dependency audit in CI
rate limit / WAF considered
operations:
structured JSON logs
metrics exported
e2e tests in CI
database migration process documented