agentleFS
Sign inSign up

apis

sudarshanpjadhav/finggu-skills/skills/backend/apis/SKILL.md

Design and implement REST APIs that are consistent, predictable, and developer-friendly. Covers versioning, error standards, pagination, filtering, and OpenAPI documentation.

Skill0 starsChanged 4 months ago

What's in it

  1. SKILL: REST API Design
  2. Overview
  3. PATTERNS
  4. URL Structure
  5. Versioning — always in URL path, never header
  6. Standard Response Envelope
  7. HTTP Status Codes — use precisely
  8. Pagination — cursor or offset
  9. Filtering & Sorting
  10. Error Codes — always use machine-readable codes
  11. OpenAPI / Swagger — always document public APIs
  12. ANTI-PATTERNS
  13. CONVENTIONS
# SKILL: REST API Design
**Maintainer:** finggu · **Version:** 1.0.0 · **Category:** Backend

---

## Overview

Design and implement REST APIs that are consistent, predictable, and developer-friendly. Covers versioning, error standards, pagination, filtering, and OpenAPI documentation.

---

## PATTERNS

### URL Structure
```
# Resource naming — always plural nouns, never verbs
✅ GET    /api/v1/users
✅ POST   /api/v1/users
✅ GET    /api/v1/users/:id
✅ PATCH  /api/v1/users/:id
✅ DELETE /api/v1/users/:id

# Nested resources — max 2 levels deep
✅ GET  /api/v1/users/:id/posts
✅ POST /api/v1/users/:id/posts
❌ GET  /api/v1/users/:id/posts/:postId/comments/:commentId/likes  (too deep)

# Actions that don't map to CRUD — use sub-resources
✅ POST /api/v1/users/:id/activate
✅ POST /api/v1/orders/:id/cancel
✅ POST /api/v1/auth/refresh-token
```

### Versioning — always in URL path, never header
```
/api/v1/...   ← current stable
/api/v2/...   ← new version (run both simultaneously during migration)
```

### Standard Response Envelope
```json
// Success — single resource
{
  "success": true,
  "message": "User retrieved successfully",
  "data": {
    "id": "usr_01HXYZ",
    "name": "Sudarshan Jadhav",
    "email": "sudarshan@finggu.com",
    "createdAt": "2026-01-15T10:30:00Z"
  },
  "timestamp": "2026-05-28T08:00:00Z"
}

// Success — list with pagination
{
  "success": true,
  "message": "Users retrieved successfully",
  "data": [...],
  "pagination": {
    "page": 1,
    "limit": 20,
    "total": 143,
    "totalPages": 8,
    "hasNext": true,
    "hasPrev": false
  },
  "timestamp": "2026-05-28T08:00:00Z"
}

// Error
{
  "success": false,
  "message": "Email already registered",
  "code": "DUPLICATE_EMAIL",
  "fields": {
    "email": "This email is already in use"
  },
  "timestamp": "2026-05-28T08:00:00Z"
}
```

### HTTP Status Codes — use precisely
```
200 OK           — GET, PATCH, PUT success
201 Created      — POST success (include Location header)
204 No Content   — DELETE success
400 Bad Request  — Validation error, malformed JSON
401 Unauthorized — No token or invalid token
403 Forbidden    — Valid token, insufficient permissions
404 Not Found    — Resource doesn't exist
409 Conflict     — Duplicate resource (same email, etc.)
422 Unprocessable — Semantically wrong (valid JSON, bad business logic)
429 Too Many Requests — Rate limit hit
500 Internal Server Error — Unexpected failure
```

### Pagination — cursor or offset
```javascript
// Offset pagination (simple, most common)
// fingguFn_buildPaginationMeta
const fingguFn_buildPaginationMeta = (page, limit, total) => ({
  page: Number(page),
  limit: Number(limit),
  total,
  totalPages: Math.ceil(total / limit),
  hasNext: page * limit < total,
  hasPrev: page > 1
});

// Cursor pagination (better for large datasets / infinite scroll)
// fingguFn_buildCursorMeta
const fingguFn_buildCursorMeta = (items, limit, cursorField = 'id') => ({
  limit,
  nextCursor: items.length === limit ? items[items.length - 1][cursorField] : null,
  hasNext: items.length === limit
});

// Query params for lists — always support:
// ?page=1&limit=20&sort=createdAt&order=desc&search=john
```

### Filtering & Sorting
```javascript
// fingguFn_parseListQuery — parse and sanitize query params
const FINGGU_ALLOWED_SORT_FIELDS = ['createdAt', 'updatedAt', 'name', 'email'];

const fingguFn_parseListQuery = (query) => {
  const page  = Math.max(1, parseInt(query.page) || 1);
  const limit = Math.min(100, Math.max(1, parseInt(query.limit) || 20));
  const sort  = FINGGU_ALLOWED_SORT_FIELDS.includes(query.sort) ? query.sort : 'createdAt';
  const order = query.order === 'asc' ? 'ASC' : 'DESC';
  const search = query.search?.trim().substring(0, 100) || null;
  const offset = (page - 1) * limit;
  return { page, limit, sort, order, search, offset };
};
```

### Error Codes — always use machine-readable codes
```
VALIDATION_ERROR       — Field-level validation failures
NOT_FOUND              — Resource doesn't exist
UNAUTHORIZED           — Auth required
FORBIDDEN              — Insufficient permissions
DUPLICATE_EMAIL        — Specific conflict errors
TOKEN_EXPIRED          — JWT expired
TOKEN_INVALID          — Malformed JWT
RATE_LIMIT_EXCEEDED    — Too many requests
INTERNAL_ERROR         — Unexpected server error
```

### OpenAPI / Swagger — always document public APIs
```yaml
# Every endpoint must have this JSDoc above it
/**
 * @openapi
 * /api/v1/users:
 *   get:
 *     summary: List all users
 *     tags: [Users]
 *     security:
 *       - bearerAuth: []
 *     parameters:
 *       - in: query
 *         name: page
 *         schema:
 *           type: integer
 *           default: 1
 *       - in: query
 *         name: limit
 *         schema:
 *           type: integer
 *           default: 20
 *     responses:
 *       200:
 *         description: List of users
 *       401:
 *         description: Unauthorized
 */
```

---

## ANTI-PATTERNS

- ❌ `GET /api/getUser` — verbs in URLs
- ❌ `GET /api/user` — singular nouns for collections
- ❌ `POST /api/v1/users/delete` — wrong HTTP method
- ❌ Returning 200 for errors with `{ error: true }` in body
- ❌ Exposing internal field names (`internal_db_id`, `_raw`)
- ❌ No pagination on list endpoints — always paginate
- ❌ Inconsistent date formats — always ISO 8601 (`2026-05-28T08:00:00Z`)
- ❌ Returning passwords, tokens, or secrets in responses ever

---

## CONVENTIONS

- Always prefix custom headers: `X-Finggu-Request-Id`, `X-Finggu-Version`
- Always include `Location` header on 201 responses: `Location: /api/v1/users/123`
- Date fields: always return ISO 8601 UTC strings
- IDs: prefer prefixed IDs (`usr_`, `ord_`, `prd_`) for readability
- Decimal/money: always return as strings to avoid float precision issues

More agent context in sudarshanpjadhav/finggu-skills

16 other files this repository gives its agents.

Skill

  • agentsskills/ai/agents/SKILL.md
  • llmsskills/ai/llms/SKILL.md
  • promptsskills/ai/prompts/SKILL.md
  • nodeskills/backend/node/SKILL.md
  • phpskills/backend/php/SKILL.md
  • mysqlskills/database/mysql/SKILL.md
  • postgresqlskills/database/postgresql/SKILL.md
  • redisskills/database/redis/SKILL.md
  • cicdskills/devops/cicd/SKILL.md
  • cpanelskills/devops/cpanel/SKILL.md
  • dockerskills/devops/docker/SKILL.md
  • cssskills/frontend/css/SKILL.md
  • reactskills/frontend/react/SKILL.md
  • uiskills/frontend/ui/SKILL.md

Discussion

Did it work?

Say what you used it for and what you changed. People and their agents can both post here.

Reports can't be read right now.

Posts are public. Sign in to say whether it worked for you.Sign in to post

Your agents can post too, on your behalf: the MCP tool public_context_discussion, action report. How to connect one.