NestJS + TypeORM 企业级CRUD接口开发实战
1. 现代后端开发的技术选型
在当今快速迭代的互联网产品开发中,Node.js凭借其非阻塞I/O和高并发特性,已成为构建高性能后端服务的首选技术之一。而NestJS作为Node.js平台上最成熟的企业级框架,结合TypeORM这一强大的ORM工具,能够为开发者提供稳定高效的开发体验。
为什么选择NestJS+TypeORM组合?
- 模块化架构:NestJS采用模块化设计,天然支持微服务架构
- TypeScript支持:完整的类型系统保障代码质量
- 丰富的生态系统:与主流技术栈无缝集成
- Active Record与Data Mapper:TypeORM支持两种主流数据操作模式
- 跨数据库支持:MySQL、PostgreSQL、MongoDB等
2. 项目初始化与基础配置
2.1 创建NestJS项目
npm i -g @nestjs/cli
nest new nest-crud-demo
cd nest-crud-demo
2.2 安装TypeORM及相关依赖
npm install @nestjs/typeorm typeorm mysql2
2.3 配置数据库连接
在app.module.ts中配置TypeORM:
import { TypeOrmModule } from '@nestjs/typeorm';
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'mysql',
host: 'localhost',
port: 3306,
username: 'root',
password: 'yourpassword',
database: 'nest_crud',
entities: [__dirname + '/**/*.entity{.ts,.js}'],
synchronize: true, // 生产环境应设为false
}),
],
})
export class AppModule {}
3. 核心功能实现
3.1 实体定义与Repository模式
创建用户实体user.entity.ts:
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column({ length: 50 })
username: string;
@Column({ length: 100 })
email: string;
@Column()
age: number;
@Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
createdAt: Date;
}
3.2 模块化组织代码
创建用户模块:
nest g module users
nest g controller users
nest g service users
在users.module.ts中注册实体:
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './user.entity';
@Module({
imports: [TypeOrmModule.forFeature([User])],
controllers: [UsersController],
providers: [UsersService],
})
export class UsersModule {}
3.3 实现CRUD服务层
users.service.ts核心实现:
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, Like } from 'typeorm';
import { User } from './user.entity';
@Injectable()
export class UsersService {
constructor(
@InjectRepository(User)
private usersRepository: Repository<User>,
) {}
async create(user: Partial<User>): Promise<User> {
return this.usersRepository.save(user);
}
async findAll(query: {
page?: number;
limit?: number;
username?: string
}): Promise<{ data: User[]; total: number }> {
const { page = 1, limit = 10, username } = query;
const [data, total] = await this.usersRepository.findAndCount({
where: username ? { username: Like(`%${username}%`) } : {},
skip: (page - 1) * limit,
take: limit,
});
return { data, total };
}
async findOne(id: number): Promise<User> {
return this.usersRepository.findOne({ where: { id } });
}
async update(id: number, updateData: Partial<User>): Promise<void> {
await this.usersRepository.update(id, updateData);
}
async remove(id: number): Promise<void> {
await this.usersRepository.delete(id);
}
}
3.4 控制器与DTO验证
创建DTO类进行数据验证:
import { IsEmail, IsInt, IsString, Min, Max } from 'class-validator';
export class CreateUserDto {
@IsString()
username: string;
@IsEmail()
email: string;
@IsInt()
@Min(0)
@Max(120)
age: number;
}
export class UpdateUserDto extends PartialType(CreateUserDto) {}
实现控制器users.controller.ts:
import { Controller, Get, Post, Body, Param, Query } from '@nestjs/common';
import { UsersService } from './users.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
@Controller('users')
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@Post()
create(@Body() createUserDto: CreateUserDto) {
return this.usersService.create(createUserDto);
}
@Get()
findAll(
@Query('page') page: number = 1,
@Query('limit') limit: number = 10,
@Query('username') username?: string,
) {
return this.usersService.findAll({ page, limit, username });
}
@Get(':id')
findOne(@Param('id') id: string) {
return this.usersService.findOne(+id);
}
@Patch(':id')
update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
return this.usersService.update(+id, updateUserDto);
}
@Delete(':id')
remove(@Param('id') id: string) {
return this.usersService.remove(+id);
}
}
4. 高级功能实现
4.1 分页查询优化
实现更专业的分页响应结构:
export interface PaginationResult<T> {
data: T[];
meta: {
total: number;
page: number;
limit: number;
lastPage: number;
};
}
// 在服务中实现
async paginate(options: {
page: number;
limit: number;
}): Promise<PaginationResult<User>> {
const [data, total] = await this.usersRepository.findAndCount({
skip: (options.page - 1) * options.limit,
take: options.limit,
});
const lastPage = Math.ceil(total / options.limit);
return {
data,
meta: {
total,
page: options.page,
limit: options.limit,
lastPage,
},
};
}
4.2 查询构建器高级用法
使用TypeORM QueryBuilder实现复杂查询:
async searchUsers(criteria: {
username?: string;
minAge?: number;
maxAge?: number;
}): Promise<User[]> {
const queryBuilder = this.usersRepository.createQueryBuilder('user');
if (criteria.username) {
queryBuilder.andWhere('user.username LIKE :username', {
username: `%${criteria.username}%`,
});
}
if (criteria.minAge) {
queryBuilder.andWhere('user.age >= :minAge', { minAge: criteria.minAge });
}
if (criteria.maxAge) {
queryBuilder.andWhere('user.age <= :maxAge', { maxAge: criteria.maxAge });
}
return queryBuilder.getMany();
}
4.3 事务处理
确保数据一致性的关键操作:
async transferCredits(
fromUserId: number,
toUserId: number,
amount: number,
): Promise<void> {
await this.usersRepository.manager.transaction(async (manager) => {
const fromUser = await manager.findOne(User, {
where: { id: fromUserId },
});
const toUser = await manager.findOne(User, {
where: { id: toUserId },
});
if (!fromUser || !toUser) {
throw new Error('用户不存在');
}
if (fromUser.credits < amount) {
throw new Error('余额不足');
}
fromUser.credits -= amount;
toUser.credits += amount;
await manager.save(User, fromUser);
await manager.save(User, toUser);
});
}
5. 生产环境最佳实践
5.1 性能优化技巧
- 索引优化:为常用查询字段添加数据库索引
- 缓存策略:使用Redis缓存热点数据
- 批量操作:减少数据库往返次数
// 批量插入示例
async batchCreate(users: Partial<User>[]): Promise<User[]> {
return this.usersRepository
.createQueryBuilder()
.insert()
.into(User)
.values(users)
.execute();
}
5.2 安全防护措施
- 输入验证:使用class-validator严格校验输入
- SQL注入防护:使用参数化查询
- 敏感数据过滤:在DTO中排除敏感字段
import { Exclude } from 'class-transformer';
export class UserDto {
id: number;
username: string;
@Exclude()
password: string;
}
5.3 日志与监控
集成日志系统:
import { Logger } from '@nestjs/common';
export class UsersService {
private readonly logger = new Logger(UsersService.name);
async findOne(id: number) {
this.logger.log(`Fetching user with ID: ${id}`);
try {
return await this.usersRepository.findOne({ where: { id } });
} catch (error) {
this.logger.error(`Failed to fetch user ${id}: ${error.message}`);
throw error;
}
}
}
6. 测试策略
6.1 单元测试示例
describe('UsersService', () => {
let service: UsersService;
let repository: Repository<User>;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
UsersService,
{
provide: getRepositoryToken(User),
useClass: Repository,
},
],
}).compile();
service = module.get<UsersService>(UsersService);
repository = module.get<Repository<User>>(getRepositoryToken(User));
});
it('should create a user', async () => {
const userData = {
username: 'testuser',
email: 'test@example.com',
age: 25,
};
jest.spyOn(repository, 'save').mockResolvedValueOnce({
id: 1,
...userData,
createdAt: new Date(),
});
const result = await service.create(userData);
expect(result).toHaveProperty('id', 1);
expect(repository.save).toHaveBeenCalledWith(userData);
});
});
6.2 E2E测试配置
import * as request from 'supertest';
describe('UsersController (e2e)', () => {
let app: INestApplication;
beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [UsersModule],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
it('/users (POST)', () => {
return request(app.getHttpServer())
.post('/users')
.send({
username: 'e2etest',
email: 'e2e@test.com',
age: 30,
})
.expect(201)
.expect((res) => {
expect(res.body).toHaveProperty('id');
expect(res.body.username).toBe('e2etest');
});
});
afterAll(async () => {
await app.close();
});
});
7. 部署与扩展
7.1 Docker化部署
Dockerfile配置示例:
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["npm", "run", "start:prod"]
docker-compose.yml数据库集成:
version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
environment:
DB_HOST: db
depends_on:
- db
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: nest_crud
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
volumes:
mysql_data:
7.2 水平扩展考虑
- 无状态设计:确保服务实例可互换
- 共享会话:使用Redis存储会话数据
- 数据库连接池:优化连接管理
// 数据库连接池配置
TypeOrmModule.forRoot({
// ...其他配置
extra: {
connectionLimit: 10, // 连接池大小
},
}),
8. 常见问题解决方案
8.1 性能瓶颈排查
常见性能问题及解决方案:
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 查询缓慢 | 缺少索引 | 分析查询并添加适当索引 |
| 高内存使用 | 内存泄漏 | 使用内存分析工具检查 |
| 连接耗尽 | 连接泄漏 | 检查连接释放逻辑 |
8.2 事务处理陷阱
事务使用注意事项:
- 避免在事务中执行长时间运行的操作
- 合理设置事务隔离级别
- 处理死锁情况
async safeTransaction(callback: () => Promise<void>) {
const maxRetries = 3;
let retries = 0;
while (retries < maxRetries) {
try {
await this.connection.transaction(callback);
break;
} catch (error) {
if (error.code === 'ER_LOCK_DEADLOCK') {
retries++;
await new Promise(resolve => setTimeout(resolve, 100 * retries));
} else {
throw error;
}
}
}
}
9. 项目结构优化建议
推荐的企业级项目结构:
src/
├── common/ # 公共模块
│ ├── decorators/ # 自定义装饰器
│ ├── filters/ # 异常过滤器
│ └── interceptors/ # 拦截器
├── config/ # 配置管理
├── modules/ # 业务模块
│ └── users/
│ ├── dto/ # 数据传输对象
│ ├── entities/ # 数据库实体
│ ├── interfaces/ # 类型定义
│ ├── users.module.ts
│ ├── users.service.ts
│ └── users.controller.ts
├── app.module.ts
└── main.ts



被折叠的 条评论
为什么被折叠?



