expressjs-development

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Express.js Development Skill

Express.js 开发技能

This skill provides comprehensive guidance for building production-ready web applications and REST APIs using Express.js, covering routing, middleware, request/response handling, error handling, authentication, validation, and deployment best practices.
本技能提供了使用Express.js构建生产就绪型Web应用和REST API的全面指南,涵盖路由、中间件、请求/响应处理、错误处理、身份验证、数据校验以及部署最佳实践。

When to Use This Skill

适用场景

Use this skill when:
  • Building RESTful APIs for web and mobile applications
  • Creating backend services and microservices
  • Developing web servers with server-side rendering
  • Implementing API gateways and proxy servers
  • Building real-time applications with WebSocket support
  • Creating middleware-based request processing pipelines
  • Developing authentication and authorization systems
  • Implementing file upload and download services
  • Building webhook handlers and integrations
  • Creating serverless functions with Express
在以下场景中使用本技能:
  • 为Web和移动应用构建RESTful API
  • 创建后端服务与微服务
  • 开发支持服务端渲染的Web服务器
  • 实现API网关与代理服务器
  • 构建支持WebSocket的实时应用
  • 创建基于中间件的请求处理流水线
  • 开发身份验证与授权系统
  • 实现文件上传与下载服务
  • 构建Webhook处理器与集成功能
  • 使用Express创建无服务器函数

Core Concepts

核心概念

Application Setup

应用搭建

Express applications are built by creating an instance of Express and configuring middleware and routes.
Basic Express Application:
javascript
const express = require('express');
const app = express();

// Middleware
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// Routes
app.get('/', (req, res) => {
  res.send('Hello World!');
});

// Start server
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});
Application with Configuration:
javascript
const express = require('express');
const app = express();

// App settings
app.set('port', process.env.PORT || 3000);
app.set('env', process.env.NODE_ENV || 'development');
app.set('trust proxy', 1); // Trust first proxy

// View engine setup (optional)
app.set('view engine', 'ejs');
app.set('views', './views');

// Static files
app.use(express.static('public'));

// Body parsing
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

module.exports = app;
Express应用通过创建Express实例并配置中间件和路由来构建。
基础Express应用:
javascript
const express = require('express');
const app = express();

// Middleware
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// Routes
app.get('/', (req, res) => {
  res.send('Hello World!');
});

// Start server
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});
带配置的应用:
javascript
const express = require('express');
const app = express();

// App settings
app.set('port', process.env.PORT || 3000);
app.set('env', process.env.NODE_ENV || 'development');
app.set('trust proxy', 1); // Trust first proxy

// View engine setup (optional)
app.set('view engine', 'ejs');
app.set('views', './views');

// Static files
app.use(express.static('public'));

// Body parsing
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

module.exports = app;

Routing

路由

Routing refers to how an application's endpoints (URIs) respond to client requests.
Basic Routes:
javascript
const express = require('express');
const app = express();

// HTTP Methods
app.get('/users', (req, res) => {
  res.json({ message: 'Get all users' });
});

app.post('/users', (req, res) => {
  res.json({ message: 'Create user' });
});

app.put('/users/:id', (req, res) => {
  res.json({ message: `Update user ${req.params.id}` });
});

app.delete('/users/:id', (req, res) => {
  res.json({ message: `Delete user ${req.params.id}` });
});

// Multiple methods on same route
app.route('/users/:id')
  .get((req, res) => res.json({ message: 'Get user' }))
  .put((req, res) => res.json({ message: 'Update user' }))
  .delete((req, res) => res.json({ message: 'Delete user' }));
Route Parameters:
javascript
// Single parameter
app.get('/users/:userId', (req, res) => {
  const { userId } = req.params;
  res.json({ userId });
});

// Multiple parameters
app.get('/users/:userId/posts/:postId', (req, res) => {
  const { userId, postId } = req.params;
  res.json({ userId, postId });
});

// Optional parameters with regex
app.get('/users/:userId/posts/:postId?', (req, res) => {
  // postId is optional
  res.json(req.params);
});

// Parameter validation
app.param('userId', (req, res, next, id) => {
  // Validate or transform parameter
  if (!id.match(/^\d+$/)) {
    return res.status(400).json({ error: 'Invalid user ID' });
  }
  req.userId = parseInt(id);
  next();
});
Query Strings:
javascript
// GET /search?q=express&limit=10&page=2
app.get('/search', (req, res) => {
  const { q, limit = 20, page = 1 } = req.query;
  res.json({
    query: q,
    limit: parseInt(limit),
    page: parseInt(page)
  });
});
Router Modules:
javascript
// routes/users.js
const express = require('express');
const router = express.Router();

router.get('/', (req, res) => {
  res.json({ message: 'Get all users' });
});

router.get('/:id', (req, res) => {
  res.json({ message: `Get user ${req.params.id}` });
});

router.post('/', (req, res) => {
  res.json({ message: 'Create user' });
});

module.exports = router;

// app.js
const usersRouter = require('./routes/users');
app.use('/api/users', usersRouter);
路由指的是应用的端点(URI)如何响应客户端请求。
基础路由:
javascript
const express = require('express');
const app = express();

// HTTP Methods
app.get('/users', (req, res) => {
  res.json({ message: 'Get all users' });
});

app.post('/users', (req, res) => {
  res.json({ message: 'Create user' });
});

app.put('/users/:id', (req, res) => {
  res.json({ message: `Update user ${req.params.id}` });
});

app.delete('/users/:id', (req, res) => {
  res.json({ message: `Delete user ${req.params.id}` });
});

// Multiple methods on same route
app.route('/users/:id')
  .get((req, res) => res.json({ message: 'Get user' }))
  .put((req, res) => res.json({ message: 'Update user' }))
  .delete((req, res) => res.json({ message: 'Delete user' }));
路由参数:
javascript
// Single parameter
app.get('/users/:userId', (req, res) => {
  const { userId } = req.params;
  res.json({ userId });
});

// Multiple parameters
app.get('/users/:userId/posts/:postId', (req, res) => {
  const { userId, postId } = req.params;
  res.json({ userId, postId });
});

// Optional parameters with regex
app.get('/users/:userId/posts/:postId?', (req, res) => {
  // postId is optional
  res.json(req.params);
});

// Parameter validation
app.param('userId', (req, res, next, id) => {
  // Validate or transform parameter
  if (!id.match(/^\d+$/)) {
    return res.status(400).json({ error: 'Invalid user ID' });
  }
  req.userId = parseInt(id);
  next();
});
查询字符串:
javascript
// GET /search?q=express&limit=10&page=2
app.get('/search', (req, res) => {
  const { q, limit = 20, page = 1 } = req.query;
  res.json({
    query: q,
    limit: parseInt(limit),
    page: parseInt(page)
  });
});
路由模块:
javascript
// routes/users.js
const express = require('express');
const router = express.Router();

router.get('/', (req, res) => {
  res.json({ message: 'Get all users' });
});

router.get('/:id', (req, res) => {
  res.json({ message: `Get user ${req.params.id}` });
});

router.post('/', (req, res) => {
  res.json({ message: 'Create user' });
});

module.exports = router;

// app.js
const usersRouter = require('./routes/users');
app.use('/api/users', usersRouter);

Middleware

中间件

Middleware functions have access to the request object (req), the response object (res), and the next middleware function in the application's request-response cycle.
Application-Level Middleware:
javascript
// Executed for every request
app.use((req, res, next) => {
  console.log(`${req.method} ${req.path}`);
  next();
});

// Executed for specific path
app.use('/api', (req, res, next) => {
  req.startTime = Date.now();
  next();
});

// Multiple middleware functions
app.use(
  express.json(),
  express.urlencoded({ extended: true }),
  cookieParser()
);
Router-Level Middleware:
javascript
const router = express.Router();

// Middleware for all routes in this router
router.use((req, res, next) => {
  console.log('Router middleware');
  next();
});

// Middleware for specific route
router.get('/users',
  authMiddleware,
  validationMiddleware,
  (req, res) => {
    res.json({ users: [] });
  }
);
Built-in Middleware:
javascript
// Parse JSON bodies
app.use(express.json());

// Parse URL-encoded bodies
app.use(express.urlencoded({ extended: true }));

// Serve static files
app.use(express.static('public'));
app.use('/uploads', express.static('uploads'));
Third-Party Middleware:
javascript
const cors = require('cors');
const helmet = require('helmet');
const morgan = require('morgan');
const compression = require('compression');

// Security headers
app.use(helmet());

// CORS
app.use(cors({
  origin: 'https://example.com',
  credentials: true
}));

// Logging
app.use(morgan('combined'));

// Compression
app.use(compression());
Custom Middleware:
javascript
// Request logging middleware
function requestLogger(req, res, next) {
  const start = Date.now();

  res.on('finish', () => {
    const duration = Date.now() - start;
    console.log(`${req.method} ${req.path} ${res.statusCode} ${duration}ms`);
  });

  next();
}

// Authentication middleware
function requireAuth(req, res, next) {
  const token = req.headers.authorization?.split(' ')[1];

  if (!token) {
    return res.status(401).json({ error: 'No token provided' });
  }

  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    req.user = decoded;
    next();
  } catch (error) {
    res.status(401).json({ error: 'Invalid token' });
  }
}

// Request validation middleware
function validateUser(req, res, next) {
  const { email, password } = req.body;

  if (!email || !password) {
    return res.status(400).json({
      error: 'Email and password are required'
    });
  }

  if (!email.includes('@')) {
    return res.status(400).json({ error: 'Invalid email' });
  }

  next();
}

app.use(requestLogger);
app.post('/login', validateUser, loginHandler);
app.get('/protected', requireAuth, protectedHandler);
中间件函数可以访问请求对象(req)、响应对象(res)以及应用请求-响应周期中的下一个中间件函数。
应用级中间件:
javascript
// Executed for every request
app.use((req, res, next) => {
  console.log(`${req.method} ${req.path}`);
  next();
});

// Executed for specific path
app.use('/api', (req, res, next) => {
  req.startTime = Date.now();
  next();
});

// Multiple middleware functions
app.use(
  express.json(),
  express.urlencoded({ extended: true }),
  cookieParser()
);
路由级中间件:
javascript
const router = express.Router();

// Middleware for all routes in this router
router.use((req, res, next) => {
  console.log('Router middleware');
  next();
});

// Middleware for specific route
router.get('/users',
  authMiddleware,
  validationMiddleware,
  (req, res) => {
    res.json({ users: [] });
  }
);
内置中间件:
javascript
// Parse JSON bodies
app.use(express.json());

// Parse URL-encoded bodies
app.use(express.urlencoded({ extended: true }));

// Serve static files
app.use(express.static('public'));
app.use('/uploads', express.static('uploads'));
第三方中间件:
javascript
const cors = require('cors');
const helmet = require('helmet');
const morgan = require('morgan');
const compression = require('compression');

// Security headers
app.use(helmet());

// CORS
app.use(cors({
  origin: 'https://example.com',
  credentials: true
}));

// Logging
app.use(morgan('combined'));

// Compression
app.use(compression());
自定义中间件:
javascript
// Request logging middleware
function requestLogger(req, res, next) {
  const start = Date.now();

  res.on('finish', () => {
    const duration = Date.now() - start;
    console.log(`${req.method} ${req.path} ${res.statusCode} ${duration}ms`);
  });

  next();
}

// Authentication middleware
function requireAuth(req, res, next) {
  const token = req.headers.authorization?.split(' ')[1];

  if (!token) {
    return res.status(401).json({ error: 'No token provided' });
  }

  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    req.user = decoded;
    next();
  } catch (error) {
    res.status(401).json({ error: 'Invalid token' });
  }
}

// Request validation middleware
function validateUser(req, res, next) {
  const { email, password } = req.body;

  if (!email || !password) {
    return res.status(400).json({
      error: 'Email and password are required'
    });
  }

  if (!email.includes('@')) {
    return res.status(400).json({ error: 'Invalid email' });
  }

  next();
}

app.use(requestLogger);
app.post('/login', validateUser, loginHandler);
app.get('/protected', requireAuth, protectedHandler);

Request Object

请求对象

The request object represents the HTTP request and has properties for query strings, parameters, body, headers, etc.
Request Properties:
javascript
app.post('/api/users/:id', (req, res) => {
  // Route parameters
  const { id } = req.params;

  // Query string
  const { sort, filter } = req.query;

  // Request body
  const { name, email } = req.body;

  // Headers
  const userAgent = req.get('User-Agent');
  const contentType = req.get('Content-Type');

  // Request info
  const method = req.method;
  const path = req.path;
  const url = req.url;
  const baseUrl = req.baseUrl;
  const protocol = req.protocol;
  const hostname = req.hostname;
  const ip = req.ip;

  // Cookies (requires cookie-parser)
  const { sessionId } = req.cookies;

  res.json({ id, name, email });
});
Request Methods:
javascript
app.post('/upload', (req, res) => {
  // Check content type
  if (req.is('application/json')) {
    // Handle JSON
  }

  // Check accept header
  if (req.accepts('json')) {
    res.json({ data: 'json response' });
  } else if (req.accepts('html')) {
    res.send('<html>html response</html>');
  }

  // Get header value
  const auth = req.get('Authorization');

  // Get range header
  const range = req.range(1000);
});
请求对象代表HTTP请求,包含查询字符串、参数、请求体、请求头等属性。
请求属性:
javascript
app.post('/api/users/:id', (req, res) => {
  // Route parameters
  const { id } = req.params;

  // Query string
  const { sort, filter } = req.query;

  // Request body
  const { name, email } = req.body;

  // Headers
  const userAgent = req.get('User-Agent');
  const contentType = req.get('Content-Type');

  // Request info
  const method = req.method;
  const path = req.path;
  const url = req.url;
  const baseUrl = req.baseUrl;
  const protocol = req.protocol;
  const hostname = req.hostname;
  const ip = req.ip;

  // Cookies (requires cookie-parser)
  const { sessionId } = req.cookies;

  res.json({ id, name, email });
});
请求方法:
javascript
app.post('/upload', (req, res) => {
  // Check content type
  if (req.is('application/json')) {
    // Handle JSON
  }

  // Check accept header
  if (req.accepts('json')) {
    res.json({ data: 'json response' });
  } else if (req.accepts('html')) {
    res.send('<html>html response</html>');
  }

  // Get header value
  const auth = req.get('Authorization');

  // Get range header
  const range = req.range(1000);
});

Response Object

响应对象

The response object represents the HTTP response that an Express app sends when it gets an HTTP request.
Sending Responses:
javascript
app.get('/api/data', (req, res) => {
  // Send JSON
  res.json({ message: 'Success', data: [] });

  // Send string
  res.send('Hello World');

  // Send status
  res.sendStatus(200); // Equivalent to res.status(200).send('OK')

  // Send file
  res.sendFile('/path/to/file.pdf');

  // Download file
  res.download('/path/to/file.pdf', 'document.pdf');

  // Render view
  res.render('index', { title: 'Home' });

  // Redirect
  res.redirect('/login');
  res.redirect(301, 'https://example.com');

  // End response
  res.end();
});
Setting Status and Headers:
javascript
app.get('/api/resource', (req, res) => {
  // Set status code
  res.status(201).json({ created: true });

  // Set headers
  res.set('Content-Type', 'application/json');
  res.set({
    'X-API-Version': '1.0',
    'X-Rate-Limit': '100'
  });

  // Set cookie
  res.cookie('name', 'value', {
    maxAge: 900000,
    httpOnly: true,
    secure: true,
    sameSite: 'strict'
  });

  // Clear cookie
  res.clearCookie('name');

  res.json({ success: true });
});
Response Formats:
javascript
app.get('/api/users/:id', (req, res) => {
  const user = { id: 1, name: 'John' };

  res.format({
    'text/plain': () => {
      res.send(`${user.name}`);
    },
    'text/html': () => {
      res.send(`<p>${user.name}</p>`);
    },
    'application/json': () => {
      res.json(user);
    },
    default: () => {
      res.status(406).send('Not Acceptable');
    }
  });
});
响应对象代表Express应用在收到HTTP请求时发送的HTTP响应。
发送响应:
javascript
app.get('/api/data', (req, res) => {
  // Send JSON
  res.json({ message: 'Success', data: [] });

  // Send string
  res.send('Hello World');

  // Send status
  res.sendStatus(200); // Equivalent to res.status(200).send('OK')

  // Send file
  res.sendFile('/path/to/file.pdf');

  // Download file
  res.download('/path/to/file.pdf', 'document.pdf');

  // Render view
  res.render('index', { title: 'Home' });

  // Redirect
  res.redirect('/login');
  res.redirect(301, 'https://example.com');

  // End response
  res.end();
});
设置状态码与响应头:
javascript
app.get('/api/resource', (req, res) => {
  // Set status code
  res.status(201).json({ created: true });

  // Set headers
  res.set('Content-Type', 'application/json');
  res.set({
    'X-API-Version': '1.0',
    'X-Rate-Limit': '100'
  });

  // Set cookie
  res.cookie('name', 'value', {
    maxAge: 900000,
    httpOnly: true,
    secure: true,
    sameSite: 'strict'
  });

  // Clear cookie
  res.clearCookie('name');

  res.json({ success: true });
});
响应格式:
javascript
app.get('/api/users/:id', (req, res) => {
  const user = { id: 1, name: 'John' };

  res.format({
    'text/plain': () => {
      res.send(`${user.name}`);
    },
    'text/html': () => {
      res.send(`<p>${user.name}</p>`);
    },
    'application/json': () => {
      res.json(user);
    },
    default: () => {
      res.status(406).send('Not Acceptable');
    }
  });
});

Error Handling

错误处理

Error-handling middleware functions have four arguments: (err, req, res, next).
Error-Handling Middleware:
javascript
// 404 handler
app.use((req, res, next) => {
  res.status(404).json({ error: 'Not found' });
});

// Error handler (must be last)
app.use((err, req, res, next) => {
  console.error(err.stack);

  res.status(err.status || 500).json({
    error: {
      message: err.message,
      ...(process.env.NODE_ENV === 'development' && { stack: err.stack })
    }
  });
});
Async Error Handling:
javascript
// Async wrapper utility
const asyncHandler = (fn) => (req, res, next) => {
  Promise.resolve(fn(req, res, next)).catch(next);
};

// Using async wrapper
app.get('/api/users/:id', asyncHandler(async (req, res) => {
  const user = await User.findById(req.params.id);

  if (!user) {
    const error = new Error('User not found');
    error.status = 404;
    throw error;
  }

  res.json(user);
}));

// Custom error classes
class AppError extends Error {
  constructor(message, status) {
    super(message);
    this.status = status;
    this.isOperational = true;
    Error.captureStackTrace(this, this.constructor);
  }
}

class NotFoundError extends AppError {
  constructor(message = 'Resource not found') {
    super(message, 404);
  }
}

class ValidationError extends AppError {
  constructor(message = 'Validation failed') {
    super(message, 400);
  }
}
错误处理中间件函数有四个参数:(err, req, res, next)。
错误处理中间件:
javascript
// 404 handler
app.use((req, res, next) => {
  res.status(404).json({ error: 'Not found' });
});

// Error handler (must be last)
app.use((err, req, res, next) => {
  console.error(err.stack);

  res.status(err.status || 500).json({
    error: {
      message: err.message,
      ...(process.env.NODE_ENV === 'development' && { stack: err.stack })
    }
  });
});
异步错误处理:
javascript
// Async wrapper utility
const asyncHandler = (fn) => (req, res, next) => {
  Promise.resolve(fn(req, res, next)).catch(next);
};

// Using async wrapper
app.get('/api/users/:id', asyncHandler(async (req, res) => {
  const user = await User.findById(req.params.id);

  if (!user) {
    const error = new Error('User not found');
    error.status = 404;
    throw error;
  }

  res.json(user);
}));

// Custom error classes
class AppError extends Error {
  constructor(message, status) {
    super(message);
    this.status = status;
    this.isOperational = true;
    Error.captureStackTrace(this, this.constructor);
  }
}

class NotFoundError extends AppError {
  constructor(message = 'Resource not found') {
    super(message, 404);
  }
}

class ValidationError extends AppError {
  constructor(message = 'Validation failed') {
    super(message, 400);
  }
}

API Reference

API参考

Express Application Methods

Express应用方法

app.use([path], middleware)
  • Mounts middleware at the specified path
  • If path is not specified, middleware is executed for every request
app.METHOD(path, [middleware...], handler)
  • Routes HTTP requests (GET, POST, PUT, DELETE, etc.)
  • Multiple middleware functions can be specified
app.route(path)
  • Returns an instance of a single route for chaining HTTP verbs
app.listen(port, [hostname], [backlog], [callback])
  • Binds and listens for connections on the specified host and port
app.param(name, callback)
  • Adds callback triggers to route parameters
app.set(name, value)
  • Assigns setting name to value
app.get(name)
  • Returns the value of setting name
app.use([path], middleware)
  • 在指定路径挂载中间件
  • 如果未指定路径,中间件会对所有请求生效
app.METHOD(path, [middleware...], handler)
  • 路由HTTP请求(GET、POST、PUT、DELETE等)
  • 可以指定多个中间件函数
app.route(path)
  • 返回单个路由的实例,用于链式调用HTTP方法
app.listen(port, [hostname], [backlog], [callback])
  • 在指定主机和端口绑定并监听连接
app.param(name, callback)
  • 为路由参数添加回调触发器
app.set(name, value)
  • 为设置项name赋值value
app.get(name)
  • 返回设置项name的值

Router Methods

路由方法

router.use([path], middleware)
  • Mounts middleware for the router
router.METHOD(path, [middleware...], handler)
  • Routes HTTP requests within the router
router.route(path)
  • Returns a route instance for chaining
router.param(name, callback)
  • Adds parameter callbacks
router.use([path], middleware)
  • 为路由挂载中间件
router.METHOD(path, [middleware...], handler)
  • 在路由内路由HTTP请求
router.route(path)
  • 返回路由实例,用于链式调用
router.param(name, callback)
  • 为路由参数添加回调

Request Properties

请求属性

  • req.body: Contains parsed request body (requires body-parser)
  • req.params: Route parameters
  • req.query: Parsed query string
  • req.headers: Request headers
  • req.cookies: Cookies (requires cookie-parser)
  • req.method: HTTP method
  • req.path: Request path
  • req.url: Full URL
  • req.ip: Remote IP address
  • req.protocol: Request protocol (http or https)
  • req.body: 包含解析后的请求体(需要body-parser中间件)
  • req.params: 路由参数
  • req.query: 解析后的查询字符串
  • req.headers: 请求头
  • req.cookies: Cookies(需要cookie-parser中间件)
  • req.method: HTTP方法
  • req.path: 请求路径
  • req.url: 完整URL
  • req.ip: 远程IP地址
  • req.protocol: 请求协议(http或https)

Request Methods

请求方法

  • req.get(header): Returns header value
  • req.is(type): Checks if content type matches
  • req.accepts(types): Checks if types are acceptable
  • req.range(size): Parses range header
  • req.get(header): 返回指定请求头的值
  • req.is(type): 检查请求内容类型是否匹配
  • req.accepts(types): 检查客户端是否接受指定类型
  • req.range(size): 解析范围请求头

Response Methods

响应方法

  • res.json(obj): Sends JSON response
  • res.send(body): Sends response
  • res.status(code): Sets status code
  • res.sendStatus(code): Sets status and sends status message
  • res.set(field, value): Sets response header
  • res.cookie(name, value, options): Sets cookie
  • res.clearCookie(name): Clears cookie
  • res.redirect([status], path): Redirects to path
  • res.render(view, locals): Renders view template
  • res.sendFile(path): Sends file
  • res.download(path, filename): Downloads file
  • res.json(obj): 发送JSON响应
  • res.send(body): 发送响应
  • res.status(code): 设置响应状态码
  • res.sendStatus(code): 设置状态码并发送状态消息
  • res.set(field, value): 设置响应头
  • res.cookie(name, value, options): 设置Cookie
  • res.clearCookie(name): 清除Cookie
  • res.redirect([status], path): 重定向到指定路径
  • res.render(view, locals): 渲染视图模板
  • res.sendFile(path): 发送文件
  • res.download(path, filename): 触发文件下载

Workflow Patterns

工作流模式

REST API Design

REST API设计

Complete REST API Example:
javascript
const express = require('express');
const router = express.Router();

// GET /api/users - List all users
router.get('/', asyncHandler(async (req, res) => {
  const { page = 1, limit = 10, sort = 'createdAt' } = req.query;

  const users = await User.find()
    .sort(sort)
    .limit(parseInt(limit))
    .skip((parseInt(page) - 1) * parseInt(limit))
    .select('-password');

  const total = await User.countDocuments();

  res.json({
    data: users,
    pagination: {
      page: parseInt(page),
      limit: parseInt(limit),
      total,
      pages: Math.ceil(total / limit)
    }
  });
}));

// GET /api/users/:id - Get single user
router.get('/:id', asyncHandler(async (req, res) => {
  const user = await User.findById(req.params.id).select('-password');

  if (!user) {
    throw new NotFoundError('User not found');
  }

  res.json({ data: user });
}));

// POST /api/users - Create user
router.post('/',
  validateUser,
  asyncHandler(async (req, res) => {
    const { email, password, name } = req.body;

    const existingUser = await User.findOne({ email });
    if (existingUser) {
      throw new ValidationError('Email already exists');
    }

    const user = await User.create({ email, password, name });

    res.status(201).json({
      data: user.toJSON(),
      message: 'User created successfully'
    });
  })
);

// PUT /api/users/:id - Update user
router.put('/:id',
  requireAuth,
  validateUserUpdate,
  asyncHandler(async (req, res) => {
    const user = await User.findByIdAndUpdate(
      req.params.id,
      req.body,
      { new: true, runValidators: true }
    ).select('-password');

    if (!user) {
      throw new NotFoundError('User not found');
    }

    res.json({
      data: user,
      message: 'User updated successfully'
    });
  })
);

// DELETE /api/users/:id - Delete user
router.delete('/:id',
  requireAuth,
  asyncHandler(async (req, res) => {
    const user = await User.findByIdAndDelete(req.params.id);

    if (!user) {
      throw new NotFoundError('User not found');
    }

    res.json({ message: 'User deleted successfully' });
  })
);

module.exports = router;
完整REST API示例:
javascript
const express = require('express');
const router = express.Router();

// GET /api/users - List all users
router.get('/', asyncHandler(async (req, res) => {
  const { page = 1, limit = 10, sort = 'createdAt' } = req.query;

  const users = await User.find()
    .sort(sort)
    .limit(parseInt(limit))
    .skip((parseInt(page) - 1) * parseInt(limit))
    .select('-password');

  const total = await User.countDocuments();

  res.json({
    data: users,
    pagination: {
      page: parseInt(page),
      limit: parseInt(limit),
      total,
      pages: Math.ceil(total / limit)
    }
  });
}));

// GET /api/users/:id - Get single user
router.get('/:id', asyncHandler(async (req, res) => {
  const user = await User.findById(req.params.id).select('-password');

  if (!user) {
    throw new NotFoundError('User not found');
  }

  res.json({ data: user });
}));

// POST /api/users - Create user
router.post('/',
  validateUser,
  asyncHandler(async (req, res) => {
    const { email, password, name } = req.body;

    const existingUser = await User.findOne({ email });
    if (existingUser) {
      throw new ValidationError('Email already exists');
    }

    const user = await User.create({ email, password, name });

    res.status(201).json({
      data: user.toJSON(),
      message: 'User created successfully'
    });
  })
);

// PUT /api/users/:id - Update user
router.put('/:id',
  requireAuth,
  validateUserUpdate,
  asyncHandler(async (req, res) => {
    const user = await User.findByIdAndUpdate(
      req.params.id,
      req.body,
      { new: true, runValidators: true }
    ).select('-password');

    if (!user) {
      throw new NotFoundError('User not found');
    }

    res.json({
      data: user,
      message: 'User updated successfully'
    });
  })
);

// DELETE /api/users/:id - Delete user
router.delete('/:id',
  requireAuth,
  asyncHandler(async (req, res) => {
    const user = await User.findByIdAndDelete(req.params.id);

    if (!user) {
      throw new NotFoundError('User not found');
    }

    res.json({ message: 'User deleted successfully' });
  })
);

module.exports = router;

Authentication

身份验证

JWT Authentication:
javascript
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');

// Register
router.post('/register',
  validateRegistration,
  asyncHandler(async (req, res) => {
    const { email, password, name } = req.body;

    // Check if user exists
    const existingUser = await User.findOne({ email });
    if (existingUser) {
      throw new ValidationError('Email already registered');
    }

    // Hash password
    const hashedPassword = await bcrypt.hash(password, 10);

    // Create user
    const user = await User.create({
      email,
      password: hashedPassword,
      name
    });

    // Generate token
    const token = jwt.sign(
      { userId: user._id, email: user.email },
      process.env.JWT_SECRET,
      { expiresIn: '7d' }
    );

    res.status(201).json({
      data: {
        user: user.toJSON(),
        token
      }
    });
  })
);

// Login
router.post('/login',
  validateLogin,
  asyncHandler(async (req, res) => {
    const { email, password } = req.body;

    // Find user
    const user = await User.findOne({ email });
    if (!user) {
      throw new ValidationError('Invalid credentials');
    }

    // Verify password
    const isValid = await bcrypt.compare(password, user.password);
    if (!isValid) {
      throw new ValidationError('Invalid credentials');
    }

    // Generate token
    const token = jwt.sign(
      { userId: user._id, email: user.email },
      process.env.JWT_SECRET,
      { expiresIn: '7d' }
    );

    res.json({
      data: {
        user: user.toJSON(),
        token
      }
    });
  })
);

// Refresh token
router.post('/refresh',
  asyncHandler(async (req, res) => {
    const { refreshToken } = req.body;

    if (!refreshToken) {
      throw new ValidationError('Refresh token required');
    }

    const decoded = jwt.verify(refreshToken, process.env.REFRESH_SECRET);

    const token = jwt.sign(
      { userId: decoded.userId, email: decoded.email },
      process.env.JWT_SECRET,
      { expiresIn: '7d' }
    );

    res.json({ data: { token } });
  })
);

// Auth middleware
function requireAuth(req, res, next) {
  const authHeader = req.headers.authorization;

  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    throw new AuthenticationError('No token provided');
  }

  const token = authHeader.split(' ')[1];

  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    req.user = decoded;
    next();
  } catch (error) {
    throw new AuthenticationError('Invalid token');
  }
}

// Role-based authorization
function requireRole(...roles) {
  return async (req, res, next) => {
    const user = await User.findById(req.user.userId);

    if (!user || !roles.includes(user.role)) {
      throw new ForbiddenError('Insufficient permissions');
    }

    next();
  };
}
JWT身份验证:
javascript
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');

// Register
router.post('/register',
  validateRegistration,
  asyncHandler(async (req, res) => {
    const { email, password, name } = req.body;

    // Check if user exists
    const existingUser = await User.findOne({ email });
    if (existingUser) {
      throw new ValidationError('Email already registered');
    }

    // Hash password
    const hashedPassword = await bcrypt.hash(password, 10);

    // Create user
    const user = await User.create({
      email,
      password: hashedPassword,
      name
    });

    // Generate token
    const token = jwt.sign(
      { userId: user._id, email: user.email },
      process.env.JWT_SECRET,
      { expiresIn: '7d' }
    );

    res.status(201).json({
      data: {
        user: user.toJSON(),
        token
      }
    });
  })
);

// Login
router.post('/login',
  validateLogin,
  asyncHandler(async (req, res) => {
    const { email, password } = req.body;

    // Find user
    const user = await User.findOne({ email });
    if (!user) {
      throw new ValidationError('Invalid credentials');
    }

    // Verify password
    const isValid = await bcrypt.compare(password, user.password);
    if (!isValid) {
      throw new ValidationError('Invalid credentials');
    }

    // Generate token
    const token = jwt.sign(
      { userId: user._id, email: user.email },
      process.env.JWT_SECRET,
      { expiresIn: '7d' }
    );

    res.json({
      data: {
        user: user.toJSON(),
        token
      }
    });
  })
);

// Refresh token
router.post('/refresh',
  asyncHandler(async (req, res) => {
    const { refreshToken } = req.body;

    if (!refreshToken) {
      throw new ValidationError('Refresh token required');
    }

    const decoded = jwt.verify(refreshToken, process.env.REFRESH_SECRET);

    const token = jwt.sign(
      { userId: decoded.userId, email: decoded.email },
      process.env.JWT_SECRET,
      { expiresIn: '7d' }
    );

    res.json({ data: { token } });
  })
);

// Auth middleware
function requireAuth(req, res, next) {
  const authHeader = req.headers.authorization;

  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    throw new AuthenticationError('No token provided');
  }

  const token = authHeader.split(' ')[1];

  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    req.user = decoded;
    next();
  } catch (error) {
    throw new AuthenticationError('Invalid token');
  }
}

// Role-based authorization
function requireRole(...roles) {
  return async (req, res, next) => {
    const user = await User.findById(req.user.userId);

    if (!user || !roles.includes(user.role)) {
      throw new ForbiddenError('Insufficient permissions');
    }

    next();
  };
}

Validation

数据校验

Input Validation with express-validator:
javascript
const { body, param, query, validationResult } = require('express-validator');

// Validation middleware
const validate = (validations) => {
  return async (req, res, next) => {
    await Promise.all(validations.map(validation => validation.run(req)));

    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      return res.status(400).json({
        error: 'Validation failed',
        details: errors.array()
      });
    }

    next();
  };
};

// User validation rules
const userValidationRules = {
  create: validate([
    body('email')
      .isEmail()
      .normalizeEmail()
      .withMessage('Invalid email address'),
    body('password')
      .isLength({ min: 8 })
      .withMessage('Password must be at least 8 characters')
      .matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/)
      .withMessage('Password must contain uppercase, lowercase, and number'),
    body('name')
      .trim()
      .isLength({ min: 2, max: 50 })
      .withMessage('Name must be between 2 and 50 characters')
  ]),

  update: validate([
    param('id')
      .isMongoId()
      .withMessage('Invalid user ID'),
    body('email')
      .optional()
      .isEmail()
      .normalizeEmail(),
    body('name')
      .optional()
      .trim()
      .isLength({ min: 2, max: 50 })
  ]),

  list: validate([
    query('page')
      .optional()
      .isInt({ min: 1 })
      .toInt(),
    query('limit')
      .optional()
      .isInt({ min: 1, max: 100 })
      .toInt()
  ])
};

// Using validation
router.post('/users', userValidationRules.create, createUser);
router.put('/users/:id', userValidationRules.update, updateUser);
router.get('/users', userValidationRules.list, listUsers);
使用express-validator进行输入校验:
javascript
const { body, param, query, validationResult } = require('express-validator');

// Validation middleware
const validate = (validations) => {
  return async (req, res, next) => {
    await Promise.all(validations.map(validation => validation.run(req)));

    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      return res.status(400).json({
        error: 'Validation failed',
        details: errors.array()
      });
    }

    next();
  };
};

// User validation rules
const userValidationRules = {
  create: validate([
    body('email')
      .isEmail()
      .normalizeEmail()
      .withMessage('Invalid email address'),
    body('password')
      .isLength({ min: 8 })
      .withMessage('Password must be at least 8 characters')
      .matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/)
      .withMessage('Password must contain uppercase, lowercase, and number'),
    body('name')
      .trim()
      .isLength({ min: 2, max: 50 })
      .withMessage('Name must be between 2 and 50 characters')
  ]),

  update: validate([
    param('id')
      .isMongoId()
      .withMessage('Invalid user ID'),
    body('email')
      .optional()
      .isEmail()
      .normalizeEmail(),
    body('name')
      .optional()
      .trim()
      .isLength({ min: 2, max: 50 })
  ]),

  list: validate([
    query('page')
      .optional()
      .isInt({ min: 1 })
      .toInt(),
    query('limit')
      .optional()
      .isInt({ min: 1, max: 100 })
      .toInt()
  ])
};

// Using validation
router.post('/users', userValidationRules.create, createUser);
router.put('/users/:id', userValidationRules.update, updateUser);
router.get('/users', userValidationRules.list, listUsers);

Database Integration

数据库集成

MongoDB with Mongoose:
javascript
const mongoose = require('mongoose');

// Connect to database
async function connectDB() {
  try {
    await mongoose.connect(process.env.MONGODB_URI, {
      useNewUrlParser: true,
      useUnifiedTopology: true
    });
    console.log('MongoDB connected');
  } catch (error) {
    console.error('MongoDB connection error:', error);
    process.exit(1);
  }
}

// User model
const userSchema = new mongoose.Schema({
  email: {
    type: String,
    required: true,
    unique: true,
    lowercase: true
  },
  password: {
    type: String,
    required: true
  },
  name: {
    type: String,
    required: true
  },
  role: {
    type: String,
    enum: ['user', 'admin'],
    default: 'user'
  }
}, {
  timestamps: true
});

userSchema.methods.toJSON = function() {
  const user = this.toObject();
  delete user.password;
  return user;
};

const User = mongoose.model('User', userSchema);

// CRUD operations
router.get('/users', asyncHandler(async (req, res) => {
  const users = await User.find().select('-password');
  res.json({ data: users });
}));

router.post('/users', asyncHandler(async (req, res) => {
  const user = await User.create(req.body);
  res.status(201).json({ data: user });
}));

router.put('/users/:id', asyncHandler(async (req, res) => {
  const user = await User.findByIdAndUpdate(
    req.params.id,
    req.body,
    { new: true, runValidators: true }
  );
  res.json({ data: user });
}));

router.delete('/users/:id', asyncHandler(async (req, res) => {
  await User.findByIdAndDelete(req.params.id);
  res.json({ message: 'User deleted' });
}));
使用Mongoose集成MongoDB:
javascript
const mongoose = require('mongoose');

// Connect to database
async function connectDB() {
  try {
    await mongoose.connect(process.env.MONGODB_URI, {
      useNewUrlParser: true,
      useUnifiedTopology: true
    });
    console.log('MongoDB connected');
  } catch (error) {
    console.error('MongoDB connection error:', error);
    process.exit(1);
  }
}

// User model
const userSchema = new mongoose.Schema({
  email: {
    type: String,
    required: true,
    unique: true,
    lowercase: true
  },
  password: {
    type: String,
    required: true
  },
  name: {
    type: String,
    required: true
  },
  role: {
    type: String,
    enum: ['user', 'admin'],
    default: 'user'
  }
}, {
  timestamps: true
});

userSchema.methods.toJSON = function() {
  const user = this.toObject();
  delete user.password;
  return user;
};

const User = mongoose.model('User', userSchema);

// CRUD operations
router.get('/users', asyncHandler(async (req, res) => {
  const users = await User.find().select('-password');
  res.json({ data: users });
}));

router.post('/users', asyncHandler(async (req, res) => {
  const user = await User.create(req.body);
  res.status(201).json({ data: user });
}));

router.put('/users/:id', asyncHandler(async (req, res) => {
  const user = await User.findByIdAndUpdate(
    req.params.id,
    req.body,
    { new: true, runValidators: true }
  );
  res.json({ data: user });
}));

router.delete('/users/:id', asyncHandler(async (req, res) => {
  await User.findByIdAndDelete(req.params.id);
  res.json({ message: 'User deleted' });
}));

Testing

测试

API Testing with Jest and Supertest:
javascript
const request = require('supertest');
const app = require('../app');
const User = require('../models/User');

describe('User API', () => {
  beforeEach(async () => {
    await User.deleteMany({});
  });

  describe('POST /api/users', () => {
    it('should create a new user', async () => {
      const userData = {
        email: 'test@example.com',
        password: 'Password123',
        name: 'Test User'
      };

      const response = await request(app)
        .post('/api/users')
        .send(userData)
        .expect(201);

      expect(response.body.data).toHaveProperty('email', userData.email);
      expect(response.body.data).not.toHaveProperty('password');
    });

    it('should return 400 for invalid email', async () => {
      const response = await request(app)
        .post('/api/users')
        .send({
          email: 'invalid-email',
          password: 'Password123',
          name: 'Test'
        })
        .expect(400);

      expect(response.body).toHaveProperty('error');
    });
  });

  describe('GET /api/users/:id', () => {
    it('should return user by id', async () => {
      const user = await User.create({
        email: 'test@example.com',
        password: 'hashed',
        name: 'Test User'
      });

      const response = await request(app)
        .get(`/api/users/${user._id}`)
        .expect(200);

      expect(response.body.data).toHaveProperty('email', user.email);
    });

    it('should return 404 for non-existent user', async () => {
      const response = await request(app)
        .get('/api/users/507f1f77bcf86cd799439011')
        .expect(404);

      expect(response.body).toHaveProperty('error');
    });
  });

  describe('Authentication', () => {
    it('should require authentication for protected routes', async () => {
      await request(app)
        .get('/api/protected')
        .expect(401);
    });

    it('should allow access with valid token', async () => {
      const token = jwt.sign({ userId: '123' }, process.env.JWT_SECRET);

      await request(app)
        .get('/api/protected')
        .set('Authorization', `Bearer ${token}`)
        .expect(200);
    });
  });
});
使用Jest和Supertest进行API测试:
javascript
const request = require('supertest');
const app = require('../app');
const User = require('../models/User');

describe('User API', () => {
  beforeEach(async () => {
    await User.deleteMany({});
  });

  describe('POST /api/users', () => {
    it('should create a new user', async () => {
      const userData = {
        email: 'test@example.com',
        password: 'Password123',
        name: 'Test User'
      };

      const response = await request(app)
        .post('/api/users')
        .send(userData)
        .expect(201);

      expect(response.body.data).toHaveProperty('email', userData.email);
      expect(response.body.data).not.toHaveProperty('password');
    });

    it('should return 400 for invalid email', async () => {
      const response = await request(app)
        .post('/api/users')
        .send({
          email: 'invalid-email',
          password: 'Password123',
          name: 'Test'
        })
        .expect(400);

      expect(response.body).toHaveProperty('error');
    });
  });

  describe('GET /api/users/:id', () => {
    it('should return user by id', async () => {
      const user = await User.create({
        email: 'test@example.com',
        password: 'hashed',
        name: 'Test User'
      });

      const response = await request(app)
        .get(`/api/users/${user._id}`)
        .expect(200);

      expect(response.body.data).toHaveProperty('email', user.email);
    });

    it('should return 404 for non-existent user', async () => {
      const response = await request(app)
        .get('/api/users/507f1f77bcf86cd799439011')
        .expect(404);

      expect(response.body).toHaveProperty('error');
    });
  });

  describe('Authentication', () => {
    it('should require authentication for protected routes', async () => {
      await request(app)
        .get('/api/protected')
        .expect(401);
    });

    it('should allow access with valid token', async () => {
      const token = jwt.sign({ userId: '123' }, process.env.JWT_SECRET);

      await request(app)
        .get('/api/protected')
        .set('Authorization', `Bearer ${token}`)
        .expect(200);
    });
  });
});

Best Practices

最佳实践

Security

安全

Security Headers with Helmet:
javascript
const helmet = require('helmet');

// Use helmet for security headers
app.use(helmet());

// Custom configuration
app.use(helmet({
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'self'"],
      styleSrc: ["'self'", "'unsafe-inline'"],
      scriptSrc: ["'self'"],
      imgSrc: ["'self'", 'data:', 'https:']
    }
  },
  hsts: {
    maxAge: 31536000,
    includeSubDomains: true,
    preload: true
  }
}));
CORS Configuration:
javascript
const cors = require('cors');

// Allow all origins (development only)
app.use(cors());

// Production configuration
app.use(cors({
  origin: process.env.ALLOWED_ORIGINS?.split(',') || 'https://example.com',
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  credentials: true,
  maxAge: 86400 // 24 hours
}));

// Dynamic origin validation
app.use(cors({
  origin: (origin, callback) => {
    const allowedOrigins = ['https://example.com', 'https://app.example.com'];

    if (!origin || allowedOrigins.includes(origin)) {
      callback(null, true);
    } else {
      callback(new Error('Not allowed by CORS'));
    }
  }
}));
Rate Limiting:
javascript
const rateLimit = require('express-rate-limit');

// General API rate limiter
const apiLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100, // Limit each IP to 100 requests per windowMs
  message: 'Too many requests from this IP',
  standardHeaders: true,
  legacyHeaders: false
});

app.use('/api/', apiLimiter);

// Strict rate limiter for authentication
const authLimiter = rateLimit({
  windowMs: 15 * 60 * 1000,
  max: 5,
  skipSuccessfulRequests: true
});

app.use('/api/login', authLimiter);
app.use('/api/register', authLimiter);

// Custom key generator
const customLimiter = rateLimit({
  windowMs: 60 * 60 * 1000,
  max: 100,
  keyGenerator: (req) => {
    return req.user?.id || req.ip;
  }
});
Input Sanitization:
javascript
const mongoSanitize = require('express-mongo-sanitize');
const xss = require('xss-clean');

// Prevent NoSQL injection
app.use(mongoSanitize());

// Prevent XSS attacks
app.use(xss());

// Custom sanitization middleware
function sanitizeInput(req, res, next) {
  if (req.body) {
    Object.keys(req.body).forEach(key => {
      if (typeof req.body[key] === 'string') {
        req.body[key] = req.body[key].trim();
      }
    });
  }
  next();
}

app.use(sanitizeInput);
使用Helmet设置安全响应头:
javascript
const helmet = require('helmet');

// Use helmet for security headers
app.use(helmet());

// Custom configuration
app.use(helmet({
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'self'"],
      styleSrc: ["'self'", "'unsafe-inline'"],
      scriptSrc: ["'self'"],
      imgSrc: ["'self'", 'data:', 'https:']
    }
  },
  hsts: {
    maxAge: 31536000,
    includeSubDomains: true,
    preload: true
  }
}));
CORS配置:
javascript
const cors = require('cors');

// Allow all origins (development only)
app.use(cors());

// Production configuration
app.use(cors({
  origin: process.env.ALLOWED_ORIGINS?.split(',') || 'https://example.com',
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  credentials: true,
  maxAge: 86400 // 24 hours
}));

// Dynamic origin validation
app.use(cors({
  origin: (origin, callback) => {
    const allowedOrigins = ['https://example.com', 'https://app.example.com'];

    if (!origin || allowedOrigins.includes(origin)) {
      callback(null, true);
    } else {
      callback(new Error('Not allowed by CORS'));
    }
  }
}));
请求频率限制:
javascript
const rateLimit = require('express-rate-limit');

// General API rate limiter
const apiLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100, // Limit each IP to 100 requests per windowMs
  message: 'Too many requests from this IP',
  standardHeaders: true,
  legacyHeaders: false
});

app.use('/api/', apiLimiter);

// Strict rate limiter for authentication
const authLimiter = rateLimit({
  windowMs: 15 * 60 * 1000,
  max: 5,
  skipSuccessfulRequests: true
});

app.use('/api/login', authLimiter);
app.use('/api/register', authLimiter);

// Custom key generator
const customLimiter = rateLimit({
  windowMs: 60 * 60 * 1000,
  max: 100,
  keyGenerator: (req) => {
    return req.user?.id || req.ip;
  }
});
输入内容清理:
javascript
const mongoSanitize = require('express-mongo-sanitize');
const xss = require('xss-clean');

// Prevent NoSQL injection
app.use(mongoSanitize());

// Prevent XSS attacks
app.use(xss());

// Custom sanitization middleware
function sanitizeInput(req, res, next) {
  if (req.body) {
    Object.keys(req.body).forEach(key => {
      if (typeof req.body[key] === 'string') {
        req.body[key] = req.body[key].trim();
      }
    });
  }
  next();
}

app.use(sanitizeInput);

Performance

性能

Response Compression:
javascript
const compression = require('compression');

// Enable compression
app.use(compression({
  level: 6,
  threshold: 1024,
  filter: (req, res) => {
    if (req.headers['x-no-compression']) {
      return false;
    }
    return compression.filter(req, res);
  }
}));
Caching:
javascript
// Simple in-memory cache
const cache = new Map();

function cacheMiddleware(duration) {
  return (req, res, next) => {
    const key = req.originalUrl;
    const cached = cache.get(key);

    if (cached && Date.now() < cached.expiry) {
      return res.json(cached.data);
    }

    res.originalJson = res.json;
    res.json = (data) => {
      cache.set(key, {
        data,
        expiry: Date.now() + duration * 1000
      });
      res.originalJson(data);
    };

    next();
  };
}

// Use cache
app.get('/api/users', cacheMiddleware(60), getUsers);

// Redis cache
const redis = require('redis');
const client = redis.createClient();

async function redisCache(duration) {
  return async (req, res, next) => {
    const key = `cache:${req.originalUrl}`;

    const cached = await client.get(key);
    if (cached) {
      return res.json(JSON.parse(cached));
    }

    res.originalJson = res.json;
    res.json = async (data) => {
      await client.setEx(key, duration, JSON.stringify(data));
      res.originalJson(data);
    };

    next();
  };
}
Request Timeout:
javascript
function timeout(ms) {
  return (req, res, next) => {
    req.setTimeout(ms, () => {
      res.status(408).json({ error: 'Request timeout' });
    });
    next();
  };
}

app.use(timeout(30000)); // 30 seconds
响应压缩:
javascript
const compression = require('compression');

// Enable compression
app.use(compression({
  level: 6,
  threshold: 1024,
  filter: (req, res) => {
    if (req.headers['x-no-compression']) {
      return false;
    }
    return compression.filter(req, res);
  }
}));
缓存:
javascript
// Simple in-memory cache
const cache = new Map();

function cacheMiddleware(duration) {
  return (req, res, next) => {
    const key = req.originalUrl;
    const cached = cache.get(key);

    if (cached && Date.now() < cached.expiry) {
      return res.json(cached.data);
    }

    res.originalJson = res.json;
    res.json = (data) => {
      cache.set(key, {
        data,
        expiry: Date.now() + duration * 1000
      });
      res.originalJson(data);
    };

    next();
  };
}

// Use cache
app.get('/api/users', cacheMiddleware(60), getUsers);

// Redis cache
const redis = require('redis');
const client = redis.createClient();

async function redisCache(duration) {
  return async (req, res, next) => {
    const key = `cache:${req.originalUrl}`;

    const cached = await client.get(key);
    if (cached) {
      return res.json(JSON.parse(cached));
    }

    res.originalJson = res.json;
    res.json = async (data) => {
      await client.setEx(key, duration, JSON.stringify(data));
      res.originalJson(data);
    };

    next();
  };
}
请求超时:
javascript
function timeout(ms) {
  return (req, res, next) => {
    req.setTimeout(ms, () => {
      res.status(408).json({ error: 'Request timeout' });
    });
    next();
  };
}

app.use(timeout(30000)); // 30 seconds

Error Handling

错误处理

Centralized Error Handling:
javascript
// Custom error classes
class AppError extends Error {
  constructor(message, statusCode) {
    super(message);
    this.statusCode = statusCode;
    this.isOperational = true;
    Error.captureStackTrace(this, this.constructor);
  }
}

class ValidationError extends AppError {
  constructor(message) {
    super(message, 400);
  }
}

class AuthenticationError extends AppError {
  constructor(message) {
    super(message, 401);
  }
}

class NotFoundError extends AppError {
  constructor(message) {
    super(message, 404);
  }
}

// Error handler
function errorHandler(err, req, res, next) {
  let error = { ...err };
  error.message = err.message;

  // Log error
  console.error(err);

  // Mongoose validation error
  if (err.name === 'ValidationError') {
    const message = Object.values(err.errors).map(e => e.message).join(', ');
    error = new ValidationError(message);
  }

  // Mongoose duplicate key
  if (err.code === 11000) {
    const field = Object.keys(err.keyValue)[0];
    error = new ValidationError(`${field} already exists`);
  }

  // JWT errors
  if (err.name === 'JsonWebTokenError') {
    error = new AuthenticationError('Invalid token');
  }

  if (err.name === 'TokenExpiredError') {
    error = new AuthenticationError('Token expired');
  }

  res.status(error.statusCode || 500).json({
    error: {
      message: error.message || 'Server error',
      ...(process.env.NODE_ENV === 'development' && { stack: err.stack })
    }
  });
}

app.use(errorHandler);
集中式错误处理:
javascript
// Custom error classes
class AppError extends Error {
  constructor(message, statusCode) {
    super(message);
    this.statusCode = statusCode;
    this.isOperational = true;
    Error.captureStackTrace(this, this.constructor);
  }
}

class ValidationError extends AppError {
  constructor(message) {
    super(message, 400);
  }
}

class AuthenticationError extends AppError {
  constructor(message) {
    super(message, 401);
  }
}

class NotFoundError extends AppError {
  constructor(message) {
    super(message, 404);
  }
}

// Error handler
function errorHandler(err, req, res, next) {
  let error = { ...err };
  error.message = err.message;

  // Log error
  console.error(err);

  // Mongoose validation error
  if (err.name === 'ValidationError') {
    const message = Object.values(err.errors).map(e => e.message).join(', ');
    error = new ValidationError(message);
  }

  // Mongoose duplicate key
  if (err.code === 11000) {
    const field = Object.keys(err.keyValue)[0];
    error = new ValidationError(`${field} already exists`);
  }

  // JWT errors
  if (err.name === 'JsonWebTokenError') {
    error = new AuthenticationError('Invalid token');
  }

  if (err.name === 'TokenExpiredError') {
    error = new AuthenticationError('Token expired');
  }

  res.status(error.statusCode || 500).json({
    error: {
      message: error.message || 'Server error',
      ...(process.env.NODE_ENV === 'development' && { stack: err.stack })
    }
  });
}

app.use(errorHandler);

Logging

日志

Morgan and Winston:
javascript
const morgan = require('morgan');
const winston = require('winston');

// Winston logger
const logger = winston.createLogger({
  level: process.env.LOG_LEVEL || 'info',
  format: winston.format.combine(
    winston.format.timestamp(),
    winston.format.errors({ stack: true }),
    winston.format.json()
  ),
  transports: [
    new winston.transports.File({ filename: 'error.log', level: 'error' }),
    new winston.transports.File({ filename: 'combined.log' })
  ]
});

if (process.env.NODE_ENV !== 'production') {
  logger.add(new winston.transports.Console({
    format: winston.format.simple()
  }));
}

// Morgan HTTP logging
app.use(morgan('combined', {
  stream: {
    write: (message) => logger.info(message.trim())
  }
}));

// Custom logging middleware
app.use((req, res, next) => {
  logger.info({
    method: req.method,
    url: req.url,
    ip: req.ip,
    userAgent: req.get('user-agent')
  });
  next();
});
使用Morgan和Winston:
javascript
const morgan = require('morgan');
const winston = require('winston');

// Winston logger
const logger = winston.createLogger({
  level: process.env.LOG_LEVEL || 'info',
  format: winston.format.combine(
    winston.format.timestamp(),
    winston.format.errors({ stack: true }),
    winston.format.json()
  ),
  transports: [
    new winston.transports.File({ filename: 'error.log', level: 'error' }),
    new winston.transports.File({ filename: 'combined.log' })
  ]
});

if (process.env.NODE_ENV !== 'production') {
  logger.add(new winston.transports.Console({
    format: winston.format.simple()
  }));
}

// Morgan HTTP logging
app.use(morgan('combined', {
  stream: {
    write: (message) => logger.info(message.trim())
  }
}));

// Custom logging middleware
app.use((req, res, next) => {
  logger.info({
    method: req.method,
    url: req.url,
    ip: req.ip,
    userAgent: req.get('user-agent')
  });
  next();
});

API Versioning

API版本控制

URL Versioning:
javascript
// Version 1 routes
const v1Router = express.Router();
v1Router.get('/users', getUsersV1);
app.use('/api/v1', v1Router);

// Version 2 routes
const v2Router = express.Router();
v2Router.get('/users', getUsersV2);
app.use('/api/v2', v2Router);
Header Versioning:
javascript
function apiVersion(version) {
  return (req, res, next) => {
    const requestedVersion = req.get('API-Version') || '1.0';

    if (requestedVersion === version) {
      next();
    } else {
      next('route');
    }
  };
}

app.get('/api/users', apiVersion('1.0'), getUsersV1);
app.get('/api/users', apiVersion('2.0'), getUsersV2);
URL版本控制:
javascript
// Version 1 routes
const v1Router = express.Router();
v1Router.get('/users', getUsersV1);
app.use('/api/v1', v1Router);

// Version 2 routes
const v2Router = express.Router();
v2Router.get('/users', getUsersV2);
app.use('/api/v2', v2Router);
请求头版本控制:
javascript
function apiVersion(version) {
  return (req, res, next) => {
    const requestedVersion = req.get('API-Version') || '1.0';

    if (requestedVersion === version) {
      next();
    } else {
      next('route');
    }
  };
}

app.get('/api/users', apiVersion('1.0'), getUsersV1);
app.get('/api/users', apiVersion('2.0'), getUsersV2);

Examples

示例

1. Basic Express Server

1. 基础Express服务器

javascript
const express = require('express');
const app = express();

app.use(express.json());

app.get('/', (req, res) => {
  res.json({ message: 'Hello Express!' });
});

app.get('/health', (req, res) => {
  res.json({
    status: 'healthy',
    timestamp: new Date().toISOString()
  });
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});
javascript
const express = require('express');
const app = express();

app.use(express.json());

app.get('/', (req, res) => {
  res.json({ message: 'Hello Express!' });
});

app.get('/health', (req, res) => {
  res.json({
    status: 'healthy',
    timestamp: new Date().toISOString()
  });
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

2. Complete REST API

2. 完整REST API

javascript
const express = require('express');
const mongoose = require('mongoose');
const app = express();

// Middleware
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// Models
const Product = mongoose.model('Product', {
  name: { type: String, required: true },
  price: { type: Number, required: true },
  description: String,
  inStock: { type: Boolean, default: true }
});

// Routes
app.get('/api/products', async (req, res, next) => {
  try {
    const products = await Product.find();
    res.json({ data: products });
  } catch (error) {
    next(error);
  }
});

app.get('/api/products/:id', async (req, res, next) => {
  try {
    const product = await Product.findById(req.params.id);
    if (!product) {
      return res.status(404).json({ error: 'Product not found' });
    }
    res.json({ data: product });
  } catch (error) {
    next(error);
  }
});

app.post('/api/products', async (req, res, next) => {
  try {
    const product = await Product.create(req.body);
    res.status(201).json({ data: product });
  } catch (error) {
    next(error);
  }
});

app.put('/api/products/:id', async (req, res, next) => {
  try {
    const product = await Product.findByIdAndUpdate(
      req.params.id,
      req.body,
      { new: true, runValidators: true }
    );
    if (!product) {
      return res.status(404).json({ error: 'Product not found' });
    }
    res.json({ data: product });
  } catch (error) {
    next(error);
  }
});

app.delete('/api/products/:id', async (req, res, next) => {
  try {
    const product = await Product.findByIdAndDelete(req.params.id);
    if (!product) {
      return res.status(404).json({ error: 'Product not found' });
    }
    res.json({ message: 'Product deleted' });
  } catch (error) {
    next(error);
  }
});

// Error handler
app.use((err, req, res, next) => {
  console.error(err);
  res.status(500).json({ error: err.message });
});

// Start server
mongoose.connect('mongodb://localhost/shop')
  .then(() => {
    app.listen(3000, () => console.log('Server running'));
  });
javascript
const express = require('express');
const mongoose = require('mongoose');
const app = express();

// Middleware
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// Models
const Product = mongoose.model('Product', {
  name: { type: String, required: true },
  price: { type: Number, required: true },
  description: String,
  inStock: { type: Boolean, default: true }
});

// Routes
app.get('/api/products', async (req, res, next) => {
  try {
    const products = await Product.find();
    res.json({ data: products });
  } catch (error) {
    next(error);
  }
});

app.get('/api/products/:id', async (req, res, next) => {
  try {
    const product = await Product.findById(req.params.id);
    if (!product) {
      return res.status(404).json({ error: 'Product not found' });
    }
    res.json({ data: product });
  } catch (error) {
    next(error);
  }
});

app.post('/api/products', async (req, res, next) => {
  try {
    const product = await Product.create(req.body);
    res.status(201).json({ data: product });
  } catch (error) {
    next(error);
  }
});

app.put('/api/products/:id', async (req, res, next) => {
  try {
    const product = await Product.findByIdAndUpdate(
      req.params.id,
      req.body,
      { new: true, runValidators: true }
    );
    if (!product) {
      return res.status(404).json({ error: 'Product not found' });
    }
    res.json({ data: product });
  } catch (error) {
    next(error);
  }
});

app.delete('/api/products/:id', async (req, res, next) => {
  try {
    const product = await Product.findByIdAndDelete(req.params.id);
    if (!product) {
      return res.status(404).json({ error: 'Product not found' });
    }
    res.json({ message: 'Product deleted' });
  } catch (error) {
    next(error);
  }
});

// Error handler
app.use((err, req, res, next) => {
  console.error(err);
  res.status(500).json({ error: err.message });
});

// Start server
mongoose.connect('mongodb://localhost/shop')
  .then(() => {
    app.listen(3000, () => console.log('Server running'));
  });

3. Authentication System

3. 身份验证系统

javascript
const express = require('express');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const app = express();

app.use(express.json());

const users = new Map(); // In-memory storage

// Register
app.post('/api/register', async (req, res) => {
  const { email, password, name } = req.body;

  if (users.has(email)) {
    return res.status(400).json({ error: 'Email already exists' });
  }

  const hashedPassword = await bcrypt.hash(password, 10);

  users.set(email, {
    email,
    password: hashedPassword,
    name,
    id: Date.now().toString()
  });

  res.status(201).json({ message: 'User created' });
});

// Login
app.post('/api/login', async (req, res) => {
  const { email, password } = req.body;

  const user = users.get(email);
  if (!user) {
    return res.status(401).json({ error: 'Invalid credentials' });
  }

  const isValid = await bcrypt.compare(password, user.password);
  if (!isValid) {
    return res.status(401).json({ error: 'Invalid credentials' });
  }

  const token = jwt.sign(
    { userId: user.id, email: user.email },
    'secret-key',
    { expiresIn: '24h' }
  );

  res.json({ token });
});

// Protected route
app.get('/api/profile', (req, res) => {
  const token = req.headers.authorization?.split(' ')[1];

  if (!token) {
    return res.status(401).json({ error: 'No token' });
  }

  try {
    const decoded = jwt.verify(token, 'secret-key');
    const user = Array.from(users.values()).find(u => u.id === decoded.userId);

    res.json({
      email: user.email,
      name: user.name
    });
  } catch (error) {
    res.status(401).json({ error: 'Invalid token' });
  }
});

app.listen(3000);
See EXAMPLES.md for 15+ additional examples covering file uploads, CORS, rate limiting, WebSockets, testing, deployment, and more.
javascript
const express = require('express');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const app = express();

app.use(express.json());

const users = new Map(); // In-memory storage

// Register
app.post('/api/register', async (req, res) => {
  const { email, password, name } = req.body;

  if (users.has(email)) {
    return res.status(400).json({ error: 'Email already exists' });
  }

  const hashedPassword = await bcrypt.hash(password, 10);

  users.set(email, {
    email,
    password: hashedPassword,
    name,
    id: Date.now().toString()
  });

  res.status(201).json({ message: 'User created' });
});

// Login
app.post('/api/login', async (req, res) => {
  const { email, password } = req.body;

  const user = users.get(email);
  if (!user) {
    return res.status(401).json({ error: 'Invalid credentials' });
  }

  const isValid = await bcrypt.compare(password, user.password);
  if (!isValid) {
    return res.status(401).json({ error: 'Invalid credentials' });
  }

  const token = jwt.sign(
    { userId: user.id, email: user.email },
    'secret-key',
    { expiresIn: '24h' }
  );

  res.json({ token });
});

// Protected route
app.get('/api/profile', (req, res) => {
  const token = req.headers.authorization?.split(' ')[1];

  if (!token) {
    return res.status(401).json({ error: 'No token' });
  }

  try {
    const decoded = jwt.verify(token, 'secret-key');
    const user = Array.from(users.values()).find(u => u.id === decoded.userId);

    res.json({
      email: user.email,
      name: user.name
    });
  } catch (error) {
    res.status(401).json({ error: 'Invalid token' });
  }
});

app.listen(3000);
更多15+示例请查看EXAMPLES.md,涵盖文件上传、CORS、请求频率限制、WebSocket、测试、部署等内容。

Summary

总结

This Express.js development skill covers:
  1. Core Concepts: Application setup, routing, middleware, request/response handling, error handling
  2. API Reference: Complete reference for Express methods and properties
  3. Workflow Patterns: REST API design, authentication, validation, database integration, testing
  4. Best Practices: Security (helmet, CORS, rate limiting), performance (compression, caching), error handling, logging, API versioning
  5. Real-world Examples: Complete implementations for common use cases
The patterns and examples are based on Express.js best practices (Trust Score: 9) and represent modern Node.js backend development standards.
本Express.js开发技能涵盖:
  1. 核心概念:应用搭建、路由、中间件、请求/响应处理、错误处理
  2. API参考:Express方法与属性的完整参考
  3. 工作流模式:REST API设计、身份验证、数据校验、数据库集成、测试
  4. 最佳实践:安全(Helmet、CORS、请求频率限制)、性能(压缩、缓存)、错误处理、日志、API版本控制
  5. 实战示例:常见场景的完整实现
本技能中的模式与示例基于Express.js最佳实践(可信度评分:9),代表了现代Node.js后端开发的标准。