Token导航 LogoToken导航TokenDH.com
研究检索需要联网github未标认证来源可访问clear审计通过

backend-route-creation后端路由创建

Agent Skill

backend-route-creation 用于查找、检索和筛选相关信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要根据关键词、任务场景或来源线索快速定位候选结果时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

总安装

188

周安装

8

GitHub Stars

1

下载量

66
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

3

许可证

MIT

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:backend-route-creation(后端路由创建)
来源仓库:https://github.com/workshop-ventures/skills
仓库路径:skills/backend-route-creation
安装命令:
npx skills add https://github.com/workshop-ventures/skills --skill backend-route-creation
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。不同来源提供的安装方式可能略有差异;本站展示可直接复制的安装命令,安装前请核对来源页面。

skills.shnpx skills
npx skills add https://github.com/workshop-ventures/skills --skill backend-route-creation

简介

backend-route-creation 基于 koa-zod-router 创建带 Zod 验证的 API 路由,确保前后端类型一致。

  • 适用于工作流和资源的 RESTful 接口开发,遵循项目约定的目录结构。
  • 通过 npx skills add 安装,需先在 types 目录定义 API 模式。
  • 生成的路由文件需配合 Mongoose 模型和数据库连接使用。
  • 建议在完成后运行集成测试验证端到端功能。

SKILL.md

Backend Route Creation

This skill creates new API routes using koa-zod-router with Zod validation schemas following established patterns.

Overview

Routes use schemas defined in @{project}/types for validation. This ensures type safety between backend and frontend.

Route File Structure

Routes are organized by resource in apps/backend/src/routes/:

apps/backend/src/routes/
├── workflows.ts        # /api/workflows endpoints
├── workflow-runs.ts    # /api/workflow-runs endpoints
└── {resource}.ts       # New resource routes

Step 1: Define API Schemas in @{project}/types

First, create schemas in libs/types/src/api/{resource}.ts:

// libs/types/src/api/{resource}.ts
import { z } from 'zod/v3';
import { ResourceStatusOptions } from '../lib/Resource'; // Import enum options if any
import {
  paginationQuerySchema,
  idParamSchema,
  listResponseSchema,
  singleResponseSchema,
  messageResponseSchema,
} from './common';

// ============================================
// Resource Entity Schema (for responses)
// ============================================

export const resourceSchema = z.object({
  id: z.string(),
  name: z.string(),
  description: z.string().optional(),
  status: z.enum(ResourceStatusOptions),
  createdAt: z.coerce.date(),
  updatedAt: z.coerce.date(),
});

// ============================================
// GET /api/resources - List Resources
// ============================================

export const listResourcesQuerySchema = paginationQuerySchema.extend({
  status: z.enum(ResourceStatusOptions).optional(),
  search: z.string().optional(),
});

export const listResourcesResponseSchema = listResponseSchema(resourceSchema);

// Use z.input for query types (parameters with defaults should be optional)
export type ListResourcesQuery = z.input<typeof listResourcesQuerySchema>;
export type ListResourcesResponse = z.infer<typeof listResourcesResponseSchema>;

// ============================================
// GET /api/resources/:id - Get Resource
// ============================================

export const getResourceParamsSchema = idParamSchema;
export const getResourceResponseSchema = singleResponseSchema(resourceSchema);

export type GetResourceParams = z.infer<typeof getResourceParamsSchema>;
export type GetResourceResponse = z.infer<typeof getResourceResponseSchema>;

// ============================================
// POST /api/resources - Create Resource
// ============================================

export const createResourceBodySchema = z.object({
  name: z.string().min(1),
  description: z.string().optional(),
  // Add required fields for creation
});

export const createResourceResponseSchema = singleResponseSchema(resourceSchema);

export type CreateResourceBody = z.infer<typeof createResourceBodySchema>;
export type CreateResourceResponse = z.infer<typeof createResourceResponseSchema>;

// ============================================
// PUT /api/resources/:id - Update Resource
// ============================================

export const updateResourceParamsSchema = idParamSchema;

export const updateResourceBodySchema = z.object({
  name: z.string().min(1).optional(),
  description: z.string().optional(),
  // All fields optional for partial updates
});

export const updateResourceResponseSchema = singleResponseSchema(resourceSchema);

export type UpdateResourceParams = z.infer<typeof updateResourceParamsSchema>;
export type UpdateResourceBody = z.infer<typeof updateResourceBodySchema>;
export type UpdateResourceResponse = z.infer<typeof updateResourceResponseSchema>;

// ============================================
// DELETE /api/resources/:id - Delete Resource
// ============================================

export const deleteResourceParamsSchema = idParamSchema;
export const deleteResourceResponseSchema = messageResponseSchema;

export type DeleteResourceParams = z.infer<typeof deleteResourceParamsSchema>;
export type DeleteResourceResponse = z.infer<typeof deleteResourceResponseSchema>;

Then export from libs/types/src/api/index.ts:

export * from './{resource}';

Step 2: Create the Route File

Create apps/backend/src/routes/{resource}.ts:

import zodRouter from 'koa-zod-router';

import Resource from '../models/Resource';
import {
  // Query/Params schemas
  listResourcesQuerySchema,
  getResourceParamsSchema,
  updateResourceParamsSchema,
  deleteResourceParamsSchema,
  // Body schemas
  createResourceBodySchema,
  updateResourceBodySchema,
} from '@{project}/types';

const router = zodRouter();

// GET /api/resources - List all
router.register({
  method: 'get',
  path: '/',
  validate: {
    query: listResourcesQuerySchema,
  },
  handler: async (ctx) => {
    const { skip, limit, status, search } = ctx.request.query;

    const query: Record<string, unknown> = {};
    if (status) query.status = status;
    if (search) query.name = { $regex: search, $options: 'i' };

    const results = await Resource.find(query)
      .sort({ createdAt: -1 })
      .skip(skip)
      .limit(limit);

    const total = await Resource.countDocuments(query);

    ctx.status = 200;
    ctx.body = { total, results };
  },
});

// GET /api/resources/:id - Get by ID
router.register({
  method: 'get',
  path: '/:id',
  validate: {
    params: getResourceParamsSchema,
  },
  handler: async (ctx) => {
    const { id } = ctx.request.params;

    const result = await Resource.findOne({ id });

    if (!result) {
      ctx.status = 404;
      ctx.body = { message: 'Resource not found' };
      return;
    }

    ctx.status = 200;
    ctx.body = { result };
  },
});

// POST /api/resources - Create
router.register({
  method: 'post',
  path: '/',
  validate: {
    body: createResourceBodySchema,
  },
  handler: async (ctx) => {
    const body = ctx.request.body;

    const result = new Resource(body);
    await result.save();

    ctx.status = 201;
    ctx.body = { result };
  },
});

// PUT /api/resources/:id - Update
router.register({
  method: 'put',
  path: '/:id',
  validate: {
    params: updateResourceParamsSchema,
    body: updateResourceBodySchema,
  },
  handler: async (ctx) => {
    const { id } = ctx.request.params;
    const updates = ctx.request.body;

    const result = await Resource.findOne({ id });

    if (!result) {
      ctx.status = 404;
      ctx.body = { message: 'Resource not found' };
      return;
    }

    Object.assign(result, updates);
    await result.save();

    ctx.status = 200;
    ctx.body = { result };
  },
});

// DELETE /api/resources/:id - Delete
router.register({
  method: 'delete',
  path: '/:id',
  validate: {
    params: deleteResourceParamsSchema,
  },
  handler: async (ctx) => {
    const { id } = ctx.request.params;

    const result = await Resource.findOne({ id });

    if (!result) {
      ctx.status = 404;
      ctx.body = { message: 'Resource not found' };
      return;
    }

    await Resource.deleteOne({ id });

    ctx.status = 200;
    ctx.body = { message: 'Resource deleted' };
  },
});

export default router;

Step 3: Mount the Route in main.ts

Add to apps/backend/src/main.ts:

import resourceRoutes from './routes/{resource}';

// ... existing middleware ...

// Mount routes
app.use(mount('/api/{resource}', resourceRoutes.routes()));

Zod Schema Patterns

Query Parameters (in @{project}/types)

Use z.coerce for converting string query params:

export const listQuerySchema = paginationQuerySchema.extend({
  status: z.enum(['active', 'inactive']).optional(),
  includeArchived: z.coerce.boolean().optional().default(false),
  search: z.string().optional(),
});

// IMPORTANT: Use z.input for query types so defaults remain optional
export type ListQuery = z.input<typeof listQuerySchema>;

URL Parameters

export const resourceParamsSchema = z.object({
  id: z.string(),
  // For numeric IDs: userId: z.coerce.number(),
});

Request Body

// Create schema - required fields
export const createBodySchema = z.object({
  name: z.string().min(1),
  email: z.string().email(),
  roles: z.array(z.enum(['admin', 'user', 'guest'])),
  metadata: z.object({
    source: z.string().optional(),
    tags: z.array(z.string()).optional(),
  }).optional(),
});

// Update schema - all fields optional
export const updateBodySchema = z.object({
  name: z.string().min(1).optional(),
  email: z.string().email().optional(),
  roles: z.array(z.enum(['admin', 'user', 'guest'])).optional(),
});

Using Shared Type Options

Import enum options from @{project}/types:

import { StatusOptions, RoleOptions } from '@{project}/types';

export const schema = z.object({
  status: z.enum(StatusOptions),
  role: z.enum(RoleOptions),
});

Route Handler Patterns

Standard List Response

ctx.status = 200;
ctx.body = {
  total: count,
  results: items,
};

Standard Single Response

ctx.status = 200;
ctx.body = { result: item };

Standard Create Response

ctx.status = 201;
ctx.body = { result: newItem };

Standard Error Responses

// Not found
ctx.status = 404;
ctx.body = { message: 'Resource not found' };
return;

// Bad request
ctx.status = 400;
ctx.body = { message: 'Invalid input', details: '...' };
return;

// Forbidden
ctx.status = 403;
ctx.body = { message: 'Access denied' };
return;

Filtering in List Endpoints

handler: async (ctx) => {
  const { skip, limit, status, search } = ctx.request.query;

  const query: Record<string, unknown> = {};

  if (status) {
    query.status = status;
  }

  if (search) {
    query.name = { $regex: search, $options: 'i' };
  }

  const results = await Model.find(query)
    .sort({ createdAt: -1 })
    .skip(skip)
    .limit(limit);

  const total = await Model.countDocuments(query);

  ctx.status = 200;
  ctx.body = { total, results };
}

Action Routes

For non-CRUD actions like /api/workflows/:id/publish:

// In @{project}/types: define the schema
export const publishResourceParamsSchema = idParamSchema;
export const publishResourceResponseSchema = singleResponseSchema(resourceSchema);

// In route file:
router.register({
  method: 'post',
  path: '/:id/publish',
  validate: {
    params: publishResourceParamsSchema,
  },
  handler: async (ctx) => {
    const { id } = ctx.request.params;

    const resource = await Resource.findOne({ id });

    if (!resource) {
      ctx.status = 404;
      ctx.body = { message: 'Resource not found' };
      return;
    }

    if (resource.status === 'published') {
      ctx.status = 400;
      ctx.body = { message: 'Resource is already published' };
      return;
    }

    resource.status = 'published';
    resource.publishedAt = new Date();
    await resource.save();

    ctx.status = 200;
    ctx.body = { result: resource };
  },
});

Complete Example

See existing implementation:

  • Schemas: libs/types/src/api/workflows.ts
  • Routes: apps/backend/src/routes/workflows.ts

Checklist

After creating a new route:

  1. Create API schemas in libs/types/src/api/{resource}.ts
  2. Export schemas from libs/types/src/api/index.ts
  3. Build types library: npx tsc -b libs/types/tsconfig.lib.json
  4. Create the route file in apps/backend/src/routes/
  5. Import and mount in main.ts with app.use(mount('/api/{path}', routes.routes()))
  6. Test the endpoints with curl or your API client
  7. Create corresponding frontend API module in apps/webapp/src/api/
  8. Create React Query hooks in apps/webapp/src/hooks/

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

04

需要参考平台分布和安装热度时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

补充不同宿主或平台的使用分布数据

能力 5

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

Claude Code

26.55%
按下载量换算18

windsurf

25.26%
按下载量换算17

trae

17.9%
按下载量换算12

OpenCode

11.72%
按下载量换算8

Codex

8.29%
按下载量换算5

Antigravity

3.6%
按下载量换算2

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

需要联网

该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。

来源信息

继续浏览同类 Skills