Skip to content
Back to skills

Backend Api

ASecurity

Design and review REST/HTTP APIs, with bounded GraphQL and gRPC contract guidance: OpenAPI, auth, pagination, compatibility, and retries.

  • 7 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 24, 2026
devopstypescriptpythongosqlnodeexpressfastapirailsdockerkubernetes

Works with

  • cursor
  • cli
  • api
  • mcp

Security analysis

A100/100

Pro scans all 6 files and shows the line behind each finding

Scanned October 3, 2026

npx -y skills add iuliandita/skills --skill backend-api --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Backend Api?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Backend Api
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/iuliandita-backend-api/badge)](https://www.skillsdirectory.com/skills/iuliandita-backend-api)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: backend-api
description: >
  Design and review REST/HTTP APIs, with bounded GraphQL and gRPC contract guidance: OpenAPI, auth, pagination, compatibility, and retries.
license: MIT
compatibility: "Optional: Python or Node.js framework context. Optional: OpenAPI-capable framework/docs tooling"
metadata:
  source: iuliandita/skills
  date_added: "2026-04-06"
  effort: high
  argument_hint: "[framework-or-api-task]"
---

# Backend APIs: HTTP Service Design and Implementation

Design and review HTTP APIs that stay coherent as they grow. Focus on contracts, auth
boundaries, error models, and framework structure for Python and Node.js services.

**Target versions** (October 2026):
- FastAPI **0.142.2**
- Express **5.2.1** (published 2025-12-01)
- NestJS **12.1.2** (major release; check migration notes before upgrading; 12.0.2+ fixes high-severity [microservice](https://github.com/nestjs/nest/security/advisories/GHSA-m8vh-jmq9-5rjg) and [Fastify middleware-bypass](https://github.com/nestjs/nest/security/advisories/GHSA-9c5c-9qcx-q35q) advisories)
- OpenAPI Specification **3.2.1** (published 2026-09-10)
- HTTP Semantics: **RFC 9110** (June 2022)
- Problem Details for HTTP APIs: **RFC 9457** (July 2023)
- OAuth 2.0 Security Best Current Practice: **RFC 9700** (January 2025)

Security check (2026-10-02): Express/NestJS upload stacks using Multer <2.3.0 are affected by
high-severity multipart denial of service, [CVE-2026-82333](https://github.com/expressjs/multer/security/advisories/GHSA-535w-7cp7-47q4).
Upgrade Multer to 2.4.0+ (2.4.0 also fixes medium-severity aborted-upload disk exhaustion,
[CVE-2026-88932](https://github.com/expressjs/multer/security/advisories/GHSA-3pph-fpjx-jg34)) and bound `limits.fieldArrayIndexLimit` to application needs; checking
only the top-level framework version misses vulnerable middleware.

This skill works across these concerns:
- **Contract design** - resources, methods, status codes, schemas, versioning
- **API ergonomics** - pagination, filtering, sorting, idempotency, error format
- **Framework structure** - FastAPI dependencies, Express middleware, NestJS modules and guards
- **Authentication** - sessions, bearer tokens, OAuth/OIDC, BFF and token mediation
- **Review** - unstable contracts, DTO leakage, auth confusion, and HTTP misuse
- **Bounded protocol patterns** - GraphQL schema/resolver and gRPC contract, deadline, retry, and
  streaming safeguards when those protocols are already in scope

## When to use

- Designing a new REST or HTTP API
- Reviewing an existing FastAPI, Express, or NestJS service
- Writing or fixing OpenAPI specifications and generated docs
- Choosing status codes, error formats, pagination, filtering, or versioning strategy
- Designing auth flows for browser, mobile, machine-to-machine, or third-party clients
- Refactoring route or controller structure that has become hard to reason about
- Planning backward-compatible API evolution
- Defining idempotency and retry behavior for write endpoints

## When NOT to use

- Standalone GraphQL federation architecture or protocol-wide gRPC platform administration - narrow
  the request first; this skill covers service contract patterns, not a framework catalog
- Database schema, indexing, replication, or query tuning - use **databases**
- General bug-finding, race-condition hunting, or correctness review outside the API boundary - use **code-review**
- Security auditing for auth bypasses, injection, secrets, or OWASP findings - use **security-audit**
- Writing or debugging automated tests - use **testing**
- Deployment, containers, gateways, ingress, or cluster config - use **docker**, **kubernetes**, or **networking**
- CI/CD pipeline design for API delivery - use **ci-cd**
- MCP-specific HTTP servers and tool handlers - use **mcp**

---

## AI Self-Check

Before returning API code, route design, or OpenAPI output, verify:

- [ ] Resource names are nouns and URL shape is stable (`/users/{userId}/sessions`, not `/doUserSessionThing`)
- [ ] Method semantics follow RFC 9110 - no "GET that mutates", no PATCH used as a vague catch-all
- [ ] `401` vs `403` is correct: unauthenticated vs authenticated-but-forbidden
- [ ] Ownership-hiding behavior is deliberate and consistent: decide when out-of-scope resources return `404` vs `403`
- [ ] Error responses use one consistent format, preferably RFC 9457 problem details
- [ ] Request and response DTOs are explicit and separate from ORM or database models
- [ ] Input validation happens server-side even if clients or SDKs also validate
- [ ] Offset pagination is not used blindly on large or high-churn collections where cursor pagination is the safer default
- [ ] Write endpoints that may be retried (`POST /payments`, webhook receivers, order creation) define idempotency behavior
- [ ] OpenAPI docs match real handler behavior, examples, status codes, and auth requirements
- [ ] First-party browser apps do not get long-lived bearer tokens stored in browser storage by default
- [ ] Cookie-based browser auth accounts for cookie scope and CSRF behavior instead of assuming cookies are automatically safe
- [ ] OAuth guidance is current: authorization code + PKCE, no implicit flow, no resource owner password credentials
- [ ] Sensitive defaults are explicit: cookie flags, token TTLs, scope boundaries, and rate limits are not hand-waved
- [ ] **Framework version checked**: FastAPI, Express, NestJS, and OpenAPI examples match current APIs and migration notes
- [ ] **Contract compatibility checked**: response codes, pagination, errors, and auth behavior preserve existing clients unless a version bump is explicit
- [ ] **Injection in handler body flagged as contract defect**: if injection (SQL, command, header, path traversal) is found inside a route handler, flag it as a missing validation-boundary defect at the contract layer and recommend fixing it there; route to **security-audit** for a full injection audit if the pattern is widespread
- [ ] Cross-cutting agent hygiene applied - see `references/agent-hygiene.md`

---

## Performance

- Paginate and filter at the data store; never load full collections just to slice in the handler.
- Set timeouts and body limits at the framework, proxy, and client layers.
- Measure p95/p99 latency and error rates for changed endpoints before optimizing internals.

---

## Best Practices

- Use explicit auth scopes and tenancy checks in handlers, not only in route grouping or UI state.

## Workflow

**Build vs. Review:** when reviewing an existing service, still walk the same steps. Determine the API boundary, audit the contract, trace auth, then compare the implementation against the published behavior instead of jumping straight into handler code.

Order matters: the contract precedes handlers. Copy and track:
- [ ] Step 1: Boundary, callers, stability promise, and house style known
- [ ] Step 2: Framework pattern matches the codebase
- [ ] Step 3: Contract (resources, methods, schemas, errors, pagination, versioning) defined
- [ ] Step 4: Auth model chosen from the client type
- [ ] Step 5: Framework surface implemented against the contract
- [ ] Step 6: Behavior and docs validated (on failure, return to Step 3 or Step 5)

### Step 1: Determine the boundary

Clarify the API before picking framework patterns:
- **Who calls it?** Browser app, mobile app, third-party integrator, internal service, webhook sender
- **What kind of API is it?** Public API, private app backend, internal service, admin API
- **Is the task greenfield or review?** New design, incremental change, migration, or bug fix
- **What stability promise exists?** Internal-only, versioned public API, or "best effort"
- **What auth model already exists?** Session cookies, JWT bearer, API keys, OAuth/OIDC, or none yet
- **What house style already exists?** Error format, pagination shape, versioning scheme, auth boundary, DTO naming

If the user asks to "just add an endpoint", inspect the surrounding service first. Most bad API
work happens when one route lands with a different error model, auth rule, pagination shape, or
DTO style than the rest of the service.

### Step 2: Choose the framework pattern

Pick the framework that matches the codebase and team constraints. Do not switch frameworks for fashion.

| Framework | Best fit | Watch for |
|-----------|----------|-----------|
| **FastAPI** | Python services with typed request/response models and strong OpenAPI output | Leaking ORM models directly, async confusion, piling business logic into route functions |
| **Express** | Thin Node.js services, custom middleware stacks, existing mature codebases | No built-in structure, validation scattered across middleware, inconsistent error handling |
| **NestJS** | Larger TypeScript services that benefit from modules, guards, pipes, interceptors, and DI | Over-abstraction, decorator-heavy indirection, hiding simple flows behind too many layers |

### Step 3: Design the contract first

Define the API contract before writing handlers:

For changes to an existing service, match the current URL, error, pagination, and auth house
style unless the task explicitly includes a migration.

1. Pick resource shapes and URIs
2. Choose methods and status codes
3. Define request and response schemas
4. Define error shapes
5. Decide pagination, filtering, sorting, and versioning rules
6. Only then wire the framework implementation

For a GraphQL or gRPC boundary, keep REST/HTTP rules as the default where they fit, then read
`references/graphql-grpc-patterns.md` for the protocol-specific contract, compatibility, deadline,
authorization, pagination, and streaming safeguards.

Prefer:
- Noun-based resources: `/users/{userId}/sessions`
- Predictable list endpoints: `GET /orders?cursor=...&limit=...&status=paid`
- Explicit write semantics: `POST` for create/commands, `PUT` for full replacement, `PATCH` for partial update, `DELETE` for delete
- Explicit retry semantics for command endpoints like `POST /orders/{orderId}/cancel`
- One canonical error model across the whole service

Read `references/http-api-patterns.md` for method semantics, pagination, idempotency, and versioning rules.

### Step 4: Design auth around the client, not around JWT hype

Start from the client type:

| Client | Default choice | Why |
|--------|----------------|-----|
| **First-party browser app** | Session cookie or BFF/token-mediating backend | Keeps tokens off the browser as much as possible |
| **Native/mobile app** | OAuth/OIDC authorization code + PKCE | Current standard path for public clients |
| **Third-party integrator** | OAuth/OIDC or scoped API keys if OAuth is overkill | Explicit delegation and revocation story |
| **Internal service-to-service** | Platform identity or short-lived service credentials | Avoid user-style auth flows between services |

Auth rules:
- Use sessions by default for first-party web apps unless there is a clear reason not to
- Use bearer tokens when the client really is a token-holding client
- Treat API keys as machine credentials, not as a universal auth shortcut
- Define scope or role boundaries at the API boundary, not ad hoc inside random handlers
- If the browser app talks to third-party identity providers, follow authorization code + PKCE and current browser-app guidance rather than resurrecting implicit flow patterns

BFF token mediation (first-party browser app calling an external API):
```
Browser -> BFF (session cookie) -> BFF attaches Bearer token -> Upstream API
```
The BFF holds the access token server-side; the browser never sees it.

Read `references/auth-and-session-patterns.md` for sessions, bearer tokens, OAuth, BFF, refresh tokens, and machine auth.

### Step 5: Implement the framework surface

Convert the contract into framework code without letting the framework dictate the contract.

**FastAPI**
- Define Pydantic models for requests and responses
- Use dependency injection for auth, DB/session acquisition, and shared request context
- Register exception handlers for consistent RFC 9457 output
- Keep route handlers thin; move business rules into services or domain modules

**Express**
- Validate inputs before controller logic
- Attach auth and request context in middleware
- Use one shared error-handling middleware path
- Keep controllers as transport adapters, not the place where every rule in the system lives

**NestJS**
- Use DTO classes for transport boundaries
- Put validation in pipes and auth in guards
- Use exception filters or interceptors to normalize response and error behavior
- Keep module boundaries meaningful; one module per vague concept is not architecture

### Step 6: Validate behavior and docs

Before returning the result, run these checks. On a failure, fix it and return to Step 3 for a
contract defect or Step 5 for an implementation defect, then recheck:
- Compare OpenAPI docs against the real routes
- Verify auth requirements per endpoint
- Check all non-2xx responses, not just the happy path
- Verify list endpoints under empty, partial, and end-of-cursor states
- Check retries and duplicate submissions for create/payment/webhook paths
- Check repeat-call behavior for command endpoints (`cancel`, `resend`, `approve`) instead of leaving retries implicit
- Compare new endpoints with neighboring endpoints for naming, DTO shape, pagination params, and error consistency
- Route follow-up testing work to **testing** and follow-up security review to **security-audit**

---

## Contract Guardrails

### Versioning

- Prefer additive change over version churn
- Version only when you need a breaking change
- Keep one versioning scheme for the service: path versioning (`/v1/...`) is the clearest default for public APIs
- Do not mix path versioning, header versioning, and ad hoc `?version=` query params in one API

### OpenAPI authoring

Author for `3.1` unless the project's toolchain proves `3.2` support end to end; see "OpenAPI Version
Choice" in `references/http-api-patterns.md`.

### Error model

- Prefer RFC 9457 problem details with stable `type`, `title`, `status`, and domain-specific extension fields
- Do not return one error shape from validation, another from auth, and a third from business rules
- Never leak raw stack traces or ORM internals to clients

Minimal RFC 9457 problem detail response:
```json
{
  "type": "https://api.example.com/errors/insufficient-funds",
  "title": "Insufficient funds",
  "status": 422,
  "detail": "Account balance is $10.00; transfer requires $50.00."
}
```

Status code decision matrix (pick the narrowest correct code):

| Scenario | Code | Notes |
|----------|------|-------|
| Malformed JSON, missing required field, wrong type | `400` | Client request is syntactically wrong |
| Well-formed input but fails domain rule (insufficient funds, invalid state) | `422` | Semantic validation |
| No credentials, expired token | `401` | Include `WWW-Authenticate` header |
| Authenticated but not permitted | `403` | Do not challenge for credentials |
| Resource does not exist, or exists but caller must not know | `404` | Pick one policy per resource and keep it |
| Write conflicts with current state (stale ETag, duplicate unique key) | `409` | Problem detail should name the conflict |
| Idempotency-Key reused with a different body | `422` | Not `409` - the key contract is broken |
| Too many requests | `429` | Include `Retry-After` |
| Unhandled server error | `500` | Never leak stack traces in the body |

FastAPI exception handler wiring for consistent problem+json:
```python
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from fastapi.exceptions import RequestValidationError

app = FastAPI()

@app.exception_handler(RequestValidationError)
async def validation_handler(request: Request, exc: RequestValidationError):
    return JSONResponse(
        status_code=400,
        media_type="application/problem+json",
        content={
            "type": "https://api.example.com/errors/validation",
            "title": "Invalid request",
            "status": 400,
            "errors": [
                {"location": [part for part in error.get("loc", ())
                              if isinstance(part, (str, int))],
                 "message": "Invalid value"}
                for error in exc.errors()
            ],
        },
    )
```

### Pagination and filtering

- Offset pagination is fine for small, stable backoffice lists
- Cursor pagination is the default for user-facing or high-write collections
- Sort order must be stable and documented
- Filter names should reflect resource fields or clear domain concepts, not internal SQL column names

### Idempotency and retries

- Define idempotency for writes that clients or gateways may retry
- Use explicit idempotency keys for payment-like or request-replay-prone operations
- Distinguish "request accepted" from "side effect completed" when async workflows exist

Use the same transaction protocol in Express/NestJS and FastAPI; a cache get/write/set
sequence is not atomic:

1. Authenticate and authorize the operation. Bound the header length and scope the key to
   the caller or tenant plus operation; hash the normalized request into a fingerprint.
2. Atomically reserve that scoped key with a unique database constraint. For an existing
   record, reject a different fingerprint; report in-progress work without executing it again.
   Replay a completed authorized outcome only for the matching caller and fingerprint.
3. Commit the business mutation and completed response record in the same transaction.
   For external effects, use a transactional outbox and the provider's idempotency facility;
   a database rollback cannot undo an already sent payment or message.
4. Retain outcomes for the documented retry window. Recover interrupted reservations through
   a defined state transition, never by blindly executing the effect again. Test concurrent
   duplicates, two callers sharing a key, body mismatch, and crashes between effect and reply.

Cursor pagination response envelope:
```json
{
  "data": [...],
  "next_cursor": "eyJpZCI6MTIzfQ",
  "has_more": true
}
```
Decode the cursor server-side (`WHERE id > :cursor_id ORDER BY id LIMIT :limit`). Never expose raw DB offsets or row numbers in the cursor.

Opaque cursor encode/decode (FastAPI, HMAC-signed to prevent client tampering):
```python
import base64, hmac, hashlib, json, os

SECRET = os.environ["CURSOR_SECRET"].encode()

def encode_cursor(payload: dict) -> str:
    body = base64.urlsafe_b64encode(json.dumps(payload, separators=(",", ":")).encode()).rstrip(b"=")
    sig = base64.urlsafe_b64encode(hmac.new(SECRET, body, hashlib.sha256).digest()[:8]).rstrip(b"=")
    return f"{body.decode()}.{sig.decode()}"

def decode_cursor(token: str) -> dict:
    body, sig = token.split(".", 1)
    expected = base64.urlsafe_b64encode(hmac.new(SECRET, body.encode(), hashlib.sha256).digest()[:8]).rstrip(b"=").decode()
    if not hmac.compare_digest(sig, expected):
        raise ValueError("invalid cursor")
    return json.loads(base64.urlsafe_b64decode(body + "=="))
```
Clients treat `next_cursor` as opaque; the server controls shape and can change it without a breaking contract.

### Graceful shutdown and rolling deploys

Rolling deploys send SIGTERM to old instances while new ones come up. Handle it on the API side or expect 502s under load:

- Trap SIGTERM, stop accepting new connections, drain in-flight requests, then exit. FastAPI/Uvicorn: set `--timeout-graceful-shutdown` (unset by default, so Uvicorn waits for in-flight requests with no timeout; set a bounded value in production); Express 5: `server.close()` then `server.closeAllConnections()` after the drain window; NestJS: `app.enableShutdownHooks()` plus `onApplicationShutdown` handlers
- Keep the app-side shutdown window shorter than the orchestrator's termination grace (Kubernetes default 30s). `app_shutdown < terminationGracePeriodSeconds` or the kernel kills in-flight work
- Set the server HTTP keep-alive timeout longer than the upstream backend-connection idle timeout (load balancer, ingress). If the LB holds a connection the server already closed, the next request hits a dead socket. Typical safe pair: server keep-alive 65s behind an LB with 60s idle
- Add a readiness probe that flips to failing on SIGTERM before the drain starts. The orchestrator stops routing new traffic while in-flight requests finish

## What NOT to Force

- Do not force cursor pagination onto every small internal list or backoffice table
- Do not cut `/v2` just because a new field got added
- Do not insist on OAuth for every internal automation path if scoped API keys or platform identity fit better
- Do not push first-party browser apps toward bearer-token-in-local-storage patterns because "JWT auth" sounds modern
- Do not turn simple Express services into pseudo-enterprise architectures with needless layers and decorators

---

## Reference Files

- `references/http-api-patterns.md` - resource design, method semantics, status codes, versioning, pagination, filtering, idempotency
- `references/auth-and-session-patterns.md` - sessions vs bearer tokens, OAuth/OIDC, BFF, refresh tokens, machine clients
- `references/graphql-grpc-patterns.md` - bounded GraphQL and gRPC contract and compatibility patterns

## Output Contract

See `references/output-contract.md` for the full contract.

- **Skill name:** BACKEND-API
- **Deliverable bucket:** `audits`
- **Mode:** conditional. When invoked to **analyze, review, audit, or improve** existing repo content, apply the reporting size and evidence rules in `references/output-contract.md` and write the deliverable to `docs/local/audits/backend-api/<YYYY-MM-DD>-<slug>.md`. When invoked to **answer a question, teach a concept, build a new artifact, or generate content**, respond freely without the contract.
- **Severity scale:** `P0 | P1 | P2 | P3 | info` (see shared contract; only used in audit/review mode).

## Related Skills

- **databases** - schema design, query tuning, migrations, and persistence concerns behind the API
- **code-review** - correctness bugs, logic errors, and non-API-specific review findings
- **security-audit** - auth bypasses, OWASP findings, secrets, and security review after the API design exists
- **testing** - route, integration, contract, and end-to-end verification
- **docker** - containerizing API services
- **kubernetes** - deploying API services on clusters
- **networking** - reverse proxies, TLS termination, CORS-adjacent network boundaries, and gateway behavior
- **ci-cd** - delivery pipelines for API services
- **mcp** - MCP-specific HTTP servers and auth patterns
- **message-queues** - brokered async delivery, queues behind `202 Accepted` endpoints, and webhook fan-out via a broker

## Rules

1. **Contract before handlers.** Design the resource model, schemas, errors, and auth boundary before writing route code.
2. **Do not let transport leak persistence.** Database tables, ORM entities, and internal enums are not public API contracts.
3. **Keep framework structure boring.** Thin controllers or routes, explicit validation, explicit auth, centralized error handling.
4. **Backward compatibility is a feature.** New fields are cheap; breaking clients is expensive.
5. **Write for real retries.** Assume clients, proxies, and job runners will replay requests.
6. **Apply protocol-specific rules.** REST is the default focus; GraphQL and gRPC require their own
   schema, compatibility, deadline, and streaming checks from the linked reference.

Files in this skill

  • SKILL.md23 KB
  • references/agent-hygiene.md1.1 KB
  • references/auth-and-session-patterns.md4.3 KB
  • references/graphql-grpc-patterns.md2.3 KB
  • references/http-api-patterns.md5 KB
  • references/output-contract.md2.8 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…