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,543 +0,0 @@
1
- ---
2
- name: questpie-tanstack-query
3
- description:
4
- QUESTPIE TanStack Query integration - createQuestpieQueryOptions option builders, useQuery useMutation queryOptions mutationOptions, collections globals routes, streamedQuery SSE realtime subscriptions, batch helpers, type inference AppConfig createClient, React data fetching caching, framework adapters TanStack Start Next.js Hono Elysia, frontend client SDK querying where orderBy pagination with select
5
- - questpie-core
6
- ---
7
-
8
- ## Overview
9
-
10
- `@questpie/tanstack-query` provides type-safe TanStack Query option builders for QUESTPIE. It creates `queryOptions()` and `mutationOptions()` objects that you pass directly to `useQuery()` and `useMutation()`. Full type inference flows from your server schema to React components.
11
-
12
- ## Installation
13
-
14
- ```bash
15
- bun add @questpie/tanstack-query @tanstack/react-query
16
- ```
17
-
18
- ## Setup
19
-
20
- ### 1. Create the QUESTPIE Client
21
-
22
- ```ts title="lib/client.ts"
23
- import { createClient } from "questpie/client";
24
- import type { AppConfig } from "#questpie";
25
-
26
- export const client = createClient<AppConfig>({
27
- baseURL:
28
- typeof window !== "undefined"
29
- ? window.location.origin
30
- : process.env.APP_URL || "http://localhost:3000",
31
- basePath: "/api",
32
- });
33
- ```
34
-
35
- ### 2. Create Query Options Proxy
36
-
37
- ```ts title="lib/queries.ts"
38
- import { createQuestpieQueryOptions } from "@questpie/tanstack-query";
39
- import { client } from "./client";
40
-
41
- export const q = createQuestpieQueryOptions(client, {
42
- keyPrefix: ["questpie"], // optional, default: ["questpie"]
43
- locale: "en", // optional, sets locale for all queries
44
- stage: undefined, // optional, workflow stage filter
45
- });
46
- ```
47
-
48
- ### 3. Wrap App with QueryClientProvider
49
-
50
- ```tsx title="app.tsx"
51
- import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
52
-
53
- const queryClient = new QueryClient();
54
-
55
- function App() {
56
- return (
57
- <QueryClientProvider client={queryClient}>
58
- <YourApp />
59
- </QueryClientProvider>
60
- );
61
- }
62
- ```
63
-
64
- ## Collection Queries
65
-
66
- ### Find (list)
67
-
68
- ```tsx
69
- import { useQuery } from "@tanstack/react-query";
70
- import { q } from "@/lib/queries";
71
-
72
- function PostList() {
73
- const { data, isLoading } = useQuery(
74
- q.collections.posts.find({
75
- where: { status: "published" },
76
- orderBy: { createdAt: "desc" },
77
- limit: 10,
78
- offset: 0,
79
- }),
80
- );
81
-
82
- if (isLoading) return <div>Loading...</div>;
83
-
84
- return (
85
- <ul>
86
- {data?.docs.map((post) => (
87
- <li key={post.id}>{post.title}</li>
88
- ))}
89
- <p>Total: {data?.totalDocs}</p>
90
- </ul>
91
- );
92
- }
93
- ```
94
-
95
- ### Find with Realtime
96
-
97
- Pass `{ realtime: true }` as the second argument to enable SSE-based live updates via `streamedQuery`:
98
-
99
- ```tsx
100
- function LivePostList() {
101
- const { data } = useQuery(
102
- q.collections.posts.find(
103
- { where: { status: "published" }, limit: 20 },
104
- { realtime: true },
105
- ),
106
- );
107
- // data auto-updates when posts change on the server
108
- return (
109
- <ul>
110
- {data?.docs.map((p) => (
111
- <li key={p.id}>{p.title}</li>
112
- ))}
113
- </ul>
114
- );
115
- }
116
- ```
117
-
118
- ### Find One
119
-
120
- ```tsx
121
- function PostDetail({ id }: { id: string }) {
122
- const { data: post } = useQuery(
123
- q.collections.posts.findOne({
124
- where: { id },
125
- with: { author: true, categories: true },
126
- }),
127
- );
128
-
129
- if (!post) return null;
130
- return <article>{post.title}</article>;
131
- }
132
- ```
133
-
134
- ### Count
135
-
136
- ```tsx
137
- const { data: count } = useQuery(
138
- q.collections.posts.count({ where: { status: "draft" } }),
139
- );
140
- ```
141
-
142
- ## Collection Mutations
143
-
144
- ### Create
145
-
146
- ```tsx
147
- import { useMutation, useQueryClient } from "@tanstack/react-query";
148
-
149
- function CreatePostForm() {
150
- const queryClient = useQueryClient();
151
- const create = useMutation({
152
- ...q.collections.posts.create(),
153
- onSuccess: () => {
154
- queryClient.invalidateQueries({
155
- queryKey: ["questpie", "collections", "posts"],
156
- });
157
- },
158
- });
159
-
160
- return (
161
- <form
162
- onSubmit={(e) => {
163
- e.preventDefault();
164
- create.mutate({
165
- title: "New Post",
166
- body: "Content here",
167
- status: "draft",
168
- });
169
- }}
170
- >
171
- <button type="submit">Create</button>
172
- </form>
173
- );
174
- }
175
- ```
176
-
177
- ### Update
178
-
179
- ```tsx
180
- const update = useMutation(q.collections.posts.update());
181
-
182
- update.mutate({ id: "post-id", data: { status: "published" } });
183
- ```
184
-
185
- ### Delete
186
-
187
- ```tsx
188
- const remove = useMutation(q.collections.posts.delete());
189
-
190
- remove.mutate({ id: "post-id" });
191
- ```
192
-
193
- ### Bulk Operations
194
-
195
- ```tsx
196
- // Update many
197
- const updateMany = useMutation(q.collections.posts.updateMany());
198
- updateMany.mutate({ where: { status: "draft" }, data: { status: "archived" } });
199
-
200
- // Delete many
201
- const deleteMany = useMutation(q.collections.posts.deleteMany());
202
- deleteMany.mutate({ where: { status: "archived" } });
203
- ```
204
-
205
- ### Versioning and Workflow Stages
206
-
207
- ```tsx
208
- const { data: versions } = useQuery(
209
- q.collections.posts.findVersions({ id: "post-id", limit: 10 }),
210
- );
211
- const revert = useMutation(q.collections.posts.revertToVersion());
212
- revert.mutate({ id: "post-id", version: 3 });
213
- const transition = useMutation(q.collections.posts.transitionStage());
214
- transition.mutate({ id: "post-id", stage: "published" });
215
- ```
216
-
217
- ## Global Queries
218
-
219
- ```tsx
220
- function SiteSettings() {
221
- const { data: settings } = useQuery(q.globals.siteSettings.get());
222
- const update = useMutation(q.globals.siteSettings.update());
223
-
224
- return (
225
- <div>
226
- <h1>{settings?.shopName}</h1>
227
- <button onClick={() => update.mutate({ shopName: "New Name" })}>
228
- Update
229
- </button>
230
- </div>
231
- );
232
- }
233
- ```
234
-
235
- ### Globals with Realtime
236
-
237
- ```tsx
238
- const { data } = useQuery(
239
- q.globals.siteSettings.get(undefined, { realtime: true }),
240
- );
241
- ```
242
-
243
- ## Routes
244
-
245
- Route calls support nested namespaces matching your `routes/` directory structure.
246
-
247
- ```tsx
248
- // routes/get-stats.ts -> routes.getStats
249
- const { data: stats } = useQuery(q.routes.getStats.query({ period: "week" }));
250
-
251
- // routes/booking/create.ts -> routes.booking.create
252
- const createBooking = useMutation(q.routes.booking.create.mutation());
253
-
254
- createBooking.mutate({
255
- barberId: "abc",
256
- serviceId: "def",
257
- scheduledAt: "2025-03-15T10:00:00Z",
258
- });
259
- ```
260
-
261
- ### Route Query Keys
262
-
263
- Access query keys for manual invalidation:
264
-
265
- ```tsx
266
- const queryClient = useQueryClient();
267
-
268
- // Get the query key for a specific route call
269
- const key = q.routes.getStats.key({ period: "week" });
270
- queryClient.invalidateQueries({ queryKey: key });
271
- ```
272
-
273
- ## Custom Queries
274
-
275
- For queries that don't fit the standard collection/global/route pattern:
276
-
277
- ```tsx
278
- const { data } = useQuery(
279
- q.custom.query({
280
- key: ["custom", "analytics"],
281
- queryFn: () => fetch("/analytics").then((r) => r.json()),
282
- }),
283
- );
284
-
285
- const mutation = useMutation(
286
- q.custom.mutation({
287
- key: ["custom", "import"],
288
- mutationFn: (file: File) => uploadFile(file),
289
- }),
290
- );
291
- ```
292
-
293
- ## Key Builder
294
-
295
- Build prefixed query keys for manual cache operations:
296
-
297
- ```tsx
298
- const key = q.key(["collections", "posts"]);
299
- // -> ["questpie", "collections", "posts"]
300
-
301
- queryClient.invalidateQueries({ queryKey: key });
302
- ```
303
-
304
- ## Query Operators (Where Clauses)
305
-
306
- All operators are type-safe based on your field definitions:
307
-
308
- ```ts
309
- // Equality
310
- where: { status: "published" }
311
-
312
- // Comparison
313
- where: { price: { gt: 1000, lte: 5000 } }
314
-
315
- // Date ranges
316
- where: { createdAt: { gte: new Date("2025-01-01"), lte: new Date("2025-12-31") } }
317
-
318
- // Text operations
319
- where: { title: { contains: "hello" } }
320
- where: { email: { startsWith: "john" } }
321
-
322
- // In
323
- where: { status: { in: ["draft", "published"] } }
324
-
325
- // Relations
326
- where: { author: "user-id-123" }
327
- ```
328
-
329
- ### Operators by Field Type
330
-
331
- | Field Type | Operators |
332
- | ------------------- | ---------------------------------------------------- |
333
- | `text` | `equals`, `contains`, `startsWith`, `endsWith`, `in` |
334
- | `number` | `equals`, `gt`, `gte`, `lt`, `lte`, `in` |
335
- | `boolean` | `equals` |
336
- | `date` / `datetime` | `equals`, `gt`, `gte`, `lt`, `lte` |
337
- | `select` | `equals`, `in` |
338
- | `relation` | `equals` (by ID) |
339
-
340
- ### OrderBy, Pagination, Relations, Select
341
-
342
- ```ts
343
- // OrderBy
344
- q.collections.posts.find({ orderBy: { createdAt: "desc" } });
345
-
346
- // Pagination
347
- q.collections.posts.find({ limit: 10, offset: 20 });
348
-
349
- // Include relations
350
- q.collections.posts.findOne({
351
- where: { id: "abc" },
352
- with: { author: true, comments: { with: { user: true } } },
353
- });
354
-
355
- // Select specific fields
356
- q.collections.posts.find({
357
- select: { id: true, title: true, status: true },
358
- });
359
- ```
360
-
361
- ## Type Inference
362
-
363
- Types flow end-to-end from schema definition to client SDK:
364
-
365
- ```text
366
- Field Definition Codegen Client SDK
367
- f.text().required() -> AppConfig type -> q.collections.posts.find()
368
- f.number() -> with field types -> where: { price: { gte: 1000 } }
369
- f.select([...]) -> and operators -> data.status === "published"
370
- ```
371
-
372
- The generated `AppConfig` type includes collections, globals, and routes:
373
-
374
- ```ts
375
- export type AppConfig = {
376
- collections: {
377
- posts: {
378
- select: { id: string; title: string; status: "draft" | "published" };
379
- insert: { title: string; status?: "draft" | "published" };
380
- where: {
381
- title?: string | { contains?: string };
382
- status?: "draft" | "published";
383
- };
384
- orderBy: { title?: "asc" | "desc"; createdAt?: "asc" | "desc" };
385
- };
386
- };
387
- };
388
- ```
389
-
390
- ## Direct Client Usage (without TanStack Query)
391
-
392
- The client can be used directly without the query options proxy:
393
-
394
- ```ts
395
- const { docs, totalDocs } = await client.collections.posts.find({
396
- where: { status: "published" },
397
- orderBy: { createdAt: "desc" },
398
- limit: 10,
399
- });
400
- const post = await client.collections.posts.findOne({
401
- where: { id: "abc" },
402
- with: { author: true },
403
- });
404
- await client.collections.posts.create({ title: "Hello", status: "draft" });
405
- await client.collections.posts.updateById({
406
- id: "abc",
407
- data: { status: "published" },
408
- });
409
- await client.collections.posts.deleteById({ id: "abc" });
410
- const settings = await client.globals.siteSettings.get();
411
- const result = await client.routes.createBooking({
412
- barberId: "abc",
413
- serviceId: "def",
414
- });
415
- client.setLocale("sk"); // Set locale for localized content
416
- ```
417
-
418
- ## Realtime
419
-
420
- Pass `{ realtime: true }` as the **typed** second argument (`RealtimeQueryConfig`) to `find()`, `count()`, or `get()` — initial data via a normal fetch, then the server pushes full access-controlled snapshots on every matching change. `findOne()` and `findVersions()` have no realtime form (a second argument there is a compile error).
421
-
422
- ```tsx
423
- const { data } = useQuery(
424
- q.collections.posts.find(
425
- { where: { status: "published" }, limit: 20 },
426
- { realtime: true },
427
- ),
428
- );
429
- ```
430
-
431
- Works zero-config (2s polling); add a realtime adapter for instant push:
432
-
433
- ```ts
434
- import { runtimeConfig } from "questpie/app";
435
- import { pgNotifyAdapter } from "questpie/adapters/pg-notify";
436
-
437
- export default runtimeConfig({
438
- realtime: {
439
- adapter: pgNotifyAdapter({ connectionString: process.env.DATABASE_URL }),
440
- },
441
- });
442
- ```
443
-
444
- Subscriptions are query-shaped topic objects (`{ resourceType, resource, where?, with? }`) — there are no channel strings. Outside React, use the typed live form of the same query: `client.collections.posts.live(options, onSnapshot)` / `liveIter(options)` (see AGENTS.md §19 Realtime).
445
-
446
- To build those topic objects yourself — e.g. manual cache invalidation or a raw `client.realtime.subscribe` call that must match the topic a query subscribed with — use the exported builders instead of hand-writing the shape:
447
-
448
- ```ts
449
- import { buildCollectionTopic, buildGlobalTopic } from "@questpie/tanstack-query"; // re-exported from questpie/client
450
-
451
- const topic = buildCollectionTopic("posts", { where: { status: "published" }, limit: 20 });
452
- const settingsTopic = buildGlobalTopic("siteSettings");
453
- ```
454
-
455
- For multi-instance deployments, create a Redis client and use `redisStreamsAdapter({ client })`.
456
-
457
- ## Framework Adapters
458
-
459
- **TanStack Start** (no adapter package needed):
460
-
461
- ```ts title="src/routes/api/$.ts"
462
- import { createAPIFileRoute } from "@tanstack/react-start/api";
463
- import { createFetchHandler } from "questpie/http";
464
- import { app } from "#questpie";
465
- const handler = createFetchHandler(app, { basePath: "/api" });
466
- export const Route = createAPIFileRoute("/api/$")({
467
- GET: ({ request }) => handler(request),
468
- POST: ({ request }) => handler(request),
469
- });
470
- ```
471
-
472
- **Next.js**: `import { questpieNextRouteHandlers } from "@questpie/next"` -- export `GET`, `POST`, `PATCH`, `DELETE` from `app/api/[...slug]/route.ts`. The lower-level `questpieNext(app, config)` returns a single fetch-style handler.
473
-
474
- **Hono**: `import { questpieHono } from "@questpie/hono/server"` -- `server.route("/api", questpieHono(app))`.
475
-
476
- **Elysia**: `import { questpieElysia } from "@questpie/elysia/server"` -- `.use(questpieElysia(app, { basePath: "/api" }))`.
477
-
478
- For server-side calls in the same process (SSR loaders, tests), `createClientFromHono` (`@questpie/hono/client`) and `createClientFromEden` (`@questpie/elysia/client`) build the typed client over the live server instance instead of HTTP.
479
-
480
- ## Common Mistakes
481
-
482
- ### HIGH: Creating the QUESTPIE client without proper base URL
483
-
484
- API calls fail silently or hit the wrong server. Always set `baseURL` correctly for both server and client environments:
485
-
486
- ```ts
487
- // WRONG -- hardcoded localhost breaks in production
488
- const client = createClient<AppConfig>({ baseURL: "http://localhost:3000" });
489
-
490
- // CORRECT -- environment-aware
491
- const client = createClient<AppConfig>({
492
- baseURL:
493
- typeof window !== "undefined"
494
- ? window.location.origin
495
- : process.env.APP_URL || "http://localhost:3000",
496
- basePath: "/api",
497
- });
498
- ```
499
-
500
- ### HIGH: Not wrapping app with QueryClientProvider
501
-
502
- Hooks throw "No QueryClient set" error. Always wrap your root component with `<QueryClientProvider client={new QueryClient()}>`.
503
-
504
- ### MEDIUM: Using raw fetch instead of the typed client
505
-
506
- Loses type safety and auth handling:
507
-
508
- ```ts
509
- // WRONG -- no types, no auth token forwarding
510
- const posts = await fetch("/api/collections/posts").then((r) => r.json());
511
-
512
- // CORRECT -- fully typed, auth handled
513
- const { docs } = await client.collections.posts.find({ limit: 10 });
514
- ```
515
-
516
- ### MEDIUM: Not setting up realtime adapter
517
-
518
- Collection changes do not auto-refresh when realtime is enabled but no adapter is configured. Add a realtime adapter in `questpie.config.ts`:
519
-
520
- ```ts
521
- import { runtimeConfig } from "questpie/app";
522
- import { pgNotifyAdapter } from "questpie/adapters/pg-notify";
523
-
524
- export default runtimeConfig({
525
- realtime: {
526
- adapter: pgNotifyAdapter({ connectionString: process.env.DATABASE_URL }),
527
- },
528
- });
529
- ```
530
-
531
- ### MEDIUM: Importing from `questpie/client` in server code or vice versa
532
-
533
- Violates the server/client boundary. Server code should import from `questpie`, client code from `questpie/client`:
534
-
535
- ```ts
536
- // WRONG -- client import in server handler
537
- import { createClient } from "questpie/client";
538
-
539
- // CORRECT -- server uses context-injected collections
540
- handler: async ({ collections }) => {
541
- return await collections.posts.find({});
542
- };
543
- ```