Node.js REST API Patterns
概览
Node.js REST API 是使用 Node.js 和 Express.js 框架构建的 RESTful API,使用中间件模式处理跨领域关注点,如认证、日志记录、错误处理和验证。
Node.js REST API 包括:
- Express.js: 构建 REST API 的 Web 框架
- 中间件模式: 可链式调用的中间件用于请求/响应处理
- TypeScript: 使用 TypeScript 进行类型安全的开
- RESTful 设计: REST API 设计原则和最佳实践
- 分层架构: 控制器-服务-仓库模式用于关注点分离
为什么这很重要
- 提高开发速度: Node.js 可以将开发速度提高 2-3 倍
- 减少上市时间: 快速开发减少了上市时间
- 提高可维护性: 分层架构提高了可维护性
- 增强开发体验: TypeScript 通过类型安全提高了 DX
- 降低学习曲线: JavaScript 生态系统有社区支持和资源
核心概念
1. 项目结构
src/
├── config/ # 配置文件
│ ├── database.ts
│ ├── env.ts
│ └── constants.ts
├── controllers/ # 请求处理器
│ ├── user.controller.ts
│ └── auth.controller.ts
├── services/ # 业务逻辑
│ ├── user.service.ts
│ └── auth.service.ts
├── repositories/ # 数据访问层
│ ├── user.repository.ts
│ └── base.repository.ts
├── middleware/ # Express 中间件
│ ├── auth.middleware.ts
│ ├── error.middleware.ts
│ └── validation.middleware.ts
├── routes/ # 路由定义
│ ├── index.ts
│ ├── user.routes.ts
│ └── auth.routes.ts
├── types/ # TypeScript 类型
│ └── index.ts
├── utils/ # 工具函数
│ ├── logger.ts
│ └── response.util.ts
├── validators/ # 请求验证模式
│ └── user.validator.ts
├── app.ts # Express 应用设置
└── server.ts # 服务器入口点
2. Express.js 设置
import express, { Application } from "express"
import cors from "cors"
import helmet from "helmet"
import morgan from "morgan"
import { errorHandler } from "./middleware/error.middleware"
import routes from "./routes"
export function createApp(): Application {
const app = express()
// 安全
app.use(helmet())
// CORS
app.use(cors())
// 日志记录
app.use(morgan("combined"))
// 正文解析
app.use(express.json())
app.use(express.urlencoded({ extended: true }))
// 路由
app.use("/api", routes)
// 错误处理(必须是最后)
app.use(errorHandler)
return app
}
3. 认证中间件
import { Request, Response, NextFunction } from "express"
import jwt from "jsonwebtoken"
interface JwtPayload {
userId: string
email: string
}
declare global {
namespace Express {
interface Request {
user?: JwtPayload
}
}
}
export function authMiddleware(req: Request, res: Response, next: NextFunction) {
const authHeader = req.headers.authorization
if (!authHeader || !authHeader.startsWith("Bearer ")) {
throw new UnauthorizedError("No token provided")
}
const token = authHeader.substring(7)
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET!) as JwtPayload
req.user = decoded
next()
} catch (error) {
throw new UnauthorizedError("Invalid token")
}
}
4. 错误处理中间件
export function errorHandler(
error: Error,
req: Request,
res: Response,
next: NextFunction
): void {
logger.error(error, { path: req.path, method: req.method })
if (error instanceof AppError) {
return res.status(error.statusCode).json({
success: false,
message: error.message,
...(error.details && { details: error.details }),
})
}
res.status(500).json({
success: false,
message: process.env.NODE_ENV === "production"
? "Internal server error"
: error.message,
})
}
5. 请求验证
import { AnyZodObject, ZodError } from "zod"
export function validate(schema: AnyZodObject) {
return (req: Request, res: Response, next: NextFunction) => {
const result = schema.safeParse(req.body)
if (!result.success) {
const errors = result.error.errors.map((err) => ({
field: err.path.join("."),
message: err.message,
}))
return res.status(400).json({
success: false,
message: "Validation failed",
errors,
})
}
req.body = result.data
next()
}
}
6. 控制器模式
export class UserController {
constructor(private userService: UserService) {}
async getAll(req: Request, res: Response, next: NextFunction) {
try {
const users = await this.userService.findAll()
res.json({ success: true, data: users })
} catch (error) {
next(error)
}
}
async getById(req: Request, res: Response, next: NextFunction) {
try {
const user = await this.userService.findById(req.params.id)
res.json({ success: true, data: user })
}
catch (error) {
next(error)
}
}
async create(req: Request, res: Response, next: NextFunction) {
try {
const user = await this.userService.create(req.body)
res.status(201).json({ success: true, data: user })
} catch (error) {
next(error)
}
}
}
7. 服务层模式
export class UserService {
constructor(private userRepository: UserRepository) {}
async findAll() {
return this.userRepository.findAll()
}
async findById(id: string) {
const user = await this.userRepository.findById(id)
if (!user) {
throw new NotFoundError("User not found")
}
return user
}
async create(data: CreateUserDto) {
const existing = await this.userRepository.findByEmail(data.email)
if (existing) {
throw new ConflictError("Email already exists")
}
return this.userRepository.create(data)
}
async update(id: string, data: UpdateUserDto) {
await this.findById(id)
return this.userRepository.update(id, data)
}
async delete(id: string) {
await this.findById(id)
await this.userRepository.delete(id)
}
}
8. 仓库模式
export class UserRepository extends BaseRepository<User> {
constructor(prisma: PrismaClient) {
super(prisma)
}
async findAll() {
return this.prisma.user.findMany()
}
async findById(id: string) {
return this.prisma.user.findUnique({ where: { id } })
}
async findByEmail(email: string) {
return this.prisma.user.findUnique({ where: { email } })
}
async create(data: CreateUserDto) {
return this.prisma.user.create({
data: {
...data,
password: await hashPassword(data.password),
},
})
}
async update(id: string, data: UpdateUserDto) {
return this.prisma.user.update({
where: { id },
data,
})
}
async delete(id: string) {
return this.prisma.user.delete({ where: { id } })
}
}
9. 依赖注入
class DIContainer {
private static instance: DIContainer
private repositories: Map<string, any>
private services: Map<string, any>
private controllers: Map<string, any>
private constructor() {
this.repositories = new Map()
this.services = new Map()
this.controllers = new Map()
this.initialize()
}
private initialize() {
this.repositories.set("user", new UserRepository(prisma))
this.services.set("user", new UserService(this.repositories.get("user")))
this.controllers.set("user", new UserController(this.services.get("user")))
}
getController(name: string) {
return this.controllers.get(name)
}
}
export const container = DIContainer.getInstance()
快速开始
-
初始化项目:
npm init -y npm install express typescript @types/express -
创建基本应用:
import express from "express" const app = express() app.get("/", (req, res) => res.json({ hello: "world" })) app.listen(3000) -
添加中间件:
npm install helmet cors morgan -
设置 TypeScript:
npx tsc --init -
启动服务器:
npm run dev
生产清单
- [ ] 使用 TypeScript 进行类型安全
- [ ] 实现分层架构
- [ ] 使用中间件处理跨领域关注点
- [ ] 实现适当的错误处理
- [ ] 添加请求验证
- [ ] 实现认证和授权
- [ ] 使用环境变量进行配置
- [ ] 添加日志记录和监控
- [ ] 为所有层编写单元测试
- [ ] 为 API 端点编写集成测试
- [ ] 使用 async/await 进行异步操作
- [ ] 实现适当的 CORS 配置
- [ ] 添加速率限制
- [ ] 使用安全头(Helmet)
- [ ] 为所有 API 文档化
反模式
- 阻塞事件循环: 正确使用 async/await
- 内存泄漏: 正确管理连接和计时器
- 未捕获异常: 实现全局错误处理程序
- 无验证: 始终验证所有输入
- 无日志记录: 记录所有请求和错误
- 无测试: 编写全面的测试
- 无文档: 为所有 API 文档化
集成点
- Express REST:
03-backend-api/express-rest - 中间件:
03-backend-api/middleware - 错误处理:
03-backend-api/error-handling - 验证:
03-backend-api/validation - 认证:
10-authentication-authorization - 数据库优化:
04-database/database-optimization