目录
Node.js 后端接口开发指南
概述
在 Node.js 中实现后端接口通常涉及几个关键步骤,包括选择一个合适的 Web 框架、设计 API 接口、编写处理逻辑以及设置路由。下面将详细介绍使用流行的 Node.js 框架来创建后端接口的过程。
1. 选择一个 Node.js 框架
常见的 Node.js 框架包括:
- Express:最流行的选择,简单易用且拥有大量的插件和中间件支持
- Fastify:性能优秀,专注于低开销和高效的开发体验
- Koa:由 Express 团队开发,使用 async/await 实现更好的异步处理
本指南将以 Express 为例进行说明。
2. 安装 Express
首先,你需要通过 npm(Node 包管理器)安装 Express。在你的项目目录中打开终端或命令提示符,运行以下命令:
# 初始化项目
npm init -y
# 安装 Express
npm install express
# 可选:安装开发依赖
npm install -D nodemon
3. 创建 Express 服务器
创建一个名为 server.js 的文件,并编写以下代码来创建一个简单的 Express 服务器:
const express = require('express');
const app = express();
const port = 3000;
// 基础路由
app.get('/', (req, res) => {
res.json({
message: 'Hello World!',
timestamp: new Date().toISOString()
});
});
// 启动服务器
app.listen(port, () => {
console.log(`Server is running on http://localhost:${port}`);
console.log(`当前时间: ${new Date().toLocaleString()}`);
});
4. 创建 API 接口
4.1 用户管理 API 示例
// 模拟用户数据
const users = [
{ id: 1, name: '张三', email: 'zhangsan@example.com', age: 25 },
{ id: 2, name: '李四', email: 'lisi@example.com', age: 30 },
{ id: 3, name: '王五', email: 'wangwu@example.com', age: 28 }
];
// 获取所有用户
app.get('/api/users', (req, res) => {
res.json({
success: true,
data: users,
count: users.length,
timestamp: new Date().toISOString()
});
});
// 获取单个用户
app.get('/api/users/:id', (req, res) => {
const userId = parseInt(req.params.id);
const user = getUserById(userId);
if (user) {
res.json({
success: true,
data: user,
message: '用户信息获取成功'
});
} else {
res.status(404).json({
success: false,
message: '用户不存在',
requestedId: userId
});
}
});
4.2 使用 MySQL 实现 getUserById(userId)
首先安装 MySQL 驱动:
npm install mysql2
然后创建数据库连接和查询函数:
const mysql = require('mysql2/promise');
// 创建数据库连接池
const pool = mysql.createPool({
host: 'localhost',
user: 'your_username',
password: 'your_password',
database: 'your_database',
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0
});
// 获取用户信息的 MySQL 查询函数
async function getUserById(userId) {
try {
const connection = await pool.getConnection();
try {
// 使用参数化查询防止 SQL 注入
const [rows] = await connection.execute(
'SELECT id, username, email, phone, avatar, created_at, updated_at FROM users WHERE id = ? AND status = ?',
[userId, 'active']
);
if (rows.length === 0) {
return null;
}
return rows[0];
} finally {
connection.release();
}
} catch (error) {
console.error('数据库查询失败:', error);
throw error;
}
}
// 扩展:获取用户详情(包含关联信息)
async function getUserDetailById(userId) {
try {
const connection = await pool.getConnection();
try {
// 查询用户基本信息
const [userRows] = await connection.execute(
`SELECT
u.id,
u.username,
u.email,
u.phone,
u.avatar,
u.role,
u.status,
u.created_at,
u.updated_at,
p.profile_intro,
p.address,
p.birthday
FROM users u
LEFT JOIN user_profiles p ON u.id = p.user_id
WHERE u.id = ? AND u.status = 'active'`,
[userId]
);
if (userRows.length === 0) {
return null;
}
const user = userRows[0];
// 查询用户的订单数量
const [orderCountRows] = await connection.execute(
'SELECT COUNT(*) as order_count FROM orders WHERE user_id = ? AND status != "cancelled"',
[userId]
);
// 查询用户的角色权限
const [roleRows] = await connection.execute(
`SELECT
r.name as role_name,
r.description,
GROUP_CONCAT(p.permission_name) as permissions
FROM user_roles ur
JOIN roles r ON ur.role_id = r.id
LEFT JOIN role_permissions rp ON r.id = rp.role_id
LEFT JOIN permissions p ON rp.permission_id = p.id
WHERE ur.user_id = ?
GROUP BY r.id`,
[userId]
);
return {
...user,
order_count: orderCountRows[0].order_count,
roles: roleRows
};
} finally {
connection.release();
}
} catch (error) {
console.error('获取用户详情失败:', error);
throw error;
}
}
// 在路由中使用
app.get('/api/users/detail/:id', async (req, res) => {
try {
const userId = parseInt(req.params.id);
// 验证输入
if (isNaN(userId) || userId <= 0) {
return res.status(400).json({
success: false,
message: '无效的用户ID',
error: 'INVALID_USER_ID'
});
}
const userDetail = await getUserDetailById(userId);
if (!userDetail) {
return res.status(404).json({
success: false,
message: '用户不存在或已被禁用',
requestedId: userId,
error: 'USER_NOT_FOUND'
});
}
res.json({
success: true,
data: userDetail,
message: '用户详情获取成功',
timestamp: new Date().toISOString()
});
} catch (error) {
console.error('服务器错误:', error);
res.status(500).json({
success: false,
message: '服务器内部错误',
error: process.env.NODE_ENV === 'development' ? error.message : undefined,
timestamp: new Date().toISOString()
});
}
});
5. 添加中间件和错误处理
5.1 安装必要的中间件
npm install cors body-parser helmet morgan
5.2 配置中间件
const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const morgan = require('morgan');
const bodyParser = require('body-parser');
const app = express();
// 安全中间件
app.use(helmet()); // 设置安全相关的 HTTP 头部
// 跨域支持
app.use(cors({
origin: process.env.CORS_ORIGIN || 'http://localhost:3000',
credentials: true,
optionsSuccessStatus: 200
}));
// 请求日志
app.use(morgan('combined'));
// 解析请求体
app.use(bodyParser.json({ limit: '10mb' }));
app.use(bodyParser.urlencoded({ extended: true, limit: '10mb' }));
// 自定义中间件:请求时间记录
app.use((req, res, next) => {
req.startTime = Date.now();
console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
next();
});
// 自定义中间件:响应时间计算
app.use((req, res, next) => {
const originalSend = res.send;
res.send = function(data) {
const duration = Date.now() - req.startTime;
console.log(`响应时间: ${duration}ms`);
if (res.get('Content-Type')?.includes('application/json')) {
const responseData = typeof data === 'string' ? JSON.parse(data) : data;
responseData.duration = `${duration}ms`;
return originalSend.call(this, JSON.stringify(responseData));
}
return originalSend.call(this, data);
};
next();
});
5.3 错误处理中间件
// 404 处理
app.use((req, res, next) => {
res.status(404).json({
success: false,
message: '请求的资源不存在',
path: req.originalUrl,
method: req.method,
timestamp: new Date().toISOString()
});
});
// 全局错误处理中间件
app.use((err, req, res, next) => {
console.error('未处理的错误:', err);
// 数据库错误处理
if (err.code === 'ER_DUP_ENTRY') {
return res.status(409).json({
success: false,
message: '数据已存在',
error: 'DUPLICATE_ENTRY'
});
}
if (err.code === 'ER_NO_REFERENCED_ROW') {
return res.status(400).json({
success: false,
message: '引用的数据不存在',
error: 'REFERENCE_NOT_FOUND'
});
}
// JWT 认证错误
if (err.name === 'JsonWebTokenError') {
return res.status(401).json({
success: false,
message: '无效的认证令牌',
error: 'INVALID_TOKEN'
});
}
if (err.name === 'TokenExpiredError') {
return res.status(401).json({
success: false,
message: '认证令牌已过期',
error: 'TOKEN_EXPIRED'
});
}
// 参数验证错误
if (err.name === 'ValidationError') {
return res.status(400).json({
success: false,
message: '参数验证失败',
errors: err.errors,
error: 'VALIDATION_ERROR'
});
}
// 默认错误处理
const statusCode = err.statusCode || 500;
const message = err.message || '服务器内部错误';
res.status(statusCode).json({
success: false,
message,
error: process.env.NODE_ENV === 'development' ? {
name: err.name,
message: err.message,
stack: err.stack
} : undefined,
timestamp: new Date().toISOString()
});
});
6. 运行服务器
6.1 使用 nodemon 运行(开发环境)
# 如果安装了 nodemon
npx nodemon server.js
# 或
npm run dev
在 package.json 中添加脚本:
{
"scripts": {
"start": "node server.js",
"dev": "nodemon server.js"
}
}
6.2 生产环境运行
# 直接运行
node server.js
# 使用 PM2 进程管理
npm install -g pm2
pm2 start server.js --name "api-server"
# 查看进程状态
pm2 status
# 查看日志
pm2 logs api-server
7. 完整的项目结构示例
my-api-server/
├── src/
│ ├── config/
│ │ └── database.js # 数据库配置
│ ├── controllers/
│ │ ├── user.controller.js
│ │ └── product.controller.js
│ ├── models/
│ │ └── user.model.js
│ ├── routes/
│ │ ├── user.routes.js
│ │ └── product.routes.js
│ ├── middleware/
│ │ ├── auth.js
│ │ └── error.js
│ ├── utils/
│ │ └── database.js
│ └── server.js
├── .env # 环境变量
├── .gitignore
├── package.json
└── README.md
8. 环境变量配置
创建 .env 文件:
# 服务器配置
PORT=3000
NODE_ENV=development
HOST=localhost
# 数据库配置
DB_HOST=localhost
DB_PORT=3306
DB_USER=root
DB_PASSWORD=yourpassword
DB_NAME=myapp
# 安全配置
JWT_SECRET=your_jwt_secret_key_here
CORS_ORIGIN=http://localhost:8080
# 应用配置
API_VERSION=v1
APP_NAME=My API Server
在代码中使用:
require('dotenv').config();
const PORT = process.env.PORT || 3000;
const DB_CONFIG = {
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME
};
9. API 文档示例
/**
* @api {get} /api/users 获取用户列表
* @apiName GetUsers
* @apiGroup Users
* @apiVersion 1.0.0
*
* @apiParam {Number} [page=1] 页码
* @apiParam {Number} [limit=10] 每页数量
* @apiParam {String} [sort=created_at] 排序字段
* @apiParam {String} [order=desc] 排序方式
*
* @apiSuccess {Boolean} success 请求是否成功
* @apiSuccess {Object[]} data 用户列表
* @apiSuccess {Number} data.id 用户ID
* @apiSuccess {String} data.username 用户名
* @apiSuccess {String} data.email 邮箱
* @apiSuccess {Number} total 总记录数
* @apiSuccess {Number} page 当前页码
* @apiSuccess {Number} pages 总页数
*
* @apiSuccessExample {json} 成功响应:
* HTTP/1.1 200 OK
* {
* "success": true,
* "data": [
* {
* "id": 1,
* "username": "张三",
* "email": "zhangsan@example.com"
* }
* ],
* "total": 100,
* "page": 1,
* "pages": 10,
* "timestamp": "2023-05-20T10:30:00Z"
* }
*
* @apiErrorExample {json} 错误响应:
* HTTP/1.1 500 Internal Server Error
* {
* "success": false,
* "message": "服务器内部错误",
* "timestamp": "2023-05-20T10:30:00Z"
* }
*/
总结
以上步骤展示了如何在 Node.js 中使用 Express 框架创建基本的后端接口,并集成了 MySQL 数据库。你可以根据需要扩展和优化这些步骤以适应更复杂的应用场景。关键点包括:
- 选择合适的框架和工具
- 建立清晰的项目结构
- 实现数据库连接和查询
- 添加必要的中间件和安全措施
- 实现完善的错误处理
- 编写清晰的 API 文档
在实际项目中,你还需要考虑性能优化、安全性、测试、部署等方面,以确保应用的稳定性和可维护性。
浙公网安备 33010602011771号