在当今快速发展的Web开发领域,Node.js已成为构建高性能后端服务的首选技术之一。然而,随着项目规模的扩大,如何从一开始就建立一个健壮、可维护且高效的Node.js项目架构,是每个开发者都需要面对的问题。本文将带你从零开始,构建一个现代化的Node.js后端项目,涵盖架构设计、工具选择和最佳实践。
1. 项目初始化与基础配置
1.1 项目创建与包管理
首先,让我们创建一个新的Node.js项目:
mkdir modern-node-backend
cd modern-node-backend
npm init -y
1.2 使用TypeScript增强类型安全
虽然JavaScript灵活,但在大型项目中,类型错误可能导致严重问题。TypeScript提供了静态类型检查,大大提高了代码的可靠性。
npm install typescript @types/node --save-dev
npx tsc --init
修改生成的tsconfig.json:
{
"compilerOptions": {
"target": "ES2022",
"module": "commonjs",
"lib": ["ES2022"],
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"moduleResolution": "node",
"allowSyntheticDefaultImports": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
1.3 配置开发工具
ESLint + Prettier 代码规范
npm install eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin prettier eslint-config-prettier eslint-plugin-prettier --save-dev
创建.eslintrc.js:
module.exports = {
parser: '@typescript-eslint/parser',
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended'
],
plugins: ['@typescript-eslint'],
env: {
node: true,
es2022: true
},
rules: {
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/no-unused-vars': ['error', { 'argsIgnorePattern': '^_' }]
}
};
创建.prettierrc:
{
"semi": true,
"trailingComma": "es5",
"singleQuote": true,
"printWidth": 80,
"tabWidth": 2,
"useTabs": false
}
2. 项目架构设计
2.1 分层架构模式
采用清晰的分层架构有助于代码组织和维护:
src/
├── core/ # 核心模块
│ ├── config/ # 配置管理
│ ├── constants/ # 常量定义
│ └── types/ # TypeScript类型定义
├── modules/ # 业务模块
│ ├── users/ # 用户模块
│ ├── products/ # 产品模块
│ └── orders/ # 订单模块
├── middleware/ # 中间件
├── utils/ # 工具函数
├── database/ # 数据库相关
├── app.ts # 应用入口
└── server.ts # 服务器启动
2.2 配置管理
创建环境敏感的配置管理系统:
// src/core/config/config.ts
import dotenv from 'dotenv';
import path from 'path';
dotenv.config({
path: path.resolve(process.cwd(), `.env.${process.env.NODE_ENV || 'development'}`)
});
export interface Config {
nodeEnv: string;
port: number;
database: {
host: string;
port: number;
name: string;
user: string;
password: string;
};
jwt: {
secret: string;
expiresIn: string;
};
redis: {
host: string;
port: number;
password?: string;
};
}
export const config: Config = {
nodeEnv: process.env.NODE_ENV || 'development',
port: parseInt(process.env.PORT || '3000', 10),
database: {
host: process.env.DB_HOST || 'localhost',
port: parseInt(process.env.DB_PORT || '5432', 10),
name: process.env.DB_NAME || 'mydb',
user: process.env.DB_USER || 'postgres',
password: process.env.DB_PASSWORD || ''
},
jwt: {
secret: process.env.JWT_SECRET || 'your-secret-key',
expiresIn: process.env.JWT_EXPIRES_IN || '7d'
},
redis: {
host: process.env.REDIS_HOST || 'localhost',
port: parseInt(process.env.REDIS_PORT || '6379', 10),
password: process.env.REDIS_PASSWORD
}
};
3. Express应用搭建
3.1 基础应用结构
// src/app.ts
import express from 'express';
import helmet from 'helmet';
import cors from 'cors';
import compression from 'compression';
import rateLimit from 'express-rate-limit';
import { config } from './core/config/config';
import { errorHandler } from './middleware/error-handler';
import { requestLogger } from './middleware/request-logger';
class App {
public app: express.Application;
constructor() {
this.app = express();
this.initializeMiddlewares();
this.initializeRoutes();
this.initializeErrorHandling();
}
private initializeMiddlewares(): void {
// 安全相关中间件
this.app.use(helmet());
this.app.use(cors({
origin: config.nodeEnv === 'production'
? process.env.ALLOWED_ORIGINS?.split(',')
: '*',
credentials: true
}));
// 请求解析
this.app.use(express.json({ limit: '10mb' }));
this.app.use(express.urlencoded({ extended: true }));
// 压缩响应
this.app.use(compression());
// 请求日志
this.app.use(requestLogger);
// 速率限制
if (config.nodeEnv === 'production') {
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15分钟
max: 100, // 每个IP限制100个请求
message: '请求过于频繁,请稍后再试'
});
this.app.use('/api/', limiter);
}
}
private initializeRoutes(): void {
// 健康检查
this.app.get('/health', (req, res) => {
res.json({
status: 'healthy',
timestamp: new Date().toISOString(),
environment: config.nodeEnv
});
});
// API路由
this.app.use('/api/v1/users', require('./modules/users/user.routes').default);
// 其他模块路由...
// 404处理
this.app.use('*', (req, res) => {
res.status(404).json({
error: 'Not Found',
message: `路由 ${req.originalUrl} 不存在`,
statusCode: 404
});
});
}
private initializeErrorHandling(): void {
this.app.use(errorHandler);
}
public listen(): void {
this.app.listen(config.port, () => {
console.log(`
🚀 服务器启动成功!
📍 环境: ${config.nodeEnv}
🌐 地址: http://localhost:${config.port}
⏰ 时间: ${new Date().toISOString()}
`);
});
}
}
export default App;
3.2 错误处理中间件
// src/middleware/error-handler.ts
import { Request, Response, NextFunction } from 'express';
import { config } from '../core/config/config';
export interface AppError extends Error {
statusCode?: number;
isOperational?: boolean;
}
export const errorHandler = (
error: AppError,
req: Request,
res: Response,
next: NextFunction
): void => {
const statusCode = error.statusCode || 500;
const message = error.message || '服务器内部错误';
// 记录错误日志
console.error(`[${new Date().toISOString()}] ${req.method} ${req.url}`, {
error: error.message,
stack: config.nodeEnv === 'development' ? error.stack : undefined,
body: