rpc4next 0.5.1 → 0.6.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/README.md +269 -232
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -2,83 +2,148 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://deepwiki.com/watanabe-1/rpc4next)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
`rpc4next` is a lightweight, type-safe RPC layer for Next.js App Router projects.
|
|
6
|
+
It scans your existing `app/**` files, generates a `PathStructure` type, and lets you call route handlers through a typed client without introducing a custom server framework.
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
It is inspired by Hono RPC and Pathpida:
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
- `route.ts` files become typed RPC endpoints
|
|
11
|
+
- `page.tsx` files become typed URL/path entries
|
|
12
|
+
- dynamic segments and exported route `Query` types are reflected in generated client types
|
|
13
|
+
- optional generated `params.ts` files can give route files a stable sibling `Params` type
|
|
10
14
|
|
|
11
|
-
|
|
15
|
+
If you want to see a full working example, start with the real integration fixture in [integration/next-app/README.md](./integration/next-app/README.md). It shows how route scanning, generated types, the client, and a real Next.js app fit together in this repository.
|
|
12
16
|
|
|
13
|
-
|
|
17
|
+
## What It Covers
|
|
14
18
|
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
19
|
+
- Typed client calls for `app/**/route.ts`
|
|
20
|
+
- Typed URL generation for `app/**/page.tsx`
|
|
21
|
+
- Dynamic routes, catch-all routes, and optional catch-all routes
|
|
22
|
+
- Route groups and parallel-route descendants
|
|
23
|
+
- Validation helpers for `params`, `query`, `json`, `headers`, and `cookies`
|
|
24
|
+
- Plain Next.js route handlers written with `NextResponse.json(...)` or `Response.json(...)`
|
|
18
25
|
|
|
19
|
-
|
|
26
|
+
Routing notes:
|
|
20
27
|
|
|
21
|
-
|
|
28
|
+
- Route group folders do not appear in generated public paths
|
|
29
|
+
- Parallel route slot names are excluded, but their descendant pages are flattened onto public URL paths
|
|
30
|
+
- Intercepting route branches are excluded from `PathStructure` because rpc4next models public URL paths
|
|
22
31
|
|
|
23
|
-
|
|
32
|
+
This is a good fit if you want typed client calls and typed URLs from an existing App Router codebase without moving to a custom RPC server framework. If you already want to keep writing normal `route.ts` and `page.tsx` files, `rpc4next` is designed for that.
|
|
24
33
|
|
|
25
|
-
|
|
26
|
-
- ✅ 既存の `app/**/route.ts` および `app/**/page.tsx` を活用するため、新たなハンドラファイルの作成は不要
|
|
27
|
-
- ✅ 最小限のセットアップで、カスタムサーバー不要
|
|
28
|
-
- ✅ 動的ルート(`[id]`、`[...slug]` など)に対応
|
|
29
|
-
- ✅ CLI による自動クライアント用型定義生成
|
|
34
|
+
## Requirements
|
|
30
35
|
|
|
31
|
-
|
|
36
|
+
- Node.js `>=20.19.2`
|
|
37
|
+
- Next.js App Router
|
|
38
|
+
- Package peer dependency support in `rpc4next` and `rpc4next-cli`: Next.js `^14`, `^15`, or `^16`
|
|
32
39
|
|
|
33
|
-
##
|
|
34
|
-
|
|
35
|
-
### 1. Install Packages
|
|
40
|
+
## Installation
|
|
36
41
|
|
|
37
42
|
```bash
|
|
38
43
|
npm install rpc4next
|
|
39
44
|
npm install -D rpc4next-cli
|
|
40
45
|
```
|
|
41
46
|
|
|
42
|
-
|
|
47
|
+
If you use Bun in your project:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
bun add rpc4next
|
|
51
|
+
bun add -d rpc4next-cli
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
`zod` is only needed if you use the server-side validation helpers such as
|
|
55
|
+
`zValidator()`. If you only use the generated client types and do not validate
|
|
56
|
+
request input with Zod, you can omit it.
|
|
57
|
+
|
|
58
|
+
If you want Zod-based request validation later:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
npm install zod
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Quick Start
|
|
65
|
+
|
|
66
|
+
If you prefer to inspect a complete app before wiring this into your own project, see [integration/next-app/README.md](./integration/next-app/README.md).
|
|
67
|
+
|
|
68
|
+
### 1. Define a Route
|
|
69
|
+
|
|
70
|
+
`rpc4next` does not require `routeHandlerFactory()`.
|
|
71
|
+
It can scan and generate client types from standard Next.js App Router handlers as-is.
|
|
72
|
+
The server helpers are optional and mainly give you stronger response and validation typing.
|
|
43
73
|
|
|
44
|
-
|
|
45
|
-
さらに、クエリパラメータ(searchParams)の型安全性を有効にするには、対象のファイル内で `Query` 型を定義し、`export` してください。
|
|
74
|
+
If you want the stronger typed server-side experience, use `routeHandlerFactory()`:
|
|
46
75
|
|
|
47
76
|
```ts
|
|
48
|
-
// app/api/
|
|
49
|
-
import {
|
|
77
|
+
// app/api/users/[userId]/route.ts
|
|
78
|
+
import { routeHandlerFactory } from "rpc4next/server";
|
|
50
79
|
|
|
51
|
-
// searchParams用の型定義
|
|
52
80
|
export type Query = {
|
|
53
|
-
|
|
54
|
-
page?: number; // 任意
|
|
81
|
+
includePosts?: "true" | "false";
|
|
55
82
|
};
|
|
56
83
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
84
|
+
const createRouteHandler = routeHandlerFactory();
|
|
85
|
+
|
|
86
|
+
export const { GET } = createRouteHandler<{
|
|
87
|
+
params: { userId: string };
|
|
88
|
+
query: Query;
|
|
89
|
+
}>().get(async (rc) => {
|
|
90
|
+
const { userId } = await rc.req.params();
|
|
91
|
+
const query = rc.req.query();
|
|
92
|
+
|
|
93
|
+
return rc.json({
|
|
94
|
+
ok: true,
|
|
95
|
+
userId,
|
|
96
|
+
includePosts: query.includePosts === "true",
|
|
97
|
+
});
|
|
98
|
+
});
|
|
65
99
|
```
|
|
66
100
|
|
|
67
|
-
|
|
101
|
+
Notes:
|
|
68
102
|
|
|
69
|
-
-
|
|
103
|
+
- `routeHandlerFactory()` is optional, not required
|
|
104
|
+
- Export `Query` from a route if you want the generated client to type `searchParams` for plain Next.js handlers too
|
|
105
|
+
- `routeHandlerFactory()` gives you typed helpers such as `rc.json()`, `rc.text()`, and `rc.redirect()`
|
|
106
|
+
- Validation helpers such as `zValidator()` are optional
|
|
70
107
|
|
|
71
|
-
|
|
108
|
+
If you also want request validation with Zod, add `zValidator()`:
|
|
72
109
|
|
|
73
|
-
|
|
110
|
+
```ts
|
|
111
|
+
import { routeHandlerFactory } from "rpc4next/server";
|
|
112
|
+
import { zValidator } from "rpc4next/server/validators/zod";
|
|
113
|
+
import { z } from "zod";
|
|
114
|
+
|
|
115
|
+
const createRouteHandler = routeHandlerFactory();
|
|
74
116
|
|
|
75
|
-
|
|
117
|
+
const querySchema = z.object({
|
|
118
|
+
includePosts: z.enum(["true", "false"]).optional(),
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
export const { GET } = createRouteHandler<{
|
|
122
|
+
params: { userId: string };
|
|
123
|
+
query: z.infer<typeof querySchema>;
|
|
124
|
+
}>().get(zValidator("query", querySchema), async (rc) => {
|
|
125
|
+
const query = rc.req.valid("query");
|
|
126
|
+
return rc.json({ ok: true, includePosts: query.includePosts === "true" });
|
|
127
|
+
});
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
`zValidator()` validates request input and returns `400` JSON errors by default on invalid input.
|
|
131
|
+
|
|
132
|
+
### 2. Generate `PathStructure`
|
|
133
|
+
|
|
134
|
+
Generate the client types from your `app` directory:
|
|
76
135
|
|
|
77
136
|
```bash
|
|
78
|
-
npx rpc4next
|
|
137
|
+
npx rpc4next app src/generated/rpc.ts
|
|
79
138
|
```
|
|
80
139
|
|
|
81
|
-
|
|
140
|
+
If you use Bun:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
bunx rpc4next app src/generated/rpc.ts
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
You can also configure the CLI with `rpc4next.config.json`:
|
|
82
147
|
|
|
83
148
|
```json
|
|
84
149
|
{
|
|
@@ -88,290 +153,262 @@ npx rpc4next <baseDir> <outputPath>
|
|
|
88
153
|
}
|
|
89
154
|
```
|
|
90
155
|
|
|
91
|
-
|
|
156
|
+
Then run:
|
|
92
157
|
|
|
93
158
|
```bash
|
|
94
159
|
npx rpc4next
|
|
95
|
-
npx rpc4next --watch
|
|
96
160
|
```
|
|
97
161
|
|
|
98
|
-
|
|
99
|
-
- `<outputPath>`: 生成された型定義ファイルの出力先
|
|
162
|
+
Or with Bun:
|
|
100
163
|
|
|
101
|
-
|
|
164
|
+
```bash
|
|
165
|
+
bunx rpc4next
|
|
166
|
+
```
|
|
102
167
|
|
|
103
|
-
|
|
104
|
-
ファイル変更を検知して自動的に再生成する場合は `--watch` or `-w` オプションを付けます。
|
|
168
|
+
Positional arguments:
|
|
105
169
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
```
|
|
170
|
+
- `<baseDir>`: the App Router root to scan, such as `app`
|
|
171
|
+
- `<outputPath>`: the file to generate, such as `src/generated/rpc.ts`
|
|
109
172
|
|
|
110
|
-
|
|
111
|
-
各ルートに対して個別のパラメータ型定義ファイルを生成する場合は、`--params-file` or `-p` オプションにファイル名を指定します。
|
|
173
|
+
Useful options:
|
|
112
174
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
```
|
|
175
|
+
- `-w`, `--watch`: regenerate on file changes
|
|
176
|
+
- `-p`, `--params-file [filename]`: generate sibling params files such as `app/users/[userId]/params.ts`
|
|
116
177
|
|
|
117
|
-
|
|
178
|
+
Examples:
|
|
118
179
|
|
|
119
|
-
|
|
180
|
+
```bash
|
|
181
|
+
npx rpc4next --watch
|
|
182
|
+
npx rpc4next app src/generated/rpc.ts --params-file params.ts
|
|
183
|
+
```
|
|
120
184
|
|
|
121
|
-
|
|
185
|
+
### 3. Create a Client
|
|
122
186
|
|
|
123
187
|
```ts
|
|
124
|
-
// lib/
|
|
125
|
-
import {
|
|
126
|
-
import type { PathStructure } from "
|
|
188
|
+
// src/lib/rpc-client.ts
|
|
189
|
+
import { createRpcClient } from "rpc4next/client";
|
|
190
|
+
import type { PathStructure } from "../generated/rpc";
|
|
127
191
|
|
|
128
|
-
export const rpc =
|
|
192
|
+
export const rpc = createRpcClient<PathStructure>("");
|
|
129
193
|
```
|
|
130
194
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
### 5. Use It in Your Components
|
|
134
|
-
|
|
135
|
-
コンポーネント内で生成された RPC クライアントを使用します。
|
|
195
|
+
Use `""` for same-origin calls in the browser, or pass an absolute base URL for server-side or cross-origin usage.
|
|
136
196
|
|
|
137
|
-
|
|
138
|
-
// app/page.tsx
|
|
139
|
-
import { rpc } from "@/lib/rpcClient";
|
|
197
|
+
### 4. Call Routes
|
|
140
198
|
|
|
141
|
-
|
|
142
|
-
const res = await rpc.api.user._id("123").$get({
|
|
143
|
-
query: { q: "hello", page: 1 },
|
|
144
|
-
});
|
|
145
|
-
const json = await res.json();
|
|
146
|
-
return <div>{json.q}</div>;
|
|
147
|
-
}
|
|
148
|
-
```
|
|
199
|
+
Generated client naming follows the App Router path shape:
|
|
149
200
|
|
|
150
|
-
-
|
|
151
|
-
-
|
|
201
|
+
- static segments stay as property access, such as `rpc.api.users`
|
|
202
|
+
- dynamic segments become callable helpers, such as `[userId] -> ._userId("123")`
|
|
203
|
+
- `route.ts` methods become `$get()`, `$post()`, and so on
|
|
204
|
+
- `page.tsx` entries can be turned into typed URLs with `$url()`
|
|
152
205
|
|
|
153
|
-
|
|
206
|
+
```ts
|
|
207
|
+
const response = await rpc.api.users._userId("123").$get({
|
|
208
|
+
url: { query: { includePosts: "true" } },
|
|
209
|
+
});
|
|
154
210
|
|
|
155
|
-
|
|
211
|
+
const data = await response.json();
|
|
212
|
+
```
|
|
156
213
|
|
|
157
|
-
|
|
214
|
+
For JSON request bodies:
|
|
158
215
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
216
|
+
```ts
|
|
217
|
+
const response = await rpc.api.posts.$post({
|
|
218
|
+
body: { json: { title: "hello" } },
|
|
219
|
+
});
|
|
220
|
+
```
|
|
162
221
|
|
|
163
|
-
|
|
164
|
-
- `status`, `content-type`, `json()`, `text()` などが適切に補完される
|
|
222
|
+
For request headers and cookies:
|
|
165
223
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
224
|
+
```ts
|
|
225
|
+
const response = await rpc.api["request-meta"].$get({
|
|
226
|
+
requestHeaders: {
|
|
227
|
+
headers: { "x-integration-test": "example" },
|
|
228
|
+
cookies: { session: "abc123" },
|
|
229
|
+
},
|
|
230
|
+
});
|
|
231
|
+
```
|
|
169
232
|
|
|
170
|
-
|
|
233
|
+
### 5. Generate Typed URLs for Pages
|
|
171
234
|
|
|
172
|
-
|
|
235
|
+
`page.tsx` files are included in the generated path tree, so you can build typed URLs even when there is no RPC method to call.
|
|
173
236
|
|
|
174
237
|
```ts
|
|
175
|
-
const
|
|
176
|
-
rc.text("error", { status: 400 }),
|
|
177
|
-
);
|
|
238
|
+
const photoUrl = rpc.photo._id("42").$url();
|
|
178
239
|
|
|
179
|
-
|
|
240
|
+
photoUrl.path;
|
|
241
|
+
photoUrl.relativePath;
|
|
242
|
+
photoUrl.pathname;
|
|
243
|
+
photoUrl.params;
|
|
180
244
|
```
|
|
181
245
|
|
|
182
|
-
|
|
246
|
+
## Server Helpers
|
|
183
247
|
|
|
184
|
-
|
|
248
|
+
### `routeHandlerFactory`
|
|
185
249
|
|
|
186
|
-
|
|
250
|
+
`routeHandlerFactory()` creates typed handlers for:
|
|
187
251
|
|
|
188
|
-
|
|
252
|
+
- `get`
|
|
253
|
+
- `post`
|
|
254
|
+
- `put`
|
|
255
|
+
- `delete`
|
|
256
|
+
- `patch`
|
|
257
|
+
- `head`
|
|
258
|
+
- `options`
|
|
189
259
|
|
|
190
|
-
|
|
260
|
+
It also supports a shared error handler:
|
|
191
261
|
|
|
192
262
|
```ts
|
|
193
|
-
import {
|
|
194
|
-
import { zValidator } from "@/path/to/zValidator";
|
|
195
|
-
import { z } from "zod";
|
|
263
|
+
import { routeHandlerFactory } from "rpc4next/server";
|
|
196
264
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
userId: z.string(),
|
|
265
|
+
const createRouteHandler = routeHandlerFactory((error, rc) => {
|
|
266
|
+
return rc.text("error", 400);
|
|
200
267
|
});
|
|
201
268
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
}>().get(
|
|
206
|
-
zValidator("params", paramsSchema), // paramsを検証
|
|
207
|
-
async (rc) => {
|
|
208
|
-
const params = rc.req.valid("params"); // バリデーション済みparamsを取得
|
|
209
|
-
return rc.json({ message: `User ID is ${params.userId}` });
|
|
210
|
-
},
|
|
211
|
-
);
|
|
269
|
+
export const { POST } = createRouteHandler().post(async (rc) => {
|
|
270
|
+
return rc.json({ ok: true }, 201);
|
|
271
|
+
});
|
|
212
272
|
```
|
|
213
273
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
サーバー側では,次のリクエスト部分を型安全に検証できます:
|
|
274
|
+
### `zValidator`
|
|
217
275
|
|
|
218
|
-
|
|
219
|
-
| :--------- | :-------------------------------------------------- |
|
|
220
|
-
| `params` | URLパラメータ ( `/user/:id` の `id`など) |
|
|
221
|
-
| `query` | クエリパラメータ (`?q=xxx&page=1`など) |
|
|
222
|
-
| `headers` | リクエストヘッダー |
|
|
223
|
-
| `cookies` | クッキー |
|
|
224
|
-
| `json` | リクエストボディ (Content-Type: `application/json`) |
|
|
276
|
+
`zValidator()` supports these targets:
|
|
225
277
|
|
|
226
|
-
|
|
278
|
+
- `params`
|
|
279
|
+
- `query`
|
|
280
|
+
- `json`
|
|
281
|
+
- `headers`
|
|
282
|
+
- `cookies`
|
|
227
283
|
|
|
228
|
-
|
|
284
|
+
Example:
|
|
229
285
|
|
|
230
286
|
```ts
|
|
231
|
-
import {
|
|
232
|
-
import { zValidator } from "
|
|
287
|
+
import { routeHandlerFactory } from "rpc4next/server";
|
|
288
|
+
import { zValidator } from "rpc4next/server/validators/zod";
|
|
233
289
|
import { z } from "zod";
|
|
234
290
|
|
|
235
|
-
const
|
|
236
|
-
page: z.string().regex(/^\d+$/),
|
|
237
|
-
});
|
|
291
|
+
const createRouteHandler = routeHandlerFactory();
|
|
238
292
|
|
|
239
293
|
const jsonSchema = z.object({
|
|
240
|
-
|
|
241
|
-
age: z.number(),
|
|
294
|
+
title: z.string().min(1),
|
|
242
295
|
});
|
|
243
296
|
|
|
244
|
-
export const { POST } = createRouteHandler
|
|
245
|
-
query: z.infer<typeof querySchema>;
|
|
246
|
-
}>().post(
|
|
247
|
-
zValidator("query", querySchema),
|
|
297
|
+
export const { POST } = createRouteHandler().post(
|
|
248
298
|
zValidator("json", jsonSchema),
|
|
249
299
|
async (rc) => {
|
|
250
|
-
const query = rc.req.valid("query");
|
|
251
300
|
const body = rc.req.valid("json");
|
|
252
|
-
return rc.json({
|
|
301
|
+
return rc.json({ title: body.title }, 201);
|
|
253
302
|
},
|
|
254
303
|
);
|
|
255
304
|
```
|
|
256
305
|
|
|
257
|
-
|
|
258
|
-
- **成功時は型安全に取得可能** (`rc.req.valid('query')`, `rc.req.valid('json')`)
|
|
259
|
-
|
|
260
|
-
---
|
|
261
|
-
|
|
262
|
-
これにより,クライアント側とサーバー側が、全面的に**型でつながる**ので,ミスを何次も防げ,開発体験を大幅に向上できます。
|
|
263
|
-
|
|
264
|
-
---
|
|
265
|
-
|
|
266
|
-
### ⚡ バリデーション失敗時のカスタムエラーハンドリング
|
|
267
|
-
|
|
268
|
-
- デフォルトでは、バリデーション失敗時に自動で `400 Bad Request` を返します。
|
|
269
|
-
- 必要に応じて、**カスタムフック**でエラー対応を制御できます。
|
|
306
|
+
If you provide a custom hook, you must return a response yourself when validation fails:
|
|
270
307
|
|
|
271
308
|
```ts
|
|
272
|
-
zValidator("
|
|
309
|
+
zValidator("json", jsonSchema, (result, rc) => {
|
|
273
310
|
if (!result.success) {
|
|
274
|
-
return rc.json({ error: result.error.
|
|
311
|
+
return rc.json({ error: result.error.issues }, 422);
|
|
275
312
|
}
|
|
276
313
|
});
|
|
277
314
|
```
|
|
278
315
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
---
|
|
282
|
-
|
|
283
|
-
## 📡 クライアント側での使い方
|
|
316
|
+
## Plain Next.js Route Handlers Also Work
|
|
284
317
|
|
|
285
|
-
|
|
318
|
+
You can keep using native App Router handlers without adopting `routeHandlerFactory()`.
|
|
319
|
+
This is useful when you want to stay close to stock Next.js APIs and only use `rpc4next` for route scanning and client generation.
|
|
286
320
|
|
|
287
|
-
|
|
321
|
+
Example with `NextResponse.json(...)`:
|
|
288
322
|
|
|
289
323
|
```ts
|
|
290
|
-
|
|
291
|
-
import type
|
|
324
|
+
// app/api/next-native/[itemId]/route.ts
|
|
325
|
+
import { type NextRequest, NextResponse } from "next/server";
|
|
292
326
|
|
|
293
|
-
|
|
327
|
+
export type Query = {
|
|
328
|
+
filter?: string;
|
|
329
|
+
};
|
|
294
330
|
|
|
295
|
-
async function
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
}
|
|
331
|
+
export async function GET(
|
|
332
|
+
request: NextRequest,
|
|
333
|
+
context: { params: Promise<{ itemId: string }> },
|
|
334
|
+
) {
|
|
335
|
+
const { itemId } = await context.params;
|
|
336
|
+
const filter = request.nextUrl.searchParams.get("filter") ?? "all";
|
|
300
337
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
// query: {
|
|
307
|
-
// page: string;
|
|
308
|
-
// };
|
|
309
|
-
// body: {
|
|
310
|
-
// name: string;
|
|
311
|
-
// age: number;
|
|
312
|
-
// };
|
|
313
|
-
// }
|
|
314
|
-
} else {
|
|
315
|
-
const error = await res.json();
|
|
316
|
-
|
|
317
|
-
// ⚠️ バリデーションエラー時は次の型が推論されます
|
|
318
|
-
// const error:
|
|
319
|
-
// | SafeParseError<{
|
|
320
|
-
// page: string;
|
|
321
|
-
// }>
|
|
322
|
-
// | SafeParseError<{
|
|
323
|
-
// name: string;
|
|
324
|
-
// age: number;
|
|
325
|
-
// }>;
|
|
326
|
-
}
|
|
338
|
+
return NextResponse.json({
|
|
339
|
+
ok: true,
|
|
340
|
+
itemId,
|
|
341
|
+
filter,
|
|
342
|
+
});
|
|
327
343
|
}
|
|
328
344
|
```
|
|
329
345
|
|
|
330
|
-
|
|
331
|
-
- サーバー側の型定義に基づいて、**型のズレを防止**できます
|
|
346
|
+
Example with `Response.json(...)`:
|
|
332
347
|
|
|
333
|
-
|
|
348
|
+
```ts
|
|
349
|
+
// app/api/next-native-response/route.ts
|
|
350
|
+
export async function GET() {
|
|
351
|
+
return Response.json({
|
|
352
|
+
ok: true,
|
|
353
|
+
source: "response-json",
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
```
|
|
334
357
|
|
|
335
|
-
|
|
358
|
+
The generated client can still call this route:
|
|
336
359
|
|
|
337
|
-
|
|
360
|
+
```ts
|
|
361
|
+
const response = await rpc.api["next-native"]
|
|
362
|
+
._itemId("item-1")
|
|
363
|
+
.$get({ url: { query: { filter: "recent" } } });
|
|
364
|
+
```
|
|
338
365
|
|
|
339
|
-
|
|
366
|
+
You can also call a plain `Response.json(...)` route:
|
|
340
367
|
|
|
341
|
-
|
|
368
|
+
```ts
|
|
369
|
+
const response = await rpc.api["next-native-response"].$get();
|
|
370
|
+
```
|
|
342
371
|
|
|
343
|
-
|
|
344
|
-
- `packages/rpc4next-cli`: CLI generator that exposes the `rpc4next` binary
|
|
345
|
-
- Install once at the repo root: `bun install`
|
|
346
|
-
- Build everything: `bun run build`
|
|
347
|
-
- Run all tests: `bun run test`
|
|
348
|
-
- Lint all packages: `bun run lint`
|
|
372
|
+
For native handlers, route discovery and request typing still work, but response typing is naturally broader than when you return rpc4next's typed helpers.
|
|
349
373
|
|
|
350
|
-
|
|
374
|
+
See [integration/next-app/README.md](./integration/next-app/README.md) for the repository's full integration fixture coverage and route-pattern notes.
|
|
351
375
|
|
|
352
|
-
|
|
353
|
-
- Node.js 20.19.2+
|
|
354
|
-
- aqua (Node.js / Bun のバージョン管理)
|
|
376
|
+
## Generated Files
|
|
355
377
|
|
|
356
|
-
|
|
357
|
-
|
|
378
|
+
When `paramsFile` is enabled, the CLI can generate sibling files such as:
|
|
379
|
+
|
|
380
|
+
```ts
|
|
381
|
+
// app/api/users/[userId]/params.ts
|
|
382
|
+
export type Params = { userId: string };
|
|
358
383
|
```
|
|
359
384
|
|
|
360
|
-
|
|
385
|
+
That lets route files import the param shape instead of repeating it manually.
|
|
386
|
+
These generated `params.ts` files are optional, and your generated `src/generated/rpc.ts` is typically not something you edit by hand.
|
|
361
387
|
|
|
362
|
-
|
|
363
|
-
aqua update-checksum
|
|
364
|
-
```
|
|
388
|
+
Your generated `src/generated/rpc.ts` exports a `PathStructure` type that includes:
|
|
365
389
|
|
|
366
|
-
-
|
|
390
|
+
- path entries from `page.tsx`
|
|
391
|
+
- callable HTTP methods from `route.ts`
|
|
392
|
+
- dynamic segment parameter types
|
|
393
|
+
- route `Query` exports where available
|
|
367
394
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
395
|
+
## Typical Workflow
|
|
396
|
+
|
|
397
|
+
1. Add or update files under `app/**`
|
|
398
|
+
2. Run `rpc4next` to regenerate `PathStructure`
|
|
399
|
+
3. Import `PathStructure` into your client
|
|
400
|
+
4. Call routes with `createRpcClient<PathStructure>(...)`
|
|
401
|
+
5. Use `routeHandlerFactory` and `zValidator` where you want stronger server-side typing
|
|
402
|
+
|
|
403
|
+
## Repository Layout
|
|
404
|
+
|
|
405
|
+
- `packages/rpc4next`: runtime client and server helpers
|
|
406
|
+
- `packages/rpc4next-cli`: route scanner and type generator
|
|
407
|
+
- `packages/rpc4next-shared`: internal shared constants and types
|
|
408
|
+
- `integration/next-app`: real Next.js integration fixture
|
|
372
409
|
|
|
373
|
-
|
|
410
|
+
If you are evaluating the repository itself, `integration/next-app` is the best place to see the full flow working in a real app.
|
|
374
411
|
|
|
375
|
-
##
|
|
412
|
+
## License
|
|
376
413
|
|
|
377
414
|
MIT
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rpc4next",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Inspired by Hono RPC and Pathpida, rpc4next brings a lightweight and intuitive RPC solution to Next.js, making server-client communication seamless",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"next.js",
|
|
@@ -102,7 +102,7 @@
|
|
|
102
102
|
"rpc4next-shared": "^0.3.0"
|
|
103
103
|
},
|
|
104
104
|
"peerDependencies": {
|
|
105
|
-
"next": "^14.0.0 || ^15.0.0"
|
|
105
|
+
"next": "^14.0.0 || ^15.0.0 || ^16.0.0"
|
|
106
106
|
},
|
|
107
107
|
"publishConfig": {
|
|
108
108
|
"access": "public"
|