【MapSheep】
[好记性不如烂笔头]

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 数据库。你可以根据需要扩展和优化这些步骤以适应更复杂的应用场景。关键点包括:

  1. 选择合适的框架和工具
  2. 建立清晰的项目结构
  3. 实现数据库连接和查询
  4. 添加必要的中间件和安全措施
  5. 实现完善的错误处理
  6. 编写清晰的 API 文档

在实际项目中,你还需要考虑性能优化、安全性、测试、部署等方面,以确保应用的稳定性和可维护性。

posted on 2026-01-08 10:50  (Play)  阅读(35)  评论(0)    收藏  举报