mcpField
E
express-typescript

express-typescript

github.com/mindrally开发2871.4k 次安装3 周前更新
代码仓库

关于 express-typescript

面向 Express.js 与 TypeScript API 开发的技能,强调可维护性,并介绍类型、路由、验证、安全、错误处理和测试实践。

主要功能

  1. 01类型化函数与接口
  2. 02按资源组织路由
  3. 03使用 Zod 验证请求
  4. 04集中式错误处理中间件
  5. 05安全与集成测试

适用场景

  1. 01组织 Express API 路由
  2. 02使用 Zod 验证请求数据
  3. 03应用统一错误处理
  4. 04使用 Jest 和 supertest 测试 API

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/mindrally/skills --skill express-typescript

安装后在 Claude Code 中输入/express-typescript确认已加载。

详细介绍

Express TypeScript 开发

你精通 Express.js 和 TypeScript 开发,深入了解如何构建可扩展、易维护的 API。

TypeScript 通用指南

基本原则
  • 所有代码和文档均使用英语
  • 始终为变量和函数(参数和返回值)声明类型
  • 避免使用 any 类型;改为创建必要的类型
  • 使用 JSDoc 为公共类和方法编写文档
  • 编写简洁、易维护且技术上准确的代码
  • 使用函数式和声明式编程模式;尽可能避免使用类
  • 优先采用迭代和模块化,以遵循 DRY 原则
命名规范
  • 类型和接口使用 PascalCase
  • 变量、函数和方法使用 camelCase
  • 文件和目录名称使用 kebab-case
  • 环境变量使用 UPPERCASE
  • 使用带助动词的描述性变量名称:isLoading、hasError、canDelete
  • 每个函数都以动词开头
函数
  • 编写简短且用途单一的函数
  • 中间件和处理程序使用箭头函数
  • 在整个代码库中统一使用 async/await
  • 多个参数时使用 RO-RO 模式
类型和接口
  • 对对象结构优先使用接口而非类型
  • 避免使用枚举;改用映射或 const 对象
  • 使用 Zod 进行运行时验证,并推断类型
  • 对不可变属性使用 readonly

Express 专属指南

项目结构
src/
  routes/
    {resource}/
      index.ts
      controller.ts
      validators.ts
  middleware/
    auth.ts
    errorHandler.ts
    requestLogger.ts
    validateRequest.ts
  services/
    {domain}Service.ts
  models/
    {entity}.ts
  types/
    express.d.ts
    index.ts
  utils/
  config/
  app.ts
  server.ts
应用设置
import express, { Express } from 'express';
import helmet from 'helmet';
import cors from 'cors';
import { errorHandler } from './middleware/errorHandler';
import { requestLogger } from './middleware/requestLogger';
import routes from './routes';

const createApp = (): Express => {
  const app = express();

  // Security middleware
  app.use(helmet());
  app.use(cors());

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

  // Request logging
  app.use(requestLogger);

  // Routes
  app.use('/api', routes);

  // Error handling (must be last)
  app.use(errorHandler);

  return app;
};

export default createApp;
中间件模式
  • 使用中间件处理横切关注点
  • 按执行顺序链接中间件
  • 在专用错误中间件中处理错误
import { Request, Response, NextFunction } from 'express';

// Request logging middleware
const requestLogger = (req: Request, res: Response, next: NextFunction): void => {
  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
const authenticate = async (req: Request, res: Response, next: NextFunction): Promise<void> => {
  try {
    const token = req.headers.authorization?.split(' ')[1];

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

    const user = await verifyToken(token);
    req.user = user;
    next();
  } catch (error) {
    res.status(401).json({ error: 'Invalid token' });
  }
};
路由
  • 按资源组织路由
  • 使用 Router 定义模块化路由
  • 在适当的层级应用中间件
import { Router } from 'express';
import { authenticate } from '../middleware/auth';
import { validateRequest } from '../middleware/validateRequest';
import { createUserSchema, updateUserSchema } from './validators';
import * as controller from './controller';

const router = Router();

router.get('/', controller.listUsers);
router.get('/:id', controller.getUser);
router.post('/', validateRequest(createUserSchema), controller.createUser);
router.put('/:id', authenticate, validateRequest(updateUserSchema), controller.updateUser);
router.delete('/:id', authenticate, controller.deleteUser);

export default router;
请求验证
  • 验证所有传入请求
  • 使用 Zod 定义和验证架构
  • 创建可复用的验证中间件
import { z } from 'zod';
import { Request, Response, NextFunction } from 'express';

const createUserSchema = z.object({
  body: z.object({
    name: z.string().min(1),
    email: z.string().email(),
    password: z.string().min(8),
  }),
});

const validateRequest = (schema: z.ZodSchema) => {
  return async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      await schema.parseAsync({
        body: req.body,
        query: req.query,
        params: req.params,
      });
      next();
    } catch (error) {
      if (error instanceof z.ZodError) {
        res.status(400).json({
          error: 'Validation failed',
          details: error.errors,
        });
        return;
      }
      next(error);
    }
  };
};
错误处理
  • 创建自定义错误类
  • 使用集中式错误处理程序中间件
  • 返回一致的错误响应
class AppError extends Error {
  constructor(
    public statusCode: number,
    public message: string,
    public code: string = 'INTERNAL_ERROR'
  ) {
    super(message);
    this.name = 'AppError';
  }
}

class NotFoundError extends AppError {
  constructor(resource: string) {
    super(404, `${resource} not found`, 'NOT_FOUND');
  }
}

// Error handler middleware
const errorHandler = (err: Error, req: Request, res: Response, next: NextFunction): void => {
  if (err instanceof AppError) {
    res.status(err.statusCode).json({
      error: err.code,
      message: err.message,
    });
    return;
  }

  console.error(err);
  res.status(500).json({
    error: 'INTERNAL_ERROR',
    message: 'An unexpected error occurred',
  });
};
TypeScript 扩展

扩展 Express 类型以添加自定义属性:

// types/express.d.ts
import { User } from '../models/User';

declare global {
  namespace Express {
    interface Request {
      user?: User;
      requestId?: string;
    }
  }
}
安全最佳实践
  • 使用 helmet 设置安全标头
  • 实施速率限制
  • 清理用户输入
  • 在生产环境中使用 HTTPS
  • 正确配置 CORS
import helmet from 'helmet';
import rateLimit from 'express-rate-limit';

app.use(helmet());

const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100, // limit each IP to 100 requests per windowMs
});

app.use('/api', limiter);
测试
  • 使用 Jest 和 supertest 进行集成测试
  • 单独测试中间件
  • 模拟外部依赖
import request from 'supertest';
import createApp from '../app';

describe('Users API', () => {
  const app = createApp();

  it('should create a user', async () => {
    const response = await request(app)
      .post('/api/users')
      .send({ name: 'John', email: '[email protected]', password: 'password123' })
      .expect(201);

    expect(response.body).toHaveProperty('id');
    expect(response.body.name).toBe('John');
  });
});

评论

支持 Markdown