Agent Skillsbiersoeckli/QuickStack › elysia-api-routes

elysia-api-routes

GitHub

指导在 QuickStack 项目中创建和更新基于 Elysia 的 REST API 路由,涵盖路由定义、Zod 校验、鉴权处理及错误规范。

.agents/skills/elysia-api-routes/SKILL.md biersoeckli/QuickStack

Trigger Scenarios

添加或编辑 src/server/api/v1 下的路由文件 定义 Elysia 路由的查询/参数/请求体/响应 Schema 处理 REST API 授权逻辑与标准错误

Install

npx skills add biersoeckli/QuickStack --skill elysia-api-routes -g -y
More Options

Non-standard path

npx skills add https://github.com/biersoeckli/QuickStack/tree/main/.agents/skills/elysia-api-routes -g -y

Use without installing

npx skills use biersoeckli/QuickStack@elysia-api-routes

指定 Agent (Claude Code)

npx skills add biersoeckli/QuickStack --skill elysia-api-routes -a claude-code -g -y

安装 repo 全部 skill

npx skills add biersoeckli/QuickStack --all -g -y

预览 repo 内 skill

npx skills add biersoeckli/QuickStack --list

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 receive identity.
  • Import ApiUtils from src/server/utils/api-response.utils.
  • Import ApiUnauthorizedException, ApiNotFoundException, and ServiceException from src/shared/model/service.exception.model as needed.
  • Declare query, params, and body directly in route options with Zod schemas.
  • Declare response with ApiUtils.mapResponseModel(successSchema).
  • Keep OpenAPI metadata in detail, with a short summary and security: [{ bearerAuth: [] }] for protected routes.

Handler Rules

  • If identity is missing, throw new ApiUnauthorizedException().
  • If a requested resource does not exist, throw new ApiNotFoundException().
  • Use shared authorization helpers such as ensureReadApp, ensureWriteApp, ensureCreateAppInProject, ensureDeleteAppInProject, ensureReadProject, and ensureAdmin.
  • Let shared authorization helpers throw; do not duplicate permission checks inline except for simple admin/read filtering already established in list routes.
  • Throw ServiceException for expected domain validation errors, such as immutable projectId violations.
  • Return success payloads directly; do not wrap them in { data }, { status }, or error envelopes.
  • Do not return ApiUtils.problem(...), raw Response, or Elysia status(...) for expected route errors.

Schema Rules

  • Use inline Zod objects for simple route params and query inputs.
  • Use existing write schemas, such as AppExtendedWriteZodModel or a local projectWriteSchema, for bodies.
  • Do not parse query, params, or body inside 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 undefined and declare response: ApiUtils.mapResponseModel(z.undefined()).
  • For deployment request routes, return { deploymentId } and declare response: 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 --noEmit after 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.md for REST API domain terms and write semantics before changing behavior.

Version History

  • 0.0.13 Current 2026-08-20 14:27

Same Skill Collection

.agents/skills/ask-matt/SKILL.md
.agents/skills/backend-services/SKILL.md
.agents/skills/backend-testing/SKILL.md
.agents/skills/code-review/SKILL.md
.agents/skills/codebase-design/SKILL.md
.agents/skills/diagnosing-bugs/SKILL.md
.agents/skills/domain-modeling/SKILL.md
.agents/skills/frontend-ui-patterns/SKILL.md
.agents/skills/grilling/SKILL.md
.agents/skills/handoff/SKILL.md
.agents/skills/implement/SKILL.md
.agents/skills/improve-codebase-architecture/SKILL.md
.agents/skills/prototype/SKILL.md
.agents/skills/research/SKILL.md
.agents/skills/resolving-merge-conflicts/SKILL.md
.agents/skills/setup-matt-pocock-skills/SKILL.md
.agents/skills/tdd/SKILL.md
.agents/skills/teach/SKILL.md
.agents/skills/to-questionnaire/SKILL.md
.agents/skills/to-spec/SKILL.md
.agents/skills/to-tickets/SKILL.md
.agents/skills/triage/SKILL.md
.agents/skills/wayfinder/SKILL.md
.agents/skills/writing-for-agents/SKILL.md
.agents/skills/grill-me/SKILL.md
.agents/skills/grill-with-docs/SKILL.md

Metadata

Files
0
Version
0.0.13
Hash
c012542a
Indexed
2026-08-20 14:27

- 위키
Copyright © 2011-2026 iteam. Current version is 2.155.2. UTC+08:00, 2026-08-25 15:50
浙ICP备14020137号-1 $방문자$