Skip to content

Full-Stack Extension (/full-stack-extension) — express

Stack flavor: express. ← Back to overview

[!TIP] Use this for modifying or extending anything that already exists — adding fields, actions, tabs, charts, filters, or behavior to existing pages, components, or endpoints. For a brand new feature, use /feature-scaffold instead.

Trigger Conditions

  • Any request to modify, extend, update, enhance, change, or add to existing functionality
  • Keywords: "Add a field", "Show X on this screen", "Implement saving", "Add a column", "Add a button", "Add a filter", "Change the behavior of", "Update the form", "Extend this page", "Add an action", "Modify the..."
  • Also trigger when: the user references an existing page/component and wants something new on it, or when backend + frontend changes are needed together

1. Data Analysis

  • [ ] DB Check: Does new data need to be stored?
  • Yes → run /db-migration before continuing
  • No → proceed to step 2
  • [ ] API Check: Is the existing endpoint sufficient, or do we need a new route/field in the response?

2. Backend Extension

  • [ ] Schema: If adding a field, update backend/src/db/schema.ts (then run /db-migration)
  • [ ] Drizzle Query: Update the query in backend/src/routes/[resource].ts to select/include new fields
  • [ ] Route / Validation: Update the Express route or Zod schema in backend/src/validation/ to handle new input/output
  • [ ] Causal order: Ensure side effects are awaited with Promise.allSettled() before res.json()

3. Frontend Service

  • [ ] API Client: Update the method in services/domains/[resource]Service.ts (or services/apiClient.ts) to reflect the changed API contract
  • [ ] Types: Update TypeScript interfaces if the response shape changed

4. UI Implementation

  • [ ] Hook: Update the relevant domain hook in hooks/use[Resource].ts if the query shape changed
  • Invalidate related queries in onSuccess if other views show this data
  • [ ] Mutation: Use mutateAsync (not mutate) when UI state depends on outcome:
    const handleSave = async () => {
        await updateMutation.mutateAsync({ id, data });
        onClose(); // Only runs after server confirms
    };
    
  • [ ] Component: Add the UI elements (inputs, displays, etc.)
  • Currency: use <FormattedCurrency /> (nl-NL, red for negatives)
  • Dates: use formatDateLocal() from lib/utils
  • Selects: use SearchableSelect, never native <select>
  • Negative values: must show in red (text-destructive)

5. Tests (REQUIRED)

  • [ ] Run /test-sync to automatically update tests for all changed files
  • [ ] Verify: All tests pass before proceeding to review

6. Review

  • [ ] Compliance: Run /architecture-review — especially check button colors and rounded corners
  • [ ] Feedback: Ensure loading uses shimmer effect and success shows a Toast
  • [ ] Types: Run npm run type-check && cd backend && npm run type-check