Get Started
Back to Skill Shed
Coding

API Endpoint Designer

Prompte27 February 2026IntermediateClaude Code, Codex, Cursor, Windsurf, Aider
apirestendpointsvalidationbackend

What This Skill Does

Guides your AI coding assistant to design clean, consistent RESTful API endpoints. Every endpoint validates input, returns consistent error shapes, handles authentication, and follows HTTP conventions that clients can rely on.

When to Use It

Activate this skill when building or extending a REST API:

  • Creating new CRUD endpoints for a resource
  • Adding authentication and authorization to routes
  • Designing error response contracts for frontend consumption
  • Implementing pagination, filtering, and sorting
  • Reviewing API design for consistency before shipping

What Changes

Your AI assistant will:

  • Use correct HTTP methods and status codes for every operation
  • Validate all request inputs at the boundary — never trust client data
  • Return consistent error response shapes across all endpoints
  • Apply authentication and rate limiting middleware
  • Design pagination, filtering, and sorting that scales

Skill File

api-endpoint-designer.skill.md
---
name: api-endpoint-designer
description: >
  Design RESTful API endpoints with correct HTTP methods, input validation,
  consistent error responses, authentication middleware, and rate limiting.
  Every endpoint is predictable and well-documented.
---

# API Endpoint Designer

You design RESTful APIs that are consistent, secure, and predictable.

## URL Design

### Resource Naming

- Use **plural nouns** for collections: `/users`, `/orders`, `/products`
- Use **IDs** for individual resources: `/users/:id`
- Use **nesting** for relationships: `/users/:id/orders`
- Maximum nesting depth: 2 levels (beyond that, use query params or top-level routes)
- Use **kebab-case**: `/user-profiles`, not `/userProfiles` or `/user_profiles`

### HTTP Methods

| Method | Purpose | Idempotent | Status |
|--------|---------|------------|--------|
| GET | Read resource(s) | Yes | 200 |
| POST | Create resource | No | 201 |
| PUT | Replace resource entirely | Yes | 200 |
| PATCH | Partial update | No | 200 |
| DELETE | Remove resource | Yes | 204 |

## Input Validation

Validate ALL inputs at the API boundary. Never trust client data.

```typescript
// Define a schema for every endpoint
const createUserSchema = z.object({
  email: z.string().email().max(255),
  name: z.string().min(1).max(100).trim(),
  role: z.enum(["user", "admin"]).default("user"),
})

// Validate early, fail fast
export async function POST(req: Request): Promise<Response> {
  const body = await req.json()
  const parsed = createUserSchema.safeParse(body)

  if (!parsed.success) {
    return Response.json(
      { error: "Validation failed", details: parsed.error.flatten() },
      { status: 400 }
    )
  }

  // parsed.data is now fully typed and validated
  const user = await createUser(parsed.data)
  return Response.json(user, { status: 201 })
}
```

### Validation Rules

1. **Validate types** — strings, numbers, booleans, arrays
2. **Validate constraints** — min/max length, regex patterns, enums
3. **Sanitize strings** — trim whitespace, escape HTML if stored
4. **Validate IDs** — ensure UUID format or numeric range
5. **Reject unknown fields** — don't silently accept extra properties

## Error Response Format

Every error response uses the same shape:

```json
{
  "error": "Human-readable message",
  "code": "MACHINE_READABLE_CODE",
  "details": {},
  "requestId": "uuid-for-debugging"
}
```

### Status Code Usage

| Code | When |
|------|------|
| 400 | Invalid input (validation failed) |
| 401 | Not authenticated (no valid token) |
| 403 | Not authorised (valid token, insufficient permissions) |
| 404 | Resource not found |
| 409 | Conflict (duplicate entry, version mismatch) |
| 422 | Semantically invalid (valid structure, impossible values) |
| 429 | Rate limit exceeded |
| 500 | Server error (log it, don't expose internals) |

### Never Expose Internals

```typescript
// BAD: Leaks implementation details
{ "error": "SQLITE_CONSTRAINT: UNIQUE constraint failed: users.email" }

// GOOD: Safe, actionable message
{ "error": "A user with this email already exists", "code": "DUPLICATE_EMAIL" }
```

## Authentication Middleware

```typescript
async function requireAuth(req: Request): Promise<User> {
  const token = req.headers.get("Authorization")?.replace("Bearer ", "")

  if (!token) {
    throw new ApiError(401, "Authentication required", "NO_TOKEN")
  }

  const payload = await verifyToken(token)
  if (!payload) {
    throw new ApiError(401, "Invalid or expired token", "INVALID_TOKEN")
  }

  return payload.user
}
```

## Rate Limiting

Apply rate limits to all endpoints:

- **Authentication endpoints** (login, register): 5 requests/minute per IP
- **Read endpoints** (GET): 100 requests/minute per user
- **Write endpoints** (POST, PUT, DELETE): 30 requests/minute per user
- **Expensive operations** (search, export): 10 requests/minute per user

Include rate limit headers in responses:

```
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 97
X-RateLimit-Reset: 1709078400
```

## Pagination

Always paginate list endpoints. Use cursor-based for large datasets:

```typescript
// GET /users?cursor=abc123&limit=20
{
  "data": [...],
  "pagination": {
    "nextCursor": "def456",
    "hasMore": true
  }
}
```

Rules:
- Default limit: 20, max limit: 100
- Never return unbounded results
- Include total count only if the query is cheap (indexed COUNT)

Install

Claude Code

Save to your project's .claude/skills/ directory. Claude Code picks it up automatically.

Save to:
.claude/skills/api-endpoint-designer.skill.md
Or use the command line:
mkdir -p .claude/skills/ && curl -o .claude/skills/api-endpoint-designer.skill.md https://prompte.app/skill-shed/api-endpoint-designer/raw

Explore more skills

Browse the full library of curated skills for your AI coding CLI.