agentsclimarketplace

Api design

Skill devjarus/coding-agent/skills/backend/api-design

REST API design conventions covering URL structure, HTTP methods, response formats, status codes, pagination, and versioning. Use when designing or reviewing API endpoints.From its SKILL.md

Install
npx -y skills add devjarus/coding-agent --skill api-design

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

4.2 KB, as published. Nobody here has run it

API Design

URL Structure

  • Use nouns, never verbs: /users, not /getUsers
  • Always plural: /orders, /products
  • Nest resources max 2 levels deep: /users/{id}/orders is fine; avoid /users/{id}/orders/{id}/items/{id}
  • Use kebab-case for multi-word segments: /blog-posts, /user-profiles

HTTP Methods

MethodSemantics
GETRead resource(s), idempotent, no body
POSTCreate a new resource or trigger an action
PUTReplace a resource entirely
PATCHPartial update of a resource
DELETERemove a resource, idempotent

Response Format

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" }]
  }
}

Status Codes

CodeWhen to use
200Successful GET, PUT, PATCH
201Successful POST that created a resource
204Successful DELETE or action with no response body
400Bad request — malformed syntax or invalid parameters
401Not authenticated
403Authenticated but not authorized
404Resource not found
409Conflict — duplicate, state mismatch
422Validation failed — well-formed but semantically invalid
500Unexpected server error

Pagination

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.

Versioning

  • Major breaking changes: URL prefix — /v1/users, /v2/users
  • Additive changes (new optional fields, new endpoints) are backward compatible and do not require a version bump
  • Deprecate old versions with Sunset response headers before removing

Routes are transports, not logic

Every 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.

Keep looking

Skills are one crate of 626,474. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.