원클릭으로
create-tier
// 创建或修改一个新的 API tier。当用户请求“新增 tier / 创建 partner tier / 新增 merchant 端 / 新增 tenant 端 / 新增 API 端 / 新增一套路由层”时使用。目标是在不修改框架核心的前提下,为新 tier 补齐配置、中间件、类型别名、路由入口和测试。
// 创建或修改一个新的 API tier。当用户请求“新增 tier / 创建 partner tier / 新增 merchant 端 / 新增 tenant 端 / 新增 API 端 / 新增一套路由层”时使用。目标是在不修改框架核心的前提下,为新 tier 补齐配置、中间件、类型别名、路由入口和测试。
| name | create-tier |
| description | 创建或修改一个新的 API tier。当用户请求“新增 tier / 创建 partner tier / 新增 merchant 端 / 新增 tenant 端 / 新增 API 端 / 新增一套路由层”时使用。目标是在不修改框架核心的前提下,为新 tier 补齐配置、中间件、类型别名、路由入口和测试。 |
| argument-hint | [tier-name] |
当前可复用的扩展原语:
BaseJwtPayload、JwtBindings、RouteHandlerWithBindings、OpenAPIWithBindingscreateTierRouter、createTierFactory、defineMiddleware默认不要修改这些核心文件:
src/types/lib.d.tssrc/lib/core/create-app.tssrc/lib/core/factory.ts| 场景 | 是否 JWT | 推荐 bindings | 备注 |
|---|---|---|---|
| 公开 tier | 否 | PublicBindings 或 BaseBindings | 没有 jwtPayload |
认证 tier,仅需 sub | 是 | JwtBindings 或 ClientBindings | 默认只带 sub |
| 认证 tier,有额外 claims | 是 | JwtBindings<CustomJwtPayload> | 例如 partnerId、tenantId |
| 带角色的 tier | 是 | JwtBindings<CustomPayload & { roles: string[] }> | 不要默认复用 admin 语义 |
如果只是“另一个业务端”,通常不需要把它加进全局核心类型,只需要在该 tier 目录下定义本地 alias。
src/routes/{tier}/
├── _middleware.ts
├── {tier}.types.ts
├── {tier}.factory.ts
└── {feature}/
├── {feature}.index.ts
├── {feature}.routes.ts
├── {feature}.handlers.ts
├── {feature}.types.ts
└── __tests__/
约定:
{tier}.types.ts 只放 tier 级 payload / bindings / route handler alias{tier}.factory.ts 只放 tier 级 router / middleware / handlers aliassrc/routes/{tier}/{feature}/ 或 src/routes/{tier}/{category}/{feature}/app.config.ts 注册 tier先补文档入口和路由装配配置:
// app.config.ts
{
name: "partner",
title: "合作方 API 文档",
token: "your-partner-token",
}
可选字段:
basePath: 自定义路径前缀routeDir: 当目录名和 tier 名不同middlewares: 显式传入中间件,跳过 src/routes/{tier}/_middleware.ts在 src/env.ts 增加对应 secret,例如:
PARTNER_JWT_SECRET: z.string().min(32, "JWT密钥长度至少32字符,建议使用强随机字符串"),
同时同步 .env / .env.test 的实际值。
优先在 src/routes/{tier}/{tier}.types.ts 定义,而不是回到 src/types/lib.d.ts 增加一个全局业务类型。
认证 tier 示例:
// src/routes/partner/partner.types.ts
import type { RouteConfig as HonoRouteConfig } from "@hono/zod-openapi";
import type {
BaseJwtPayload,
JwtBindings,
RouteHandlerWithBindings,
} from "@/types/lib";
export type PartnerJwtPayload = BaseJwtPayload & {
partnerId: string;
};
export type PartnerBindings = JwtBindings<PartnerJwtPayload>;
export type PartnerRouteHandler<R extends HonoRouteConfig>
= RouteHandlerWithBindings<R, PartnerBindings>;
公开 tier 示例:
// src/routes/portal/portal.types.ts
import type { PublicBindings, RouteHandlerWithBindings } from "@/types/lib";
import type { RouteConfig as HonoRouteConfig } from "@hono/zod-openapi";
export type PortalBindings = PublicBindings;
export type PortalRouteHandler<R extends HonoRouteConfig>
= RouteHandlerWithBindings<R, PortalBindings>;
如果 tier 除了 jwtPayload 还需要额外上下文变量,可以直接组合 BaseVariables:
import type { BaseVariables, BaseJwtPayload } from "@/types/lib";
type PartnerJwtPayload = BaseJwtPayload & { partnerId: string };
export type PartnerBindings = {
Variables: BaseVariables & {
jwtPayload: PartnerJwtPayload;
partnerCode: string;
};
};
在 src/routes/{tier}/{tier}.factory.ts 本地封装,不要把 createPartnerRouter 之类的东西加回核心。
// src/routes/partner/partner.factory.ts
import { createTierRouter } from "@/lib/core/create-app";
import { createTierFactory } from "@/lib/core/factory";
import type { PartnerBindings } from "./partner.types";
const partnerFactory = createTierFactory<PartnerBindings>();
export const createPartnerMiddleware = partnerFactory.createMiddleware;
export const createPartnerHandlers = partnerFactory.createHandlers;
export function createPartnerRouter() {
return createTierRouter<PartnerBindings>();
}
默认放在 src/routes/{tier}/_middleware.ts,由框架自动加载。
认证 tier 示例:
// src/routes/partner/_middleware.ts
import { jwt } from "hono/jwt";
import env from "@/env";
import { defineMiddleware } from "@/lib/core/define-config";
export default defineMiddleware([
jwt({ secret: env.PARTNER_JWT_SECRET, alg: "HS256" }),
]);
带白名单跳过时,使用 { handler, except }:
export default defineMiddleware([
{
handler: jwt({ secret: env.PARTNER_JWT_SECRET, alg: "HS256" }),
except: c => c.req.path.endsWith("/auth/login"),
},
]);
只有当业务语义完全一致时,才复用 admin 的 authorize、operationLog。不要因为“也有角色”就直接套 admin 中间件。
入口文件使用本地 tier router:
// src/routes/partner/orders/orders.index.ts
import { createPartnerRouter } from "../partner.factory";
import * as handlers from "./orders.handlers";
import * as routes from "./orders.routes";
export default createPartnerRouter()
.openapi(routes.list, handlers.list)
.openapi(routes.get, handlers.get);
模块类型文件继续基于 tier alias:
// src/routes/partner/orders/orders.types.ts
import type * as routes from "./orders.routes";
import type { PartnerRouteHandler } from "../partner.types";
type RouteTypes = {
[K in keyof typeof routes]: typeof routes[K];
};
export type PartnerOrdersRouteHandlerType<T extends keyof RouteTypes>
= PartnerRouteHandler<RouteTypes[T]>;
至少做下面这些:
__tests__c.get("jwtPayload") 的字段推断正确pnpm typecheckpnpm test --run可参考现有的组合测试思路:src/lib/core/__tests__/tier-composition.test.ts
目标:新增一个带 partnerId claim 的合作方端。
最少需要改这些地方:
app.config.ts 新增 { name: "partner", title, token }src/env.ts 增加 PARTNER_JWT_SECRETsrc/routes/partner/partner.types.tssrc/routes/partner/partner.factory.tssrc/routes/partner/_middleware.tssrc/routes/partner/... 下创建业务模块pnpm typecheck && pnpm test --run注意:这整个流程默认不需要修改:
src/types/lib.d.tssrc/lib/core/create-app.tssrc/lib/core/factory.ts不要这样做:
src/lib/core/create-app.ts 加一个 createXxxRoutersrc/lib/core/factory.ts 加一个 createXxxMiddlewaresrc/types/lib.d.ts 塞一个业务专属全局类型AdminBindings 或 admin 专属中间件src/env.ts 和测试环境 secret当用户要求“新增一个 tier”时,按下面顺序执行:
app.config.ts 注册 tiersrc/routes/{tier}/ 下创建本地 types / factory / middleware如果过程中发现自己想改核心文件,先停一下,确认这是不是“所有 tier 共享的新能力”;如果不是,就回到本地组合方案。
Effect v4 模式指南。当需要创建 Effect 服务、定义错误类型、编写 Effect 程序、管理 Layer 组合、或使用 Effect 封装异步操作时使用
创建或修改 BullMQ 队列任务。当需要创建新队列、添加任务类型、注册 Worker、设置定时任务、或用户请求"添加后台任务/队列处理"时使用
创建或修改 CRUD 模块。当需要创建新的增删改查 API、修改现有路由模块、添加新字段、新增接口、或用户请求"创建/修改 XX 管理"时使用
创建或修改数据库 Schema。当需要创建新表、修改表结构、定义字段、设置索引约束、或涉及 Drizzle ORM / drizzle-zod 操作时使用
Drizzle ORM v1 关系查询指南。当需要定义 Relations v2、编写关系查询、使用 through 多对多、预定义过滤器、或从旧版 Drizzle 迁移时使用