npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@newaifree-tool/request-bus

v2.2.0

Published

请求库业务层封装,包含CLI工具

Readme

@newaifree-tool/request-bus

request-bus 是业务 API 契约层。

它负责把接口文档中的路径、方法、参数、响应类型和接口级能力声明成类型安全的 API 方法。

它不负责真实 HTTP 请求,也不负责 token、baseURL、请求/响应拦截器。那些能力应该放在 AxiosRequestorFetchRequestor 或其他基础 requestor 上。

安装

pnpm add @newaifree-tool/request-bus @newaifree-tool/request-core

通常还需要安装一个 requestor 实现包:

pnpm add @newaifree-tool/request-axios-imp

基础用法

import { createBus, defineEndpoint } from "@newaifree-tool/request-bus";
import { AxiosRequestor } from "@newaifree-tool/request-axios-imp";

interface User {
  id: number;
  name: string;
}

interface CreateUserDTO {
  name: string;
  age: number;
}

const requestor = new AxiosRequestor({
  baseURL: "https://api.example.com",
  timeout: 10_000,
});

const bus = createBus({
  requestor,
});

const userApi = bus.createApiService({
  getUser: defineEndpoint<User, { id: number }>({
    path: "/users/:id",
    method: "GET",
    cache: true,
  }),

  createUser: defineEndpoint<User, void, CreateUserDTO>({
    path: "/users",
    method: "POST",
    idempotent: true,
  }),
});

const user = await userApi.getUser({ id: 1 });
const created = await userApi.createUser(undefined, {
  name: "Tom",
  age: 18,
});

类型推导

defineEndpoint<TResponse, TParams, TData>() 用于声明接口类型。

defineEndpoint<User, { id: number }>({
  path: "/users/:id",
  method: "GET",
});

生成后的 API 方法类型为:

(params: { id: number }) => Promise<User>

无参数 GET:

const api = bus.createApiService({
  listUsers: defineEndpoint<User[]>({
    path: "/users",
    method: "GET",
  }),
});

const users = await api.listUsers();

无 params 但有 body 的 POST:

const api = bus.createApiService({
  createUser: defineEndpoint<User, void, CreateUserDTO>({
    path: "/users",
    method: "POST",
  }),
});

const user = await api.createUser(undefined, {
  name: "Tom",
  age: 18,
});

路径参数和查询参数

路径中的 :id 会从 params 中取值并替换。

const api = bus.createApiService({
  getUserPosts: defineEndpoint<Post[], { id: number; page: number }>({
    path: "/users/:id/posts",
    method: "GET",
  }),
});

await api.getUserPosts({
  id: 1,
  page: 2,
});

最终请求配置大致为:

{
  url: "/users/1/posts",
  method: "GET",
  params: {
    page: 2,
  },
}

如果缺少路径参数,request-bus 会直接抛错,避免发出错误请求。

接口级能力配置

不同接口可以按需开启不同能力。

const api = bus.createApiService({
  listUsers: defineEndpoint<User[]>({
    path: "/users",
    method: "GET",
    cache: {
      duration: 60_000,
    },
    retry: {
      maxCount: 3,
    },
  }),

  createOrder: defineEndpoint<Order, void, CreateOrderDTO>({
    path: "/orders",
    method: "POST",
    idempotent: {
      mode: "concurrent",
    },
  }),
});

推荐使用方式:

  • GET 查询接口:适合 cacheretry
  • POST 创建、支付、下单接口:适合 idempotent,重试需要谨慎。
  • 高并发场景:可以按接口开启 concurrency

内部组合顺序:

requestor -> retry -> cache -> concurrency -> idempotent

执行时最外层的 idempotent 会先处理重复请求。

拦截器应该放在哪里

拦截器放在基础 requestor 上,不放在 bus 上。

const requestor = new AxiosRequestor({
  baseURL: "https://api.example.com",
});

requestor.interceptors.request.use((config) => {
  config.headers = {
    ...config.headers,
    Authorization: `Bearer ${accessToken}`,
  };

  return config;
});

const bus = createBus({ requestor });

这样职责更清晰:

  • requestor 负责真实 HTTP 请求、baseURL、token、通用拦截器。
  • request-core 负责缓存、重试、并发控制、幂等。
  • request-bus 负责 API 契约、路径参数、类型安全 API 生成、接口级能力声明。

多业务线使用

createBus 是实例化 API,不是全局单例。多个业务线可以创建多个 bus。

const userRequestor = new AxiosRequestor({
  baseURL: "https://user.example.com",
});

const orderRequestor = new AxiosRequestor({
  baseURL: "https://order.example.com",
});

const userBus = createBus({
  requestor: userRequestor,
});

const orderBus = createBus({
  requestor: orderRequestor,
});

CLI

request-bus 包含 CLI 入口:

npx request-bus --help

CLI 可用于根据接口 schema 生成 API 契约代码。生成出来的代码会把 baseURL 放在 requestor 初始化中,而不是放在 createBus 中。

初始化配置文件:

request-bus init

默认生成的 apiSchemaUrl./api-schema.json,表示本地 schema 文件。远程 schema 地址需要显式指定:

request-bus init --schema https://api.example.com/schema --base-url https://api.example.com

生成代码:

request-bus generate -s api-schema.json -o ./src/api --base-url https://api.example.com

如果希望覆盖已存在的 index.ts,需要显式添加 --force

request-bus generate -s api-schema.json -o ./src/api --force

generate 会优先使用命令行参数;如果没有传 -s/--schema,会读取 request-bus.config.json 中的 apiSchemaUrl

因为后端 schema 来源可能不可控,CLI 会在生成前校验 schema 结构。至少需要满足:

  • 根对象包含 endpoints 对象。
  • 每个 endpoint 必须包含非空字符串 path
  • 每个 endpoint 的 method 必须是 GET/POST/PUT/DELETE/PATCH/HEAD/OPTIONS 之一。
  • auth 必须是布尔值,cache/retry/idempotent/concurrency 必须是布尔值或配置对象。
  • 不支持的 endpoint 字段会直接报错,避免生成出 TypeScript 无法通过的代码。

如果需要生成类型安全的 defineEndpoint<TResponse, TParams, TData>,可以在 schema 中增加类型字段:

{
  "typeImports": [
    {
      "from": "../types",
      "named": ["Order"]
    }
  ],
  "endpoints": {
    "order": {
      "createOrder": {
        "path": "/api/orders",
        "method": "POST",
        "auth": true,
        "idempotent": true,
        "responseType": "Order",
        "paramsType": "void",
        "dataType": "{ items: string[] }"
      }
    }
  }
}

生成结果:

import type { Order } from "../types";

createOrder: defineEndpoint<Order, void, { items: string[] }>({
  path: "/api/orders",
  method: "POST",
  auth: true,
  idempotent: true,
});

responseType/paramsType/dataType 只用于代码生成,不会进入最终的运行时 endpoint 配置。命名类型必须通过 typeImports 声明来源;如果不想导入类型,也可以直接把 responseType 写成内联类型,例如 { id: string; items: string[] }

本地未发布时,可以直接执行构建后的 CLI 文件:

pnpm --filter @newaifree-tool/request-bus build
node packages/request-bus/dist/cli.js --help
node packages/request-bus/dist/cli.js generate -s api-schema.json -o ./src/api --base-url http://localhost:3000 --force

也可以在 packages/request-bus 下执行 npm pack,把生成的 .tgz 安装到测试项目中,模拟发布后的真实使用方式。

常见问题

bus 会不会执行请求?

会触发请求调用,但不会实现 HTTP 请求。

request-bus 生成的 API 方法会把接口契约转换成 RequestConfig,然后调用传入的 requestor.request(config)

真实 HTTP 细节由 AxiosRequestorFetchRequestor 或自定义 requestor 负责。

bus 为什么没有 baseURL?

baseURL 属于底层请求实现的职责。

如果 bus 也配置 baseURL,会和 axios/fetch requestor 职责重复,也不利于多 requestor 组合。

bus 为什么没有 onRequest/onResponse?

请求、响应、错误拦截器属于 request-core 的基础 requestor 能力。

bus 只做 API 契约和接口级能力编排,不重复实现拦截器。