0.2.1 • Published 5 months ago

@aqcq/express-reply v0.2.1

Weekly downloads
-
License
MIT
Repository
-
Last release
5 months ago

@aqcq/express-reply

Express Reply middleware,为 Express Response 对象添加统一的响应方法和自动错误捕获功能。

特色功能

自动错误捕获 - 无需手动调用 next(error),直接 throw 即可
🚀 统一响应格式 - 标准化成功和错误响应结构
📝 完整 TypeScript 支持 - 类型提示和自动补全
🔧 灵活配置 - 支持预定义和自定义错误类型

安装

npm install @aqcq/express-reply
# 或
yarn add @aqcq/express-reply

核心功能

🎯 自动错误捕获(推荐方式)

使用 catchError 包装器,让你可以直接 throw 错误,无需调用 next()

import express from 'express';
import { errorMiddleware, ResponseError, catchError } from '@aqcq/express-reply';

const app = express();

// 异步路由 - 自动捕获错误
app.get('/api/user/:id', catchError(async (req, res) => {
  const { id } = req.params;
  
  if (id === '404') {
    // 直接抛出错误,无需 next()!
    throw new ResponseError('VALIDATION_ERROR');
  }
  
  // 模拟异步数据库操作
  const user = await getUserFromDB(id);
  res.reply(user);
}));

// 同步路由 - 自动捕获错误
app.get('/api/sync', catchError((req, res) => {
  // 同步代码中也可以直接抛出错误
  throw new ResponseError('HTTP_ERROR');
}));

// 错误处理中间件(必须放在最后)
app.use(errorMiddleware());
app.listen(3000);

🔧 针对性错误捕获

如果你明确知道路由是同步还是异步的,可以使用更具体的包装器:

import { asyncHandler, syncHandler } from '@aqcq/express-reply';

// 专门用于异步路由
app.get('/api/async', asyncHandler(async (req, res) => {
  await someAsyncOperation();
  throw new ResponseError('REQUEST_AI_ERROR');
}));

// 专门用于同步路由
app.get('/api/sync', syncHandler((req, res) => {
  throw new ResponseError('RATE_LIMIT_ERROR');
}));

📋 传统方式(向后兼容)

如果不使用错误捕获包装器,仍需手动调用 next()

app.get('/api/traditional', async (req, res, next) => {
  try {
    throw new ResponseError('VALIDATION_ERROR');
  } catch (error) {
    next(error); // 传统方式需要手动调用
  }
});

✅ 统一响应格式

使用内置的响应方法来标准化你的API响应:

import express from 'express';
import { replyMiddleware, catchError } from '@aqcq/express-reply';

const app = express();

// 注册 reply 中间件
app.use(replyMiddleware);

app.get('/api/success', catchError(async (req, res) => {
  // 成功响应 - 统一格式
  res.reply({ 
    message: 'Hello World', 
    data: { id: 1, name: '用户名' } 
  });
}));

app.get('/api/fail', catchError((req, res) => {
  // 方式1: 直接使用响应方法
  res.replyFail('VALIDATION_ERROR');
  
  // 方式2: 抛出错误(推荐)
  // throw new ResponseError('VALIDATION_ERROR');
}));

app.listen(3000);

TypeScript 支持

本包提供完整的 TypeScript 类型支持。安装后,你的 Express Response 对象会自动扩展以下方法:

interface Response {
  reply: (data: any) => void;
  replyFail: (name: ErrorName) => void;
}

在 TypeScript 项目中使用时,你会获得完整的类型提示和自动补全:

import express from 'express';
import { replyMiddleware, ErrorName } from '@aqcq/express-reply';

const app = express();
app.use(replyMiddleware);

app.get('/api/users', (req, res) => {
  // TypeScript 会为 res.reply 和 res.replyFail 提供类型提示
  res.reply({ users: [] });
  res.replyFail('USER_LIMIT_ERROR'); // 错误名称会有自动补全
});

API 参考

错误捕获包装器

catchError(handler) - 推荐使用

通用错误捕获包装器,自动处理同步和异步错误:

import { catchError } from '@aqcq/express-reply';

app.get('/api/route', catchError(async (req, res) => {
  // 可以是同步或异步函数
  throw new ResponseError('VALIDATION_ERROR');
}));

asyncHandler(handler)

专门用于异步路由的错误捕获:

import { asyncHandler } from '@aqcq/express-reply';

app.get('/api/async', asyncHandler(async (req, res) => {
  await someAsyncOperation();
  throw new ResponseError('REQUEST_AI_ERROR');
}));

syncHandler(handler)

专门用于同步路由的错误捕获:

import { syncHandler } from '@aqcq/express-reply';

app.get('/api/sync', syncHandler((req, res) => {
  throw new ResponseError('HTTP_ERROR');
}));

响应方法

res.reply(data)

发送成功响应。

  • data: 要返回的数据(任意类型)

响应格式:

{
  "code": 0,
  "message": "success",
  "timestamp": 1234567890,
  "data": "你传入的数据"
}

res.replyFail(errorName, code?)

发送错误响应。

  • errorName: 错误名称(预定义类型或自定义字符串)
  • code: 可选的错误码,默认为 5000

响应格式:

{
  "code": 2001,
  "message": "parameter validation error",
  "success": false
}

错误类

new ResponseError(message, code?)

创建一个可以被错误中间件捕获的错误实例。

  • message: 错误名称(预定义类型)或自定义错误消息
  • code: 可选的错误码,默认为 500
// 使用预定义错误类型
throw new ResponseError('VALIDATION_ERROR');

// 使用自定义错误消息和错误码
throw new ResponseError('自定义错误消息', 4001);

支持的错误类型及错误码

[
  {
    code: 1001,
    name: 'HTTP_ERROR',
    message: 'internal unknown error',
    _apiDesc: '内部未知错误',
  },
  {
    code: 2001,
    name: 'VALIDATION_ERROR',
    message: 'parameter validation error',
    _apiDesc: '参数校验错误',
  },
  {
    code: 4002,
    name: 'REQUEST_AI_ERROR',
    message: 'requestAI error',
  },
  {
    code: 4003,
    name: 'USER_LIMIT_ERROR',
    message:
      'The limit for current model has been reached. Please wait until the next period or upgrade your plan to get more.',
  },
  {
    code: 4003,
    name: 'NOT_BASE_BOT_USAGE_LIMIT_ERROR',
    message:
      'The monthly queries for GPT-4, which this bot is based on, has been exhausted. Please use another bot.',
  },
  {
    code: 4004,
    name: 'USAGE_LIMIT_FOR_FREE_ERROR',
    message:
      'Free users can only use the standard model ( 10 queries per day ). Please try again tomorrow or upgrade your plan to get more.',
  },
  {
    code: 4005,
    name: 'BASE_BOT_NOT_FOUND_ERRO',
    message:
      'The base bot for your current bot has been deleted. Please reconfigure another base robot.',
  },
  {
    code: 4006,
    name: 'ONLY_USE_GPT3DOT5_ERROR',
    message:
      'Your complimentary queries has been exhausted, you can now only use GPT-3.5 (10 queries per day).',
  },
  {
    code: 4008,
    name: 'REQUEST_AI_WITH_NETWORK_AUTH_ERROR',
    message: 'Unauthorized',
  },
  {
    code: 4009,
    name: 'ONLY_USE_BASIC_BOT_ERROR',
    message:
      'The advanced drawing model is not available for free users. Upgrade your plan to get more advanced features.',
  },
  {
    code: 4010,
    name: 'BOT_NETWORK_ERROR',
    message: 'Internet access is not available for free users.',
  },
  {
    code: 4011,
    name: 'ONLY_PRO_USER_CAN_USE_ERRO',
    message: 'insufficient User Permissions',
    _apiDesc: 'insufficient User Permissions',
  },
  {
    code: 4012,
    name: 'AI_USE_PRO_ERROR',
    message: 'The advanced drawing model is not available for Air Plan.Please upgrade your plan.',
  },
  {
    code: 4013,
    name: 'MODEL_NOT_FOUND_ERROR',
    message: 'Model not found',
    _apiDesc: 'Model not found',
  },
  {
    code: 4014,
    name: 'MODEL_UNAVAILABLE_ERROR',
    message: 'The model you are using is not available. Please try another model.',
  },
  {
    code: 4015,
    name: 'MODEL_UNAVAILABLE_ERROR',
    message: 'Model unavailable',
    _apiDesc: 'Model unavailable',
  },
  {
    code: 5001,
    name: 'CREATE_ACTION_ERROR',
    message: 'Creation failure',
    _apiDesc: 'Creation failure',
  },
  {
    code: 5040,
    name: 'RESPONSE_MODEL_NOT_FOUND_ERRO',
    message: 'model not found',
    _apiDesc: '模型不存在',
  },
  {
    code: 5041,
    name: 'RESPONSE_GEN_TITLE_ERROR',
    message: 'generate title failed',
    _apiDesc: '生成标题失败',
  },
  {
    code: 5042,
    name: 'RESPONSE_QUERY_ERROR',
    message: 'query failed',
    _apiDesc: '查询失败',
  },
  {
    code: 5043,
    name: 'RESPONSE_BOT_OPERATION_ERROR',
    message: 'operation failed',
    _apiDesc: 'bot操作失败',
  },
  {
    code: 5044,
    name: 'RESPONSE_BOT_EXISTS_ERROR',
    message: 'Bot already exists',
    _apiDesc: 'bot已存在',
  },
  {
    code: 6001,
    name: 'RATE_LIMIT_ERROR',
    message: 'Request rate limit',
    _apiDesc: 'Creation failure',
  },
]

完整的错误类型列表请参考源码中的 ERROR_MESSAGES

最佳实践

✅ 推荐做法

import { replyMiddleware, errorMiddleware, catchError, ResponseError } from '@aqcq/express-reply';

const app = express();

// 1. 注册 reply 中间件
app.use(replyMiddleware);

// 2. 使用 catchError 包装所有路由
app.get('/api/users', catchError(async (req, res) => {
  // 3. 直接抛出错误,无需 next()
  if (!req.user) {
    throw new ResponseError('VALIDATION_ERROR');
  }
  
  // 4. 使用统一的响应格式
  res.reply({ users: await getUsers() });
}));

// 5. 错误处理中间件放在最后
app.use(errorMiddleware());

❌ 避免的做法

// 不推荐:混合使用传统方式和新方式
app.get('/api/bad', async (req, res, next) => {
  try {
    throw new ResponseError('ERROR');
  } catch (error) {
    next(error); // 多余的 next() 调用
  }
});

// 不推荐:忘记使用错误捕获包装器
app.get('/api/bad2', async (req, res, next) => {
  throw new ResponseError('ERROR'); // 异步错误不会被捕获
});

注意事项

  1. 中间件顺序很重要

    • replyMiddleware 要在业务路由之前注册
    • errorMiddleware 要在所有路由之后注册
  2. 错误捕获

    • 使用 catchError 包装器可以自动捕获所有错误
    • 不使用包装器时,异步错误需要手动调用 next(error)
  3. 响应格式

    • res.reply()res.replyFail() 会自动设置适当的 HTTP 状态码
    • 调用后响应立即发送,无需手动调用其他响应方法
  4. TypeScript 支持

    • 包含完整的类型定义,提供最佳的开发体验
    • 错误名称会有自动补全功能

License

MIT

0.2.1

5 months ago

0.2.0

5 months ago

0.1.4

5 months ago

0.1.3

5 months ago

0.1.2

5 months ago

0.1.1

5 months ago

0.1.0

5 months ago

0.0.3

5 months ago

0.0.1

5 months ago