@honestjs/rpc-plugin 1.5.0 → 1.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 +193 -134
- package/dist/index.d.mts +69 -8
- package/dist/index.d.ts +69 -8
- package/dist/index.js +235 -64
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +232 -64
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# RPC Plugin
|
|
2
2
|
|
|
3
|
-
The RPC Plugin automatically analyzes your HonestJS controllers and, by default,
|
|
4
|
-
client with proper parameter typing. You
|
|
3
|
+
The RPC Plugin automatically analyzes your HonestJS controllers and, by default,
|
|
4
|
+
generates a fully-typed TypeScript RPC client with proper parameter typing. You
|
|
5
|
+
can also provide custom generators.
|
|
5
6
|
|
|
6
7
|
## Installation
|
|
7
8
|
|
|
@@ -16,47 +17,55 @@ pnpm add @honestjs/rpc-plugin
|
|
|
16
17
|
## Basic Setup
|
|
17
18
|
|
|
18
19
|
```typescript
|
|
19
|
-
import { RPCPlugin } from
|
|
20
|
-
import { Application } from
|
|
21
|
-
import AppModule from
|
|
20
|
+
import { RPCPlugin } from "@honestjs/rpc-plugin";
|
|
21
|
+
import { Application } from "honestjs";
|
|
22
|
+
import AppModule from "./app.module";
|
|
22
23
|
|
|
23
24
|
const { hono } = await Application.create(AppModule, {
|
|
24
|
-
plugins: [new RPCPlugin()]
|
|
25
|
-
})
|
|
25
|
+
plugins: [new RPCPlugin()],
|
|
26
|
+
});
|
|
26
27
|
|
|
27
|
-
export default hono
|
|
28
|
+
export default hono;
|
|
28
29
|
```
|
|
29
30
|
|
|
30
31
|
## Configuration Options
|
|
31
32
|
|
|
32
33
|
```typescript
|
|
33
34
|
interface RPCPluginOptions {
|
|
34
|
-
readonly controllerPattern?: string // Glob pattern for controller files (default: 'src/modules/*/*.controller.ts')
|
|
35
|
-
readonly tsConfigPath?: string // Path to tsconfig.json (default: 'tsconfig.json')
|
|
36
|
-
readonly outputDir?: string // Output directory for generated files (default: './generated/rpc')
|
|
37
|
-
readonly generateOnInit?: boolean // Generate files on initialization (default: true)
|
|
38
|
-
readonly generators?: readonly RPCGenerator[] // Optional list of generators to execute
|
|
35
|
+
readonly controllerPattern?: string; // Glob pattern for controller files (default: 'src/modules/*/*.controller.ts')
|
|
36
|
+
readonly tsConfigPath?: string; // Path to tsconfig.json (default: 'tsconfig.json')
|
|
37
|
+
readonly outputDir?: string; // Output directory for generated files (default: './generated/rpc')
|
|
38
|
+
readonly generateOnInit?: boolean; // Generate files on initialization (default: true)
|
|
39
|
+
readonly generators?: readonly RPCGenerator[]; // Optional list of generators to execute
|
|
40
|
+
readonly mode?: "strict" | "best-effort"; // strict fails on warnings/fallbacks
|
|
41
|
+
readonly logLevel?: "silent" | "error" | "warn" | "info" | "debug"; // default: info
|
|
42
|
+
readonly customClassMatcher?: (
|
|
43
|
+
classDeclaration: ClassDeclaration,
|
|
44
|
+
) => boolean; // optional override; default discovery uses decorators
|
|
45
|
+
readonly failOnSchemaError?: boolean; // default true in strict mode
|
|
46
|
+
readonly failOnRouteAnalysisWarning?: boolean; // default true in strict mode
|
|
39
47
|
readonly context?: {
|
|
40
|
-
readonly namespace?: string // Default: 'rpc'
|
|
48
|
+
readonly namespace?: string; // Default: 'rpc'
|
|
41
49
|
readonly keys?: {
|
|
42
|
-
readonly artifact?: string // Default: 'artifact'
|
|
43
|
-
}
|
|
44
|
-
}
|
|
50
|
+
readonly artifact?: string; // Default: 'artifact'
|
|
51
|
+
};
|
|
52
|
+
};
|
|
45
53
|
}
|
|
46
54
|
```
|
|
47
55
|
|
|
48
56
|
### Generator Behavior
|
|
49
57
|
|
|
50
|
-
- If `generators` is omitted, the plugin uses the built-in
|
|
58
|
+
- If `generators` is omitted, the plugin uses the built-in
|
|
59
|
+
`TypeScriptClientGenerator` by default.
|
|
51
60
|
- If `generators` is provided, only those generators are executed.
|
|
52
61
|
- You can still use the built-in TypeScript client generator explicitly:
|
|
53
62
|
|
|
54
63
|
```typescript
|
|
55
|
-
import { RPCPlugin, TypeScriptClientGenerator } from
|
|
64
|
+
import { RPCPlugin, TypeScriptClientGenerator } from "@honestjs/rpc-plugin";
|
|
56
65
|
|
|
57
66
|
new RPCPlugin({
|
|
58
|
-
generators: [new TypeScriptClientGenerator(
|
|
59
|
-
})
|
|
67
|
+
generators: [new TypeScriptClientGenerator("./generated/rpc")],
|
|
68
|
+
});
|
|
60
69
|
```
|
|
61
70
|
|
|
62
71
|
## Application Context Artifact
|
|
@@ -65,68 +74,78 @@ After analysis, RPC plugin publishes this artifact to the application context:
|
|
|
65
74
|
|
|
66
75
|
```typescript
|
|
67
76
|
type RpcArtifact = {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
77
|
+
artifactVersion: string;
|
|
78
|
+
routes: ExtendedRouteInfo[];
|
|
79
|
+
schemas: SchemaInfo[];
|
|
80
|
+
};
|
|
71
81
|
```
|
|
72
82
|
|
|
73
|
-
Default key is `'rpc.artifact'` (from
|
|
83
|
+
Default key is `'rpc.artifact'` (from
|
|
84
|
+
`context.namespace + '.' + context.keys.artifact`). This enables direct
|
|
85
|
+
integration with API docs:
|
|
74
86
|
|
|
75
87
|
```typescript
|
|
76
|
-
import { ApiDocsPlugin } from
|
|
88
|
+
import { ApiDocsPlugin } from "@honestjs/api-docs-plugin";
|
|
77
89
|
|
|
78
90
|
const { hono } = await Application.create(AppModule, {
|
|
79
|
-
plugins: [new RPCPlugin(), new ApiDocsPlugin({ artifact:
|
|
80
|
-
})
|
|
91
|
+
plugins: [new RPCPlugin(), new ApiDocsPlugin({ artifact: "rpc.artifact" })],
|
|
92
|
+
});
|
|
81
93
|
```
|
|
82
94
|
|
|
95
|
+
`artifactVersion` is currently `"1"` and is used for compatibility checks.
|
|
96
|
+
|
|
83
97
|
## What It Generates
|
|
84
98
|
|
|
85
99
|
The plugin generates files in the output directory (default: `./generated/rpc`):
|
|
86
100
|
|
|
87
|
-
| File
|
|
88
|
-
|
|
|
89
|
-
| `client.ts`
|
|
90
|
-
| `.rpc-checksum`
|
|
91
|
-
| `rpc-artifact.json`
|
|
101
|
+
| File | Description | When generated |
|
|
102
|
+
| ---------------------- | ---------------------------------------------------------------------- | ------------------------------ |
|
|
103
|
+
| `client.ts` | Type-safe RPC client with all DTOs | When TypeScript generator runs |
|
|
104
|
+
| `.rpc-checksum` | Hash of source files for incremental caching | Always |
|
|
105
|
+
| `rpc-artifact.json` | Serialized routes/schemas artifact for cache-backed context publishing | Always |
|
|
106
|
+
| `rpc-diagnostics.json` | Diagnostics report (mode, warnings, cache status) | Always |
|
|
92
107
|
|
|
93
108
|
### TypeScript RPC Client (`client.ts`)
|
|
94
109
|
|
|
95
|
-
The plugin generates a single comprehensive file that includes both the client
|
|
110
|
+
The plugin generates a single comprehensive file that includes both the client
|
|
111
|
+
and all type definitions:
|
|
96
112
|
|
|
97
113
|
- **Controller-based organization**: Methods grouped by controller
|
|
98
114
|
- **Type-safe parameters**: Path, query, and body parameters with proper typing
|
|
99
|
-
- **Flexible request options**: Clean separation of params, query, body, and
|
|
115
|
+
- **Flexible request options**: Clean separation of params, query, body, and
|
|
116
|
+
headers
|
|
100
117
|
- **Error handling**: Built-in error handling with custom ApiError class
|
|
101
118
|
- **Header management**: Easy custom header management
|
|
102
|
-
- **Custom fetch support**: Inject custom fetch implementations for testing,
|
|
103
|
-
|
|
119
|
+
- **Custom fetch support**: Inject custom fetch implementations for testing,
|
|
120
|
+
middleware, and compatibility
|
|
121
|
+
- **Integrated types**: All DTOs, interfaces, and utility types included in the
|
|
122
|
+
same file
|
|
104
123
|
|
|
105
124
|
```typescript
|
|
106
125
|
// Generated client usage
|
|
107
|
-
import { ApiClient } from
|
|
126
|
+
import { ApiClient } from "./generated/rpc/client";
|
|
108
127
|
|
|
109
128
|
// Create client instance with base URL
|
|
110
|
-
const apiClient = new ApiClient(
|
|
129
|
+
const apiClient = new ApiClient("http://localhost:3000");
|
|
111
130
|
|
|
112
131
|
// Type-safe API calls
|
|
113
132
|
const user = await apiClient.users.create({
|
|
114
|
-
body: { name:
|
|
115
|
-
})
|
|
133
|
+
body: { name: "John", email: "john@example.com" },
|
|
134
|
+
});
|
|
116
135
|
|
|
117
136
|
const users = await apiClient.users.list({
|
|
118
|
-
query: { page: 1, limit: 10 }
|
|
119
|
-
})
|
|
137
|
+
query: { page: 1, limit: 10 },
|
|
138
|
+
});
|
|
120
139
|
|
|
121
140
|
const user = await apiClient.users.getById({
|
|
122
|
-
params: { id:
|
|
123
|
-
})
|
|
141
|
+
params: { id: "123" },
|
|
142
|
+
});
|
|
124
143
|
|
|
125
144
|
// Set custom headers
|
|
126
145
|
apiClient.setDefaultHeaders({
|
|
127
|
-
|
|
128
|
-
Authorization:
|
|
129
|
-
})
|
|
146
|
+
"X-API-Key": "your-api-key",
|
|
147
|
+
Authorization: "Bearer your-jwt-token",
|
|
148
|
+
});
|
|
130
149
|
```
|
|
131
150
|
|
|
132
151
|
The generated `client.ts` file contains everything you need:
|
|
@@ -142,7 +161,8 @@ The RPC client supports custom fetch implementations, which is useful for:
|
|
|
142
161
|
|
|
143
162
|
- **Testing**: Inject mock fetch functions for unit testing
|
|
144
163
|
- **Custom Logic**: Add logging, retries, or other middleware
|
|
145
|
-
- **Environment Compatibility**: Use different fetch implementations
|
|
164
|
+
- **Environment Compatibility**: Use different fetch implementations
|
|
165
|
+
(node-fetch, undici, etc.)
|
|
146
166
|
- **Interceptors**: Wrap requests with custom logic before/after execution
|
|
147
167
|
|
|
148
168
|
### Basic Custom Fetch Example
|
|
@@ -150,13 +170,18 @@ The RPC client supports custom fetch implementations, which is useful for:
|
|
|
150
170
|
```typescript
|
|
151
171
|
// Simple logging wrapper
|
|
152
172
|
const loggingFetch = (input: RequestInfo | URL, init?: RequestInit) => {
|
|
153
|
-
console.log(
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
173
|
+
console.log(
|
|
174
|
+
`[${new Date().toISOString()}] Making ${
|
|
175
|
+
init?.method || "GET"
|
|
176
|
+
} request to:`,
|
|
177
|
+
input,
|
|
178
|
+
);
|
|
179
|
+
return fetch(input, init);
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const apiClient = new ApiClient("http://localhost:3000", {
|
|
183
|
+
fetchFn: loggingFetch,
|
|
184
|
+
});
|
|
160
185
|
```
|
|
161
186
|
|
|
162
187
|
### Advanced Custom Fetch Examples
|
|
@@ -167,24 +192,26 @@ const retryFetch = (maxRetries = 3) => {
|
|
|
167
192
|
return async (input: RequestInfo | URL, init?: RequestInit) => {
|
|
168
193
|
for (let i = 0; i <= maxRetries; i++) {
|
|
169
194
|
try {
|
|
170
|
-
const response = await fetch(input, init)
|
|
171
|
-
if (response.ok) return response
|
|
195
|
+
const response = await fetch(input, init);
|
|
196
|
+
if (response.ok) return response;
|
|
172
197
|
|
|
173
|
-
if (i === maxRetries) return response
|
|
198
|
+
if (i === maxRetries) return response;
|
|
174
199
|
|
|
175
200
|
// Wait with exponential backoff
|
|
176
|
-
await new Promise((resolve) =>
|
|
201
|
+
await new Promise((resolve) =>
|
|
202
|
+
setTimeout(resolve, Math.pow(2, i) * 1000)
|
|
203
|
+
);
|
|
177
204
|
} catch (error) {
|
|
178
|
-
if (i === maxRetries) throw error
|
|
205
|
+
if (i === maxRetries) throw error;
|
|
179
206
|
}
|
|
180
207
|
}
|
|
181
|
-
throw new Error(
|
|
182
|
-
}
|
|
183
|
-
}
|
|
208
|
+
throw new Error("Max retries exceeded");
|
|
209
|
+
};
|
|
210
|
+
};
|
|
184
211
|
|
|
185
|
-
const apiClientWithRetry = new ApiClient(
|
|
186
|
-
fetchFn: retryFetch(3)
|
|
187
|
-
})
|
|
212
|
+
const apiClientWithRetry = new ApiClient("http://localhost:3000", {
|
|
213
|
+
fetchFn: retryFetch(3),
|
|
214
|
+
});
|
|
188
215
|
|
|
189
216
|
// Request/response interceptor
|
|
190
217
|
const interceptorFetch = (input: RequestInfo | URL, init?: RequestInit) => {
|
|
@@ -193,20 +220,20 @@ const interceptorFetch = (input: RequestInfo | URL, init?: RequestInit) => {
|
|
|
193
220
|
...init,
|
|
194
221
|
headers: {
|
|
195
222
|
...init?.headers,
|
|
196
|
-
|
|
197
|
-
}
|
|
198
|
-
}
|
|
223
|
+
"X-Request-ID": crypto.randomUUID(),
|
|
224
|
+
},
|
|
225
|
+
};
|
|
199
226
|
|
|
200
227
|
return fetch(input, enhancedInit).then((response) => {
|
|
201
228
|
// Post-response logic
|
|
202
|
-
console.log(`Response status: ${response.status}`)
|
|
203
|
-
return response
|
|
204
|
-
})
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
const apiClientWithInterceptor = new ApiClient(
|
|
208
|
-
fetchFn: interceptorFetch
|
|
209
|
-
})
|
|
229
|
+
console.log(`Response status: ${response.status}`);
|
|
230
|
+
return response;
|
|
231
|
+
});
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
const apiClientWithInterceptor = new ApiClient("http://localhost:3000", {
|
|
235
|
+
fetchFn: interceptorFetch,
|
|
236
|
+
});
|
|
210
237
|
```
|
|
211
238
|
|
|
212
239
|
### Testing with Custom Fetch
|
|
@@ -215,37 +242,45 @@ const apiClientWithInterceptor = new ApiClient('http://localhost:3000', {
|
|
|
215
242
|
// Mock fetch for testing
|
|
216
243
|
const mockFetch = jest.fn().mockResolvedValue({
|
|
217
244
|
ok: true,
|
|
218
|
-
json: () => Promise.resolve({ data: { id:
|
|
219
|
-
})
|
|
245
|
+
json: () => Promise.resolve({ data: { id: "123", name: "Test User" } }),
|
|
246
|
+
});
|
|
220
247
|
|
|
221
|
-
const testApiClient = new ApiClient(
|
|
222
|
-
fetchFn: mockFetch
|
|
223
|
-
})
|
|
248
|
+
const testApiClient = new ApiClient("http://test.com", {
|
|
249
|
+
fetchFn: mockFetch,
|
|
250
|
+
});
|
|
224
251
|
|
|
225
252
|
// Your test can now verify the mock was called
|
|
226
|
-
expect(mockFetch).toHaveBeenCalledWith(
|
|
253
|
+
expect(mockFetch).toHaveBeenCalledWith(
|
|
254
|
+
"http://test.com/api/v1/users/123",
|
|
255
|
+
expect.objectContaining({ method: "GET" }),
|
|
256
|
+
);
|
|
227
257
|
```
|
|
228
258
|
|
|
229
259
|
## Hash-based Caching
|
|
230
260
|
|
|
231
|
-
On startup the plugin hashes all controller source files (SHA-256) and stores
|
|
261
|
+
On startup the plugin hashes all controller source files (SHA-256) and stores
|
|
262
|
+
the checksum in `.rpc-checksum` inside the output directory. On subsequent runs,
|
|
263
|
+
if the hash matches and the expected output files already exist, the expensive
|
|
264
|
+
analysis and generation pipeline is skipped entirely. This significantly reduces
|
|
265
|
+
startup time in large projects.
|
|
232
266
|
|
|
233
267
|
Caching is automatic and requires no configuration. To force regeneration:
|
|
234
268
|
|
|
235
269
|
```typescript
|
|
236
|
-
// Manual call — defaults to force=true, always regenerates
|
|
237
|
-
await rpcPlugin.analyze()
|
|
238
|
-
|
|
239
270
|
// Explicit cache bypass
|
|
240
|
-
await rpcPlugin.analyze(true)
|
|
271
|
+
await rpcPlugin.analyze({ force: true });
|
|
241
272
|
|
|
242
273
|
// Respect the cache (same behavior as automatic startup)
|
|
243
|
-
await rpcPlugin.analyze(false)
|
|
274
|
+
await rpcPlugin.analyze({ force: false });
|
|
244
275
|
```
|
|
245
276
|
|
|
246
|
-
You can also delete `.rpc-checksum` from the output directory to clear the
|
|
277
|
+
You can also delete `.rpc-checksum` from the output directory to clear the
|
|
278
|
+
cache.
|
|
247
279
|
|
|
248
|
-
> **Note:** The hash covers controller files matched by the `controllerPattern`
|
|
280
|
+
> **Note:** The hash covers controller files matched by the `controllerPattern`
|
|
281
|
+
> glob. If you only change a DTO/model file that lives outside that pattern, the
|
|
282
|
+
> cache won't invalidate automatically. Use `analyze()` or delete
|
|
283
|
+
> `.rpc-checksum` in that case.
|
|
249
284
|
|
|
250
285
|
## How It Works
|
|
251
286
|
|
|
@@ -285,21 +320,40 @@ export class ApiClient {
|
|
|
285
320
|
get users() {
|
|
286
321
|
return {
|
|
287
322
|
create: async <Result = User>(
|
|
288
|
-
options: RequestOptions<
|
|
323
|
+
options: RequestOptions<
|
|
324
|
+
{ name: string; email: string },
|
|
325
|
+
undefined,
|
|
326
|
+
undefined,
|
|
327
|
+
undefined
|
|
328
|
+
>,
|
|
289
329
|
) => {
|
|
290
|
-
return this.request<Result>(
|
|
330
|
+
return this.request<Result>("POST", `/api/v1/users/`, options);
|
|
291
331
|
},
|
|
292
332
|
list: async <Result = User[]>(
|
|
293
|
-
options?: RequestOptions<
|
|
333
|
+
options?: RequestOptions<
|
|
334
|
+
undefined,
|
|
335
|
+
{ page: number; limit: number },
|
|
336
|
+
undefined,
|
|
337
|
+
undefined
|
|
338
|
+
>,
|
|
294
339
|
) => {
|
|
295
|
-
return this.request<Result>(
|
|
340
|
+
return this.request<Result>("GET", `/api/v1/users/`, options);
|
|
296
341
|
},
|
|
297
342
|
getById: async <Result = User>(
|
|
298
|
-
options: RequestOptions<
|
|
343
|
+
options: RequestOptions<
|
|
344
|
+
undefined,
|
|
345
|
+
{ id: string },
|
|
346
|
+
undefined,
|
|
347
|
+
undefined
|
|
348
|
+
>,
|
|
299
349
|
) => {
|
|
300
|
-
return this.request<Result>(
|
|
301
|
-
|
|
302
|
-
|
|
350
|
+
return this.request<Result>(
|
|
351
|
+
"GET",
|
|
352
|
+
`/api/v1/users/:id`,
|
|
353
|
+
options,
|
|
354
|
+
);
|
|
355
|
+
},
|
|
356
|
+
};
|
|
303
357
|
}
|
|
304
358
|
}
|
|
305
359
|
|
|
@@ -308,24 +362,29 @@ export type RequestOptions<
|
|
|
308
362
|
TParams = undefined,
|
|
309
363
|
TQuery = undefined,
|
|
310
364
|
TBody = undefined,
|
|
311
|
-
THeaders = undefined
|
|
312
|
-
> =
|
|
313
|
-
(
|
|
314
|
-
(
|
|
315
|
-
(
|
|
365
|
+
THeaders = undefined,
|
|
366
|
+
> =
|
|
367
|
+
& (TParams extends undefined ? object : { params: TParams })
|
|
368
|
+
& (TQuery extends undefined ? object : { query: TQuery })
|
|
369
|
+
& (TBody extends undefined ? object : { body: TBody })
|
|
370
|
+
& (THeaders extends undefined ? object : { headers: THeaders });
|
|
316
371
|
```
|
|
317
372
|
|
|
318
373
|
## Plugin Lifecycle
|
|
319
374
|
|
|
320
|
-
The plugin automatically generates files when your HonestJS application starts
|
|
321
|
-
subsequent startups, the hash-based cache
|
|
375
|
+
The plugin automatically generates files when your HonestJS application starts
|
|
376
|
+
up (if `generateOnInit` is true). On subsequent startups, the hash-based cache
|
|
377
|
+
will skip regeneration if controller files haven't changed.
|
|
322
378
|
|
|
323
379
|
You can also manually trigger generation:
|
|
324
380
|
|
|
325
381
|
```typescript
|
|
326
|
-
const rpcPlugin = new RPCPlugin()
|
|
327
|
-
await rpcPlugin.analyze() // Force regeneration (bypasses cache)
|
|
328
|
-
await rpcPlugin.analyze(false) // Respect cache
|
|
382
|
+
const rpcPlugin = new RPCPlugin();
|
|
383
|
+
await rpcPlugin.analyze({ force: true }); // Force regeneration (bypasses cache)
|
|
384
|
+
await rpcPlugin.analyze({ force: false }); // Respect cache
|
|
385
|
+
|
|
386
|
+
// Analyze-only mode (no files generated, diagnostics still emitted)
|
|
387
|
+
await rpcPlugin.analyze({ force: true, dryRun: true });
|
|
329
388
|
```
|
|
330
389
|
|
|
331
390
|
## Advanced Usage
|
|
@@ -336,9 +395,9 @@ If your controllers follow a different file structure:
|
|
|
336
395
|
|
|
337
396
|
```typescript
|
|
338
397
|
new RPCPlugin({
|
|
339
|
-
controllerPattern:
|
|
340
|
-
outputDir:
|
|
341
|
-
})
|
|
398
|
+
controllerPattern: "src/controllers/**/*.controller.ts",
|
|
399
|
+
outputDir: "./src/generated/api",
|
|
400
|
+
});
|
|
342
401
|
```
|
|
343
402
|
|
|
344
403
|
### Manual Generation Control
|
|
@@ -347,11 +406,11 @@ Disable automatic generation and control when files are generated:
|
|
|
347
406
|
|
|
348
407
|
```typescript
|
|
349
408
|
const rpcPlugin = new RPCPlugin({
|
|
350
|
-
generateOnInit: false
|
|
351
|
-
})
|
|
409
|
+
generateOnInit: false,
|
|
410
|
+
});
|
|
352
411
|
|
|
353
412
|
// Later in your code
|
|
354
|
-
await rpcPlugin.analyze()
|
|
413
|
+
await rpcPlugin.analyze();
|
|
355
414
|
```
|
|
356
415
|
|
|
357
416
|
## Integration with HonestJS
|
|
@@ -361,32 +420,32 @@ await rpcPlugin.analyze()
|
|
|
361
420
|
Here's how your controllers should be structured for optimal RPC generation:
|
|
362
421
|
|
|
363
422
|
```typescript
|
|
364
|
-
import {
|
|
423
|
+
import { Body, Controller, Get, Param, Post, Query } from "honestjs";
|
|
365
424
|
|
|
366
425
|
interface CreateUserDto {
|
|
367
|
-
name: string
|
|
368
|
-
email: string
|
|
426
|
+
name: string;
|
|
427
|
+
email: string;
|
|
369
428
|
}
|
|
370
429
|
|
|
371
430
|
interface ListUsersQuery {
|
|
372
|
-
page?: number
|
|
373
|
-
limit?: number
|
|
431
|
+
page?: number;
|
|
432
|
+
limit?: number;
|
|
374
433
|
}
|
|
375
434
|
|
|
376
|
-
@Controller(
|
|
435
|
+
@Controller("/users")
|
|
377
436
|
export class UsersController {
|
|
378
|
-
@Post(
|
|
437
|
+
@Post("/")
|
|
379
438
|
async create(@Body() createUserDto: CreateUserDto): Promise<User> {
|
|
380
439
|
// Implementation
|
|
381
440
|
}
|
|
382
441
|
|
|
383
|
-
@Get(
|
|
442
|
+
@Get("/")
|
|
384
443
|
async list(@Query() query: ListUsersQuery): Promise<User[]> {
|
|
385
444
|
// Implementation
|
|
386
445
|
}
|
|
387
446
|
|
|
388
|
-
@Get(
|
|
389
|
-
async getById(@Param(
|
|
447
|
+
@Get("/:id")
|
|
448
|
+
async getById(@Param("id") id: string): Promise<User> {
|
|
390
449
|
// Implementation
|
|
391
450
|
}
|
|
392
451
|
}
|
|
@@ -397,13 +456,13 @@ export class UsersController {
|
|
|
397
456
|
Ensure your controllers are properly registered in modules:
|
|
398
457
|
|
|
399
458
|
```typescript
|
|
400
|
-
import { Module } from
|
|
401
|
-
import { UsersController } from
|
|
402
|
-
import { UsersService } from
|
|
459
|
+
import { Module } from "honestjs";
|
|
460
|
+
import { UsersController } from "./users.controller";
|
|
461
|
+
import { UsersService } from "./users.service";
|
|
403
462
|
|
|
404
463
|
@Module({
|
|
405
464
|
controllers: [UsersController],
|
|
406
|
-
|
|
465
|
+
services: [UsersService],
|
|
407
466
|
})
|
|
408
467
|
export class UsersModule {}
|
|
409
468
|
```
|
|
@@ -415,13 +474,13 @@ The generated client includes comprehensive error handling:
|
|
|
415
474
|
```typescript
|
|
416
475
|
try {
|
|
417
476
|
const user = await apiClient.users.create({
|
|
418
|
-
body: { name:
|
|
419
|
-
})
|
|
477
|
+
body: { name: "John", email: "john@example.com" },
|
|
478
|
+
});
|
|
420
479
|
} catch (error) {
|
|
421
480
|
if (error instanceof ApiError) {
|
|
422
|
-
console.error(`API Error ${error.statusCode}: ${error.message}`)
|
|
481
|
+
console.error(`API Error ${error.statusCode}: ${error.message}`);
|
|
423
482
|
} else {
|
|
424
|
-
console.error(
|
|
483
|
+
console.error("Unexpected error:", error);
|
|
425
484
|
}
|
|
426
485
|
}
|
|
427
486
|
```
|