关于 zenstack-crud-server
zenstack-crud-server 是 ZenStack V3 的 CRUD 服务开发技能,可根据数据模型生成受策略保护的 RPC 或 REST API,并适配多种 Web 框架。适合构建数据服务的开发者。
主要功能
- 01生成 RPC 与 REST API
- 02按请求绑定策略化 ORM 客户端
- 03适配多种服务端框架
- 04限制 API 暴露的模型与操作
- 05生成 OpenAPI 规范
- 06序列化非 JSON 数据类型
适用场景
- 01为模型快速提供 CRUD 接口
- 02按当前用户策略隔离请求数据
- 03在 Express 或 Next.js 集成 API
- 04生成可供工具使用的 OpenAPI 文档
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ zenstackhq/ skills --skill zenstack-crud-server安装后在 Claude Code 中输入/zenstack-crud-server确认已加载。
详细介绍
ZenStack V3 — 自动 CRUD 服务(查询即服务)
ZenStack 可将数据模型转换为安全的 CRUD Web API,无需手写控制器。它由两个部分组成:
- API 处理器 — 与框架无关;定义 API 风格(RPC 或 RESTful),并将 HTTP 请求转换为 ORM 查询。来自
@zenstackhq/server/api。 - 服务适配器 — 与框架相关的连接层,可将处理器安装到 Express/Next.js 等框架中,并提供按请求隔离、受策略约束的 ORM 客户端。来自
@zenstackhq/server/<framework>。
HTTP request → server adapter → API handler (RPC | REST) → ZenStack ORM (policies applied) → DB
本功能构建于其他技能之上:使用 zenstack-schema-modeling 定义架构,使用 zenstack-access-control 保护架构,并使用 zenstack-querying 创建基础客户端。安装服务包:npm install @zenstackhq/server。
API 处理器
RPC — 与 ORM API 一一对应。路由示例:GET /post/findMany?q=<urlencoded-json> 和 POST /post/create(请求体为 { data: ... });响应会封装在 { data: ... } 中。
import { RPCApiHandler } from '@zenstackhq/server/api';
import { schema } from '~/zenstack/schema';
const apiHandler = new RPCApiHandler({ schema });
RPC 端点:所有 ORM 操作(findMany、findUnique、findFirst、count、aggregate、
groupBy、create、createMany、createManyAndReturn、upsert、update、updateMany、
updateManyAndReturn、delete、deleteMany),以及用于自定义过程的 $procs/<name> 和 $transaction/sequential。状态码:201 表示创建成功,200 表示其他操作成功,400 表示格式错误,403 表示违反策略,404 表示未找到,422 表示验证错误,500 表示意外错误。
RESTful — 符合 JSON:API v1.1。路由示例:GET /post、GET /post/:id、POST /post、
PUT|PATCH /post/:id、DELETE /post/:id,以及关系路由。
import { RestApiHandler } from '@zenstackhq/server/api'; // note: RestApiHandler, not RESTful
const apiHandler = new RestApiHandler({
schema,
endpoint: 'http://localhost:3000/api', // required — used to build resource links
});
RestApiHandler 选项:endpoint(必填)、pageSize(默认值为 100;Infinity 可禁用分页)、modelNameMapping({ User: 'users' })、externalIdMapping({ Tag: 'name' })、nestedRoutes(布尔值)。REST 查询参数:filter[field]=、filter[field$op]=($lt、$gt、$contains、$startsWith 等)、sort=field,-other、page[offset]=/page[limit]=、include=rel,rel.nested、fields[type]=a,b。
两个处理器还都接受 queryOptions,用于切片/省略 API 暴露的内容:
new RPCApiHandler({
schema,
queryOptions: {
slicing: {
includedModels: ['User', 'Post'],
models: { post: { excludedOperations: ['delete'] } },
},
omit: { user: { password: true } },
},
});
服务适配器 — 访问控制由 getClient 负责
每个适配器都接受 apiHandler 和一个 getClient(request) => ClientContract 回调。通过 $setAuth(参见 zenstack-access-control)返回一个绑定到当前用户且受策略约束的客户端,以确保每个请求相互隔离:
getClient: (req) => authDb.$setAuth(getSessionUser(req)),
authDb 是 db.$use(new PolicyPlugin())。匿名用户可返回 authDb.$setAuth(undefined);也可以返回原始 db 以绕过策略(对于公共 API,这通常不是你想要的行为)。
Express
import { ZenStackMiddleware } from '@zenstackhq/server/express';
app.use(express.json());
app.use(
'/api/model',
ZenStackMiddleware({
apiHandler,
getClient: (req) => authDb.$setAuth(getSessionUser(req)),
}),
);
(可选的 sendResponse: false 会将内容写入 res.locals,并调用 next()。)
Fastify
import { ZenStackFastifyPlugin } from '@zenstackhq/server/fastify';
server.register(ZenStackFastifyPlugin, {
prefix: '/api/model', // required
apiHandler,
getClient: (req) => authDb.$setAuth(getSessionUser(req)),
});
Next.js — App Router
// src/app/api/model/[...path]/route.ts
import { NextRequestHandler } from '@zenstackhq/server/next';
const handler = NextRequestHandler({
apiHandler,
getClient: (req) => authDb.$setAuth(getSessionUser(req)),
useAppDir: true,
});
export {
handler as GET,
handler as POST,
handler as PUT,
handler as PATCH,
handler as DELETE,
};
Next.js — Pages Router
// src/pages/api/model/[...path].ts
import { NextRequestHandler } from '@zenstackhq/server/next';
export default NextRequestHandler({
apiHandler,
getClient: (req, res) => authDb.$setAuth(getSessionUser(req, res)),
});
Nuxt
// server/api/model/[...].ts
import { createEventHandler } from '@zenstackhq/server/nuxt';
export default createEventHandler({
apiHandler,
getClient: (event) => authDb.$setAuth(getSessionUser(event)),
});
SvelteKit(API 路由 — 推荐;通配符参数必须命名为 path)
// src/routes/api/model/[...path]/+server.ts
import { SvelteKitRouteHandler } from '@zenstackhq/server/sveltekit';
const handler = SvelteKitRouteHandler({
apiHandler,
getClient: (event) => authDb.$setAuth(getSessionUser(event)),
});
export const GET = handler,
POST = handler,
PUT = handler,
PATCH = handler,
DELETE = handler;
(已弃用旧版 SvelteKitHandler,它位于 hooks.server.ts 中并带有 prefix 选项。)
Hono
import { createHonoHandler } from '@zenstackhq/server/hono';
app.use(
'/api/model/*',
createHonoHandler({
apiHandler,
getClient: (ctx) => authDb.$setAuth(getSessionUser(ctx)),
}),
);
Elysia
import { createElysiaHandler } from '@zenstackhq/server/elysia';
app.group('/crud', (app) =>
app.use(
createElysiaHandler({
apiHandler,
basePath: '/api/model',
getClient: (ctx) => authDb.$setAuth(getSessionUser(ctx)),
}),
),
);
TanStack Start
// app/routes/api/$.ts
import { TanStackStartHandler } from '@zenstackhq/server/tanstack-start';
const handler = TanStackStartHandler({
apiHandler,
getClient: (req) => authDb.$setAuth(getSessionUser(req)),
});
export const Route = createFileRoute('/api/$')({
server: {
handlers: {
GET: handler,
POST: handler,
PUT: handler,
PATCH: handler,
DELETE: handler,
},
},
});
数据类型序列化
处理器使用 superjson,确保非 JSON 类型可以通过网络传输:DateTime → ISO 字符串,Bytes →
base64,BigInt/Decimal → 字符串。生成的客户端 SDK 会自动处理这些类型。若要手动构造请求,请在 serialization 键下包含 superjson meta({ "data": ..., "meta": { "serialization": <meta> } });响应也会携带相同的内容。
使用 API
Fetch 客户端(v3.7.0+,仅限 RPC API)
import { createClient } from '@zenstackhq/fetch-client';
import { schema } from '~/zenstack/schema';
const client = createClient(schema, {
endpoint: 'https://example.com/api/model',
});
const users = await client.user.findMany({ include: { posts: true } });
await client.post.create({ data: { title: 'Hello' } });
通过自定义 fetch 添加身份验证:
import type { FetchFn } from '@zenstackhq/fetch-client';
const fetchFn: FetchFn = (url, init) =>
fetch(url, {
...init,
headers: { ...init?.headers, authorization: `Bearer ${getToken()}` },
});
createClient(schema, { endpoint, fetch: fetchFn });
自定义过程:client.$procs.getStats.query() / client.$procs.send.mutate({ args })。
顺序事务:client.$transaction([{ model, op, args }, ...])。
TanStack Query hooks(仅限 RPC API;React 18+/Vue 3+/Svelte 5.25+)
安装 @zenstackhq/tanstack-query 和匹配的 @tanstack/*-query。在根节点附近提供设置(此处展示 React;Vue 使用 provideQuerySettingsContext,Svelte 使用 setQuerySettingsContext):
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { QuerySettingsProvider } from '@zenstackhq/tanstack-query/react';
<QueryClientProvider client={new QueryClient()}>
<QuerySettingsProvider value={{ endpoint: '/api/model' }}>
{children}
</QuerySettingsProvider>
</QueryClientProvider>;
通过 useClientQueries(schema),hooks 与 ORM 客户端保持一致:
import { useClientQueries } from '@zenstackhq/tanstack-query/react';
const client = useClientQueries(schema);
const { data } = client.user.useFindMany({ include: { posts: true } });
const create = client.post.useCreate();
create.mutate({ data: { title: 'New post' } });
- 自动失效:mutation 会自动使受影响的查询失效(支持嵌套读写和级联)。可通过
{ invalidateQueries: false }针对单个 mutation 关闭此功能。 - 乐观更新:
client.post.useCreate({ optimisticUpdate: true });可使用optimisticUpdateProvider自定义。 - 其他功能:
useInfiniteFindMany、$procs.<name>.useQuery()/useMutation()、$transaction.useSequential(),以及用于 JSON null 的DbNull/JsonNull/AnyNull导出项。
社区还提供了 zenstack-pinia-colada 集成(Vue 3)。
OpenAPI 规范
两个 handler 均可通过 generateSpec() 生成 OpenAPI 规范(RPC v3.6.0+,REST v3.5.0+):
import type { OpenApiSpecOptions } from '@zenstackhq/server/api';
app.get('/api/openapi.json', async (_req, res) => {
const spec = await apiHandler.generateSpec({
title: 'My Blog API',
version: '2.0.0',
respectAccessPolicies: true, // emit 403 responses for policy-protected models
});
res.json(spec);
});
选项:title(默认值为 'ZenStack Generated API')、version(默认值为 '1.0.0')、description、
summary、respectAccessPolicies(默认值为 false)。handler 上的
queryOptions.slicing/omit 也会影响规范中包含的内容。
参考文档
此主题的完整 ZenStack 文档已随附在 references/ 目录下:
- service-overview.md — 自动 CRUD 服务概览
- server-adapter.md — 服务端适配器概念
- api-handler-overview.md — API handler 概览
- api-handler-rpc.md — RPC API handler
- api-handler-rest.md — RESTful API handler
- client-sdk-overview.md — 客户端 SDK 概览
- fetch-client.md — fetch 客户端
- tanstack-query.md — TanStack Query hooks
- openapi-overview.md — OpenAPI 概览
- openapi-rpc.md — RPC 的 OpenAPI
- openapi-restful.md — REST 的 OpenAPI
- api-reference.md — 服务端 API 参考
- 服务端适配器:express、fastify、next、nuxt、sveltekit、hono、elysia、tanstack-start