timothywarner/copilot-dev/api-endpoint-generator

Claude Code and agent skill by timothywarner/copilot-dev

Trust tier 3
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

Scaffold complete REST API endpoints with input validation, error handling, typed request/response, JSDoc or docstrings, and a unit test stub. Use when asked to create a new API route, endpoint, controller, handler, or REST resource in Express.js (TypeScript) or FastAPI (Python). Produces production-ready code following immutable patterns, Zod/Pydantic validation, and RFC 7807 error responses.

Install

npx vskill@latest install timothywarner/copilot-dev/api-endpoint-generator

SKILL.md

API Endpoint Generator Skill

You are an expert API designer. When this skill activates, you generate complete, production-ready REST API endpoints for either TypeScript/Express or Python/FastAPI. Follow every step below exactly.

Step 1: Gather Requirements

Before generating any code, determine:

  1. Framework: TypeScript + Express, or Python + FastAPI?
  2. Resource: What entity is being managed? (e.g., User, Order, Product)
  3. HTTP method and path: GET /users/:id, POST /products, etc.
  4. Input shape: What fields does the request body, path params, or query params contain?
  5. Output shape: What does a success response look like?
  6. Business rules: Any constraints on the input? (e.g., email must be unique, age >= 18)

If any of these are missing from the user's prompt, ask for them before generating.

Step 2: Apply the Standard Output Structure

Every generated endpoint must include all five layers:

LayerPurpose
Schema / ValidatorZod (TS) or Pydantic (Python) defines input shape and validates at the boundary
Route HandlerThe async function that orchestrates validation, logic, and response
Typed Request/ResponseTypeScript generics or Python type hints — no any, no dict without hints
Error HandlingRFC 7807 Problem Details format for all 4xx/5xx responses
Unit Test StubVitest (TS) or pytest (Python) — all happy path + key error paths

Step 3: TypeScript / Express Pattern

Follow this exact template for TypeScript endpoints:

typescript
import { Router, Request, Response, NextFunction } from 'express';
import { z } from 'zod';
// --- 1. Input schema (validates and infers the TS type simultaneously) ---
const CreateUserSchema = z.object({
email: z.string().email({ message: 'Must be a valid email address' }),
name: z.string().min(2).max(100),
age: z.number().int().min(0).max(150),
});
// Infer the TypeScript type from the schema — single source of truth
type CreateUserInput = z.infer<typeof CreateUserSchema>;
// --- 2. Response type ---
interface UserResponse {
id: string;
email: string;
name: string;
createdAt: string;
}
// --- 3. Route handler ---
/**
* POST /users
*
* Creates a new user account.
*
* @param req.body - {@link CreateUserInput}
* @returns 201 with the created {@link UserResponse}
* @throws 400 if the request body fails validation
* @throws 409 if the email address is already registered
*/
async function createUser(
req: Request,
res: Response<UserResponse>,
next: NextFunction,
): Promise<void> {
// Validate input — parse() throws ZodError on failure
const parseResult = CreateUserSchema.safeParse(req.body);
if (!parseResult.success) {
// RFC 7807 Problem Details — consistent error envelope
res.status(400).json({
type: 'https://errors.myapp.com/validation-error',
title: 'Validation Error',
status: 400,
detail: 'One or more fields failed validation.',
errors: parseResult.error.flatten().fieldErrors,
} as any);
return;
}
// parseResult.data is now fully typed as CreateUserInput
const input: CreateUserInput = parseResult.data;
try {
// Replace with your actual service/repository call
const user = await userService.create(input);
// Return immutable response — never mutate the entity object directly
res.status(201).json({
id: user.id,
email: user.email,
name: user.name,
createdAt: user.createdAt.toISOString(),
});
} catch (error) {
// Delegate unexpected errors to Express error middleware
next(error);
}
}
// --- 4. Router registration ---
export const userRouter = Router();
userRouter.post('/', createUser);
Key rules for TypeScript endpoints
  • Never use `any` for request/response types — use Zod inference
  • Always use `safeParse()` not parse() — lets you control the 400 response format
  • Immutable response objects — spread or construct new objects, never mutate DB entities
  • Pass unknown errors to `next(error)` — let centralized error middleware handle 500s
  • Use `async/await` — no callbacks, no Promise chains

Step 4: Python / FastAPI Pattern

Follow this exact template for Python endpoints:

python
from fastapi import APIRouter, HTTPException, status
from pydantic import BaseModel, EmailStr, Field, field_validator
from uuid import UUID, uuid4
from datetime import datetime, timezone
router = APIRouter(prefix="/users", tags=["users"])
# --- 1. Request schema (Pydantic v2) ---
class CreateUserRequest(BaseModel):
"""Request body for creating a new user."""
email: EmailStr
name: str = Field(min_length=2, max_length=100)
age: int = Field(ge=0, le=150)
@field_validator("name")
@classmethod
def name_must_not_be_blank(cls, value: str) -> str:
if not value.strip():
raise ValueError("Name must not be blank or whitespace only")
return value.strip()
# --- 2. Response schema ---
class UserResponse(BaseModel):
"""Response body returned after creating a user."""
id: UUID
email: str
name: str
created_at: datetime
# --- 3. Route handler ---
@router.post(
"/",
response_model=UserResponse,
status_code=status.HTTP_201_CREATED,
summary="Create a new user account",
responses={
409: {"description": "Email address already registered"},
},
)
async def create_user(payload: CreateUserRequest) -> UserResponse:
"""
Create a new user account.
- **email**: Must be a valid email address and unique in the system
- **name**: Between 2 and 100 characters
- **age**: Between 0 and 150
"""
# FastAPI validates payload automatically via Pydantic — 422 on failure
try:
# Replace with your actual service/repository call
created = await user_service.create(
email=payload.email,
name=payload.name,
age=payload.age,
)
except DuplicateEmailError as exc:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail=f"Email '{payload.email}' is already registered.",
) from exc
# Return an immutable response model — Pydantic serializes it
return UserResponse(
id=created.id,
email=created.email,
name=created.name,
created_at=created.created_at,
)
Key rules for Python endpoints
  • Use `EmailStr` from pydantic-email-validator for email fields
  • `Field(ge=..., le=...)` for numeric constraints — not custom logic in the handler
  • `@field_validator` for cross-field or complex validation
  • `response_model=` on the decorator — FastAPI filters and validates output automatically
  • Raise `HTTPException` for expected business errors, not generic Exception
  • Never return raw dict — always return a Pydantic model instance

Step 5: Error Response Format (RFC 7807)

All error responses must use this envelope — consistent across both frameworks:

json
{
"type": "https://errors.myapp.com/validation-error",
"title": "Validation Error",
"status": 400,
"detail": "One or more fields failed validation.",
"errors": {
"email": ["Must be a valid email address"],
"age": ["Must be between 0 and 150"]
}
}

FastAPI returns 422 Unprocessable Entity with a similar format automatically from Pydantic.

Step 6: Unit Test Stub

Always generate a test file alongside the endpoint. Name it:

  • TypeScript: users.test.ts using Vitest
  • Python: test_users.py using pytest + httpx

The stub must include test cases for:

  1. Happy path — valid input returns correct status code and response shape
  2. Validation error — missing required field returns 400/422
  3. Validation error — invalid field type returns 400/422
  4. Business error — e.g., duplicate email returns 409

Step 7: Output Checklist

Before delivering any generated code:

  • [ ] Input schema covers all required fields with appropriate constraints
  • [ ] TypeScript: safeParse() used; type inferred via z.infer<>
  • [ ] Python: response_model= set on route decorator
  • [ ] No any types (TypeScript) or untyped dict (Python)
  • [ ] Error response uses RFC 7807 format
  • [ ] Unknown errors delegated to middleware (next(error)) or re-raised as HTTPException
  • [ ] JSDoc / docstring covers: what it does, params, returns, throws
  • [ ] Test stub covers happy path + at least 2 error paths

Reference Files

  • endpoint-template.ts — Complete TypeScript/Express endpoint with Zod validation
  • endpoint-template.py — Complete Python/FastAPI endpoint with Pydantic v2

Security

  • Security scanPassed, 95 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
43
Repository forks
44
7-day trend score
19

Stars and forks are for the whole GitHub repository, not this skill alone. Refreshed 19 Mar 2026, 7 months ago.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/timothywarner/copilot-dev/api-endpoint-generator/badge)](https://verified-skill.com/skills/timothywarner/copilot-dev/api-endpoint-generator)

Something wrong with this skill or its page? Tell us.