create-questpie 2.0.4 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/dist/index.mjs +362 -119
  2. package/package.json +2 -3
  3. package/templates/elysia/AGENTS.md +56 -0
  4. package/templates/elysia/CLAUDE.md +39 -0
  5. package/templates/elysia/Dockerfile +24 -0
  6. package/templates/elysia/README.md +148 -0
  7. package/templates/elysia/docker/init-extensions.sql +11 -0
  8. package/templates/elysia/docker-compose.yml +21 -0
  9. package/templates/elysia/env.example +16 -0
  10. package/templates/elysia/gitignore +6 -0
  11. package/templates/elysia/package.json +47 -0
  12. package/templates/elysia/questpie.config.ts +12 -0
  13. package/templates/elysia/src/index.ts +21 -0
  14. package/templates/elysia/src/lib/auth-client.ts +32 -0
  15. package/templates/elysia/src/lib/client.ts +13 -0
  16. package/templates/elysia/src/lib/env.ts +24 -0
  17. package/templates/elysia/src/lib/query-client.ts +18 -0
  18. package/templates/elysia/src/lib/query.ts +18 -0
  19. package/templates/elysia/src/questpie/server/.generated/context.gen.ts +200 -0
  20. package/templates/elysia/src/questpie/server/.generated/entities.gen.ts +84 -0
  21. package/templates/elysia/src/questpie/server/.generated/factories.ts +65 -0
  22. package/templates/elysia/src/questpie/server/.generated/index.ts +131 -0
  23. package/templates/elysia/src/questpie/server/.generated/names.gen.ts +25 -0
  24. package/templates/elysia/src/questpie/server/app.ts +10 -0
  25. package/templates/elysia/src/questpie/server/collections/index.ts +1 -0
  26. package/templates/elysia/src/questpie/server/collections/posts.collection.ts +10 -0
  27. package/templates/elysia/src/questpie/server/config/auth.ts +8 -0
  28. package/templates/elysia/src/questpie/server/config/openapi.ts +10 -0
  29. package/templates/elysia/src/questpie/server/globals/index.ts +1 -0
  30. package/templates/elysia/src/questpie/server/globals/site-settings.global.ts +10 -0
  31. package/templates/elysia/src/questpie/server/modules.ts +8 -0
  32. package/templates/elysia/src/questpie/server/questpie.config.ts +21 -0
  33. package/templates/elysia/tsconfig.json +28 -0
  34. package/templates/hono/AGENTS.md +56 -0
  35. package/templates/hono/CLAUDE.md +39 -0
  36. package/templates/hono/Dockerfile +24 -0
  37. package/templates/hono/README.md +148 -0
  38. package/templates/hono/docker/init-extensions.sql +11 -0
  39. package/templates/hono/docker-compose.yml +21 -0
  40. package/templates/hono/env.example +16 -0
  41. package/templates/hono/gitignore +6 -0
  42. package/templates/hono/package.json +47 -0
  43. package/templates/hono/questpie.config.ts +12 -0
  44. package/templates/hono/src/index.ts +30 -0
  45. package/templates/hono/src/lib/auth-client.ts +32 -0
  46. package/templates/hono/src/lib/client.ts +13 -0
  47. package/templates/hono/src/lib/env.ts +24 -0
  48. package/templates/hono/src/lib/query-client.ts +18 -0
  49. package/templates/hono/src/lib/query.ts +18 -0
  50. package/templates/hono/src/questpie/server/.generated/context.gen.ts +200 -0
  51. package/templates/hono/src/questpie/server/.generated/entities.gen.ts +84 -0
  52. package/templates/hono/src/questpie/server/.generated/factories.ts +65 -0
  53. package/templates/hono/src/questpie/server/.generated/index.ts +131 -0
  54. package/templates/hono/src/questpie/server/.generated/names.gen.ts +25 -0
  55. package/templates/hono/src/questpie/server/app.ts +10 -0
  56. package/templates/hono/src/questpie/server/collections/index.ts +1 -0
  57. package/templates/hono/src/questpie/server/collections/posts.collection.ts +10 -0
  58. package/templates/hono/src/questpie/server/config/auth.ts +8 -0
  59. package/templates/hono/src/questpie/server/config/openapi.ts +10 -0
  60. package/templates/hono/src/questpie/server/globals/index.ts +1 -0
  61. package/templates/hono/src/questpie/server/globals/site-settings.global.ts +10 -0
  62. package/templates/hono/src/questpie/server/modules.ts +8 -0
  63. package/templates/hono/src/questpie/server/questpie.config.ts +21 -0
  64. package/templates/hono/tsconfig.json +28 -0
  65. package/templates/next/AGENTS.md +55 -0
  66. package/templates/next/CLAUDE.md +39 -0
  67. package/templates/next/Dockerfile +25 -0
  68. package/templates/next/README.md +148 -0
  69. package/templates/next/components.json +22 -0
  70. package/templates/next/docker/init-extensions.sql +11 -0
  71. package/templates/next/docker-compose.yml +21 -0
  72. package/templates/next/env.example +16 -0
  73. package/templates/next/gitignore +10 -0
  74. package/templates/next/next-env.d.ts +5 -0
  75. package/templates/next/next.config.ts +20 -0
  76. package/templates/next/package.json +54 -0
  77. package/templates/next/postcss.config.mjs +8 -0
  78. package/templates/next/public/.gitkeep +0 -0
  79. package/templates/next/questpie.config.ts +12 -0
  80. package/templates/next/src/app/admin/[[...all]]/page.tsx +34 -0
  81. package/templates/next/src/app/admin/admin.css +4 -0
  82. package/templates/next/src/app/admin/layout.tsx +63 -0
  83. package/templates/next/src/app/api/[...all]/route.ts +24 -0
  84. package/templates/next/src/app/layout.tsx +24 -0
  85. package/templates/next/src/app/not-found.tsx +18 -0
  86. package/templates/next/src/app/page.tsx +74 -0
  87. package/templates/next/src/app/providers.tsx +11 -0
  88. package/templates/next/src/lib/auth-client.ts +12 -0
  89. package/templates/next/src/lib/client.ts +13 -0
  90. package/templates/next/src/lib/env.ts +24 -0
  91. package/templates/next/src/lib/query-client.ts +18 -0
  92. package/templates/next/src/lib/query.ts +18 -0
  93. package/templates/next/src/questpie/admin/.generated/client.ts +13 -0
  94. package/templates/next/src/questpie/admin/admin.ts +9 -0
  95. package/templates/next/src/questpie/admin/modules.ts +3 -0
  96. package/templates/next/src/questpie/server/.generated/context.gen.ts +204 -0
  97. package/templates/next/src/questpie/server/.generated/entities.gen.ts +100 -0
  98. package/templates/next/src/questpie/server/.generated/factories.ts +204 -0
  99. package/templates/next/src/questpie/server/.generated/index.ts +139 -0
  100. package/templates/next/src/questpie/server/.generated/names.gen.ts +31 -0
  101. package/templates/next/src/questpie/server/app.ts +10 -0
  102. package/templates/next/src/questpie/server/collections/index.ts +1 -0
  103. package/templates/next/src/questpie/server/collections/posts.collection.ts +58 -0
  104. package/templates/next/src/questpie/server/config/admin.ts +80 -0
  105. package/templates/next/src/questpie/server/config/auth.ts +8 -0
  106. package/templates/next/src/questpie/server/config/openapi.ts +10 -0
  107. package/templates/next/src/questpie/server/globals/index.ts +1 -0
  108. package/templates/next/src/questpie/server/globals/site-settings.global.ts +19 -0
  109. package/templates/next/src/questpie/server/modules.ts +9 -0
  110. package/templates/next/src/questpie/server/questpie.config.ts +21 -0
  111. package/templates/next/src/styles.css +125 -0
  112. package/templates/next/tsconfig.json +37 -0
  113. package/templates/tanstack-start/AGENTS.md +35 -607
  114. package/templates/tanstack-start/CLAUDE.md +26 -134
  115. package/templates/tanstack-start/README.md +13 -1
  116. package/templates/tanstack-start/docker/init-extensions.sql +11 -0
  117. package/templates/tanstack-start/docker-compose.yml +1 -0
  118. package/templates/tanstack-start/src/lib/auth-client.ts +1 -1
  119. package/templates/tanstack-start/src/lib/client.ts +1 -1
  120. package/templates/tanstack-start/src/lib/query.ts +18 -0
  121. package/templates/tanstack-start/src/questpie/server/collections/index.ts +1 -1
  122. package/templates/tanstack-start/src/questpie/server/globals/index.ts +1 -1
  123. package/templates/tanstack-start/src/questpie/server/questpie.config.ts +1 -1
  124. package/templates/tanstack-start/src/routes/__root.tsx +31 -1
  125. package/templates/tanstack-start/src/routes/api/$.ts +1 -1
  126. package/templates/tanstack-start/src/routes/index.tsx +97 -0
  127. package/skills/questpie/AGENTS.md +0 -2871
  128. package/skills/questpie/SKILL.md +0 -293
  129. package/skills/questpie/coverage.json +0 -213
  130. package/skills/questpie/references/auth.md +0 -236
  131. package/skills/questpie/references/business-logic.md +0 -620
  132. package/skills/questpie/references/codegen-plugin-api.md +0 -382
  133. package/skills/questpie/references/crud-api.md +0 -580
  134. package/skills/questpie/references/data-modeling.md +0 -509
  135. package/skills/questpie/references/extend.md +0 -584
  136. package/skills/questpie/references/field-types.md +0 -398
  137. package/skills/questpie/references/infrastructure-adapters.md +0 -720
  138. package/skills/questpie/references/mcp.md +0 -147
  139. package/skills/questpie/references/multi-tenancy.md +0 -363
  140. package/skills/questpie/references/production.md +0 -640
  141. package/skills/questpie/references/query-operators.md +0 -125
  142. package/skills/questpie/references/quickstart.md +0 -562
  143. package/skills/questpie/references/rules.md +0 -454
  144. package/skills/questpie/references/sandbox.md +0 -110
  145. package/skills/questpie/references/tanstack-query.md +0 -543
  146. package/skills/questpie/references/type-inference.md +0 -167
  147. package/skills/questpie/references/workflows.md +0 -155
  148. package/skills/questpie-admin/AGENTS.md +0 -1515
  149. package/skills/questpie-admin/SKILL.md +0 -443
  150. package/skills/questpie-admin/references/blocks.md +0 -331
  151. package/skills/questpie-admin/references/custom-ui.md +0 -305
  152. package/skills/questpie-admin/references/views.md +0 -449
@@ -1,125 +0,0 @@
1
- # Query Operators Reference
2
-
3
- Full reference for all `where` clause operators in QUESTPIE CRUD queries.
4
-
5
- ## Text Fields
6
-
7
- Applies to: `text`, `textarea`, `richText`, `email`, `url`, `slug`
8
-
9
- | Operator | Example | Description |
10
- | ------------ | --------------------------------- | ----------------------- |
11
- | equality | `{ title: "Hello" }` | Exact match (shorthand) |
12
- | `contains` | `{ title: { contains: "ell" } }` | Substring match |
13
- | `startsWith` | `{ title: { startsWith: "He" } }` | Prefix match |
14
- | `endsWith` | `{ title: { endsWith: "lo" } }` | Suffix match |
15
- | `in` | `{ title: { in: ["A", "B"] } }` | One of values |
16
-
17
- ## Number Fields
18
-
19
- Applies to: `number`
20
-
21
- | Operator | Example | Description |
22
- | -------- | --------------------------------- | --------------------- |
23
- | equality | `{ price: 1000 }` | Exact match |
24
- | `gt` | `{ price: { gt: 1000 } }` | Greater than |
25
- | `gte` | `{ price: { gte: 1000 } }` | Greater than or equal |
26
- | `lt` | `{ price: { lt: 5000 } }` | Less than |
27
- | `lte` | `{ price: { lte: 5000 } }` | Less than or equal |
28
- | `in` | `{ price: { in: [1000, 2000] } }` | One of values |
29
-
30
- ## Boolean Fields
31
-
32
- Applies to: `boolean`
33
-
34
- | Operator | Example | Description |
35
- | -------- | -------------------- | ----------- |
36
- | equality | `{ isActive: true }` | Exact match |
37
-
38
- ## Date / DateTime Fields
39
-
40
- Applies to: `date`, `dateTime`
41
-
42
- | Operator | Example | Description |
43
- | -------- | --------------------------------- | ------------ |
44
- | equality | `{ date: "2025-03-01" }` | Exact match |
45
- | `gt` | `{ date: { gt: "2025-01-01" } }` | After |
46
- | `gte` | `{ date: { gte: "2025-01-01" } }` | On or after |
47
- | `lt` | `{ date: { lt: "2025-12-31" } }` | Before |
48
- | `lte` | `{ date: { lte: "2025-12-31" } }` | On or before |
49
-
50
- ## Select Fields
51
-
52
- Applies to: `select`, `multiSelect`
53
-
54
- | Operator | Example | Description |
55
- | -------- | -------------------------------------------- | ------------- |
56
- | equality | `{ status: "published" }` | Exact match |
57
- | `in` | `{ status: { in: ["draft", "published"] } }` | One of values |
58
-
59
- ## Relation Fields
60
-
61
- Applies to: `relation`
62
-
63
- | Operator | Example | Description |
64
- | -------- | ----------------------- | ------------------- |
65
- | equality | `{ author: "user-id" }` | Match by related ID |
66
-
67
- ## Combining Operators
68
-
69
- ### Multiple Fields (AND)
70
-
71
- All top-level fields are combined with AND:
72
-
73
- ```ts
74
- where: {
75
- status: "published",
76
- price: { gte: 1000 },
77
- createdAt: { gte: "2025-01-01" },
78
- }
79
- // status = "published" AND price >= 1000 AND createdAt >= 2025-01-01
80
- ```
81
-
82
- ### Multiple Operators on Same Field (AND)
83
-
84
- Multiple operators on one field are ANDed together:
85
-
86
- ```ts
87
- where: {
88
- price: { gte: 1000, lt: 5000 },
89
- }
90
- // 1000 <= price < 5000
91
- ```
92
-
93
- ### Equality Shorthand
94
-
95
- Direct values are equivalent to exact match:
96
-
97
- ```ts
98
- // These are equivalent:
99
- where: {
100
- status: "published";
101
- }
102
- where: {
103
- status: {
104
- eq: "published";
105
- }
106
- }
107
- ```
108
-
109
- ## Complete Example
110
-
111
- ```ts
112
- const result = await collections.products.find({
113
- where: {
114
- status: "published",
115
- price: { gte: 1000, lte: 50000 },
116
- title: { contains: "premium" },
117
- category: { in: ["electronics", "software"] },
118
- createdAt: { gte: "2025-01-01" },
119
- },
120
- orderBy: { price: "asc" },
121
- limit: 20,
122
- offset: 0,
123
- with: { category: true },
124
- });
125
- ```
@@ -1,562 +0,0 @@
1
- ---
2
- name: questpie-quickstart
3
- description: >
4
- End-to-end getting started with QUESTPIE — from scaffolding to production.
5
- Load when starting a new project, onboarding, or following the happy path
6
- from zero to a running app with collections, admin panel, and migrations.
7
- ---
8
-
9
- # QUESTPIE Quickstart
10
-
11
- Complete lifecycle guide: scaffold, define data, generate, migrate, serve, deploy.
12
-
13
- ---
14
-
15
- ## 1. Scaffold a New Project
16
-
17
- ```bash
18
- bun create questpie my-app
19
- cd my-app
20
- ```
21
-
22
- Options:
23
-
24
- - `-t, --template <name>` — template to use (default: `tanstack-start`)
25
- - `--no-install` — skip `bun install`
26
- - `--no-git` — skip git init
27
-
28
- After scaffolding:
29
-
30
- ```bash
31
- cp .env.example .env # Set DATABASE_URL, APP_URL, APP_SECRET
32
- ```
33
-
34
- ### Required Environment Variables
35
-
36
- | Variable | Required | Description |
37
- | -------------- | -------- | ---------------------------- |
38
- | `DATABASE_URL` | Yes | PostgreSQL connection string |
39
- | `APP_URL` | Yes | Public URL of the app |
40
- | `APP_SECRET` | Yes | Secret for auth sessions |
41
- | `SMTP_HOST` | No | Email SMTP host |
42
- | `SMTP_PORT` | No | Email SMTP port |
43
-
44
- ---
45
-
46
- ## 2. Project Structure
47
-
48
- ```text
49
- my-app/
50
- ├── questpie.config.ts # CLI config (re-exports server config)
51
- ├── src/
52
- │ ├── questpie/
53
- │ │ ├── server/
54
- │ │ │ ├── questpie.config.ts # Runtime config (DB, adapters, secrets)
55
- │ │ │ ├── modules.ts # Module dependencies
56
- │ │ │ ├── config/ # Typed configuration files
57
- │ │ │ │ ├── auth.ts # authConfig({...}) — Better Auth options
58
- │ │ │ │ ├── app.ts # appConfig({ locale, access, hooks, context })
59
- │ │ │ │ ├── admin.ts # adminConfig({ sidebar, dashboard, branding, locale })
60
- │ │ │ │ └── openapi.ts # openApiConfig({ info, scalar })
61
- │ │ │ ├── collections/ # One file per collection
62
- │ │ │ ├── globals/ # One file per global
63
- │ │ │ ├── routes/ # App routes (JSON or raw)
64
- │ │ │ ├── jobs/ # Background tasks
65
- │ │ │ ├── services/ # Singleton services
66
- │ │ │ ├── blocks/ # Content blocks (admin plugin)
67
- │ │ │ ├── emails/ # Email templates
68
- │ │ │ └── .generated/ # Codegen output (NEVER edit)
69
- │ │ └── admin/ # Admin client customizations
70
- │ │ ├── .generated/ # Codegen output (NEVER edit)
71
- │ │ └── blocks/ # Block renderers (React)
72
- │ ├── lib/
73
- │ │ └── client.ts # Typed client SDK
74
- │ └── routes/
75
- │ └── api/
76
- │ └── $.ts # API catch-all handler
77
- ```
78
-
79
- ### Discovery Rules
80
-
81
- Codegen discovers files by **directory name** and **export pattern**:
82
-
83
- | Directory | Key derivation | Example |
84
- | --------------- | -------------------------- | ------------------------------------------ |
85
- | `collections/` | Factory arg to camelCase | `collection("blog-posts")` -> `blogPosts` |
86
- | `globals/` | Factory arg to camelCase | `global("siteSettings")` -> `siteSettings` |
87
- | `routes/` | Filename to camelCase/path | `create-booking.ts` -> `createBooking` |
88
- | `jobs/` | Filename to camelCase | `send-email.ts` -> `sendEmail` |
89
- | `routes/` (raw) | Filename to path | `webhook.ts` -> `webhook` |
90
- | `services/` | Filename to camelCase | `blog.ts` -> `blog` |
91
- | `blocks/` | Factory arg/export name | `block("hero")` -> `hero` |
92
- | `emails/` | Filename to camelCase | `welcome.ts` -> `welcome` |
93
-
94
- Routes support nested directories for namespacing (`routes/booking/create.ts` -> `client.routes.booking.create()`).
95
-
96
- Only hyphens are camelized in factory args; underscores are preserved (`global("site_settings")` -> `site_settings`).
97
-
98
- ---
99
-
100
- ## 3. Runtime Config
101
-
102
- ```ts
103
- // src/questpie/server/questpie.config.ts
104
- import { runtimeConfig } from "questpie/app";
105
- export default runtimeConfig({
106
- app: {
107
- url: process.env.APP_URL || "http://localhost:3000",
108
- },
109
- db: {
110
- url: process.env.DATABASE_URL || "postgres://localhost/myapp",
111
- },
112
- secret: process.env.APP_SECRET || "change-me-in-production",
113
- });
114
- ```
115
-
116
- The CLI config at the project root re-exports the server config:
117
-
118
- ```ts
119
- // questpie.config.ts (project root)
120
- export { default } from "./src/questpie/server/questpie.config";
121
- ```
122
-
123
- ---
124
-
125
- ## 4. Define a Collection
126
-
127
- ```ts
128
- // src/questpie/server/collections/tasks.ts
129
- import { collection } from "#questpie/factories";
130
-
131
- export default collection("tasks").fields(({ f }) => ({
132
- title: f.text(255).required(),
133
- description: f.textarea(),
134
- priority: f
135
- .select([
136
- { value: "low", label: "Low" },
137
- { value: "medium", label: "Medium" },
138
- { value: "high", label: "High" },
139
- ])
140
- .default("medium")
141
- .required(),
142
- dueDate: f.date(),
143
- completed: f.boolean().default(false).required(),
144
- }));
145
- ```
146
-
147
- This creates:
148
-
149
- - A `tasks` database table with typed columns
150
- - CRUD API endpoints at `/api/tasks`
151
- - Zod validation for create/update
152
- - Type-safe query operators for `where`, `orderBy`
153
-
154
- ### Built-in Field Types
155
-
156
- Core: `text`, `number`, `boolean`, `date`, `datetime`, `time`, `select`, `relation`, `upload`, `object`, `json`, `email`, `url`, `textarea`. Admin module fields: `richText`, `blocks`.
157
-
158
- ---
159
-
160
- ## 5. Add a Route
161
-
162
- ```ts
163
- // src/questpie/server/routes/get-overdue-tasks.ts
164
- import { route } from "questpie/services";
165
- import z from "zod";
166
-
167
- export default route()
168
- .post()
169
- .schema(z.object({}))
170
- .handler(async ({ collections }) => {
171
- return await collections.tasks.find({
172
- where: {
173
- completed: false,
174
- dueDate: { lt: new Date() },
175
- },
176
- orderBy: { dueDate: "asc" },
177
- });
178
- });
179
- ```
180
-
181
- Typed JSON routes are exposed as flat endpoints at `/api/<route-path>`.
182
-
183
- ---
184
-
185
- ## 6. Run Codegen
186
-
187
- ```bash
188
- bunx questpie generate
189
- ```
190
-
191
- This scans your file convention directories and generates:
192
-
193
- - `src/questpie/server/.generated/index.ts` — `app` instance, `AppConfig` type
194
- - `src/questpie/server/.generated/module.ts` — merged module with all discovered entities
195
- - Module augmentation for `AppContext` (typed `collections`, `queue`, `email` in every handler)
196
-
197
- Use `#questpie/factories` in collection, global, and block files (they need codegen-generated types). Routes, jobs, services, emails use `"questpie"` directly. Use `#questpie` for the generated app/runtime exports.
198
-
199
- **Run codegen again every time you add, rename, or remove a file in a convention directory.**
200
-
201
- ---
202
-
203
- ## 7. Push Schema / Run Migrations
204
-
205
- ### Development (quick iteration)
206
-
207
- ```bash
208
- bunx questpie push
209
- ```
210
-
211
- Syncs your Drizzle schema directly to the database. No migration files created. Use this during development only.
212
-
213
- ### Production (migration files)
214
-
215
- ```bash
216
- # Generate a migration from schema diff
217
- bunx questpie migrate:generate
218
-
219
- # Run pending migrations
220
- bunx questpie migrate:up
221
-
222
- # Rollback last migration
223
- bunx questpie migrate:down
224
-
225
- # Reset all migrations
226
- bunx questpie migrate:reset
227
-
228
- # Reset + run all (fresh start)
229
- bunx questpie migrate:fresh
230
- ```
231
-
232
- ---
233
-
234
- ## 8. Wire Up the HTTP Handler
235
-
236
- ### TanStack Start (default template)
237
-
238
- ```ts
239
- // src/routes/api/$.ts
240
- import { createAPIFileRoute } from "@tanstack/react-start/api";
241
- import { createFetchHandler } from "questpie/http";
242
- import { app } from "#questpie";
243
-
244
- const handler = createFetchHandler(app, { basePath: "/api" });
245
-
246
- export const Route = createAPIFileRoute("/api/$")({
247
- GET: ({ request }) => handler(request),
248
- POST: ({ request }) => handler(request),
249
- PATCH: ({ request }) => handler(request),
250
- DELETE: ({ request }) => handler(request),
251
- });
252
- ```
253
-
254
- ### Hono
255
-
256
- ```ts
257
- import { questpieHono } from "@questpie/hono/server";
258
- import { Hono } from "hono";
259
- import { app } from "#questpie";
260
-
261
- export default new Hono().route("/api", questpieHono(app));
262
- ```
263
-
264
- ### Available Adapters
265
-
266
- | Adapter | Package | Use case |
267
- | -------------- | ------------------ | --------------------- |
268
- | Hono | `@questpie/hono` | General purpose, fast |
269
- | Elysia | `@questpie/elysia` | Bun-native |
270
- | Next.js | `@questpie/next` | Next.js API routes |
271
- | TanStack Start | (built-in) | Generic fetch handler |
272
-
273
- ---
274
-
275
- ## 9. Add the Admin Panel
276
-
277
- ### Install
278
-
279
- ```bash
280
- bun add @questpie/admin
281
- ```
282
-
283
- ### Register the admin module
284
-
285
- ```ts
286
- // src/questpie/server/modules.ts
287
- import { adminModule } from "@questpie/admin/modules/admin";
288
-
289
- export default [adminModule] as const;
290
- ```
291
-
292
- ### Re-run codegen
293
-
294
- ```bash
295
- bunx questpie generate
296
- ```
297
-
298
- This picks up admin conventions (`config/admin.ts`, blocks, views, components) and generates `admin/.generated/client.ts`.
299
-
300
- Navigate to `/admin` to see the admin panel with your collections.
301
-
302
- ---
303
-
304
- ## 10. Typed Client SDK
305
-
306
- ```ts
307
- // src/lib/client.ts
308
- import { createClient } from "questpie/client";
309
- import type { AppConfig } from "#questpie";
310
-
311
- export const client = createClient<AppConfig>({
312
- baseURL: "http://localhost:3000",
313
- basePath: "/api",
314
- });
315
- ```
316
-
317
- Usage with full type inference:
318
-
319
- ```ts
320
- // Typed collection queries — autocomplete on field names and operators
321
- const tasks = await client.collections.tasks.find({
322
- where: { completed: false },
323
- orderBy: { priority: "desc" },
324
- });
325
-
326
- // Call route
327
- const overdue = await client.routes.getOverdueTasks({});
328
- ```
329
-
330
- ---
331
-
332
- ## 11. Start Development
333
-
334
- ```bash
335
- bun dev
336
- ```
337
-
338
- Test: `curl http://localhost:3000/api/tasks` for collection CRUD, `curl -X POST http://localhost:3000/api/get-overdue-tasks -H "Content-Type: application/json" -d '{}'` for typed route calls.
339
-
340
- ---
341
-
342
- ## Common Mistakes
343
-
344
- ### CRITICAL: Using npm/yarn/pnpm instead of Bun
345
-
346
- QUESTPIE requires **Bun** as the package manager and runtime. All commands use `bun` or `bunx`:
347
-
348
- ```bash
349
- # WRONG
350
- npm install questpie
351
- npx questpie generate
352
- yarn add questpie
353
-
354
- # CORRECT
355
- bun add questpie
356
- bunx questpie generate
357
- bun dev
358
- ```
359
-
360
- ### HIGH: Forgetting to run `questpie generate` after changes
361
-
362
- Every time you add, rename, or remove a file in a convention directory (`collections/`, `routes/`, `globals/`, `jobs/`, etc.), you must re-run:
363
-
364
- ```bash
365
- bunx questpie generate
366
- ```
367
-
368
- Without this, the `app` instance and types will be stale. New collections will not appear in CRUD endpoints or admin.
369
-
370
- ### HIGH: Not setting DATABASE_URL
371
-
372
- QUESTPIE requires PostgreSQL. Set `DATABASE_URL` in `.env` before running `push` or `migrate`:
373
-
374
- ```bash
375
- DATABASE_URL=postgres://user:pass@localhost:5432/myapp
376
- ```
377
-
378
- ### MEDIUM: Missing `export default` on convention files
379
-
380
- Codegen discovers files by their **default export**. Files without `export default` are silently ignored:
381
-
382
- ```ts
383
- // WRONG — codegen will not discover this
384
- export const tasks = collection("tasks").fields(/* ... */);
385
-
386
- // CORRECT
387
- export default collection("tasks").fields(/* ... */);
388
- ```
389
-
390
- ### MEDIUM: Putting business logic in route handlers
391
-
392
- Framework route handlers should only mount the QUESTPIE fetch handler. Business logic belongs in `routes/`, `jobs/`, or collection hooks:
393
-
394
- ```ts
395
- // WRONG — business logic in route file
396
- export const Route = createAPIFileRoute("/api/custom")({
397
- POST: async ({ request }) => {
398
- const db = getDB();
399
- // ... manual queries
400
- },
401
- });
402
-
403
- // CORRECT — use a typed route
404
- // src/questpie/server/routes/my-logic.ts
405
- export default route()
406
- .post()
407
- .schema(
408
- z.object({
409
- /* ... */
410
- }),
411
- )
412
- .handler(async ({ input, collections }) => {
413
- return await collections.tasks.create(input);
414
- });
415
- ```
416
-
417
- ---
418
-
419
- ## Quick Reference: CLI Commands
420
-
421
- | Command | Purpose |
422
- | -------------------------------- | ------------------------------------------- |
423
- | `bun create questpie my-app` | Scaffold a new project |
424
- | `bunx questpie generate` | Scan conventions, generate types and app |
425
- | `bunx questpie push` | Push schema to DB (dev only, no migrations) |
426
- | `bunx questpie migrate:generate` | Generate migration from schema diff |
427
- | `bunx questpie migrate:up` | Run pending migrations |
428
- | `bunx questpie migrate:down` | Rollback last migration |
429
- | `bunx questpie migrate:fresh` | Reset + run all migrations |
430
- | `bun dev` | Start development server |
431
-
432
- ---
433
-
434
- ## Minimal Complete Example
435
-
436
- Starting from zero — every file needed for a working app:
437
-
438
- ```ts
439
- // questpie.config.ts (project root)
440
- export { default } from "./src/questpie/server/questpie.config";
441
- ```
442
-
443
- ```ts
444
- // src/questpie/server/questpie.config.ts
445
- import { runtimeConfig } from "questpie/app";
446
- export default runtimeConfig({
447
- app: { url: process.env.APP_URL || "http://localhost:3000" },
448
- db: { url: process.env.DATABASE_URL! },
449
- secret: process.env.APP_SECRET || "dev-secret",
450
- });
451
- ```
452
-
453
- ```ts
454
- // src/questpie/server/collections/posts.ts
455
- import { collection } from "#questpie/factories";
456
-
457
- export default collection("posts").fields(({ f }) => ({
458
- title: f.text().required(),
459
- body: f.richText(),
460
- status: f.select([
461
- { value: "draft", label: "Draft" },
462
- { value: "published", label: "Published" },
463
- ]),
464
- }));
465
- ```
466
-
467
- ```ts
468
- // src/routes/api/$.ts
469
- import { createAPIFileRoute } from "@tanstack/react-start/api";
470
- import { createFetchHandler } from "questpie/http";
471
- import { app } from "#questpie";
472
-
473
- const handler = createFetchHandler(app, { basePath: "/api" });
474
-
475
- export const Route = createAPIFileRoute("/api/$")({
476
- GET: ({ request }) => handler(request),
477
- POST: ({ request }) => handler(request),
478
- PATCH: ({ request }) => handler(request),
479
- DELETE: ({ request }) => handler(request),
480
- });
481
- ```
482
-
483
- Then run:
484
-
485
- ```bash
486
- bunx questpie generate
487
- bunx questpie push
488
- bun dev
489
- ```
490
-
491
- ---
492
-
493
- ## 12. Live Preview (Optional)
494
-
495
- Add split-screen live preview to any collection with `.preview()`. Live Preview uses the existing admin `FormView`, Preview button, `LivePreviewMode`, and iframe. Do not introduce a second default form view or parallel preview API names.
496
-
497
- ### Add Preview to a Collection
498
-
499
- ```ts
500
- // src/questpie/server/collections/pages.ts
501
- import { collection } from "#questpie/factories";
502
-
503
- export default collection("pages")
504
- .fields(({ f }) => ({
505
- title: f.text().required(),
506
- slug: f.text().required(),
507
- content: f.blocks(),
508
- }))
509
- .preview({
510
- enabled: true,
511
- position: "right",
512
- defaultWidth: 50,
513
- url: ({ record }) => `/${record.slug}?preview=true`,
514
- });
515
- ```
516
-
517
- ### Add Preview Support to the Frontend Page
518
-
519
- Frontend checklist:
520
-
521
- 1. Call `useCollectionPreview({ initialData, onRefresh })`.
522
- 2. Wrap the rendered output in `PreviewProvider`.
523
- 3. Render from `preview.data`, not directly from loader data.
524
- 4. Wrap editable scalar text in `PreviewField`.
525
- 5. Render blocks with `BlockRenderer` when the page uses `f.blocks()`.
526
-
527
- ```tsx
528
- import {
529
- BlockRenderer,
530
- PreviewField,
531
- PreviewProvider,
532
- useCollectionPreview,
533
- } from "@questpie/admin/client";
534
- import admin from "@/questpie/admin/.generated/client";
535
-
536
- function PageView({ page }) {
537
- const router = useRouter();
538
- const preview = useCollectionPreview({
539
- initialData: page,
540
- onRefresh: () => router.invalidate(),
541
- });
542
-
543
- return (
544
- <PreviewProvider preview={preview}>
545
- <PreviewField field="title" editable="text" as="h1">
546
- {preview.data.title}
547
- </PreviewField>
548
- <BlockRenderer
549
- content={preview.data.content}
550
- data={preview.data.content?._data}
551
- renderers={admin.blocks}
552
- selectedBlockId={preview.selectedBlockId}
553
- onBlockClick={
554
- preview.isPreviewMode ? preview.handleBlockClick : undefined
555
- }
556
- />
557
- </PreviewProvider>
558
- );
559
- }
560
- ```
561
-
562
- The form remains authoritative. Save, autosave, Cmd+S, history, workflow, locks, and actions stay in the existing form lifecycle.