create-questpie 2.0.3 → 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.
- package/dist/index.mjs +544 -87
- package/package.json +2 -3
- package/templates/elysia/AGENTS.md +56 -0
- package/templates/elysia/CLAUDE.md +39 -0
- package/templates/elysia/Dockerfile +24 -0
- package/templates/elysia/README.md +148 -0
- package/templates/elysia/docker/init-extensions.sql +11 -0
- package/templates/elysia/docker-compose.yml +21 -0
- package/templates/elysia/env.example +16 -0
- package/templates/elysia/gitignore +6 -0
- package/templates/elysia/package.json +47 -0
- package/templates/elysia/questpie.config.ts +12 -0
- package/templates/elysia/src/index.ts +21 -0
- package/templates/elysia/src/lib/auth-client.ts +32 -0
- package/templates/elysia/src/lib/client.ts +13 -0
- package/templates/elysia/src/lib/env.ts +24 -0
- package/templates/elysia/src/lib/query-client.ts +18 -0
- package/templates/elysia/src/lib/query.ts +18 -0
- package/templates/elysia/src/questpie/server/.generated/context.gen.ts +200 -0
- package/templates/elysia/src/questpie/server/.generated/entities.gen.ts +84 -0
- package/templates/elysia/src/questpie/server/.generated/factories.ts +65 -0
- package/templates/elysia/src/questpie/server/.generated/index.ts +131 -0
- package/templates/elysia/src/questpie/server/.generated/names.gen.ts +25 -0
- package/templates/elysia/src/questpie/server/app.ts +10 -0
- package/templates/elysia/src/questpie/server/collections/index.ts +1 -0
- package/templates/elysia/src/questpie/server/collections/posts.collection.ts +10 -0
- package/templates/elysia/src/questpie/server/config/auth.ts +8 -0
- package/templates/elysia/src/questpie/server/config/openapi.ts +10 -0
- package/templates/elysia/src/questpie/server/globals/index.ts +1 -0
- package/templates/elysia/src/questpie/server/globals/site-settings.global.ts +10 -0
- package/templates/elysia/src/questpie/server/modules.ts +8 -0
- package/templates/elysia/src/questpie/server/questpie.config.ts +21 -0
- package/templates/elysia/tsconfig.json +28 -0
- package/templates/hono/AGENTS.md +56 -0
- package/templates/hono/CLAUDE.md +39 -0
- package/templates/hono/Dockerfile +24 -0
- package/templates/hono/README.md +148 -0
- package/templates/hono/docker/init-extensions.sql +11 -0
- package/templates/hono/docker-compose.yml +21 -0
- package/templates/hono/env.example +16 -0
- package/templates/hono/gitignore +6 -0
- package/templates/hono/package.json +47 -0
- package/templates/hono/questpie.config.ts +12 -0
- package/templates/hono/src/index.ts +30 -0
- package/templates/hono/src/lib/auth-client.ts +32 -0
- package/templates/hono/src/lib/client.ts +13 -0
- package/templates/hono/src/lib/env.ts +24 -0
- package/templates/hono/src/lib/query-client.ts +18 -0
- package/templates/hono/src/lib/query.ts +18 -0
- package/templates/hono/src/questpie/server/.generated/context.gen.ts +200 -0
- package/templates/hono/src/questpie/server/.generated/entities.gen.ts +84 -0
- package/templates/hono/src/questpie/server/.generated/factories.ts +65 -0
- package/templates/hono/src/questpie/server/.generated/index.ts +131 -0
- package/templates/hono/src/questpie/server/.generated/names.gen.ts +25 -0
- package/templates/hono/src/questpie/server/app.ts +10 -0
- package/templates/hono/src/questpie/server/collections/index.ts +1 -0
- package/templates/hono/src/questpie/server/collections/posts.collection.ts +10 -0
- package/templates/hono/src/questpie/server/config/auth.ts +8 -0
- package/templates/hono/src/questpie/server/config/openapi.ts +10 -0
- package/templates/hono/src/questpie/server/globals/index.ts +1 -0
- package/templates/hono/src/questpie/server/globals/site-settings.global.ts +10 -0
- package/templates/hono/src/questpie/server/modules.ts +8 -0
- package/templates/hono/src/questpie/server/questpie.config.ts +21 -0
- package/templates/hono/tsconfig.json +28 -0
- package/templates/next/AGENTS.md +55 -0
- package/templates/next/CLAUDE.md +39 -0
- package/templates/next/Dockerfile +25 -0
- package/templates/next/README.md +148 -0
- package/templates/next/components.json +22 -0
- package/templates/next/docker/init-extensions.sql +11 -0
- package/templates/next/docker-compose.yml +21 -0
- package/templates/next/env.example +16 -0
- package/templates/next/gitignore +10 -0
- package/templates/next/next-env.d.ts +5 -0
- package/templates/next/next.config.ts +20 -0
- package/templates/next/package.json +54 -0
- package/templates/next/postcss.config.mjs +8 -0
- package/templates/next/public/.gitkeep +0 -0
- package/templates/next/questpie.config.ts +12 -0
- package/templates/next/src/app/admin/[[...all]]/page.tsx +34 -0
- package/templates/next/src/app/admin/admin.css +4 -0
- package/templates/next/src/app/admin/layout.tsx +63 -0
- package/templates/next/src/app/api/[...all]/route.ts +24 -0
- package/templates/next/src/app/layout.tsx +24 -0
- package/templates/next/src/app/not-found.tsx +18 -0
- package/templates/next/src/app/page.tsx +74 -0
- package/templates/next/src/app/providers.tsx +11 -0
- package/templates/next/src/lib/auth-client.ts +12 -0
- package/templates/next/src/lib/client.ts +13 -0
- package/templates/next/src/lib/env.ts +24 -0
- package/templates/next/src/lib/query-client.ts +18 -0
- package/templates/next/src/lib/query.ts +18 -0
- package/templates/next/src/questpie/admin/.generated/client.ts +13 -0
- package/templates/next/src/questpie/admin/admin.ts +9 -0
- package/templates/next/src/questpie/admin/modules.ts +3 -0
- package/templates/next/src/questpie/server/.generated/context.gen.ts +204 -0
- package/templates/next/src/questpie/server/.generated/entities.gen.ts +100 -0
- package/templates/next/src/questpie/server/.generated/factories.ts +204 -0
- package/templates/next/src/questpie/server/.generated/index.ts +139 -0
- package/templates/next/src/questpie/server/.generated/names.gen.ts +31 -0
- package/templates/next/src/questpie/server/app.ts +10 -0
- package/templates/next/src/questpie/server/collections/index.ts +1 -0
- package/templates/next/src/questpie/server/collections/posts.collection.ts +58 -0
- package/templates/next/src/questpie/server/config/admin.ts +80 -0
- package/templates/next/src/questpie/server/config/auth.ts +8 -0
- package/templates/next/src/questpie/server/config/openapi.ts +10 -0
- package/templates/next/src/questpie/server/globals/index.ts +1 -0
- package/templates/next/src/questpie/server/globals/site-settings.global.ts +19 -0
- package/templates/next/src/questpie/server/modules.ts +9 -0
- package/templates/next/src/questpie/server/questpie.config.ts +21 -0
- package/templates/next/src/styles.css +125 -0
- package/templates/next/tsconfig.json +37 -0
- package/templates/tanstack-start/AGENTS.md +35 -600
- package/templates/tanstack-start/CLAUDE.md +26 -127
- package/templates/tanstack-start/README.md +20 -7
- package/templates/tanstack-start/docker/init-extensions.sql +11 -0
- package/templates/tanstack-start/docker-compose.yml +1 -0
- package/templates/tanstack-start/package.json +1 -0
- package/templates/tanstack-start/src/lib/auth-client.ts +1 -1
- package/templates/tanstack-start/src/lib/client.ts +1 -1
- package/templates/tanstack-start/src/lib/query.ts +18 -0
- package/templates/tanstack-start/src/questpie/admin/modules.ts +3 -1
- package/templates/tanstack-start/src/questpie/server/.generated/factories.ts +10 -9
- package/templates/tanstack-start/src/questpie/server/collections/index.ts +1 -1
- package/templates/tanstack-start/src/questpie/server/config/auth.ts +1 -1
- package/templates/tanstack-start/src/questpie/server/globals/index.ts +1 -1
- package/templates/tanstack-start/src/questpie/server/modules.ts +4 -5
- package/templates/tanstack-start/src/questpie/server/questpie.config.ts +3 -2
- package/templates/tanstack-start/src/routes/__root.tsx +31 -1
- package/templates/tanstack-start/src/routes/api/$.ts +2 -3
- package/templates/tanstack-start/src/routes/index.tsx +97 -0
- package/templates/tanstack-start/vite.config.ts +2 -2
- package/skills/questpie/AGENTS.md +0 -2670
- package/skills/questpie/SKILL.md +0 -260
- package/skills/questpie/references/auth.md +0 -121
- package/skills/questpie/references/business-logic.md +0 -550
- package/skills/questpie/references/codegen-plugin-api.md +0 -382
- package/skills/questpie/references/crud-api.md +0 -378
- package/skills/questpie/references/data-modeling.md +0 -493
- package/skills/questpie/references/extend.md +0 -557
- package/skills/questpie/references/field-types.md +0 -386
- package/skills/questpie/references/infrastructure-adapters.md +0 -545
- package/skills/questpie/references/multi-tenancy.md +0 -364
- package/skills/questpie/references/production.md +0 -475
- package/skills/questpie/references/query-operators.md +0 -125
- package/skills/questpie/references/quickstart.md +0 -564
- package/skills/questpie/references/rules.md +0 -389
- package/skills/questpie/references/tanstack-query.md +0 -520
- package/skills/questpie-admin/AGENTS.md +0 -1508
- package/skills/questpie-admin/SKILL.md +0 -436
- package/skills/questpie-admin/references/blocks.md +0 -331
- package/skills/questpie-admin/references/custom-ui.md +0 -305
- package/skills/questpie-admin/references/views.md +0 -449
|
@@ -1,475 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: questpie-core/production
|
|
3
|
-
description:
|
|
4
|
-
QUESTPIE production deployment authentication better-auth OAuth database PostgreSQL Drizzle storage S3 Flydrive queue pg-boss jobs realtime SSE pgNotify Redis migrations email SMTP KV key-value logger Pino OpenAPI Docker environment variables adapters infrastructure
|
|
5
|
-
- questpie-core
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
This skill builds on questpie-core.
|
|
9
|
-
|
|
10
|
-
## Overview
|
|
11
|
-
|
|
12
|
-
QUESTPIE uses an adapter-based architecture for all infrastructure. Development defaults work out of the box; production requires explicit adapter configuration in `questpie.config.ts`.
|
|
13
|
-
|
|
14
|
-
| Service | Dev Default | Production Adapter |
|
|
15
|
-
| -------- | --------------------- | ------------------------------------- |
|
|
16
|
-
| Database | PostgreSQL (local) | PostgreSQL (remote, SSL) |
|
|
17
|
-
| Storage | Local filesystem | S3-compatible (`s3` driver) |
|
|
18
|
-
| Queue | None (jobs skip) | pg-boss (`pgBossAdapter`) |
|
|
19
|
-
| Realtime | pgNotify | Redis Streams (`redisStreamsAdapter`) |
|
|
20
|
-
| Email | Console (logs output) | SMTP (`SmtpAdapter`) |
|
|
21
|
-
| KV Store | In-memory | Redis (`"redis"` adapter) |
|
|
22
|
-
| Logger | Pino (console) | Pino (structured JSON) |
|
|
23
|
-
|
|
24
|
-
## Authentication
|
|
25
|
-
|
|
26
|
-
QUESTPIE uses [Better Auth](https://www.better-auth.com/). Configure via `config/auth.ts`:
|
|
27
|
-
|
|
28
|
-
```ts
|
|
29
|
-
// src/questpie/server/config/auth.ts
|
|
30
|
-
import { authConfig } from "questpie";
|
|
31
|
-
|
|
32
|
-
export default authConfig({
|
|
33
|
-
emailAndPassword: {
|
|
34
|
-
enabled: true,
|
|
35
|
-
requireEmailVerification: false,
|
|
36
|
-
},
|
|
37
|
-
baseURL: process.env.APP_URL || "http://localhost:3000",
|
|
38
|
-
basePath: "/api/auth",
|
|
39
|
-
secret: process.env.BETTER_AUTH_SECRET || "change-me",
|
|
40
|
-
});
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
### Auth Options
|
|
44
|
-
|
|
45
|
-
| Option | Type | Description |
|
|
46
|
-
| ------------------------------------------- | --------- | --------------------------------------------------- |
|
|
47
|
-
| `emailAndPassword.enabled` | `boolean` | Enable email/password login |
|
|
48
|
-
| `emailAndPassword.requireEmailVerification` | `boolean` | Require email verification |
|
|
49
|
-
| `baseURL` | `string` | App public URL |
|
|
50
|
-
| `basePath` | `string` | Auth API path prefix |
|
|
51
|
-
| `secret` | `string` | Session signing secret (min 32 chars in production) |
|
|
52
|
-
|
|
53
|
-
### Session in Handlers
|
|
54
|
-
|
|
55
|
-
Access the current session in functions, hooks, and access rules:
|
|
56
|
-
|
|
57
|
-
```ts
|
|
58
|
-
handler: async ({ session }) => {
|
|
59
|
-
if (!session) throw new Error("Not authenticated");
|
|
60
|
-
const user = session.user;
|
|
61
|
-
// user.id, user.email, user.name
|
|
62
|
-
};
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
### Access Control with Session
|
|
66
|
-
|
|
67
|
-
```ts
|
|
68
|
-
.access({
|
|
69
|
-
read: true,
|
|
70
|
-
create: ({ session }) => !!session,
|
|
71
|
-
update: ({ session }) => (session?.user as any)?.role === "admin",
|
|
72
|
-
delete: ({ session }) => (session?.user as any)?.role === "admin",
|
|
73
|
-
})
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
The `adminModule` provides a built-in `user` collection for storing user accounts.
|
|
77
|
-
|
|
78
|
-
## Database
|
|
79
|
-
|
|
80
|
-
PostgreSQL with Drizzle ORM. Schema is generated from your collection and global definitions.
|
|
81
|
-
|
|
82
|
-
```ts
|
|
83
|
-
export default runtimeConfig({
|
|
84
|
-
db: {
|
|
85
|
-
url: process.env.DATABASE_URL || "postgres://localhost/myapp",
|
|
86
|
-
},
|
|
87
|
-
});
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
Raw access via `db` context, indexes via `.indexes()`. See `references/infrastructure-adapters.md` for field-to-column mapping and full details.
|
|
91
|
-
|
|
92
|
-
## Migrations
|
|
93
|
-
|
|
94
|
-
### Development: Push
|
|
95
|
-
|
|
96
|
-
Sync schema directly without migration files:
|
|
97
|
-
|
|
98
|
-
```bash
|
|
99
|
-
bunx questpie push
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
### Production: Migration Files
|
|
103
|
-
|
|
104
|
-
```bash
|
|
105
|
-
# Generate migration from schema diff
|
|
106
|
-
bunx questpie migrate:generate
|
|
107
|
-
|
|
108
|
-
# Run pending migrations
|
|
109
|
-
bunx questpie migrate:up
|
|
110
|
-
|
|
111
|
-
# Rollback last migration
|
|
112
|
-
bunx questpie migrate:down
|
|
113
|
-
|
|
114
|
-
# Drop everything and re-run (DESTRUCTIVE -- dev only)
|
|
115
|
-
bunx questpie migrate:fresh
|
|
116
|
-
|
|
117
|
-
# Reset migration tracking
|
|
118
|
-
bunx questpie migrate:reset
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
Configure migration and seed directories in `questpie.config.ts` under `cli.migrations.directory` and `cli.seeds.directory`. Run seeds with `bunx questpie seed`.
|
|
122
|
-
|
|
123
|
-
## Storage
|
|
124
|
-
|
|
125
|
-
QUESTPIE uses [Flydrive](https://flydrive.dev/) for file storage.
|
|
126
|
-
|
|
127
|
-
### Local (Development Default)
|
|
128
|
-
|
|
129
|
-
```ts
|
|
130
|
-
export default runtimeConfig({
|
|
131
|
-
storage: {
|
|
132
|
-
basePath: "/api",
|
|
133
|
-
},
|
|
134
|
-
});
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
### S3 (Production)
|
|
138
|
-
|
|
139
|
-
```ts
|
|
140
|
-
import { S3Driver } from "flydrive/drivers/s3";
|
|
141
|
-
|
|
142
|
-
export default runtimeConfig({
|
|
143
|
-
storage: {
|
|
144
|
-
basePath: "/api",
|
|
145
|
-
driver: new S3Driver({
|
|
146
|
-
bucket: process.env.S3_BUCKET,
|
|
147
|
-
region: process.env.S3_REGION,
|
|
148
|
-
accessKeyId: process.env.S3_ACCESS_KEY,
|
|
149
|
-
secretAccessKey: process.env.S3_SECRET_KEY,
|
|
150
|
-
}),
|
|
151
|
-
},
|
|
152
|
-
});
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
### Upload Fields
|
|
156
|
-
|
|
157
|
-
```ts
|
|
158
|
-
avatar: f.upload({
|
|
159
|
-
to: "assets",
|
|
160
|
-
mimeTypes: ["image/*"],
|
|
161
|
-
maxSize: 5_000_000,
|
|
162
|
-
}),
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
## Queue
|
|
166
|
-
|
|
167
|
-
Background job processing with [pg-boss](https://github.com/timgit/pg-boss). Jobs stored in PostgreSQL.
|
|
168
|
-
|
|
169
|
-
```ts
|
|
170
|
-
import { pgBossAdapter, runtimeConfig } from "questpie";
|
|
171
|
-
|
|
172
|
-
export default runtimeConfig({
|
|
173
|
-
queue: {
|
|
174
|
-
adapter: pgBossAdapter({
|
|
175
|
-
connectionString: process.env.DATABASE_URL,
|
|
176
|
-
}),
|
|
177
|
-
},
|
|
178
|
-
});
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
### Publishing Jobs
|
|
182
|
-
|
|
183
|
-
From hooks, functions, or other jobs:
|
|
184
|
-
|
|
185
|
-
```ts
|
|
186
|
-
handler: async ({ queue }) => {
|
|
187
|
-
await queue.sendAppointmentConfirmation.publish({
|
|
188
|
-
appointmentId: "abc",
|
|
189
|
-
customerId: "def",
|
|
190
|
-
});
|
|
191
|
-
};
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
The `queue` object is fully typed -- autocompletion shows all registered jobs and their payload schemas.
|
|
195
|
-
|
|
196
|
-
## Realtime
|
|
197
|
-
|
|
198
|
-
SSE-based live updates via `POST /realtime` multiplexed endpoint.
|
|
199
|
-
|
|
200
|
-
### pgNotify (Single Instance)
|
|
201
|
-
|
|
202
|
-
```ts
|
|
203
|
-
import { pgNotifyAdapter, runtimeConfig } from "questpie";
|
|
204
|
-
|
|
205
|
-
export default runtimeConfig({
|
|
206
|
-
realtime: {
|
|
207
|
-
adapter: pgNotifyAdapter({
|
|
208
|
-
connectionString: process.env.DATABASE_URL,
|
|
209
|
-
}),
|
|
210
|
-
},
|
|
211
|
-
});
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
### Redis Streams (Multi-Instance)
|
|
215
|
-
|
|
216
|
-
Required for horizontal scaling:
|
|
217
|
-
|
|
218
|
-
```ts
|
|
219
|
-
import { redisStreamsAdapter } from "questpie";
|
|
220
|
-
|
|
221
|
-
export default runtimeConfig({
|
|
222
|
-
realtime: {
|
|
223
|
-
adapter: redisStreamsAdapter({
|
|
224
|
-
url: process.env.REDIS_URL,
|
|
225
|
-
}),
|
|
226
|
-
},
|
|
227
|
-
});
|
|
228
|
-
```
|
|
229
|
-
|
|
230
|
-
## Email
|
|
231
|
-
|
|
232
|
-
Transactional email with typed templates. Two adapters: `SmtpAdapter` (production) and `ConsoleAdapter` (development).
|
|
233
|
-
|
|
234
|
-
```ts
|
|
235
|
-
import { SmtpAdapter, ConsoleAdapter, runtimeConfig } from "questpie";
|
|
236
|
-
|
|
237
|
-
export default runtimeConfig({
|
|
238
|
-
email: {
|
|
239
|
-
adapter:
|
|
240
|
-
process.env.NODE_ENV === "development"
|
|
241
|
-
? new ConsoleAdapter({ logHtml: false })
|
|
242
|
-
: new SmtpAdapter({
|
|
243
|
-
transport: { host: process.env.SMTP_HOST, port: 587, secure: true },
|
|
244
|
-
}),
|
|
245
|
-
},
|
|
246
|
-
});
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
Templates go in `emails/` directory using the `email()` factory. Send via `email.sendTemplate()` in handlers. See `references/infrastructure-adapters.md` for template examples.
|
|
250
|
-
|
|
251
|
-
## Search
|
|
252
|
-
|
|
253
|
-
PostgreSQL full-text search. Mark collections as searchable:
|
|
254
|
-
|
|
255
|
-
```ts
|
|
256
|
-
.searchable(["title", "body", "tags"])
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
Client usage:
|
|
260
|
-
|
|
261
|
-
```ts
|
|
262
|
-
const results = await client.search.search({
|
|
263
|
-
query: "haircut styles",
|
|
264
|
-
collections: ["posts", "services"],
|
|
265
|
-
limit: 20,
|
|
266
|
-
});
|
|
267
|
-
```
|
|
268
|
-
|
|
269
|
-
## KV Store
|
|
270
|
-
|
|
271
|
-
### Custom Adapter
|
|
272
|
-
|
|
273
|
-
```ts
|
|
274
|
-
export default runtimeConfig({
|
|
275
|
-
kv: {
|
|
276
|
-
adapter: myKvAdapter,
|
|
277
|
-
defaultTtl: 3600,
|
|
278
|
-
},
|
|
279
|
-
});
|
|
280
|
-
```
|
|
281
|
-
|
|
282
|
-
### In-Memory Default
|
|
283
|
-
|
|
284
|
-
```ts
|
|
285
|
-
kv: {
|
|
286
|
-
defaultTtl: 3600,
|
|
287
|
-
}
|
|
288
|
-
```
|
|
289
|
-
|
|
290
|
-
### Usage
|
|
291
|
-
|
|
292
|
-
```ts
|
|
293
|
-
handler: async ({ kv }) => {
|
|
294
|
-
await kv.set("key", "value", { ttl: 3600 });
|
|
295
|
-
const value = await kv.get("key");
|
|
296
|
-
await kv.delete("key");
|
|
297
|
-
};
|
|
298
|
-
```
|
|
299
|
-
|
|
300
|
-
## Logger
|
|
301
|
-
|
|
302
|
-
Structured logging with [Pino](https://getpino.io):
|
|
303
|
-
|
|
304
|
-
```ts
|
|
305
|
-
handler: async ({ logger }) => {
|
|
306
|
-
logger.info("Processing booking");
|
|
307
|
-
logger.error({ err: error }, "Booking failed");
|
|
308
|
-
logger.debug({ barberId, serviceId }, "Checking availability");
|
|
309
|
-
};
|
|
310
|
-
```
|
|
311
|
-
|
|
312
|
-
Log levels: `trace`, `debug`, `info`, `warn`, `error`, `fatal`.
|
|
313
|
-
|
|
314
|
-
Structured data goes as the first argument:
|
|
315
|
-
|
|
316
|
-
```ts
|
|
317
|
-
logger.info({ appointmentId: "abc", action: "created" }, "Appointment created");
|
|
318
|
-
```
|
|
319
|
-
|
|
320
|
-
## OpenAPI
|
|
321
|
-
|
|
322
|
-
Auto-generate OpenAPI 3.1 spec with `@questpie/openapi`. Install with `bun add @questpie/openapi`, add `openApiModule` to `modules.ts`, configure it in `config/openapi.ts` with `openApiConfig({ info: { title: "My API", version: "1.0.0" } })`, then run `bunx questpie generate`. Serves spec at `/api/openapi.json` and Scalar docs at `/api/docs`. See `references/infrastructure-adapters.md` for full options.
|
|
323
|
-
|
|
324
|
-
## Deployment
|
|
325
|
-
|
|
326
|
-
### Docker
|
|
327
|
-
|
|
328
|
-
```dockerfile
|
|
329
|
-
FROM oven/bun:1 AS base
|
|
330
|
-
WORKDIR /app
|
|
331
|
-
|
|
332
|
-
FROM base AS build
|
|
333
|
-
COPY package.json bun.lockb ./
|
|
334
|
-
RUN bun install --frozen-lockfile
|
|
335
|
-
COPY . .
|
|
336
|
-
RUN bunx questpie generate
|
|
337
|
-
RUN bun run build
|
|
338
|
-
|
|
339
|
-
FROM base AS production
|
|
340
|
-
COPY --from=build /app/.output /app/.output
|
|
341
|
-
EXPOSE 3000
|
|
342
|
-
CMD ["bun", "run", ".output/server/index.mjs"]
|
|
343
|
-
```
|
|
344
|
-
|
|
345
|
-
### Environment Variables
|
|
346
|
-
|
|
347
|
-
| Variable | Required | Description |
|
|
348
|
-
| ----------------------------------- | -------- | ------------------------------------- |
|
|
349
|
-
| `DATABASE_URL` | Yes | PostgreSQL connection string |
|
|
350
|
-
| `APP_URL` | Yes | Public URL of the application |
|
|
351
|
-
| `APP_SECRET` / `BETTER_AUTH_SECRET` | Yes | Session signing secret (min 32 chars) |
|
|
352
|
-
| `SMTP_HOST` | No | Email SMTP host |
|
|
353
|
-
| `SMTP_PORT` | No | Email SMTP port |
|
|
354
|
-
| `REDIS_URL` | No | Redis URL (for KV, realtime) |
|
|
355
|
-
| `S3_BUCKET` | No | S3 bucket name |
|
|
356
|
-
| `S3_REGION` | No | S3 region |
|
|
357
|
-
| `S3_ACCESS_KEY` | No | S3 access key |
|
|
358
|
-
| `S3_SECRET_KEY` | No | S3 secret key |
|
|
359
|
-
|
|
360
|
-
### Production Checklist
|
|
361
|
-
|
|
362
|
-
- Set strong `APP_SECRET` (min 32 characters)
|
|
363
|
-
- Use production `DATABASE_URL` with SSL
|
|
364
|
-
- Run `bunx questpie migrate:up` before deploying
|
|
365
|
-
- Configure SMTP for transactional email
|
|
366
|
-
- Set `APP_URL` to your public domain
|
|
367
|
-
- Enable HTTPS
|
|
368
|
-
- Configure S3 or persistent storage for uploads
|
|
369
|
-
- Use `redisStreamsAdapter` if running multiple instances
|
|
370
|
-
- Set up health checks
|
|
371
|
-
|
|
372
|
-
### Health Check
|
|
373
|
-
|
|
374
|
-
```ts
|
|
375
|
-
// routes/health.ts
|
|
376
|
-
import { route } from "questpie";
|
|
377
|
-
|
|
378
|
-
export default route({
|
|
379
|
-
method: "GET",
|
|
380
|
-
handler: async ({ db }) => {
|
|
381
|
-
await db.execute(sql`SELECT 1`);
|
|
382
|
-
return new Response(JSON.stringify({ status: "ok" }), {
|
|
383
|
-
headers: { "Content-Type": "application/json" },
|
|
384
|
-
});
|
|
385
|
-
},
|
|
386
|
-
});
|
|
387
|
-
```
|
|
388
|
-
|
|
389
|
-
## Common Mistakes
|
|
390
|
-
|
|
391
|
-
### CRITICAL: Missing BETTER_AUTH_SECRET in production
|
|
392
|
-
|
|
393
|
-
Without a strong secret, sessions can be forged. The default `"change-me"` is for development only.
|
|
394
|
-
|
|
395
|
-
```ts
|
|
396
|
-
// WRONG -- in production
|
|
397
|
-
secret: "change-me";
|
|
398
|
-
|
|
399
|
-
// CORRECT -- strong random secret from environment
|
|
400
|
-
secret: process.env.BETTER_AUTH_SECRET; // min 32 chars
|
|
401
|
-
```
|
|
402
|
-
|
|
403
|
-
### HIGH: Not running migrations after schema changes
|
|
404
|
-
|
|
405
|
-
When you add, remove, or change collection fields, the database schema must be updated. Without migrations, queries fail or return stale data.
|
|
406
|
-
|
|
407
|
-
```bash
|
|
408
|
-
# After changing any collection fields:
|
|
409
|
-
bunx questpie migrate:generate # create migration file
|
|
410
|
-
bunx questpie migrate:up # apply to database
|
|
411
|
-
|
|
412
|
-
# Or in development:
|
|
413
|
-
bunx questpie push # direct schema sync (no migration file)
|
|
414
|
-
```
|
|
415
|
-
|
|
416
|
-
### HIGH: Using local storage in production without persistent volume
|
|
417
|
-
|
|
418
|
-
The local storage adapter writes to the filesystem. In containerized deployments, files are lost when the container restarts.
|
|
419
|
-
|
|
420
|
-
```ts
|
|
421
|
-
// WRONG -- files lost on container restart
|
|
422
|
-
storage: { basePath: "/api" }
|
|
423
|
-
|
|
424
|
-
// CORRECT -- persistent S3 storage
|
|
425
|
-
import { S3Driver } from "flydrive/drivers/s3";
|
|
426
|
-
|
|
427
|
-
storage: {
|
|
428
|
-
basePath: "/api",
|
|
429
|
-
driver: new S3Driver({
|
|
430
|
-
bucket: process.env.S3_BUCKET,
|
|
431
|
-
region: process.env.S3_REGION,
|
|
432
|
-
accessKeyId: process.env.S3_ACCESS_KEY,
|
|
433
|
-
secretAccessKey: process.env.S3_SECRET_KEY,
|
|
434
|
-
}),
|
|
435
|
-
}
|
|
436
|
-
```
|
|
437
|
-
|
|
438
|
-
### MEDIUM: Missing queue adapter for background jobs
|
|
439
|
-
|
|
440
|
-
Without pg-boss configured, job `.publish()` calls silently do nothing. Jobs defined in `jobs/` will never run.
|
|
441
|
-
|
|
442
|
-
```ts
|
|
443
|
-
// REQUIRED for jobs to actually execute
|
|
444
|
-
queue: {
|
|
445
|
-
adapter: pgBossAdapter({
|
|
446
|
-
connectionString: process.env.DATABASE_URL,
|
|
447
|
-
}),
|
|
448
|
-
}
|
|
449
|
-
```
|
|
450
|
-
|
|
451
|
-
## Realtime and Live Preview
|
|
452
|
-
|
|
453
|
-
The realtime adapter (`pgNotifyAdapter` or `redisStreamsAdapter`) is relevant for **detached or shared preview sessions** — when the preview runs in a separate browser tab, or multiple collaborators view the same preview.
|
|
454
|
-
|
|
455
|
-
For the default **same-tab preview**, realtime is NOT involved. Current same-tab preview uses `postMessage` for refresh/focus messages between the editor and the iframe.
|
|
456
|
-
|
|
457
|
-
| Preview mode | Transport | Requires realtime adapter? |
|
|
458
|
-
| ------------------- | -------------- | -------------------------- |
|
|
459
|
-
| Same-tab (default) | `postMessage` | No |
|
|
460
|
-
| Detached tab | SSE / realtime | Yes |
|
|
461
|
-
| Shared / multi-user | SSE / realtime | Yes |
|
|
462
|
-
|
|
463
|
-
If your app only uses same-tab preview (the default), you do not need to configure a realtime adapter for preview purposes. Configure it when you need detached preview, multi-user collaboration, or other realtime features (live notifications, presence, etc.).
|
|
464
|
-
|
|
465
|
-
### MEDIUM: Missing APP_URL environment variable
|
|
466
|
-
|
|
467
|
-
Auth callbacks, email links, and storage URLs all depend on `APP_URL`. Without it, OAuth redirects break and email links point to `localhost`.
|
|
468
|
-
|
|
469
|
-
```bash
|
|
470
|
-
# WRONG
|
|
471
|
-
# APP_URL not set
|
|
472
|
-
|
|
473
|
-
# CORRECT
|
|
474
|
-
APP_URL=https://myapp.example.com
|
|
475
|
-
```
|
|
@@ -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
|
-
```
|