UNPKG

create-hokage-js-app

Version:

🔥 Best CLI tool to create a MERN stack template. Quick, clean, and customizable.

200 lines (139 loc) • 8.98 kB
# Backend Rules Rules for server-side application code: HTTP adapters, services, persistence, jobs, and resilience. Pair with `architecture.md`, `api.md`, `database.md`, and `security.md`. --- ## Controllers / Route Handlers - Controllers may: parse/validate input (via schema), call one application service/use case, map result to HTTP status/body, set headers. - Controllers must not: contain business rules, open DB transactions directly, call multiple repositories, perform complex branching on domain state. - Keep handlers thin — typically under 30 lines excluding schema definitions. - One route handler → one primary use case. Fan-out orchestration belongs in the application service. - Never return ORM entities directly; map to response DTOs. **Why:** Fat controllers become untestable and duplicate rules across endpoints. --- ## Services / Use Cases - Own business orchestration, authorization decisions for the operation, and transaction boundaries. - Must not import HTTP framework request/response classes. - Must not embed raw SQL (use repositories). - Accept and return DTOs or domain types — not transport objects. - Keep side effects explicit: persistence, events, emails, metrics. - For multi-step workflows that can fail mid-way, define compensation or use transactional outbox + async continuation. --- ## Repositories - May only access persistence (DB, search index adapter for that aggregate, etc.). - No HTTP calls, no sending emails, no publishing events unless the project’s unit-of-work explicitly owns outbox writes in the same transaction. - Methods return domain objects or query models — not `DataFrame`-style unstructured bags without a type. - Never use `SELECT *` in hand-written SQL. List columns explicitly. - All list queries that can grow must accept a limit (and usually offset/cursor). - Encapsulate query details; callers should not assemble SQL fragments. --- ## Domain Models - Enforce invariants in constructors/factories (`Order.create(...)` throws/rejects on invalid lines). - Prefer value objects for Money, Email, PhoneNumber with validation once at creation. - Identity: use explicit IDs; do not rely on ORM dirty-checking alone for domain events. - Keep domain free of framework annotations when practicing Clean/Hexagonal architecture; if the project uses active-record style, document that exception and still keep HTTP out of models. --- ## Validation - Validate all external input at the boundary (HTTP, queue message, gRPC) with a schema (Zod, Joi, class-validator, Pydantic, Bean Validation, etc.). - Re-validate or enforce invariants again in domain when crossing into privileged operations. - Reject unknown fields on write APIs when using strict schemas (prevents mass-assignment). - Return 400 with field-level errors for validation failures — see `api.md`. - Never trust client-supplied `userId` for authorization; take identity from the authenticated principal. --- ## DTOs - Request DTOs: only fields the client may set. - Response DTOs: only fields the client may see; strip internal flags, password hashes, internal notes. - Separate create/update/response types when shapes differ. - Mapping functions are pure and unit-tested for critical fields. --- ## Authentication - Authenticate before authorization. - Prefer standard mechanisms already in the repo (session cookie, JWT bearer, mTLS). - Passwords: hash with Argon2id or bcrypt (cost calibrated); never store plaintext or reversible encryption for passwords. - Tokens: store refresh tokens hashed at rest; rotate on use when the project requires. - Session fixation: rotate session ID on login. - Clock skew: allow small leeway for JWT `exp`/`nbf` only as configured. --- ## Authorization - Check authorization in the application service (or a dedicated policy module called from it), not only in UI. - Prefer deny-by-default RBAC/ABAC policies. - Object-level auth: verify the principal owns or may access the resource (`order.customerId === principal.id` or policy service). - Never use “hidden URL” as access control. - Log authorization denials at `warn` with principal id and resource type/id (no sensitive payload). --- ## Transactions - Start transactions in the application service / unit of work, not in controllers. - Keep transactions short: no external HTTP calls inside an open DB transaction. - Set explicit isolation when required (e.g. `SERIALIZABLE` for contested inventory) and document why. - On failure, roll back fully; do not catch-and-continue leaving partial writes. - Use outbox table for events that must commit atomically with state changes. --- ## Background Jobs & Queues - Enqueue jobs with a stable `jobId` / idempotency key for operations that must not double-run. - Handlers must be idempotent: safe under at-least-once delivery. - Set visibility timeout / ack deadline > worst-case processing time. - Dead-letter failed messages after max retries; alert on DLQ depth. - Do not process unbounded payloads in-memory; stream or chunk large files. - Pass identifiers in messages, not giant snapshots, unless snapshot is required for consistency. --- ## Redis & Caching - Every cache key includes a version or namespace prefix: `{service}:{entity}:{id}:v{n}`. - Set TTL on all keys unless using explicit persistent Redis data structures with documented lifecycle. - Cache only data that is safe to serve stale for TTL duration, or use explicit invalidation on write. - Never cache unauthorized personalized data under a key that omits user/tenant id. - Protect against cache stampede: singleflight / locking / probabilistic early expiration for hot keys. - Treat Redis as best-effort for cache; source of truth remains the primary database unless Redis is the designated store for that data structure (e.g. rate limit counters). --- ## Pagination, Filtering, Search - Default page size: 20 (or project default). Max page size: 100 unless a justified export endpoint uses streaming/async jobs. - Prefer cursor pagination for large/stable feeds; offset is acceptable for small admin UIs. - Filtering: whitelist allowed fields and operators; never accept raw SQL/`order by` strings from clients. - Search: use parameterized full-text or search engine APIs; escape user input per engine rules. - Return pagination metadata: `nextCursor` / `total` only if cheap; avoid exact `COUNT(*)` on huge tables when approximate is enough. --- ## File Uploads - Validate content type by magic bytes, not only `Content-Type` header. - Enforce max size at reverse proxy and application. - Store files outside the web root (object storage). Serve via signed URLs. - Generate server-side object keys; never use raw user filenames as storage paths without sanitization. - Scan for malware when policy requires. - Do not process ZIP bombs / unbounded decompression in request threads. --- ## Rate Limiting - Apply rate limits per principal and per IP at the edge or gateway. - Return `429` with `Retry-After` when limited. - Separate limits for authenticated vs anonymous traffic. - Expensive endpoints (search, export, AI, password reset) get stricter limits. --- ## Idempotency - Mutating endpoints that may be retried by clients (`POST` payments, provisioning) accept `Idempotency-Key` header. - Persist key + request hash + response for a documented retention window. - Replays with same key + same body return the original result; same key + different body return `409`. - Idempotency keys are scoped per principal. **Why:** Mobile and gateway retries duplicate charges without idempotency. --- ## Retry Policies - Retry only idempotent side effects, or retries guarded by idempotency keys. - Exponential backoff with jitter; cap attempts (e.g. 3–5 for sync HTTP). - Do not retry `400`/`401`/`403`/`422`; do retry `408`/`429`/`502`/`503`/`504` with backoff. - Propagate correlation IDs on retries. --- ## Circuit Breakers - Wrap calls to unreliable dependencies (payments, email, partner APIs) with circuit breakers. - Open circuit → fail fast or degraded mode; do not pile up threads. - Emit metrics on open/half-open/close transitions. - Timeouts must be shorter than upstream SLOs chain budgets. --- ## Configuration - Read config from environment / secret manager at startup; fail fast on missing required vars. - Never invent environment variable names — use those documented in the repo. - Feature flags default to safe-off for risky paths. --- ## Backend Checklist (Inline) - [ ] Controller thin; business logic in service - [ ] Input validated; authn/authz enforced - [ ] Repository has no business rules; no `SELECT *` - [ ] Transactions short; no remote calls inside - [ ] External calls: timeout + retry policy + circuit breaker where needed - [ ] Mutating retried operations are idempotent - [ ] Cache keys include tenant/user when needed; TTLs set - [ ] Uploads validated and stored safely - [ ] No secrets in code; config from env/secret manager