Backend Service Skill
When to use
- Designing or implementing a new API surface (REST, GraphQL, gRPC, WebSocket)
- Adding database models, migrations, or data-access layers
- Implementing auth (sessions, JWT, OAuth 2.0, API keys)
- Building background jobs, queues, cron tasks, or event consumers
- Integrating third-party services (payment providers, email, storage, AI APIs)
- Preparing a service for production: observability, rate limiting, graceful shutdown
Workflow
Classify the project — Read
docs/specs/product-spec.mdand.claude/stack-matrix/backend.mdto confirm language/runtime, API style, database(s), and deployment target (container, serverless, VPS). If these don't exist, interview the founder first (.claude/agents/core/orchestrator.md).Define the API contract first — Write the contract before any implementation:
- REST: OpenAPI 3.1 YAML with request/response schemas, error codes, and auth scheme
- GraphQL: SDL schema with descriptions on every type and field
- gRPC:
.protofile with service definition and message types Store the contract atdocs/api/and reference it inREADME.md. Never break a published contract without versioning.
Design the data model — Write the schema (SQL DDL or ODM schema) before coding:
- Name tables/collections in singular snake_case (e.g.
user,order_item) - Add
created_at,updated_attimestamps to every mutable table - Choose primary key type deliberately: UUID v7 (time-sortable) for distributed systems, auto-increment for simple single-DB apps
- Write migrations with a migration tool (Alembic, Flyway, Prisma Migrate, golang-migrate); never alter production schema manually
- Name tables/collections in singular snake_case (e.g.
Implement the data-access layer — Centralise all DB queries in a repository or DAO layer:
- No raw SQL in route handlers or controllers
- Use parameterised queries / ORM — never string-interpolate user input into SQL
- Write read replicas logic separately from primary writes if applicable
Implement auth and authorisation
- Auth (who are you?): prefer an established library or managed identity provider (Auth0, Supabase Auth, Keycloak) over hand-rolled JWT logic
- Authorisation (what can you do?): enforce at the service layer, not only the API layer; implement RBAC or ABAC as the spec requires
- Rotate secrets on a schedule; invalidate tokens on logout (maintain a denylist or use short expiry + refresh)
Reference
.claude/skills/security/SKILL.md.
Handle errors consistently — All error responses must follow a single shape:
{ "error": { "code": "MACHINE_READABLE_CODE", "message": "Human message", "details": {} } }Use HTTP status codes correctly: 400 for client errors, 401 unauthenticated, 403 forbidden, 404 not found, 422 validation, 429 rate-limited, 500 server errors.
Add input validation — Validate and sanitise every inbound payload at the API boundary before it reaches any business logic. Use a schema validator (Zod, Pydantic,
class-validator,ozzo-validation). Reject unknown fields.Implement background jobs — Use a dedicated queue (BullMQ, Celery, Sidekiq, Temporal, AWS SQS) rather than spawning goroutines/threads in the request handler:
- Jobs must be idempotent — safe to retry on failure
- Store job state and completion receipts; expose a status endpoint
- Set dead-letter queues for unprocessable messages
Add observability
- Structured JSON logging with trace-id, request-id, user-id on every log line
- Metrics: request count, latency p50/p95/p99, error rate, queue depth — expose as Prometheus endpoint or push to Datadog/Grafana Cloud
- Distributed tracing: instrument with OpenTelemetry; attach spans to DB queries, outbound HTTP calls, and queue operations
Reference
.claude/checklists/production.md.
Implement rate limiting — Apply at the API gateway or service layer per (user, IP, or API key). Return
Retry-Afterheader on 429 responses. Separate limits for auth endpoints (stricter) vs. read endpoints.Write tests
- Unit: pure functions, validators, business-logic services (mock the DB)
- Integration: API routes with a real (test) database using transactions rolled back after each test
- Contract: Pact or Dredd tests to verify the API matches the published spec
Reference
.claude/checklists/qa.md.
Deploy safely — Use the deploy sequence from
.claude/agents/core/orchestrator.md:- Run migrations before deploying new code (additive-only migrations are safe; destructive ones require a multi-phase deploy)
- Implement graceful shutdown: stop accepting new requests, drain in-flight requests, close DB pool
- Use readiness and liveness probes for container orchestrators
Standards
Do
- Return pagination metadata (
total,page,limit,next_cursor) on all list endpoints. - Cache expensive read queries at the service layer (Redis, Memcached) with explicit TTLs; invalidate on write.
- Use connection pooling for all databases; configure pool size to match server resources, not to unlimited.
- Set timeouts on all outbound HTTP calls (default client timeout is often infinite).
- Prefer async I/O; block threads only when the library forces it and document why.
- Keep environment-specific config in env vars; use a validated config loader at startup so the service fails fast on misconfiguration.
Do not
- Log passwords, tokens, PII, or card numbers — use field masking in your log sanitiser.
- Return stack traces in production API responses — log them server-side only.
- Use
SELECT *in production queries — select explicit columns;*breaks when columns are added. - Perform database migrations inside the application startup path — run migrations as a separate pre-deploy step.
- Hard-code retry counts or timeouts as magic numbers in business code — put them in config.
- Trust
Content-Typewithout validating the actual payload.
Common mistakes to avoid
- N+1 queries — loading a list then fetching related rows per item in a loop; use eager-loading (
JOIN,include,prefetch_related) or batch fetching. - Missing database indexes — any column used in a
WHERE,ORDER BY, orJOIN ONclause in a hot query needs an index; runEXPLAIN ANALYZEto verify. - Unbounded response payloads — list endpoints without pagination return millions of rows on a mature system; always default to a sensible
limit(e.g. 25). - Silent job failures — a background job that fails and logs nothing is invisible; always catch exceptions, log with context, and update job status to
failed. - Secrets in environment variables logged at startup —
console.log(process.env)or equivalent dumps all secrets; redact them explicitly. - CORS misconfiguration —
Access-Control-Allow-Origin: *on authenticated endpoints; restrict to known origins. - Forgetting idempotency keys on payment/mutation endpoints — duplicate requests (network retry, user double-click) must not create duplicate charges or records.
Output format
A production-ready backend service should include:
/
├── src/ (or app/, cmd/)
│ ├── api/ # Route handlers / controllers
│ ├── services/ # Business logic
│ ├── repositories/ # Data-access layer
│ ├── models/ # Domain models / DB schema types
│ ├── jobs/ # Background job definitions
│ ├── middleware/ # Auth, logging, rate-limit middleware
│ └── config/ # Validated env-var config loader
├── migrations/ # Database migration files (in order)
├── tests/
│ ├── unit/
│ └── integration/
├── docs/
│ └── api/ # OpenAPI / GraphQL SDL / proto files
├── .env.example
├── Dockerfile
└── README.md # Local dev setup, migration steps, test commands
Related checklists
.claude/checklists/security.md.claude/checklists/performance.md.claude/checklists/qa.md.claude/checklists/production.md
Related skills
.claude/skills/api-design/SKILL.md— owns the API contract (resources, versioning, error taxonomy, pagination). Design the contract there first; this skill covers its implementation.
Related agents
.claude/agents/core/orchestrator.md.claude/agents/quality/security-auditor.md.claude/agents/engineering/database-architect.md