--- description: Fastify backend, API design, Prisma, auth, and error handling globs: apps/api/**/* alwaysApply: false --- You are working on the backend in `apps/api`. ## Backend framework - Use Fastify as the default backend framework. - Prefer Fastify plugins and encapsulation patterns over ad hoc global behavior. ## API design - Keep business logic out of route handlers. - Route handlers should be thin and focused on: - validation - auth/context - calling application services - shaping responses - Prefer explicit service-layer or domain-layer boundaries for business logic. ## Validation and contracts - Validate all incoming requests with Zod or approved schema wrappers. - Validate file uploads and all user-controlled input. - Avoid trusting frontend input. - Use explicit schemas for request and response shapes where practical. ## API documentation - Maintain Swagger/OpenAPI support. - New or changed endpoints should update the API schema/docs as part of the work. - Keep API docs accurate and usable. ## Data access - Prefer Prisma as the default ORM unless explicitly directed otherwise. - Use Postgres as the primary relational database. - Store relational/domain data in Postgres. - Store document/image metadata in Postgres and file binaries in appropriate storage. - Avoid leaking raw ORM/database logic into unrelated layers. ## Auth and permissions - Roles and permissions are first-class concerns. - Design with support for: - board members - treasurers - owners - tenants - future administrative roles - Support passwordless authentication patterns: - magic link - OIDC - passkeys - Do not hard-code assumptions that one deployment always maps to one HOA. - Self-hosted mode supports single tenancy. - SaaS mode must remain compatible with future or current multi-tenant architecture decisions. ## Auditing - All destructive or sensitive actions must be auditable. - Deletions and other important changes should be recorded in an audit log. - Prefer soft-delete or explicit audit-aware flows where appropriate. - Do not implement silent destructive behavior. ## Error handling - Use explicit, typed, and user-safe error handling. - Never use silent catch blocks. - Do not swallow errors. - Return structured, predictable error responses. **Example:** ```typescript // BAD: silent catch try { await doSomething(); } catch {} // GOOD: log, type, rethrow or return structured error try { await doSomething(); } catch (e) { logger.error({ err: e }, 'doSomething failed'); throw new ServiceError('Operation failed', { cause: e }); } ```