Shipflash logoDocs

Customization Recipes

Recipes tell buyers how to turn the foundation into their own SaaS while preserving source-of-truth code, tests, and operational safety.

On this page

Recipe: add a dashboard page

Add a dashboard surface without mixing route entrypoint code, UI state, server reads, and access checks.

Inspect

  • src/lib/auth/guards/route.ts
  • src/lib/auth/rbac/nav-config.ts
  • src/features/settings/ui/section.tsx

Edit

  • src/app/(dashboard)/<route>/page.tsx
  • src/features/<domain>/ui/<page>.tsx
  • src/features/<domain>/server/queries/<query>.ts
  • src/lib/auth/rbac/nav-config.ts

Steps

  • Create a thin route page that checks access and delegates UI.
  • Put screen composition in feature UI.
  • Put server reads in feature queries or services.
  • Add navigation only after the route is protected.
  • Add focused tests for access and server behavior when practical.

Verify

  • pnpm run test:features
  • pnpm run quality

Recipe: add an admin module

Add admin-only work with fail-closed access, auditability, and predictable navigation.

Inspect

  • src/lib/auth/guards/route.ts
  • src/lib/auth/rbac/features.ts
  • src/lib/auth/rbac/nav-config.ts
  • src/lib/audit/log.ts

Edit

  • src/app/(dashboard)/admin/<module>/page.tsx
  • src/features/<domain>/server/actions/<action>.ts
  • src/features/<domain>/ui/<module>.tsx
  • src/lib/auth/rbac/nav-config.ts

Steps

  • Define the feature key and route access before adding UI.
  • Gate the route on the server.
  • Keep admin actions server-owned and validate inputs.
  • Log important changes with actor and target context.
  • Add navigation after access is correct.

Verify

  • pnpm run test:features
  • pnpm run quality

Recipe: add an analytics card

Add dashboard analytics without creating provider calls in render loops or unbounded database scans.

Inspect

  • src/features/analytics/server/queries/platform-metrics.ts
  • src/features/analytics/server/queries/revenue-dashboard-data.ts
  • src/features/analytics/shared/chart-config.ts
  • src/features/analytics/ui/platform-dashboard.tsx

Edit

  • src/features/analytics/server/queries/<metric>.ts
  • src/features/analytics/shared/chart-config.ts
  • src/features/analytics/ui/<card>.tsx

Steps

  • Define the metric and date range.
  • Aggregate on the server with bounded reads.
  • Return only the fields the chart needs.
  • Render empty and loading states.
  • Add tests for range behavior and empty data.

Verify

  • pnpm run test:features
  • pnpm run quality

Recipe: add a database table

Add durable product data without leaving schema, generated types, RLS, grants, and feature persistence out of sync.

Inspect

  • supabase/migrations/20260426000000_init.sql
  • src/lib/supabase/server.ts
  • src/lib/supabase/types.ts

Edit

  • supabase/migrations/<timestamp>_<table>.sql
  • src/lib/supabase/types.ts
  • src/features/<domain>/server/persistence/<table>.ts
  • src/features/<domain>/server/schemas/<table>.ts

Steps

  • Define ownership, uniqueness, status, timestamps, and foreign keys before writing the migration.
  • Add the table, constraints, RLS policies, and grants in a migration.
  • Regenerate generated Supabase types.
  • Keep reads and writes in feature-owned persistence or query files.
  • Add tests for invalid input, auth failures, and duplicate writes when relevant.

Verify

  • pnpm run contract:migrations
  • pnpm run supabase:types
  • pnpm run test:features
  • pnpm run quality

Common mistakes

  • Do not expose a new table before deciding its RLS policy.
  • Do not put one-off seed or repair data into durable schema migrations.

Recipe: add a protected API route

Add an API boundary that validates input, fails closed on auth, delegates real work, and returns a stable response shape.

Inspect

  • src/lib/auth/guards/api.ts
  • src/app/api/billing/checkout/route.ts
  • src/features/billing/server/checkout/service.ts
  • src/features/contact/shared/schemas.ts
  • src/features/contact/server/services.ts

Edit

  • src/app/api/<feature>/route.ts
  • src/features/<domain>/server/services/<use-case>.ts
  • src/features/<domain>/server/schemas/<payload>.ts

Steps

  • Keep the route handler thin: parse request, call the auth guard, validate payload, delegate to a service, and return the response.
  • Put business rules in the service, not the route.
  • Validate request bodies with a feature-owned schema.
  • Return safe error copy without leaking provider payloads or secrets.
  • Add API or feature tests for unauthenticated, invalid, and successful requests.

Verify

  • pnpm run test:api
  • pnpm run test:features
  • pnpm run quality

Common mistakes

  • Do not trust user ids from request JSON when server identity is available.
  • Do not call provider SDKs directly from generic route glue.

Recipe: add an email template

Add a transactional message without bypassing event definitions, runtime rendering, delivery registration, or outbox retry behavior.

Inspect

  • src/features/notifications/server/notification-events.ts
  • src/features/notifications/server/templates/shared/defaults.ts
  • src/features/notifications/server/templates/services/runtime.ts
  • src/features/notifications/server/templates/actions/management.ts
  • src/features/notifications/server/delivery/registry.ts

Edit

  • src/features/notifications/server/notification-events.ts
  • src/features/notifications/server/templates/shared/defaults.ts
  • src/features/notifications/server/templates/shared/types.ts

Steps

  • Add or select the notification event before writing template content.
  • Define default subject, body, and required variables.
  • Confirm runtime rendering validates variables and produces the expected output.
  • Send through the existing delivery and outbox path.
  • Add focused tests for missing variables, rendered content, and delivery registration.

Verify

  • pnpm run test:features
  • pnpm run quality

Common mistakes

  • Do not send directly from feature code when the outbox should own retries.
  • Do not log rendered messages if they can contain user data.

Recipe: add a billing product

Add a product or price without breaking provider setup, checkout eligibility, customer projection, or webhook handling.

Inspect

  • src/features/billing/server/runtime/provider.ts
  • src/features/billing/server/catalog/configuration.ts
  • src/features/billing/server/runtime/readiness.ts
  • src/features/billing/server/checkout/service.ts
  • src/app/api/webhooks/stripe/billing/route.ts

Edit

  • BILLING_CATALOG_JSON
  • BILLING_ENABLED_MODELS
  • src/features/billing/server/catalog/configuration.ts

Steps

  • Choose the billing model and provider before editing catalog data.
  • Add provider price ids and product metadata to the catalog.
  • Confirm setup checks understand the new item.
  • Run a test checkout for the new price.
  • Confirm webhook projection updates the local customer billing state.

Verify

  • pnpm run test:features
  • pnpm run quality
  • Open /billing/setup and complete a provider test checkout.

Common mistakes

  • Do not mix provider price ids across providers.
  • Do not skip webhook verification after checkout succeeds.

Recipe: add a settings field

Add configurable product behavior without splitting validation, defaults, normalization, persistence, and UI state.

Inspect

  • src/features/settings/server/actions/website-settings.ts
  • src/features/settings/server/schemas/settings.ts
  • src/features/settings/server/normalizer/settings-normalizer.ts
  • src/features/settings/server/config/defaults.ts
  • src/features/settings/server/persistence/settings.ts
  • src/features/settings/ui/website-form.tsx

Edit

  • src/features/settings/server/schemas/settings.ts
  • src/features/settings/server/config/defaults.ts
  • src/features/settings/server/normalizer/settings-normalizer.ts
  • src/features/settings/ui/website-form.tsx

Steps

  • Add the field to the server schema and define its safe default.
  • Normalize persisted values before returning settings to UI.
  • Update the settings form with loading, invalid, and saved states.
  • Persist through the existing settings action.
  • Add tests for defaulting, invalid values, and updates.

Verify

  • pnpm run test:features
  • pnpm run quality

Common mistakes

  • Do not rely on UI-only validation.
  • Do not introduce fallback env names for settings that should be database-backed.

Recipe: add a waitlist field

Capture a new lead attribute while preserving validation, duplicate handling, settings, notifications, and rate limits.

Inspect

  • src/features/waitlist/server/signup/schemas/payload.ts
  • src/features/waitlist/server/signup/actions/submit-route.ts
  • src/features/waitlist/server/signup/services/submit.ts
  • src/features/waitlist/server/signup/persistence/signups.ts
  • src/features/waitlist/server/settings.ts
  • src/lib/security/rate-limit/policy.ts

Edit

  • supabase/migrations/<timestamp>_waitlist_field.sql
  • src/features/waitlist/server/signup/schemas/payload.ts
  • src/features/waitlist/server/signup/services/submit.ts
  • src/features/waitlist/server/signup/persistence/signups.ts

Steps

  • Decide whether the field is required, optional, or admin-only.
  • Add durable storage and regenerate generated types if the field is persisted.
  • Update payload validation and persistence mapping together.
  • Preserve duplicate signup handling and notification side effects.
  • Add tests for missing, invalid, duplicate, and successful submissions.

Verify

  • pnpm run contract:migrations
  • pnpm run supabase:types
  • pnpm run test:features
  • pnpm run quality

Common mistakes

  • Do not store unbounded free-form text without validation.
  • Do not bypass rate limits when adding a new public field.

Recipe: remove a module safely

Remove an unwanted module without leaving routes, nav items, schema, jobs, webhooks, env vars, tests, or docs pointing at dead behavior.

Inspect

  • src/lib/auth/rbac/nav-config.ts
  • src/lib/auth/rbac/features.ts
  • src/features/settings/server/actions/website-settings.ts
  • src/features/notifications/server/notification-events.ts
  • supabase/migrations/20260426000000_init.sql

Edit

  • src/app/<route>
  • src/features/<domain>
  • src/lib/auth/rbac/nav-config.ts
  • src/lib/auth/rbac/features.ts
  • supabase/migrations/<timestamp>_<removal>.sql

Steps

  • Inventory public routes, dashboard routes, API routes, feature code, settings, jobs, webhooks, env vars, database tables, tests, and docs references.
  • Disable navigation and access before deleting shared data or provider behavior.
  • Remove or migrate durable data with an explicit migration plan.
  • Delete tests only after replacing them with coverage for the intended absence when the boundary is risky.
  • Run search for the module name and route paths before shipping.

Verify

  • pnpm run test:features
  • pnpm run test:contracts
  • pnpm run quality

Common mistakes

  • Do not remove only the UI and leave API routes or jobs active.
  • Do not drop tables without a buyer-owned data export or migration decision.