From coding-agent
REST API design conventions covering URL structure, HTTP methods, response formats, status codes, pagination, and versioning. Use when designing or reviewing API endpoints.
npx claudepluginhub devjarus/coding-agentThis skill uses the workspace's default tool permissions.
- Use nouns, never verbs: `/users`, not `/getUsers`
Generates design tokens/docs from CSS/Tailwind/styled-components codebases, audits visual consistency across 10 dimensions, detects AI slop in UI.
Records polished WebM UI demo videos of web apps using Playwright with cursor overlay, natural pacing, and three-phase scripting. Activates for demo, walkthrough, screen recording, or tutorial requests.
Delivers idiomatic Kotlin patterns for null safety, immutability, sealed classes, coroutines, Flows, extensions, DSL builders, and Gradle DSL. Use when writing, reviewing, refactoring, or designing Kotlin code.
/users, not /getUsers/orders, /products/users/{id}/orders is fine; avoid /users/{id}/orders/{id}/items/{id}/blog-posts, /user-profiles| Method | Semantics |
|---|---|
| GET | Read resource(s), idempotent, no body |
| POST | Create a new resource or trigger an action |
| PUT | Replace a resource entirely |
| PATCH | Partial update of a resource |
| DELETE | Remove a resource, idempotent |
Success:
{
"data": { ... },
"meta": { "requestId": "abc123", "timestamp": "2024-01-01T00:00:00Z" }
}
Error:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Human-readable description",
"details": [{ "field": "email", "issue": "Invalid format" }]
}
}
| Code | When to use |
|---|---|
| 200 | Successful GET, PUT, PATCH |
| 201 | Successful POST that created a resource |
| 204 | Successful DELETE or action with no response body |
| 400 | Bad request — malformed syntax or invalid parameters |
| 401 | Not authenticated |
| 403 | Authenticated but not authorized |
| 404 | Resource not found |
| 409 | Conflict — duplicate, state mismatch |
| 422 | Validation failed — well-formed but semantically invalid |
| 500 | Unexpected server error |
Cursor-based (for large or frequently updated datasets):
{
"data": [...],
"meta": {
"nextCursor": "eyJpZCI6MTAwfQ==",
"hasMore": true
}
}
Offset-based (for simple, small datasets):
{
"data": [...],
"meta": {
"total": 250,
"page": 2,
"perPage": 25,
"totalPages": 10
}
}
Always return a meta object even when there is only one page.
/v1/users, /v2/usersSunset response headers before removingEvery route handler should be roughly 10 lines: (1) parse input, (2) call a core function, (3) return the serialized result, (4) catch errors and map them to status codes. Business logic lives in the core library or service layer, not in the route.
// GOOD — thin wrapper around a core function
app.get('/api/posts/:slug', async (req, res, next) => {
try {
const post = await posts.findBySlug(req.params.slug);
if (!post) return res.status(404).json({ error: { code: 'NOT_FOUND', message: 'Post not found' } });
return res.status(200).json({ data: post });
} catch (err) {
return next(err);
}
});
// BAD — business logic embedded in the route
app.get('/api/posts/:slug', async (req, res) => {
const db = getDb();
const row = await db.prepare('SELECT * FROM posts WHERE slug = ?').get(req.params.slug);
if (!row) return res.status(404).json(...);
const excerpt = row.content.split('\n').slice(0, 3).join(' ').substring(0, 150); // ← logic
const tags = row.tags?.split(',') ?? []; // ← logic
return res.status(200).json({ data: { ...row, excerpt, tags } });
});
Why it matters: testing the core library is effectively testing every route, because routes add nothing beyond transport glue. If your route handler is >15 lines or contains branching business logic beyond error-to-status mapping, extract it into a pure core function and call that from the route.
Applies to: Next.js Route Handlers, Express middlewares, FastAPI endpoints, Gin handlers, Rails controllers — any framework where you're tempted to put logic "right in the handler because it's convenient."
Exception: trivial projections (e.g., picking 3 fields out of a row for a list view) are fine inline. The rule targets branching, validation, data transformation, and external calls — not one-line field selection.