File Convention
How QUESTPIE discovers your files and wires them into the runtime — naming rules, layouts, and discovery mechanics.
QUESTPIE uses your file system as the source of truth. Drop a file in the right directory, run codegen, and it's part of your app. No manual registration.
Directory Categories
Each directory maps to a category of entity:
| Directory | Entity | Export Style | Key Derivation |
|---|---|---|---|
collections/ | Collections | Default or named | Filename → camelCase |
globals/ | Globals | Default or named | Filename → camelCase |
routes/ | Routes | Default | Filename → camelCase/slash path |
jobs/ | Jobs | Default | Filename → camelCase |
routes/ (raw) | Routes | Default | Filename → slash-separated path |
services/ | Services | Default | Filename → camelCase |
emails/ | Email templates | Default | Filename → camelCase |
blocks/ | Blocks | Named exports | Factory arg → export name |
messages/ | i18n messages | Default | Filename → locale key |
migrations/ | DB migrations | Default | Array (ordered) |
seeds/ | DB seeds | Default | Array (ordered) |
Name derivation
Filenames are converted from kebab-case to camelCase:
blog-posts.ts → blogPosts
create-booking.ts → createBooking
site-settings.ts → siteSettingsSingle-File Conventions
Some configs are single files, not directories:
| File | Factory | Purpose |
|---|---|---|
questpie.config.ts | runtimeConfig({...}) | DB, plugins, adapters |
modules.ts | export default [...] | Module dependencies |
auth.ts | satisfies AuthConfig | Auth configuration |
locale.ts | locale({...}) | Content locales |
hooks.ts | export default { collections, globals } | Global lifecycle hooks (applied across all entities) |
access.ts | export default { read, create, update, delete } | Default access rules for all entities |
config/app.ts | export default appConfig({...}) | App-level config (context, locale, tenant scoping) |
fields.ts | export default { ...customFields } | Custom field type definitions |
sidebar.ts | sidebar({...}) | Admin sidebar |
dashboard.ts | dashboard({...}) | Admin dashboard |
branding.ts | branding({...}) | Admin branding |
admin-locale.ts | adminLocale({...}) | Admin UI locale |
Layouts
By-Type (default)
Group files by entity type:
src/questpie/server/
├── collections/
│ ├── posts.ts
│ ├── categories.ts
│ └── comments.ts
├── routes/
│ ├── create-booking.ts
│ └── get-stats.ts
├── jobs/
│ └── send-email.ts
└── services/
└── blog.tsBy-Feature
Group files by domain:
src/questpie/server/
├── features/
│ ├── blog/
│ │ ├── collections/
│ │ │ └── posts.ts
│ │ ├── routes/
│ │ │ └── publish.ts
│ │ └── jobs/
│ │ └── notify-subscribers.ts
│ └── booking/
│ ├── collections/
│ │ └── appointments.ts
│ └── routes/
│ └── create-booking.tsMixed
Both layouts can coexist. Codegen scans all configured paths.
Nested Namespacing
Routes support nested directories:
routes/
├── booking/
│ ├── create.ts → client.routes.booking.create()
│ └── cancel.ts → client.routes.booking.cancel()
├── admin/
│ └── stats.ts → client.routes.admin.stats()
└── get-barbers.ts → client.routes.getBarbers()Discovery Process
- Scan — Codegen walks the configured directories
- Match — Files matching the category pattern are picked up
- Import — Each file is imported and its exports are read
- Key derivation — Factory string arg → export name → filename (camelCase)
- Merge — Project entities + module entities are merged
- Emit — Generated types and runtime wiring in
.generated/
The #questpie Import
Collection and global files use #questpie as an import alias:
import { collection } from "#questpie/factories";This resolves to the generated app context, giving the builders access to field types and relation targets for autocompletion. It's configured via TypeScript path mapping.
Related Pages
- Codegen — What gets generated
- Modules — Module composition
- Plugins — Plugin system
- Project Structure — Full layout reference