@questpie/next 0.0.1

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.
@@ -0,0 +1,14 @@
1
+
2
+ $ tsdown
3
+ ℹ tsdown v0.18.4 powered by rolldown v1.0.0-beta.57
4
+ ℹ config file: /Users/drepkovsky/questpie/repos/questpie-cms/packages/next/tsdown.config.ts
5
+ (node:61187) ExperimentalWarning: Type Stripping is an experimental feature and might change at any time
6
+ (Use `node --trace-warnings ...` to show where the warning was created)
7
+ ℹ entry: src/server.ts
8
+ ℹ tsconfig: tsconfig.json
9
+ ℹ Build start
10
+ ℹ Cleaning 2 files
11
+ ℹ dist/server.mjs 0.84 kB │ gzip: 0.43 kB
12
+ ℹ dist/server.d.mts 0.61 kB │ gzip: 0.31 kB
13
+ ℹ 2 files, total: 1.46 kB
14
+ ✔ Build complete in 5270ms
@@ -0,0 +1 @@
1
+ $ tsc --noEmit
package/CHANGELOG.md ADDED
@@ -0,0 +1,9 @@
1
+ # @questpie/next
2
+
3
+ ## 0.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - feat: initial release
8
+ - Updated dependencies
9
+ - questpie@0.0.1
package/README.md ADDED
@@ -0,0 +1,287 @@
1
+ # @questpie/next
2
+
3
+ Next.js App Router adapter for QUESTPIE CMS.
4
+
5
+ ## Features
6
+
7
+ - **App Router Support** - Catch-all route handlers for Next.js 13+
8
+ - **Type-Safe Client** - Full TypeScript support with `@questpie/cms/client`
9
+ - **Server Components** - Works with React Server Components
10
+ - **Edge Runtime** - Compatible with Edge Runtime (with limitations)
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ bun add @questpie/next @questpie/cms
16
+ ```
17
+
18
+ ## Quick Start
19
+
20
+ ### 1. Configure CMS
21
+
22
+ ```typescript
23
+ // src/cms/index.ts
24
+ import { defineQCMS } from "@questpie/cms";
25
+
26
+ export const cms = defineQCMS()
27
+ .db({ connectionString: process.env.DATABASE_URL! })
28
+ .collections({
29
+ /* your collections */
30
+ })
31
+ .auth({
32
+ baseURL: process.env.NEXT_PUBLIC_URL!,
33
+ secret: process.env.AUTH_SECRET!,
34
+ })
35
+ .build();
36
+
37
+ export type AppCMS = typeof cms;
38
+ ```
39
+
40
+ ### 2. Create Route Handler
41
+
42
+ ```typescript
43
+ // app/api/cms/[...path]/route.ts
44
+ import { questpieNextRouteHandlers } from "@questpie/next";
45
+ import { cms } from "@/cms";
46
+
47
+ export const { GET, POST, PUT, PATCH, DELETE } = questpieNextRouteHandlers(
48
+ cms,
49
+ {
50
+ basePath: "/api/cms",
51
+ },
52
+ );
53
+
54
+ // Enable dynamic rendering
55
+ export const dynamic = "force-dynamic";
56
+ ```
57
+
58
+ ### 3. Use the Client
59
+
60
+ ```typescript
61
+ // src/lib/cms-client.ts
62
+ import { createQCMSClient } from "@questpie/cms/client";
63
+ import type { AppCMS } from "@/cms";
64
+
65
+ export const cmsClient = createQCMSClient<AppCMS>({
66
+ baseURL: process.env.NEXT_PUBLIC_URL!,
67
+ basePath: "/api/cms",
68
+ });
69
+ ```
70
+
71
+ ## Configuration Options
72
+
73
+ ```typescript
74
+ questpieNextRouteHandlers(cms, {
75
+ // Base path for CMS routes (must match your route location)
76
+ basePath: "/api/cms",
77
+ });
78
+ ```
79
+
80
+ ## API Routes
81
+
82
+ The adapter creates the following routes under your base path:
83
+
84
+ ### Collections
85
+
86
+ | Method | Route | Description |
87
+ | ------ | ---------------------------------------- | -------------------- |
88
+ | GET | `/api/cms/collections/:name` | List items |
89
+ | POST | `/api/cms/collections/:name` | Create item |
90
+ | GET | `/api/cms/collections/:name/:id` | Get item |
91
+ | PATCH | `/api/cms/collections/:name/:id` | Update item |
92
+ | DELETE | `/api/cms/collections/:name/:id` | Delete item |
93
+ | POST | `/api/cms/collections/:name/:id/restore` | Restore soft-deleted |
94
+
95
+ ### Globals
96
+
97
+ | Method | Route | Description |
98
+ | ------ | ------------------------ | ------------- |
99
+ | GET | `/api/cms/globals/:name` | Get global |
100
+ | PATCH | `/api/cms/globals/:name` | Update global |
101
+
102
+ ### Storage
103
+
104
+ | Method | Route | Description |
105
+ | ------ | ------------------------- | ----------- |
106
+ | POST | `/api/cms/storage/upload` | Upload file |
107
+
108
+ ### Authentication
109
+
110
+ | Method | Route | Description |
111
+ | ------ | ----------------- | ------------------ |
112
+ | ALL | `/api/cms/auth/*` | Better Auth routes |
113
+
114
+ ### Realtime
115
+
116
+ | Method | Route | Description |
117
+ | ------ | -------------------------------------- | ---------------- |
118
+ | GET | `/api/cms/collections/:name/subscribe` | SSE subscription |
119
+ | GET | `/api/cms/globals/:name/subscribe` | SSE subscription |
120
+
121
+ ## Usage Examples
122
+
123
+ ### Server Component
124
+
125
+ ```typescript
126
+ // app/posts/page.tsx
127
+ import { cmsClient } from "@/lib/cms-client"
128
+
129
+ export default async function PostsPage() {
130
+ const posts = await cmsClient.collections.posts.find({
131
+ where: { published: true },
132
+ orderBy: { publishedAt: "desc" },
133
+ limit: 10,
134
+ })
135
+
136
+ return (
137
+ <ul>
138
+ {posts.data.map((post) => (
139
+ <li key={post.id}>{post.title}</li>
140
+ ))}
141
+ </ul>
142
+ )
143
+ }
144
+ ```
145
+
146
+ ### With Relations
147
+
148
+ ```typescript
149
+ const post = await cmsClient.collections.posts.findOne({
150
+ where: { slug: params.slug },
151
+ with: {
152
+ author: true,
153
+ category: true,
154
+ },
155
+ });
156
+ ```
157
+
158
+ ### Client Component with TanStack Query
159
+
160
+ ```typescript
161
+ "use client";
162
+
163
+ import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
164
+ import { cmsClient } from "@/lib/cms-client";
165
+
166
+ export function PostsList() {
167
+ const { data: posts } = useQuery({
168
+ queryKey: ["posts"],
169
+ queryFn: () => cmsClient.collections.posts.find({ limit: 10 }),
170
+ });
171
+
172
+ const queryClient = useQueryClient();
173
+
174
+ const createPost = useMutation({
175
+ mutationFn: (data: { title: string }) =>
176
+ cmsClient.collections.posts.create(data),
177
+ onSuccess: () => {
178
+ queryClient.invalidateQueries({ queryKey: ["posts"] });
179
+ },
180
+ });
181
+
182
+ // ...
183
+ }
184
+ ```
185
+
186
+ ### File Upload
187
+
188
+ ```typescript
189
+ async function uploadFile(file: File) {
190
+ const formData = new FormData();
191
+ formData.append("file", file);
192
+
193
+ const response = await fetch("/api/cms/storage/upload", {
194
+ method: "POST",
195
+ body: formData,
196
+ });
197
+
198
+ return response.json();
199
+ }
200
+ ```
201
+
202
+ ### Authentication
203
+
204
+ ```typescript
205
+ // Sign in
206
+ await cmsClient.auth.signIn({
207
+ email: "user@example.com",
208
+ password: "password",
209
+ });
210
+
211
+ // Get session
212
+ const session = await cmsClient.auth.getSession();
213
+
214
+ // Sign out
215
+ await cmsClient.auth.signOut();
216
+ ```
217
+
218
+ ## Server Actions
219
+
220
+ You can also use the CMS directly in Server Actions:
221
+
222
+ ```typescript
223
+ // app/actions.ts
224
+ "use server";
225
+
226
+ import { cms } from "@/cms";
227
+ import { revalidatePath } from "next/cache";
228
+
229
+ export async function createPost(formData: FormData) {
230
+ const post = await cms.collections.posts.create({
231
+ title: formData.get("title") as string,
232
+ content: formData.get("content") as string,
233
+ });
234
+
235
+ revalidatePath("/posts");
236
+ return post;
237
+ }
238
+ ```
239
+
240
+ ## Middleware
241
+
242
+ Protect routes with Next.js middleware:
243
+
244
+ ```typescript
245
+ // middleware.ts
246
+ import { NextResponse } from "next/server";
247
+ import type { NextRequest } from "next/server";
248
+
249
+ export function middleware(request: NextRequest) {
250
+ const session = request.cookies.get("session");
251
+
252
+ if (!session && request.nextUrl.pathname.startsWith("/admin")) {
253
+ return NextResponse.redirect(new URL("/login", request.url));
254
+ }
255
+
256
+ return NextResponse.next();
257
+ }
258
+
259
+ export const config = {
260
+ matcher: ["/admin/:path*"],
261
+ };
262
+ ```
263
+
264
+ ## Environment Variables
265
+
266
+ ```env
267
+ # Required
268
+ DATABASE_URL=postgresql://...
269
+ NEXT_PUBLIC_URL=http://localhost:3000
270
+ AUTH_SECRET=your-secret-key
271
+
272
+ # Optional (for storage)
273
+ S3_BUCKET=your-bucket
274
+ S3_REGION=us-east-1
275
+ S3_ACCESS_KEY=...
276
+ S3_SECRET_KEY=...
277
+ ```
278
+
279
+ ## Related Packages
280
+
281
+ - [`@questpie/cms`](../cms) - Core CMS engine
282
+ - [`@questpie/admin`](../admin) - Admin UI
283
+ - [`@questpie/tanstack-query`](../tanstack-query) - TanStack Query integration
284
+
285
+ ## License
286
+
287
+ MIT
@@ -0,0 +1,15 @@
1
+ import { CMSAdapterConfig, Questpie } from "questpie";
2
+
3
+ //#region src/server.d.ts
4
+ type NextAdapterConfig = CMSAdapterConfig;
5
+ type NextHandler = (request: Request) => Promise<Response>;
6
+ /**
7
+ * Create a Next.js-compatible handler for QUESTPIE CMS routes.
8
+ */
9
+ declare const questpieNext: (cms: Questpie<any>, config?: NextAdapterConfig) => NextHandler;
10
+ /**
11
+ * Convenience helpers for Next.js route handlers.
12
+ */
13
+ declare const questpieNextRouteHandlers: (cms: Questpie<any>, config?: NextAdapterConfig) => Record<string, NextHandler>;
14
+ //#endregion
15
+ export { NextAdapterConfig, questpieNext, questpieNextRouteHandlers };
@@ -0,0 +1,34 @@
1
+ import { createFetchHandler } from "questpie";
2
+
3
+ //#region src/server.ts
4
+ const notFoundResponse = () => new Response(JSON.stringify({ error: "Not found" }), {
5
+ status: 404,
6
+ headers: { "Content-Type": "application/json" }
7
+ });
8
+ /**
9
+ * Create a Next.js-compatible handler for QUESTPIE CMS routes.
10
+ */
11
+ const questpieNext = (cms, config = {}) => {
12
+ const handler = createFetchHandler(cms, config);
13
+ return async (request) => {
14
+ return await handler(request) ?? notFoundResponse();
15
+ };
16
+ };
17
+ /**
18
+ * Convenience helpers for Next.js route handlers.
19
+ */
20
+ const questpieNextRouteHandlers = (cms, config = {}) => {
21
+ const handler = questpieNext(cms, config);
22
+ return {
23
+ GET: handler,
24
+ POST: handler,
25
+ PATCH: handler,
26
+ DELETE: handler,
27
+ PUT: handler,
28
+ OPTIONS: handler,
29
+ HEAD: handler
30
+ };
31
+ };
32
+
33
+ //#endregion
34
+ export { questpieNext, questpieNextRouteHandlers };
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@questpie/next",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "scripts": {
6
+ "build": "tsdown",
7
+ "check-types": "tsc --noEmit"
8
+ },
9
+ "dependencies": {
10
+ "questpie": "workspace:*"
11
+ },
12
+ "devDependencies": {
13
+ "@questpie/typescript-config": "workspace:*",
14
+ "bun-types": "latest",
15
+ "tsdown": "^0.18.3"
16
+ },
17
+ "peerDependencies": {
18
+ "questpie": "workspace:*"
19
+ },
20
+ "exports": {
21
+ ".": {
22
+ "import": "./dist/server.mjs",
23
+ "types": "./dist/server.d.mts"
24
+ }
25
+ },
26
+ "publishConfig": {
27
+ "access": "public"
28
+ }
29
+ }
package/src/server.ts ADDED
@@ -0,0 +1,48 @@
1
+ import { createFetchHandler, type CMSAdapterConfig, type Questpie } from "questpie";
2
+
3
+ export type NextAdapterConfig = CMSAdapterConfig;
4
+
5
+ type NextHandler = (request: Request) => Promise<Response>;
6
+
7
+ const notFoundResponse = () =>
8
+ new Response(JSON.stringify({ error: "Not found" }), {
9
+ status: 404,
10
+ headers: {
11
+ "Content-Type": "application/json",
12
+ },
13
+ });
14
+
15
+ /**
16
+ * Create a Next.js-compatible handler for QUESTPIE CMS routes.
17
+ */
18
+ export const questpieNext = (
19
+ cms: Questpie<any>,
20
+ config: NextAdapterConfig = {},
21
+ ): NextHandler => {
22
+ const handler = createFetchHandler(cms, config);
23
+
24
+ return async (request) => {
25
+ const response = await handler(request);
26
+ return response ?? notFoundResponse();
27
+ };
28
+ };
29
+
30
+ /**
31
+ * Convenience helpers for Next.js route handlers.
32
+ */
33
+ export const questpieNextRouteHandlers = (
34
+ cms: Questpie<any>,
35
+ config: NextAdapterConfig = {},
36
+ ): Record<string, NextHandler> => {
37
+ const handler = questpieNext(cms, config);
38
+
39
+ return {
40
+ GET: handler,
41
+ POST: handler,
42
+ PATCH: handler,
43
+ DELETE: handler,
44
+ PUT: handler,
45
+ OPTIONS: handler,
46
+ HEAD: handler,
47
+ };
48
+ };
package/tsconfig.json ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "extends": "@questpie/typescript-config/bun.json",
3
+ "include": ["src/**/*"],
4
+ "exclude": ["node_modules", "dist"]
5
+ }