api-patterns
API route implementation patterns with RLS, Zod validation, and error handling. Use when creating API routes, implementing endpoints, or adding server-side validation.
When & Why to Use This Skill
This Claude skill provides standardized patterns for implementing secure and robust API routes. It focuses on Row Level Security (RLS), Zod-based request validation, and structured error handling to ensure production-grade backend development. By following these checklists and templates, developers can prevent common security pitfalls like direct database bypasses or unvalidated user inputs, significantly improving code quality and system security.
Use Cases
- Developing secure CRUD endpoints with mandatory authentication and Row Level Security (RLS) context to prevent unauthorized data access.
- Implementing strict request validation using Zod schemas to ensure data integrity and prevent malicious input or injection attacks.
- Standardizing API error handling across a project with structured JSON responses and appropriate HTTP status codes for better frontend integration.
- Creating secure webhook handlers that require specific validation and authentication patterns to protect server-side logic.
- Refactoring legacy API routes to comply with modern security best practices and architectural standards using provided templates and checklists.
| name | api-patterns |
|---|---|
| description | API route implementation patterns with RLS, Zod validation, and error handling. Use when creating API routes, implementing endpoints, or adding server-side validation. |
API Patterns Skill
Purpose
Route to existing API patterns and provide checklists for safe, validated API route implementation. All API routes MUST use RLS context helpers—see rls-patterns skill.
When This Skill Applies
Invoke this skill when:
- Creating new API routes
- Implementing CRUD endpoints
- Adding request/response validation
- Handling webhooks
- Implementing error handling patterns
Authoritative References (MUST READ)
| Pattern | Location | Purpose |
|---|---|---|
| User Context API | docs/patterns/api/user-context-api.md |
User-scoped operations |
| Admin Context API | docs/patterns/api/admin-context-api.md |
Admin-scoped operations |
| Zod Validation | docs/patterns/api/zod-validation-api.md |
Request/response validation |
| Webhook Handler | docs/patterns/api/webhook-handler.md |
Webhook processing |
| Bonus Content | docs/patterns/api/bonus-content-delivery.md |
Protected content delivery |
Stop-the-Line Conditions
FORBIDDEN Patterns
// FORBIDDEN: Direct Prisma calls (bypass RLS)
const users = await prisma.user.findMany();
// Must use: withUserContext, withAdminContext, or withSystemContext
// FORBIDDEN: Missing authentication check
export async function GET(req: Request) {
return getUserData(); // No auth check!
}
// FORBIDDEN: Unvalidated user input
const { userId } = await req.json();
// Must validate with Zod schema
// FORBIDDEN: Generic error responses
return new Response("Error", { status: 500 });
// Must use structured error response
CORRECT Patterns
// CORRECT: RLS context + auth check
export async function GET(req: Request) {
const { userId } = await auth();
if (!userId) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
const data = await withUserContext(prisma, userId, async (client) => {
return client.user.findUnique({ where: { user_id: userId } });
});
return NextResponse.json(data);
}
// CORRECT: Zod validation
const schema = z.object({
email: z.string().email(),
name: z.string().min(1),
});
const result = schema.safeParse(body);
if (!result.success) {
return NextResponse.json(
{ error: "Validation failed", details: result.error.flatten() },
{ status: 400 },
);
}
API Route Checklist
Before ANY API route:
- Authentication check with
await auth()from Clerk - Proper 401 response for unauthenticated
- Request validation with Zod schema
- RLS context wrapper (
withUserContext/withAdminContext/withSystemContext) - Structured error responses with appropriate status codes
- TypeScript types for request/response
Standard Response Patterns
Success Response
return NextResponse.json({ data, success: true }, { status: 200 });
Error Response
return NextResponse.json(
{
error: "Human-readable error message",
code: "ERROR_CODE",
details: optional_details,
},
{ status: 400 | 401 | 403 | 404 | 500 },
);
Status Codes
| Code | When to Use |
|---|---|
| 200 | Success |
| 201 | Created (POST) |
| 400 | Bad request / validation error |
| 401 | Not authenticated |
| 403 | Forbidden (authenticated but not authorized) |
| 404 | Resource not found |
| 500 | Server error |
API Route Template
import { auth } from "@clerk/nextjs/server";
import { NextResponse } from "next/server";
import { z } from "zod";
import { withUserContext } from "@/lib/rls-helpers";
import { prisma } from "@/lib/prisma";
// Request validation schema
const RequestSchema = z.object({
// Define expected fields
});
export async function POST(req: Request) {
try {
// 1. Authenticate
const { userId } = await auth();
if (!userId) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
// 2. Parse and validate request
const body = await req.json();
const result = RequestSchema.safeParse(body);
if (!result.success) {
return NextResponse.json(
{ error: "Validation failed", details: result.error.flatten() },
{ status: 400 },
);
}
// 3. Execute with RLS context
const data = await withUserContext(prisma, userId, async (client) => {
return client.resource.create({ data: result.data });
});
// 4. Return success response
return NextResponse.json({ data, success: true }, { status: 201 });
} catch (error) {
console.error("API error:", error);
return NextResponse.json(
{ error: "Internal server error" },
{ status: 500 },
);
}
}
API Documentation Template
For documenting new endpoints:
## Endpoint: POST /api/resource
### Description
Creates a new resource for the authenticated user.
### Authentication
Required: Clerk session
### Request Body
| Field | Type | Required | Description |
| ----- | ------ | -------- | ------------- |
| name | string | Yes | Resource name |
| type | string | No | Resource type |
### Response
**Success (201)**:
\`\`\`json
{ "data": { "id": 1, "name": "..." }, "success": true }
\`\`\`
**Error (400)**:
\`\`\`json
{ "error": "Validation failed", "details": {...} }
\`\`\`
### RLS Context
Uses `withUserContext` - user can only access own resources.
Related Skills
- rls-patterns: RLS context helper usage (REQUIRED for all DB operations)
- security-audit: API security validation
- testing-patterns: API endpoint testing