From appgen
Provides guidance for designing RESTful APIs with proper endpoints, authentication, and error handling.
How this skill is triggered — by the user, by Claude, or both
Slash command
/appgen:skills/api-designThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Provides guidance for designing RESTful APIs with proper endpoints, authentication, and error handling.
Provides guidance for designing RESTful APIs with proper endpoints, authentication, and error handling.
This skill helps the appgen agent design sound API architectures with well-structured endpoints, input validation, and security.
Use plural nouns for resources:
/api/users/api/userUse nesting for relationships:
/api/users/:id/posts/api/user-posts?userId=:idKeep URLs simple:
/api/products/api/get-all-products| Method | Purpose | Example |
|---|---|---|
| GET | Retrieve resources | GET /api/users |
| POST | Create resource | POST /api/users |
| PUT | Replace resource | PUT /api/users/:id |
| PATCH | Update fields | PATCH /api/users/:id |
| DELETE | Delete resource | DELETE /api/users/:id |
GET /api/users?page=1&limit=10&sort=name&order=asc
Query Parameters:
page (number) - Page number (default: 1)limit (number) - Items per page (default: 10, max: 100)sort (string) - Field to sort byorder (asc|desc) - Sort directionResponse:
{
"data": [
{ "id": "1", "name": "Alice", "email": "[email protected]" },
{ "id": "2", "name": "Bob", "email": "[email protected]" }
],
"pagination": {
"page": 1,
"limit": 10,
"total": 42,
"totalPages": 5
}
}
GET /api/users/:id
Response:
{
"data": {
"id": "1",
"name": "Alice",
"email": "[email protected]",
"createdAt": "2024-01-15T10:00:00Z"
}
}
POST /api/users
Content-Type: application/json
{
"name": "Alice",
"email": "[email protected]"
}
Response (201 Created):
{
"data": {
"id": "1",
"name": "Alice",
"email": "[email protected]",
"createdAt": "2024-01-15T10:00:00Z"
}
}
PATCH /api/users/:id
Content-Type: application/json
{
"name": "Alice Smith"
}
Response:
{
"data": {
"id": "1",
"name": "Alice Smith",
"email": "[email protected]",
"updatedAt": "2024-01-15T11:00:00Z"
}
}
DELETE /api/users/:id
Response (204 No Content): Empty body
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid request body",
"details": [
{
"field": "email",
"message": "Invalid email format"
}
]
}
}
| Code | Meaning | Use Case |
|---|---|---|
| 200 | OK | Successful GET, PATCH, PUT |
| 201 | Created | Successful POST |
| 204 | No Content | Successful DELETE |
| 400 | Bad Request | Validation error |
| 401 | Unauthorized | Missing or invalid auth token |
| 403 | Forbidden | Valid token but insufficient permissions |
| 404 | Not Found | Resource doesn't exist |
| 409 | Conflict | Duplicate resource (e.g., email already exists) |
| 422 | Unprocessable Entity | Business logic error |
| 429 | Too Many Requests | Rate limit exceeded |
| 500 | Internal Server Error | Unexpected server error |
// Validation error (400)
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid request body",
"details": [
{ "field": "email", "message": "Invalid email format" }
]
}
}
// Unauthorized (401)
{
"error": {
"code": "UNAUTHORIZED",
"message": "Authentication required"
}
}
// Forbidden (403)
{
"error": {
"code": "FORBIDDEN",
"message": "Insufficient permissions"
}
}
// Not found (404)
{
"error": {
"code": "NOT_FOUND",
"message": "User not found"
}
}
// Conflict (409)
{
"error": {
"code": "DUPLICATE_EMAIL",
"message": "Email already in use"
}
}
// Server error (500)
{
"error": {
"code": "INTERNAL_ERROR",
"message": "An unexpected error occurred"
}
}
import { z } from 'zod';
// Create user schema
export const createUserSchema = z.object({
name: z.string().min(1).max(100),
email: z.string().email(),
role: z.enum(['USER', 'ADMIN']).default('USER'),
});
// Update user schema (all fields optional)
export const updateUserSchema = createUserSchema.partial();
// Query parameters schema
export const listUsersQuerySchema = z.object({
page: z.coerce.number().int().positive().default(1),
limit: z.coerce.number().int().positive().max(100).default(10),
sort: z.enum(['name', 'createdAt']).default('createdAt'),
order: z.enum(['asc', 'desc']).default('desc'),
});
// Hono example
app.post('/api/users', async (c) => {
try {
// Parse and validate request body
const body = await c.req.json();
const data = createUserSchema.parse(body);
// Create user
const user = await prisma.user.create({ data });
return c.json({ data: user }, 201);
} catch (error) {
if (error instanceof z.ZodError) {
return c.json({
error: {
code: 'VALIDATION_ERROR',
message: 'Invalid request body',
details: error.errors.map(e => ({
field: e.path.join('.'),
message: e.message,
})),
},
}, 400);
}
throw error;
}
});
Endpoints:
POST /api/auth/register # Create account
POST /api/auth/login # Get JWT token
POST /api/auth/refresh # Refresh token
GET /api/auth/me # Get current user (protected)
Login Flow:
// POST /api/auth/login
{
"email": "[email protected]",
"password": "secure123"
}
// Response (200)
{
"data": {
"accessToken": "eyJhbGciOiJIUzI1NiIs...",
"refreshToken": "eyJhbGciOiJIUzI1NiIs...",
"expiresIn": 3600, // seconds
"user": {
"id": "1",
"name": "Alice",
"email": "[email protected]"
}
}
}
Protected Endpoint:
// Middleware to verify JWT
export async function authMiddleware(c: Context, next: Next) {
const authHeader = c.req.header('Authorization');
if (!authHeader?.startsWith('Bearer ')) {
return c.json({
error: {
code: 'UNAUTHORIZED',
message: 'Authentication required',
},
}, 401);
}
const token = authHeader.substring(7);
try {
const payload = await verifyJWT(token);
c.set('userId', payload.userId);
await next();
} catch (error) {
return c.json({
error: {
code: 'INVALID_TOKEN',
message: 'Invalid or expired token',
},
}, 401);
}
}
// Use middleware
app.get('/api/users/:id', authMiddleware, async (c) => {
const userId = c.get('userId'); // From middleware
const targetId = c.req.param('id');
// Check authorization
if (userId !== targetId) {
return c.json({
error: {
code: 'FORBIDDEN',
message: 'Cannot access other users',
},
}, 403);
}
// Fetch user...
});
// Middleware for admin-only routes
export async function adminOnly(c: Context, next: Next) {
const userId = c.get('userId');
const user = await prisma.user.findUnique({
where: { id: userId },
});
if (user?.role !== 'ADMIN') {
return c.json({
error: {
code: 'FORBIDDEN',
message: 'Admin access required',
},
}, 403);
}
await next();
}
// Admin-only endpoint
app.delete('/api/users/:id', authMiddleware, adminOnly, async (c) => {
// Delete user...
});
// Ensure user owns the resource
app.patch('/api/posts/:id', authMiddleware, async (c) => {
const userId = c.get('userId');
const postId = c.req.param('id');
const post = await prisma.post.findUnique({
where: { id: postId },
});
if (!post) {
return c.json({
error: { code: 'NOT_FOUND', message: 'Post not found' },
}, 404);
}
if (post.authorId !== userId) {
return c.json({
error: { code: 'FORBIDDEN', message: 'Not your post' },
}, 403);
}
// Update post...
});
Before finalizing API design:
{ data: ... })Save API documentation to api/design.md:
# API Design
## Base URL
\`\`\`
http://localhost:3000/api/v1
\`\`\`
## Authentication
All protected endpoints require a JWT token in the Authorization header:
\`\`\`
Authorization: Bearer <token>
\`\`\`
## Endpoints
### Authentication
#### Register
\`\`\`
POST /auth/register
\`\`\`
**Request Body:**
\`\`\`json
{
"name": "Alice",
"email": "[email protected]",
"password": "secure123"
}
\`\`\`
**Response (201):**
[Example response...]
#### Login
\`\`\`
POST /auth/login
\`\`\`
[Continue for each endpoint...]
### Users
#### List Users
\`\`\`
GET /users?page=1&limit=10
\`\`\`
**Auth:** Required
**Query Params:**
- \`page\` (number, default: 1)
- \`limit\` (number, default: 10, max: 100)
**Response (200):**
[Example response...]
[Continue for each resource...]
## Error Responses
[Document error format and codes...]
v1.0 (2024-12-13)
npx claudepluginhub gaurangrshah/gsc-plugins --plugin appgenGuides RESTful API design and implementation: resource naming, HTTP methods, URL patterns, error responses, versioning, and core principles.
Designs RESTful API routes with Next.js App Router, Zod validation, auth guards, and typed responses. Activates when discussing API endpoints, route structure, or request/response schemas.
Establishes REST API design patterns for resource naming, HTTP methods and status codes, pagination, filtering, error responses, versioning, and rate limiting for production APIs.