Application Slices Guide
Use this guide when adding a business feature to a FaasJS app or asking an AI coding agent to build one.
A FaasJS application slice is a small, complete vertical feature that keeps UI, API, validation, database changes, CLI tools, jobs, types, and tests easy to find and review together. Slices replace generator-heavy workflows by giving humans and agents stable conventions to follow directly.
What A Slice Includes
A database-driven business slice usually includes:
- a feature folder under
src/features/<feature>/ - an optional React entry such as
src/features/<feature>/index.tsx - components, hooks, services, schemas, CLI tools, or jobs owned by that feature
- one or more
.api.tsfiles under the feature'sapi/directory when the feature exposes backend actions - inline
defineApischemas for boundary validation - PostgreSQL migrations under
src/db/migrations/**when data shape changes @faasjs/pgtable declarations undersrc/db/tables/**- API tests near the API files
- PG assertions when database behavior matters
- React tests when UI request flow, loading, errors, or user interaction matters
Keep the slice small enough that a reviewer can understand the behavior without jumping through unrelated framework layers.
Recommended Layout
The admin starter generated by create-faas-app includes a copyable users slice. For a users slice, prefer a feature-based layout like:
src/db/migrations/20250101000000-create-users.ts
src/db/tables/users.ts
src/features/users/index.tsx
src/features/users/components/UserTable.tsx
src/features/users/hooks/useUserItems.ts
src/features/users/api/list.api.ts
src/features/users/api/create.api.ts
src/features/users/api/__tests__/list.test.ts
src/features/users/api/__tests__/create.test.ts
For nested capabilities, keep them under the owning feature instead of moving them to a detached route, API, or tools tree:
src/features/settings/users/index.tsx
src/features/settings/users/api/list.api.ts
src/features/settings/users/api/update-role.api.ts
src/features/settings/users/api/__tests__/update-role.test.ts
Use short relative imports inside the slice unless an existing TypeScript alias is already configured. Remember that .api.ts and .job.ts paths are externally visible: src/features/users/api/list.api.ts maps to /features/users/api/list, and src/features/users/jobs/sync.job.ts maps to features/users/jobs/sync.
Do not centralize tests in a catch-all package-level src/__tests__ folder. This includes src/__tests__/<feature> sub folders: the feature folder itself should own __tests__. Each feature, slice, API folder, job folder, helper folder, or component folder should own its own __tests__ folder so tests stay attached to the behavior they protect.
When a slice would otherwise be a single business file, turn that file into a folder module and keep its tests under that folder's __tests__:
# Avoid
src/useBilling.ts
src/__tests__/useBilling.test.ts
src/__tests__/useBilling/useBilling.test.ts
# Prefer
src/features/billing/index.ts
src/features/billing/__tests__/billing.test.ts
API, Validation, And Security
Use defineApi for every API entrypoint.
Keep schemas close to the handler unless they are reused across multiple APIs or form a real boundary. Prefer explicit params, explicit returning columns, and narrow response shapes so UI code and tests stay type-aware.
Before reading or mutating data, check whether the slice needs current-user, tenant, organization, project, role, or permission scoping. Put cross-cutting business context in project plugins, then type the injected fields so handlers stay explicit.
Use explicit HTTP status codes for expected business, auth, permission, missing-resource, and conflict failures. Reserve plain unexpected failures for real internal errors.
Avoid generic helpers that hide validation, authorization, database access, or response contracts. A little repetition is acceptable when it makes the slice easier for agents and reviewers to understand.
Database Changes
When a slice changes data shape:
- add a timestamped migration under
src/db/migrations/** - update
@faasjs/pgtable declarations undersrc/db/tables/<table_name>.ts - keep table row types concrete
- test important database behavior with
@faasjs/pg-dev
Do not rely on broad Record<string, any> row shapes for business tables.
UI Changes
Use React and the FaasJS Ant Design path for business UI.
Prefer @faasjs/ant-design wrappers such as Form, Table, Description, Title, Tabs, Loading, and ErrorBoundary when they cover the scenario. Drop to raw Ant Design components only when the wrapper does not fit or the custom UI is clearer.
Keep feature UI state and request flow readable. After create, update, or delete flows, intentionally refresh, close, or invalidate the affected surface and show user feedback. Extract components, hooks, or helpers only when they are reused, create a real boundary, or simplify a large block.
Tests
Put API tests under the API folder's __tests__.
For non-API tests, use the same feature-local layout principle: place React, job, CLI, helper, or integration tests in the __tests__ folder inside the feature, job, utility, or slice folder they cover instead of centralizing them under a detached package-level test tree.
Test the behavior that defines the slice:
- valid input and expected output
- validation failures and important error paths
- database writes, reads, ordering, and counts when relevant
- permission, tenant, or current-user behavior when a plugin or scoped query affects the API
- UI request flow when the page contains meaningful interaction
Avoid wide mocks that bypass FaasJS plugins, validation, or database behavior unless the test is intentionally unit-scoped.
Agent Workflow
When asking an AI coding agent to add or change a feature, describe the slice instead of asking for a generator command.
Good prompt shape:
Add a users feature with a list UI, create API, PostgreSQL migration, table types, and API tests. Follow the FaasJS curated stack and keep files under src/features/users.
The agent should:
- inspect nearby slices and guides first
- create or update all files needed for the vertical behavior
- include auth, tenant, and permission scope when the data is user- or organization-specific
- keep business-specific concerns in app code or plugins
- run the smallest meaningful tests
- avoid unrelated refactors
Why Not Rails-style Generators
Rails generators help humans create repetitive structure. FaasJS assumes AI coding agents can write that structure directly when conventions, examples, schemas, and tests are clear.
Invest in better slices, examples, and docs before adding generator commands. Add a generator only when it clearly improves the curated path and cannot be replaced by better conventions or examples.