Agent Skills
› biersoeckli/QuickStack
› elysia-api-routes
elysia-api-routes
GitHub指导在 QuickStack 项目中创建和更新基于 Elysia 的 REST API 路由,涵盖路由定义、Zod 校验、鉴权处理及错误规范。
Trigger Scenarios
添加或编辑 src/server/api/v1 下的路由文件
定义 Elysia 路由的查询/参数/请求体/响应 Schema
处理 REST API 授权逻辑与标准错误
Install
npx skills add biersoeckli/QuickStack --skill elysia-api-routes -g -y
SKILL.md
Frontmatter
{
"name": "elysia-api-routes",
"description": "Create and update QuickStack Elysia REST API routes using the project's established \/api\/v1 route conventions. Use when adding or editing files under src\/server\/api\/v1, defining Elysia query\/params\/body\/response schemas, or handling REST API authorization and errors."
}
Elysia API Routes
Quick Start
For QuickStack REST routes under src/server/api/v1, follow the current examples in app/route.ts and project/route.ts:
export const resourceRoutes = new Elysia()
.derive(ApiUtils.deriveFunc)
.get('/resources/:id', async ({ params, identity }) => {
if (!identity) throw new ApiUnauthorizedException()
const resource = await resourceService.getByIdOrUndefined(params.id);
if (!resource) throw new ApiNotFoundException();
ensureReadResource(identity, resource.id);
return resource;
}, {
params: z.object({
id: z.string(),
}),
response: ApiUtils.mapResponseModel(ResourceModel),
detail: { summary: 'Get resource by id', security: [{ bearerAuth: [] }] }
});
Required Route Shape
- Start each route module with
new Elysia().derive(ApiUtils.deriveFunc)so handlers receiveidentity. - Import
ApiUtilsfromsrc/server/utils/api-response.utils. - Import
ApiUnauthorizedException,ApiNotFoundException, andServiceExceptionfromsrc/shared/model/service.exception.modelas needed. - Declare
query,params, andbodydirectly in route options with Zod schemas. - Declare
responsewithApiUtils.mapResponseModel(successSchema). - Keep OpenAPI metadata in
detail, with a shortsummaryandsecurity: [{ bearerAuth: [] }]for protected routes.
Handler Rules
- If
identityis missing, thrownew ApiUnauthorizedException(). - If a requested resource does not exist, throw
new ApiNotFoundException(). - Use shared authorization helpers such as
ensureReadApp,ensureWriteApp,ensureCreateAppInProject,ensureDeleteAppInProject,ensureReadProject, andensureAdmin. - Let shared authorization helpers throw; do not duplicate permission checks inline except for simple admin/read filtering already established in list routes.
- Throw
ServiceExceptionfor expected domain validation errors, such as immutableprojectIdviolations. - Return success payloads directly; do not wrap them in
{ data },{ status }, or error envelopes. - Do not return
ApiUtils.problem(...), rawResponse, or Elysiastatus(...)for expected route errors.
Schema Rules
- Use inline Zod objects for simple route params and query inputs.
- Use existing write schemas, such as
AppExtendedWriteZodModelor a localprojectWriteSchema, for bodies. - Do not parse
query,params, orbodyinside the handler if the route option already declares the schema. - Do not use nested
schema: { query, params, body }in these route modules. - For delete routes, return
undefinedand declareresponse: ApiUtils.mapResponseModel(z.undefined()). - For deployment request routes, return
{ deploymentId }and declareresponse: ApiUtils.mapResponseModel(z.object({ deploymentId: z.string() })).
Write Route Pattern
Use POST upsert semantics:
.post('/projects', async ({ body, identity }) => {
if (!identity) throw new ApiUnauthorizedException()
ensureAdmin(identity);
let existing: Project | null = null;
if (body.id) {
existing = await projectService.getByIdOrUndefined(body.id);
if (!existing) throw new ApiNotFoundException();
}
return projectService.save({ id: existing?.id, name: body.name });
}, {
body: projectWriteSchema,
response: ApiUtils.mapResponseModel(ProjectModel),
detail: { summary: 'Create or update project', security: [{ bearerAuth: [] }] }
})
Validation Checklist
- Run
yarn tsc --noEmitafter route changes. - Check that every accepted input has a route-level Zod schema.
- Check that every route has
response: ApiUtils.mapResponseModel(...). - Check that expected failures are thrown as exceptions; route mounting maps them centrally with
ApiUtils.mapError(...). - Check
CONTEXT.mdfor REST API domain terms and write semantics before changing behavior.
Version History
- 0.0.13 Current 2026-08-20 14:27


