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.
- package/dist/index.mjs +362 -119
- 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 -607
- package/templates/tanstack-start/CLAUDE.md +26 -134
- package/templates/tanstack-start/README.md +13 -1
- package/templates/tanstack-start/docker/init-extensions.sql +11 -0
- package/templates/tanstack-start/docker-compose.yml +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/server/collections/index.ts +1 -1
- package/templates/tanstack-start/src/questpie/server/globals/index.ts +1 -1
- package/templates/tanstack-start/src/questpie/server/questpie.config.ts +1 -1
- package/templates/tanstack-start/src/routes/__root.tsx +31 -1
- package/templates/tanstack-start/src/routes/api/$.ts +1 -1
- package/templates/tanstack-start/src/routes/index.tsx +97 -0
- package/skills/questpie/AGENTS.md +0 -2871
- package/skills/questpie/SKILL.md +0 -293
- package/skills/questpie/coverage.json +0 -213
- package/skills/questpie/references/auth.md +0 -236
- package/skills/questpie/references/business-logic.md +0 -620
- package/skills/questpie/references/codegen-plugin-api.md +0 -382
- package/skills/questpie/references/crud-api.md +0 -580
- package/skills/questpie/references/data-modeling.md +0 -509
- package/skills/questpie/references/extend.md +0 -584
- package/skills/questpie/references/field-types.md +0 -398
- package/skills/questpie/references/infrastructure-adapters.md +0 -720
- package/skills/questpie/references/mcp.md +0 -147
- package/skills/questpie/references/multi-tenancy.md +0 -363
- package/skills/questpie/references/production.md +0 -640
- package/skills/questpie/references/query-operators.md +0 -125
- package/skills/questpie/references/quickstart.md +0 -562
- package/skills/questpie/references/rules.md +0 -454
- package/skills/questpie/references/sandbox.md +0 -110
- package/skills/questpie/references/tanstack-query.md +0 -543
- package/skills/questpie/references/type-inference.md +0 -167
- package/skills/questpie/references/workflows.md +0 -155
- package/skills/questpie-admin/AGENTS.md +0 -1515
- package/skills/questpie-admin/SKILL.md +0 -443
- 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,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 |
|