@fishka/express

中文文档
0.9.25发布于 2026/01/29MITTypeScriptESM + CJS近 30 天下载 6161

Express.js 有内置验证的扩展和类型安全

中文使用文档
文档目录20 节
  1. 安装
  2. 概览
  3. 快速启动
  4. 公用事业
  5. pathParam(req, name, validator?)
  6. queryParam(req, name, validator?)
  7. body(req, validator)
  8. patchExpressAsyncErrors()
  9. addErrorHandling(app)
  10. 参数验证
  11. 可用的运算符
  12. 所需参数
  13. 认证
  14. 限制费率
  15. HTTP 错误
  16. 配置
  17. 请求编号
  18. 处理器
  19. 完整示例
  20. 许可证

快捷 API

Express.js的功能公用事业,具有类型安全验证和出错处理功能.

安装

npm install @fishka/express

概览

这个软件包提供了与Express直接合作的独立公用功能. 每个特性都可以独立使用——混合并匹配你所需要的.

快速启动

import express from 'express';
import { addErrorHandling, body, patchExpressAsyncErrors, pathParam, queryParam } from '@fishka/express';
import { assertString } from '@fishka/assertions';

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

// Enable automatic async error catching (call once at startup)
patchExpressAsyncErrors();

// GET /users/:id - with typed path params
app.get('/users/:id', async (req, res) => {
  const id = pathParam(req, 'id'); // string - validated inline
  res.json({ id, name: 'John' });
});

// GET /users - with query params
app.get('/users', async (req, res) => {
  const page = queryParam(req, 'page'); // string - throws 400 if missing
  res.json({ page, users: [] });
});

// POST /users - with body validation
app.post('/users', async (req, res) => {
  const data = body(req, { name: assertString });
  res.json({ id: 1, name: data.name }); // validators infer a type for the data!
});

// Add error handling middleware (must be last)
addErrorHandling(app);

app.listen(3000);

公用事业

pathParam(req, name, validator?)

从Express请求获取并验证路径参数.

import { pathParam, transform, toInt, minLength } from '@fishka/express';

app.get('/users/:id', async (req, res) => {
  // Simple - returns string, throws 400 if missing
  const id1 = pathParam(req, 'id');

  // With validation - transform to number
  const id2 = pathParam(req, 'id', transform(toInt()));

  // Transform with validation - string min length
  const slug = pathParam(req, 'slug', transform(minLength(3)));

  res.json({ id: '...' });
});

queryParam(req, name, validator?)

从Express请求获取并验证查询参数.

import { queryParam, transform, toInt, min, range, oneOf } from '@fishka/express';

app.get('/users', async (req, res) => {
  // Simple - returns string, throws 400 if missing
  const search = queryParam(req, 'search');

  // With validation - transform to number with min value
  const page = queryParam(req, 'page', transform(toInt(), min(1)));

  // With validation - number range
  const limit = queryParam(req, 'limit', transform(toInt(), range(1, 100)));

  // With validation - enum
  const sort = queryParam(req, 'sort', transform(oneOf('asc', 'desc')));

  res.json({ page, limit, users: [] });
});

body(req, validator)

获取并验证请求机构.

import { body } from '@fishka/express';
import { assertString, assertNumber } from '@fishka/assertions';

app.post('/users', async (req, res) => {
  // Object assertion - validates and infers a type.
  const data = body(req, {
    name: assertString,
    age: assertNumber,
  });

  res.json({ id: 1, name: data.name });
});

patchExpressAsyncErrors()

补丁快取以自动捕获同步错误 。 应用程序启动时呼叫一次 .

import { patchExpressAsyncErrors } from '@fishka/express';

// Call before defining routes
patchExpressAsyncErrors();

// Now async route handlers don't need try/catch
app.get('/users', async (req, res) => {
  const users = await db.users.findAll(); // Errors are caught automatically
  res.json(users);
});

addErrorHandling(app)

添加处理中间软件的集中错误 。 必须在所有路线后呼叫.

import { addErrorHandling, HttpError } from '@fishka/express';

// Define routes...
app.get('/users/:id', async (req, res) => {
  const user = await db.users.findById(req.params.id);
  assertHttp(user, 404, 'User not found');
  res.json(user);
});

// Add error handling last
addErrorHandling(app);

参数验证

使用 transform() 用于验证和转换参数。 所有运算符都是可复合的 :

import { transform, toInt, minLength, matches, min, range, oneOf } from '@fishka/express';

app.get('/users/:id', async (req, res) => {
  const id = pathParam(req, 'id', transform(toInt())); // string → number
  const page = queryParam(req, 'page', transform(toInt(), min(1))); // number >= 1
  const limit = queryParam(req, 'limit', transform(toInt(), range(1, 100))); // number 1-100
  const sort = queryParam(req, 'sort', transform(oneOf('asc', 'desc'))); // enum
  const search = queryParam(req, 'search', transform(minLength(3))); // string min 3 chars

  res.json({ id, page, limit });
});

可用的运算符

转换(字符串-T):

  • toInt() - 解为整数
  • toNumber() - 解说数字
  • toBool() - 分析"真"/"假"以布尔
  • oneOf('a', 'b') - 激发值

字符串验证器 :

  • minLength(n) - 最小长度
  • maxLength(n) - 最大长度
  • matches(/regex/) - 正则匹配
  • trim - 修剪白空间
  • lowercase 编号 : uppercase - 案件转换

数字验证符 :

  • min(n) - 最低价值
  • max(n) - 最大值
  • range(min, max) - 数值范围

常规 :

  • transform(...ops) - 验证器/转换器链
  • check(predicate, msg) - 带有上游的自定义验证
  • validator(fn) - 返回字符串的自定义验证器
  • map(fn) - 转换价值

所需参数

  • pathParam(req, 'name') - 返回字符串( 如果缺少的话, 移动400)
  • queryParam(req, 'name') - 返回字符串( 如果缺少/ 空)
  • queryParam(req, 'name', validator) - 返回已验证的值( 如果缺少/ 空/ 无效, 则扔出 400)
  • 所有参数都需要 - 缺少或空值丢出 BAD REQEST

认证

import { createAuthMiddleware, BasicAuthStrategy, getAuthUser } from '@fishka/express';

const auth = new BasicAuthStrategy(async (user, pass) =>
  user === 'admin' && pass === 'secret' ? { id: '1', role: 'admin' } : null,
);

app.get('/profile', createAuthMiddleware(auth), async (req, res) => {
  const user = getAuthUser(req);
  res.json({ id: user.id });
});

限制费率

import { createRateLimiterMiddleware } from '@fishka/express';

app.use(
  await createRateLimiterMiddleware({
    points: { read: 100, write: 50 },
    duration: 60,
  }),
);

HTTP 错误

使用 HttpError 特定状态代码:

import { HttpError, HTTP_UNAUTHORIZED, HTTP_NOT_FOUND } from '@fishka/express';

app.get('/users/:id', async (req, res) => {
  const user = await db.users.findById(req.params.id);

  if (!req.headers.authorization) {
    throw new HttpError(HTTP_UNAUTHORIZED, 'Authorization required');
  }

  if (!user) {
    throw new HttpError(HTTP_NOT_FOUND, 'User not found');
  }

  res.json(user);
});

使用 assertHttp 内在断言:

import { assertHttp, HTTP_UNAUTHORIZED, HTTP_NOT_FOUND } from '@fishka/express';

app.get('/admin', async (req, res) => {
  assertHttp(req.headers.authorization, HTTP_UNAUTHORIZED, 'Authorization required');

  const user = await db.users.findById(req.params.id);
  assertHttp(user, HTTP_NOT_FOUND, 'User not found');
  assertHttp(user.isAdmin, HTTP_FORBIDDEN, 'Admin access required');

  res.json({ admin: true });
});

配置

请求编号

import { configureExpressApi, createTlsMiddleware } from '@fishka/express';

// Enable request ID with custom header name
configureExpressApi({
  requestIdHeader: 'x-request-id', // or 'x-correlation-id', 'trace-id', etc.
  trustRequestIdHeader: true, // default: true, if we trust and propagate request ID passed by client.
});

// Add TLS middleware to track request context
app.use(createTlsMiddleware());

处理器

处理未发现的错误和优雅的关闭 :

import { installProcessHandlers } from '@fishka/express';

installProcessHandlers({
  onUncaughtException: err => sendToMonitoring(err),
  onUnhandledRejection: reason => sendToMonitoring(reason),
  onShutdown: async () => {
    await database.close();
    await server.close();
  },
  shutdownTimeout: 15000,
});

完整示例

import express from 'express';
import {
  addErrorHandling,
  body,
  min,
  patchExpressAsyncErrors,
  pathParam,
  queryParam,
  range,
  toInt,
  transform,
} from '@fishka/express';
import { assertString } from '@fishka/assertions';
import { assertHttp } from './api.types';

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

// Enable automatic async error catching
patchExpressAsyncErrors();

// Routes
app.get('/users/:id', async (req, res) => {
  const id = pathParam(req, 'id', transform(toInt()));
  const user = await db.users.findById(id);
  assertHttp(user, 404, 'User not found');
  res.json(user);
});

app.get('/users', async (req, res) => {
  const page = queryParam(req, 'page', transform(toInt(), min(1)));
  const limit = queryParam(req, 'limit', transform(toInt(), range(1, 100)));

  const users = await db.users.findAll({ page, limit });
  res.json({ users, page, limit });
});

app.post('/users', async (req, res) => {
  const data = body(req, { name: assertString });
  const user = await db.users.create(data);
  res.status(201).json(user);
});

// Error handling (must be last)
addErrorHandling(app);

app.listen(3000);

许可证

麻省理工学院

版本与发布记录

@fishka/express 共发布 21 个版本,首次发布于 2026/01/07;过去 12 个月发布了 21 个版本。

版本发布日期距今
0.9.25latest2026/01/299 个月前
0.9.242026/01/149 个月前
0.9.232026/01/149 个月前
0.9.222026/01/149 个月前
0.9.212026/01/149 个月前
0.9.202026/01/149 个月前

关于 @fishka/express 的常见问题

@fishka/express 是什么?

Express.js 有内置验证的扩展和类型安全

如何安装 @fishka/express?

在项目目录运行 npm install @fishka/express,或使用 pnpm add / yarn add / bun add。

@fishka/express 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@fishka/express 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@fishka/express 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=20.0.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@fishka/express 可以商用吗?

@fishka/express 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@fishka/express 的下载量有多少?

根据 npm 官方下载统计,@fishka/express 在 2026/10/15 – 2026/11/13 这 30 天内被下载 6,161 次,日均约 205 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@fishka/express 还在维护吗?

最新版本 0.9.25 发布于 2026/01/29(9 个月前);至今共发布 21 个版本,其中过去 12 个月发布了 21 个;首次发布于 2026/01/07。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。