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,720 +0,0 @@
1
- # Infrastructure Adapters Reference
2
-
3
- All adapter configurations for QUESTPIE production infrastructure.
4
-
5
- ## Database
6
-
7
- PostgreSQL with Drizzle ORM. Configured in `questpie.config.ts`:
8
-
9
- ```ts
10
- import { runtimeConfig } from "questpie/app";
11
- export default runtimeConfig({
12
- db: {
13
- url: process.env.DATABASE_URL || "postgres://localhost/myapp",
14
- },
15
- });
16
- ```
17
-
18
- ### Field-to-Column Mapping
19
-
20
- | QUESTPIE Field | Drizzle Column Type |
21
- | -------------- | ------------------- |
22
- | `f.text()` | `varchar` / `text` |
23
- | `f.number()` | `integer` |
24
- | `f.boolean()` | `boolean` |
25
- | `f.date()` | `date` |
26
- | `f.datetime()` | `timestamp` |
27
- | `f.select()` | `varchar` |
28
- | `f.json()` | `jsonb` |
29
- | `f.object()` | `jsonb` |
30
- | `.array()` | `jsonb` |
31
- | `f.relation()` | `varchar` (FK) |
32
-
33
- ### Raw Access
34
-
35
- ```ts
36
- handler: async ({ db }) => {
37
- // Raw SQL
38
- const result = await db.execute(sql`SELECT COUNT(*) FROM posts`);
39
-
40
- // Drizzle query builder
41
- const rows = await db.select().from(table).where(eq(table.id, id));
42
- };
43
- ```
44
-
45
- ### Indexes
46
-
47
- ```ts
48
- import { uniqueIndex, index } from "drizzle-orm/pg-core";
49
-
50
- collection("posts")
51
- .fields(({ f }) => ({ slug: f.text().required() }))
52
- .indexes(({ table }) => [
53
- uniqueIndex("posts_slug_unique").on(table.slug),
54
- index("posts_status_idx").on(table.status),
55
- ]);
56
- ```
57
-
58
- ## Storage
59
-
60
- File storage via [Files SDK](https://files-sdk.dev/).
61
-
62
- ### Local (Development)
63
-
64
- Default adapter. Files stored on local filesystem:
65
-
66
- ```ts
67
- export default runtimeConfig({
68
- storage: {
69
- basePath: "/api",
70
- },
71
- });
72
- ```
73
-
74
- ### S3-Compatible (Production)
75
-
76
- Works with AWS S3, MinIO, DigitalOcean Spaces, and other S3-compatible
77
- providers that do not have a dedicated Files SDK adapter:
78
-
79
- ```ts
80
- import { s3 } from "files-sdk/s3";
81
-
82
- export default runtimeConfig({
83
- storage: {
84
- basePath: "/api",
85
- adapter: s3({
86
- bucket: process.env.S3_BUCKET,
87
- region: process.env.S3_REGION,
88
- credentials: {
89
- accessKeyId: process.env.S3_ACCESS_KEY!,
90
- secretAccessKey: process.env.S3_SECRET_KEY!,
91
- },
92
- }),
93
- },
94
- });
95
- ```
96
-
97
- ### Cloudflare R2 (Production)
98
-
99
- Use Files SDK's dedicated R2 adapter:
100
-
101
- ```ts
102
- import { r2 } from "files-sdk/r2";
103
-
104
- export default runtimeConfig({
105
- storage: {
106
- basePath: "/api",
107
- adapter: r2({
108
- bucket: process.env.R2_BUCKET!,
109
- accountId: process.env.R2_ACCOUNT_ID!,
110
- accessKeyId: process.env.R2_ACCESS_KEY_ID!,
111
- secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!,
112
- }),
113
- },
114
- });
115
- ```
116
-
117
- ### Upload Fields
118
-
119
- ```ts
120
- avatar: f.upload({
121
- to: "assets", // target upload collection
122
- mimeTypes: ["image/*"], // allowed MIME types
123
- maxSize: 5_000_000, // max file size in bytes (5MB)
124
- }),
125
- ```
126
-
127
- ### Client Upload
128
-
129
- ```ts
130
- const asset = await client.collections.assets.upload(file, {
131
- onProgress: (percent) => console.log(`${percent}%`),
132
- });
133
-
134
- const assets = await client.collections.assets.uploadMany(files, {
135
- onProgress: (percent) => console.log(`${percent}%`),
136
- });
137
- ```
138
-
139
- Failed uploads reject with `UploadError` (from `questpie/client`) carrying the HTTP status and server message.
140
-
141
- ### Signed URLs (Private Files)
142
-
143
- Upload rows carry a `url` populated automatically on read: `visibility: "public"` files get a plain collection-scoped URL (`{basePath}/{collection}/files/{key}`); `"private"` files get an HMAC-signed token appended (`?token=...`), signed with `app.config.secret` and expiring after `storage.signedUrlExpiration` (default `3600` seconds):
144
-
145
- ```ts
146
- export default runtimeConfig({
147
- storage: {
148
- basePath: "/api",
149
- signedUrlExpiration: 900, // 15 minutes
150
- },
151
- });
152
- ```
153
-
154
- Serving stays collection-scoped — the file route verifies the token **and** the collection's `serve` access rule. To mint URLs manually (custom emails, server-rendered pages):
155
-
156
- ```ts
157
- import { buildStorageFileUrl, generateSignedUrlToken } from "questpie/storage";
158
-
159
- const token = await generateSignedUrlToken(asset.key, app.config.secret!, 900, "assets");
160
- const url = buildStorageFileUrl(app.config.app.url, "/api", "assets", asset.key, token);
161
- ```
162
-
163
- (`verifySignedUrlToken` is the verification half the serve route runs — you rarely call it yourself.)
164
-
165
- ## Queue
166
-
167
- Background jobs via [pg-boss](https://github.com/timgit/pg-boss), BullMQ, or a custom queue adapter.
168
-
169
- ### Configuration
170
-
171
- ```ts
172
- import { runtimeConfig } from "questpie/app";
173
- import { pgBossAdapter } from "questpie/adapters/pg-boss";
174
-
175
- export default runtimeConfig({
176
- queue: {
177
- adapter: pgBossAdapter({
178
- connectionString: process.env.DATABASE_URL,
179
- }),
180
- },
181
- });
182
- ```
183
-
184
- ### BullMQ
185
-
186
- ```ts
187
- import { runtimeConfig } from "questpie/app";
188
- import { bullMQAdapter } from "questpie/adapters/bullmq";
189
-
190
- export default runtimeConfig({
191
- queue: {
192
- adapter: bullMQAdapter({
193
- connection: { url: process.env.REDIS_URL! },
194
- queuePrefix: "my-app",
195
- }),
196
- },
197
- });
198
- ```
199
-
200
- The built-in BullMQ adapter targets open-source BullMQ and does not expose per-group FIFO behavior. Use a native grouped queue adapter if the workload needs one active job per group with cross-group parallelism.
201
-
202
- ### Publishing Jobs
203
-
204
- The `queue` context object is fully typed:
205
-
206
- ```ts
207
- handler: async ({ queue }) => {
208
- await queue.sendConfirmation.publish({
209
- appointmentId: "abc",
210
- customerId: "def",
211
- });
212
- };
213
- ```
214
-
215
- ## Realtime
216
-
217
- SSE-based live updates.
218
-
219
- ### pgNotify (Single Instance)
220
-
221
- Uses PostgreSQL `LISTEN/NOTIFY`. Best for single-server deployments:
222
-
223
- ```ts
224
- import { runtimeConfig } from "questpie/app";
225
- import { pgNotifyAdapter } from "questpie/adapters/pg-notify";
226
-
227
- export default runtimeConfig({
228
- realtime: {
229
- adapter: pgNotifyAdapter({
230
- connectionString: process.env.DATABASE_URL,
231
- }),
232
- },
233
- });
234
- ```
235
-
236
- ### Redis Streams (Multi-Instance)
237
-
238
- Required for horizontal scaling across multiple server instances:
239
-
240
- ```ts
241
- import { runtimeConfig } from "questpie/app";
242
- import { redisStreamsAdapter } from "questpie/adapters/redis-streams";
243
-
244
- export default runtimeConfig({
245
- realtime: {
246
- adapter: redisStreamsAdapter({
247
- url: process.env.REDIS_URL,
248
- }),
249
- },
250
- });
251
- ```
252
-
253
- ### When to Use Which
254
-
255
- | Adapter | Use Case |
256
- | --------------------- | ----------------------------------------------------- |
257
- | `pgNotifyAdapter` | Single server, development, simple deployments |
258
- | `redisStreamsAdapter` | Multiple servers, horizontal scaling, high throughput |
259
-
260
- ## Search
261
-
262
- Full-text search via PostgreSQL (no extra service). The adapter goes directly on the `search` key:
263
-
264
- ```ts
265
- import { runtimeConfig } from "questpie/app";
266
- import { createPostgresSearchAdapter } from "questpie/adapters/postgres-search";
267
-
268
- export default runtimeConfig({
269
- search: createPostgresSearchAdapter(), // pg_trgm + tsvector FTS
270
- });
271
- ```
272
-
273
- ### Semantic Search (pgvector + Embeddings)
274
-
275
- `createPgVectorSearchAdapter` wraps the Postgres adapter and adds an `embedding` vector column + cosine-distance search. It needs the `pgvector` extension (`CREATE EXTENSION "vector";` — the adapter ships its own migrations) and an embedding provider:
276
-
277
- ```ts
278
- import { runtimeConfig } from "questpie/app";
279
- import {
280
- createOpenAIEmbeddingProvider,
281
- createPgVectorSearchAdapter,
282
- } from "questpie/adapters/pgvector-search";
283
-
284
- export default runtimeConfig({
285
- search: createPgVectorSearchAdapter({
286
- embeddingProvider: createOpenAIEmbeddingProvider({
287
- apiKey: process.env.OPENAI_API_KEY!,
288
- model: "text-embedding-3-small",
289
- }),
290
- // Hybrid scoring weights (defaults shown)
291
- lexicalWeight: 0.4,
292
- semanticWeight: 0.6,
293
- indexType: "ivfflat", // or "hnsw"
294
- }),
295
- });
296
- ```
297
-
298
- Search modes: `lexical` (FTS + trigram), `semantic` (pure vector similarity), `hybrid` (weighted combination). Embeddings are generated on `index()`; if generation fails, the row still indexes for lexical search.
299
-
300
- For non-OpenAI providers, `createCustomEmbeddingProvider({ name, model, dimensions, generate })` wraps any embedding function.
301
-
302
- ## Email
303
-
304
- Transactional email with typed templates.
305
-
306
- ### SMTP (Production)
307
-
308
- ```ts
309
- import { runtimeConfig } from "questpie/app";
310
- import { SmtpAdapter } from "questpie/adapters/smtp";
311
-
312
- export default runtimeConfig({
313
- email: {
314
- adapter: new SmtpAdapter({
315
- transport: {
316
- host: process.env.SMTP_HOST,
317
- port: parseInt(process.env.SMTP_PORT || "587"),
318
- secure: true,
319
- },
320
- }),
321
- },
322
- });
323
- ```
324
-
325
- ### Console (Development)
326
-
327
- Logs emails to console instead of sending:
328
-
329
- ```ts
330
- import { runtimeConfig } from "questpie/app";
331
- import { ConsoleAdapter } from "questpie/adapters/console";
332
-
333
- export default runtimeConfig({
334
- email: {
335
- adapter: new ConsoleAdapter({ logHtml: false }),
336
- },
337
- });
338
- ```
339
-
340
- ### Resend (HTTP API)
341
-
342
- For [Resend](https://resend.com) and Resend-compatible providers — no SMTP credentials needed:
343
-
344
- ```ts
345
- import { runtimeConfig } from "questpie/app";
346
- import { resendAdapter } from "questpie/adapters/resend";
347
-
348
- export default runtimeConfig({
349
- email: {
350
- adapter: resendAdapter({
351
- apiKey: process.env.RESEND_API_KEY!,
352
- // baseUrl: "https://api.resend.com", // override for compatible providers
353
- }),
354
- },
355
- });
356
- ```
357
-
358
- ### Plunk (HTTP API)
359
-
360
- For [Plunk](https://www.useplunk.com) transactional email (also self-hosted):
361
-
362
- ```ts
363
- import { runtimeConfig } from "questpie/app";
364
- import { plunkAdapter } from "questpie/adapters/plunk";
365
-
366
- export default runtimeConfig({
367
- email: {
368
- adapter: plunkAdapter({
369
- apiKey: process.env.PLUNK_API_KEY!, // secret key — public keys only track events
370
- // baseUrl: "https://next-api.useplunk.com", // override for self-hosted
371
- }),
372
- },
373
- });
374
- ```
375
-
376
- ### Custom Mail Adapter
377
-
378
- For any other provider, extend the `MailAdapter` base class from `questpie/mailer` (implement `send(options)`) and pass an instance as `email.adapter`.
379
-
380
- ### Environment-Based Switching
381
-
382
- ```ts
383
- email: {
384
- adapter:
385
- process.env.NODE_ENV === "development"
386
- ? new ConsoleAdapter({ logHtml: false })
387
- : new SmtpAdapter({
388
- transport: {
389
- host: process.env.SMTP_HOST,
390
- port: parseInt(process.env.SMTP_PORT || "587"),
391
- secure: true,
392
- },
393
- }),
394
- }
395
- ```
396
-
397
- ### Defining Templates
398
-
399
- Templates go in the `emails/` directory:
400
-
401
- ```ts
402
- // emails/welcome.ts
403
- import { email } from "questpie/services";
404
- import z from "zod";
405
-
406
- export default email({
407
- name: "welcome",
408
- schema: z.object({
409
- userName: z.string(),
410
- loginUrl: z.string(),
411
- }),
412
- handler: ({ input }) => ({
413
- subject: `Welcome, ${input.userName}!`,
414
- html: `<h1>Welcome!</h1><p><a href="${input.loginUrl}">Sign in here</a></p>`,
415
- }),
416
- });
417
- ```
418
-
419
- ### Sending
420
-
421
- ```ts
422
- handler: async ({ email }) => {
423
- await email.sendTemplate({
424
- template: "welcome",
425
- input: { userName: "John", loginUrl: "https://app.example.com/login" },
426
- to: "john@example.com",
427
- });
428
- };
429
- ```
430
-
431
- ## KV Store
432
-
433
- Key-value storage for caching, rate limiting, ephemeral data.
434
-
435
- ### Redis
436
-
437
- ```ts
438
- import { createClient } from "redis";
439
- import { redisKVAdapter } from "questpie/adapters/redis-kv";
440
-
441
- async function getRedis() {
442
- const redis = createClient({ url: process.env.REDIS_URL! });
443
- await redis.connect();
444
- return redis;
445
- }
446
-
447
- export default runtimeConfig({
448
- kv: {
449
- adapter: redisKVAdapter({ client: getRedis, keyPrefix: "my-app:" }),
450
- defaultTtl: 3600,
451
- },
452
- });
453
- ```
454
-
455
- ### Custom Adapter
456
-
457
- ```ts
458
- export default runtimeConfig({
459
- kv: {
460
- adapter: myKvAdapter,
461
- defaultTtl: 3600,
462
- },
463
- });
464
- ```
465
-
466
- ### In-Memory Default
467
-
468
- ```ts
469
- export default runtimeConfig({
470
- kv: {
471
- defaultTtl: 3600,
472
- },
473
- });
474
- ```
475
-
476
- ### API
477
-
478
- ```ts
479
- handler: async ({ kv }) => {
480
- // Set with TTL (seconds)
481
- await kv.set("session:abc", JSON.stringify(data), 3600);
482
-
483
- // Get
484
- const value = await kv.get("session:abc");
485
-
486
- // Delete
487
- await kv.delete("session:abc");
488
- };
489
- ```
490
-
491
- ## Logger
492
-
493
- Structured logging via [Pino](https://getpino.io).
494
-
495
- ### Usage
496
-
497
- ```ts
498
- handler: async ({ logger }) => {
499
- logger.info("Processing request");
500
- logger.error({ err: error }, "Request failed");
501
- logger.debug({ userId, action }, "User action");
502
- };
503
- ```
504
-
505
- ### Log Levels
506
-
507
- | Level | Usage |
508
- | ------- | --------------------- |
509
- | `trace` | Detailed debugging |
510
- | `debug` | Development debugging |
511
- | `info` | Normal operations |
512
- | `warn` | Potential issues |
513
- | `error` | Errors |
514
- | `fatal` | Critical failures |
515
-
516
- ### Structured Data
517
-
518
- Pass objects as the first argument:
519
-
520
- ```ts
521
- logger.info(
522
- {
523
- appointmentId: "abc",
524
- action: "created",
525
- barberId: "def",
526
- },
527
- "Appointment created",
528
- );
529
- ```
530
-
531
- ## OpenAPI
532
-
533
- Auto-generates OpenAPI 3.1 spec from your schema.
534
-
535
- ### Setup
536
-
537
- ```bash
538
- bun add @questpie/openapi
539
- ```
540
-
541
- ```ts
542
- // src/questpie/server/modules.ts
543
- import { adminModule } from "@questpie/admin/modules/admin";
544
- import { openApiModule } from "@questpie/openapi/modules/openapi";
545
-
546
- export default [adminModule, openApiModule] as const;
547
- ```
548
-
549
- `openApiModule` carries its codegen plugin — do not also add `openApiPlugin()` to `questpie.config.ts` unless you deliberately omit the module.
550
-
551
- Configure it in `config/openapi.ts`:
552
-
553
- ```ts
554
- import { openApiConfig } from "@questpie/openapi/server";
555
-
556
- export default openApiConfig({
557
- info: { title: "My API", version: "1.0.0" },
558
- });
559
- ```
560
-
561
- Then run codegen:
562
-
563
- ```bash
564
- bunx questpie generate
565
- ```
566
-
567
- ### Configuration Options
568
-
569
- ```ts
570
- openApiConfig({
571
- info: {
572
- title: "My API",
573
- version: "1.0.0",
574
- description: "Backend for my app",
575
- },
576
- servers: [{ url: "https://api.example.com", description: "Production" }],
577
- basePath: "/api",
578
- exclude: {
579
- collections: ["_internal_logs"],
580
- globals: ["_cache"],
581
- },
582
- auth: true, // include auth endpoints
583
- search: true, // include search endpoints
584
- scalar: {
585
- theme: "purple", // Scalar UI theme
586
- },
587
- specPath: "openapi.json", // custom spec route
588
- docsPath: "docs", // custom docs route
589
- });
590
- ```
591
-
592
- ### Routes
593
-
594
- | Route | Description |
595
- | ----------------------- | -------------------------------- |
596
- | `GET /api/openapi.json` | OpenAPI 3.1 JSON spec |
597
- | `GET /api/docs` | Scalar interactive API reference |
598
-
599
- ### What Gets Documented
600
-
601
- | Source | Documented As |
602
- | ----------- | ------------------------------------------------------- |
603
- | Collections | CRUD endpoints (`GET`, `POST`, `PUT`, `DELETE`) |
604
- | Globals | Read/update endpoints (`GET`, `PUT`) |
605
- | Routes | App route endpoints (`/api/{path}`) |
606
- | Auth | Sign-in, sign-up, session endpoints (when `auth: true`) |
607
- | Search | Search endpoints (when `search: true`) |
608
-
609
- ### Manual Route Approach
610
-
611
- Instead of the module, create route files directly:
612
-
613
- ```ts
614
- // routes/openapi.json.ts
615
- import { openApiRoute } from "@questpie/openapi/server";
616
-
617
- export default openApiRoute({
618
- info: { title: "My API", version: "1.0.0" },
619
- });
620
- ```
621
-
622
- ```ts
623
- // routes/docs.ts
624
- import { docsRoute } from "@questpie/openapi/server";
625
-
626
- export default docsRoute({
627
- scalar: { theme: "purple" },
628
- });
629
- ```
630
-
631
- ### Programmatic Access
632
-
633
- ```ts
634
- import { generateOpenApiSpec } from "@questpie/openapi/server";
635
-
636
- const spec = generateOpenApiSpec(app, {
637
- info: { title: "My API", version: "1.0.0" },
638
- });
639
- ```
640
-
641
- ## Migrations CLI
642
-
643
- | Command | Description |
644
- | -------------------------------- | ------------------------------------------------ |
645
- | `bunx questpie push` | Direct schema sync (dev only, no migration file) |
646
- | `bunx questpie migrate:generate` | Generate migration from schema diff |
647
- | `bunx questpie migrate:up` | Run pending migrations |
648
- | `bunx questpie migrate:down` | Rollback last migration |
649
- | `bunx questpie migrate:fresh` | Drop all and re-run (DESTRUCTIVE) |
650
- | `bunx questpie migrate:reset` | Reset migration tracking |
651
- | `bunx questpie seed` | Run seed files |
652
-
653
- ## Complete Production Config Example
654
-
655
- ```ts
656
- import { runtimeConfig } from "questpie/app";
657
- import { pgBossAdapter } from "questpie/adapters/pg-boss";
658
- import { pgNotifyAdapter } from "questpie/adapters/pg-notify";
659
- import { SmtpAdapter } from "questpie/adapters/smtp";
660
- import { s3 } from "files-sdk/s3";
661
-
662
- export default runtimeConfig({
663
- db: {
664
- url: process.env.DATABASE_URL!,
665
- },
666
- storage: {
667
- basePath: "/api",
668
- adapter: s3({
669
- bucket: process.env.S3_BUCKET!,
670
- region: process.env.S3_REGION!,
671
- credentials: {
672
- accessKeyId: process.env.S3_ACCESS_KEY!,
673
- secretAccessKey: process.env.S3_SECRET_KEY!,
674
- },
675
- }),
676
- },
677
- queue: {
678
- adapter: pgBossAdapter({
679
- connectionString: process.env.DATABASE_URL!,
680
- }),
681
- },
682
- realtime: {
683
- adapter: pgNotifyAdapter({
684
- connectionString: process.env.DATABASE_URL!,
685
- }),
686
- },
687
- email: {
688
- adapter: new SmtpAdapter({
689
- transport: {
690
- host: process.env.SMTP_HOST!,
691
- port: parseInt(process.env.SMTP_PORT || "587"),
692
- secure: true,
693
- },
694
- }),
695
- },
696
- kv: {
697
- adapter: myKvAdapter,
698
- defaultTtl: 3600,
699
- },
700
- cli: {
701
- migrations: { directory: "./src/migrations" },
702
- seeds: { directory: "./src/seeds" },
703
- },
704
- });
705
- ```
706
-
707
- ## Environment Variables Summary
708
-
709
- | Variable | Service | Required |
710
- | -------------------- | ----------------------------- | ------------------- |
711
- | `DATABASE_URL` | Database, Queue, Realtime | Yes |
712
- | `APP_URL` | Auth, Email links | Yes |
713
- | `BETTER_AUTH_SECRET` | Auth sessions | Yes (production) |
714
- | `REDIS_URL` | KV, Realtime (multi-instance) | No |
715
- | `S3_BUCKET` | Storage | No (if using local) |
716
- | `S3_REGION` | Storage | No |
717
- | `S3_ACCESS_KEY` | Storage | No |
718
- | `S3_SECRET_KEY` | Storage | No |
719
- | `SMTP_HOST` | Email | No |
720
- | `SMTP_PORT` | Email | No |