@rexeus/typeweaver 0.0.2 → 0.0.4
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 +403 -70
- package/dist/LICENSE +202 -0
- package/dist/NOTICE +4 -0
- package/dist/{cli-C3cTfNLj.js → cli-Cz6q9I7F.js} +935 -1001
- package/dist/{flow--vV0j3Y-.js → flow-q2wMXrDa.js} +8 -8
- package/dist/{glimmer-B-ODUU1A.js → glimmer-wgjvri6H.js} +34 -34
- package/dist/index.d.ts +1 -1
- package/dist/index.js +8 -6
- package/dist/{markdown-BsYj_q7V.js → markdown-Nz6Lc3gB.js} +6 -6
- package/dist/{postcss-DdgOJBTx.js → postcss-yEOijaXJ.js} +42 -42
- package/dist/run-cli-with-tsx.js +8 -6
- package/dist/templates/Index.ejs +8 -0
- package/dist/{typescript-C4gnKzhB.js → typescript-Cv79a1Qz.js} +2 -2
- package/dist/{yaml-B0tq6Ttj.js → yaml-DT3qlFoE.js} +2 -2
- package/package.json +33 -20
package/README.md
CHANGED
|
@@ -1,66 +1,201 @@
|
|
|
1
|
-
# @rexeus/typeweaver
|
|
1
|
+
# 🧵✨ @rexeus/typeweaver
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@rexeus/typeweaver)
|
|
4
|
+
[](https://opensource.org/licenses/Apache-2.0)
|
|
5
|
+
[](https://www.typescriptlang.org/)
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
Typeweaver is a type-safe HTTP API framework built for API-first development with a focus on
|
|
8
|
+
developer experience. Use typeweaver to specify your HTTP APIs in TypeScript and Zod, and generate
|
|
9
|
+
clients, validators, routers, and more ✨
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 📥 Installation
|
|
6
14
|
|
|
7
15
|
```bash
|
|
16
|
+
# Install the CLI as a dev dependency
|
|
8
17
|
npm install -D @rexeus/typeweaver
|
|
18
|
+
|
|
19
|
+
# Install the runtime as a dependency
|
|
20
|
+
npm install @rexeus/typeweaver-core
|
|
9
21
|
```
|
|
10
22
|
|
|
11
|
-
|
|
23
|
+
Now you are ready to start building! Check out [Quickstart](#-get-started)
|
|
24
|
+
|
|
25
|
+
## 🎯 Why typeweaver?
|
|
26
|
+
|
|
27
|
+
- 📝 **Define once, generate everything**: API contracts in Zod become clients, servers, validators,
|
|
28
|
+
and docs.
|
|
29
|
+
- 📂 **Resource-based architecture**: APIs organized by resources (like user, todo, project, tag,
|
|
30
|
+
blog-post, etc.), each with its operations and generated components (e.g. clients). Scale
|
|
31
|
+
naturally as your API grows.
|
|
32
|
+
- 🔒 **Real type safety**: From API definition to client usage, every request and response is fully
|
|
33
|
+
typed. No more `any` types sneaking in.
|
|
34
|
+
- ✅ **Automatic validation**: Invalid requests never reach your code.
|
|
35
|
+
- 🔌 **Bring your own framework**: Ready-made adapters for popular frameworks, extensible plugin
|
|
36
|
+
system for everything else.
|
|
37
|
+
- 😊 **Finally, DX that doesn't suck**: One schema, no duplication, pure TypeScript.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 🔌 Available Plugins
|
|
42
|
+
|
|
43
|
+
| Package | Description | Version |
|
|
44
|
+
| -------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- |
|
|
45
|
+
| [@rexeus/typeweaver-types](https://github.com/rexeus/typeweaver/tree/main/packages/types/README.md) | Plugin for request/response types and validation - the foundation for all other plugins and always included |  |
|
|
46
|
+
| [@rexeus/typeweaver-clients](https://github.com/rexeus/typeweaver/tree/main/packages/clients/README.md) | Plugin for HTTP clients using Axios |  |
|
|
47
|
+
| [@rexeus/typeweaver-hono](https://github.com/rexeus/typeweaver/tree/main/packages/hono/README.md) | Plugin for Hono routers |  |
|
|
48
|
+
| [@rexeus/typeweaver-aws-cdk](https://github.com/rexeus/typeweaver/tree/main/packages/aws-cdk/README.md) | Plugin for AWS CDK constructs for API Gateway V2 |  |
|
|
49
|
+
|
|
50
|
+
More plugins are planned. If you want to build your own, check out the plugin system
|
|
51
|
+
|
|
52
|
+
[Plugin system](https://github.com/rexeus/typeweaver/tree/main/packages/gen/README.md#-how-to-use).
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## ⌨️ CLI
|
|
12
57
|
|
|
13
58
|
Generate TypeScript code from your API definitions:
|
|
14
59
|
|
|
15
60
|
```bash
|
|
16
|
-
|
|
61
|
+
# Generate with clients plugin:
|
|
62
|
+
npx typeweaver generate --input ./api/definition --output ./api/generated --plugins clients
|
|
63
|
+
|
|
64
|
+
# Generate with multiple plugins:
|
|
65
|
+
npx typeweaver generate --input ./api/definition --output ./api/generated --plugins clients,hono,aws-cdk
|
|
66
|
+
|
|
67
|
+
# Using all available plugins:
|
|
68
|
+
npx typeweaver generate --input ./api/definition --output ./api/generated --plugins all
|
|
17
69
|
```
|
|
18
70
|
|
|
19
|
-
### Options
|
|
71
|
+
### ⚙️ Options
|
|
20
72
|
|
|
21
73
|
- `--input, -i <path>`: Input directory containing API definitions (required)
|
|
22
74
|
- `--output, -o <path>`: Output directory for generated code (required)
|
|
75
|
+
- `-s, --shared <path>`: Shared directory for reusable schemas (optional, defaults to
|
|
76
|
+
`<input-path>/shared`)
|
|
23
77
|
- `--config, -c <path>`: Configuration file path (optional)
|
|
24
|
-
- `--plugins, -p <plugins>`: Comma-separated list of plugins to use (e.g., "clients,
|
|
78
|
+
- `--plugins, -p <plugins>`: Comma-separated list of plugins to use (e.g., "clients,hono" or "all"
|
|
79
|
+
for all plugins)
|
|
25
80
|
- `--prettier / --no-prettier`: Enable/disable code formatting with Prettier (default: true)
|
|
26
81
|
- `--clean / --no-clean`: Enable/disable output directory cleaning (default: true)
|
|
27
82
|
|
|
28
|
-
###
|
|
83
|
+
### 📝 Configuration File
|
|
29
84
|
|
|
30
|
-
|
|
85
|
+
Create a config file (e.g. `typeweaver.config.js`) for more complex configurations:
|
|
31
86
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
87
|
+
```javascript
|
|
88
|
+
export default {
|
|
89
|
+
input: "./api/definition",
|
|
90
|
+
output: "./api/generated",
|
|
91
|
+
plugins: ["clients", "hono", "aws-cdk"],
|
|
92
|
+
prettier: true,
|
|
93
|
+
clean: true,
|
|
94
|
+
};
|
|
95
|
+
```
|
|
38
96
|
|
|
39
|
-
|
|
97
|
+
Then run:
|
|
40
98
|
|
|
41
|
-
|
|
99
|
+
```bash
|
|
100
|
+
npx typeweaver generate --config ./typeweaver.config.js
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## 🌱 Get Started
|
|
104
|
+
|
|
105
|
+
### 📁 Project Structure
|
|
106
|
+
|
|
107
|
+
Your API definition must follow this structure:
|
|
108
|
+
|
|
109
|
+
- Each resource needs its own directory under the specified input directory (e.g. input dir:
|
|
110
|
+
`api/definition` contains `user/`, `post/` subdirectories)
|
|
111
|
+
- The directory name defines the resource name (e.g. `user`, `post`)
|
|
112
|
+
- The structure inside a resource directory can be nested to provide better organization (e.g.
|
|
113
|
+
`user/errors/...`, `user/mutations/...`)
|
|
114
|
+
- Inside a resource directory, each operation or response definition gets its own file (e.g.
|
|
115
|
+
`CreateUserDefinition.ts`, `UserNotFoundErrorDefinition.ts`)
|
|
116
|
+
- An operation definition file must include one default export of a `HttpOperationDefinition`
|
|
117
|
+
instance (e.g. `export default new HttpOperationDefinition({...})`)
|
|
118
|
+
- It is recommended to specify separate schemas for requests and responses, but this is not strictly
|
|
119
|
+
required.
|
|
120
|
+
- If separating schemas, Zod utilities can be used to apply general schemas case-specifically
|
|
121
|
+
(useful Zod utilities: pick, omit, merge...)
|
|
122
|
+
- A response definition file must include one default export of a `HttpResponseDefinition` instance
|
|
123
|
+
(e.g. `export default new HttpResponseDefinition({...})`)
|
|
124
|
+
- Responses shared across operations are possible, but need to be placed in the `shared` directory.
|
|
125
|
+
- The shared directory can be specified using the `--shared` option, but must be located within
|
|
126
|
+
the input directory
|
|
127
|
+
- Default shared directory is `<input-path>/shared`
|
|
128
|
+
- The shared directory is suitable not only as a place for responses but also for shared schemas
|
|
129
|
+
|
|
130
|
+
As you can see, the structure of the input directory is essential. However, you are completely free
|
|
131
|
+
to choose the structure and nesting within resource directories.
|
|
132
|
+
|
|
133
|
+
**Important**: All definition files and their dependencies (like separate schemas etc.) must be
|
|
134
|
+
self-contained within the input directory. Generated code creates an immutable snapshot of your
|
|
135
|
+
definitions, so any external imports (relative imports outside the input directory) will not work.
|
|
136
|
+
NPM package imports continue to work normally.
|
|
42
137
|
|
|
43
138
|
```
|
|
44
|
-
api/
|
|
45
|
-
├──
|
|
46
|
-
│ ├──
|
|
47
|
-
│
|
|
48
|
-
│
|
|
49
|
-
|
|
139
|
+
api/definition/
|
|
140
|
+
├── user/ # Resource directory
|
|
141
|
+
│ ├── errors/ # Resource-specific error definitions
|
|
142
|
+
│ │ │ # -> Because they are inside a resource directory,
|
|
143
|
+
│ │ │ # they can only be used within this resource
|
|
144
|
+
│ │ └── UserNotFoundErrorDefinition.ts
|
|
145
|
+
│ │ └── UserStatusTransitionInvalidErrorDefinition.ts
|
|
146
|
+
│ ├── CreateUserDefinition.ts # Operation definitions
|
|
147
|
+
│ ├── GetUserDefinition.ts
|
|
148
|
+
│ ├── ListUserDefinition.ts
|
|
149
|
+
│ ├── UpdateUserDefinition.ts
|
|
150
|
+
│ └── userSchema.ts # Schema for the resource, can be reused across operations
|
|
151
|
+
├── post/
|
|
152
|
+
│ ├── errors/
|
|
153
|
+
│ ├── CreatePostDefinition.ts
|
|
50
154
|
│ ├── GetPostDefinition.ts
|
|
51
|
-
│
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
155
|
+
│ ├── ...
|
|
156
|
+
├── ...
|
|
157
|
+
└── shared/ # Shared responses and schemas
|
|
158
|
+
│ # -> While it doesn't matter where schemas are defined
|
|
159
|
+
│ # inside the input directory, responses can only be
|
|
160
|
+
│ # shared across resources if they are located in the
|
|
161
|
+
│ # shared directory
|
|
162
|
+
├── ConflictErrorDefinition.ts
|
|
163
|
+
├── ForbiddenErrorDefinition.ts
|
|
164
|
+
├── InternalServerErrorDefinition.ts
|
|
165
|
+
├── NotFoundErrorDefinition.ts # Like BaseApiErrors, can be extended to be resource-specific
|
|
166
|
+
├── TooManyRequestsErrorDefinition.ts
|
|
167
|
+
├── UnauthorizedErrorDefinition.ts
|
|
168
|
+
├── ValidationErrorDefinition.ts
|
|
169
|
+
└── sharedResponses.ts # Collection of responses relevant for every operation
|
|
56
170
|
```
|
|
57
171
|
|
|
58
|
-
###
|
|
172
|
+
### 💻 Sample Definitions
|
|
59
173
|
|
|
60
174
|
```typescript
|
|
61
|
-
// api/
|
|
175
|
+
// api/definition/user/userSchema.ts
|
|
176
|
+
import { z } from "zod/v4";
|
|
177
|
+
|
|
178
|
+
// General schema for user status
|
|
179
|
+
export const userStatusSchema = z.enum(["ACTIVE", "INACTIVE", "SUSPENDED"]);
|
|
180
|
+
|
|
181
|
+
// General user schema, can be reused across operations
|
|
182
|
+
export const userSchema = z.object({
|
|
183
|
+
id: z.uuid(),
|
|
184
|
+
name: z.string(),
|
|
185
|
+
email: z.email(),
|
|
186
|
+
status: userStatusSchema,
|
|
187
|
+
createdAt: z.iso.date(),
|
|
188
|
+
updatedAt: z.iso.date(),
|
|
189
|
+
});
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
// api/definition/user/GetUserDefinition.ts
|
|
62
194
|
import { HttpOperationDefinition, HttpMethod, HttpStatusCode } from "@rexeus/typeweaver-core";
|
|
63
195
|
import { z } from "zod/v4";
|
|
196
|
+
import { sharedResponses } from "../shared/sharedResponses";
|
|
197
|
+
import { userSchema } from "./userSchema";
|
|
198
|
+
import UserNotFoundErrorDefinition from "./errors/UserNotFoundErrorDefinition";
|
|
64
199
|
|
|
65
200
|
export default new HttpOperationDefinition({
|
|
66
201
|
operationId: "GetUser",
|
|
@@ -72,74 +207,272 @@ export default new HttpOperationDefinition({
|
|
|
72
207
|
}),
|
|
73
208
|
},
|
|
74
209
|
responses: [
|
|
210
|
+
// - the only success response in this operation is defined inline
|
|
211
|
+
// - the response could also be defined in a separate file and be imported here
|
|
212
|
+
// - generally also multiple success responses could be defined
|
|
213
|
+
// - in this case the "general" user schema is imported and used
|
|
75
214
|
{
|
|
76
215
|
statusCode: HttpStatusCode.OK,
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
email: z.email(),
|
|
216
|
+
description: "User successfully retrieved",
|
|
217
|
+
header: z.object({
|
|
218
|
+
"Content-Type": z.literal("application/json"),
|
|
81
219
|
}),
|
|
220
|
+
body: userSchema,
|
|
82
221
|
},
|
|
222
|
+
UserNotFoundErrorDefinition, // Resource specific response
|
|
223
|
+
...sharedResponses, // Commonly used responses across all operations, e.g. 401, 403, 500...
|
|
83
224
|
],
|
|
84
225
|
});
|
|
85
226
|
```
|
|
86
227
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
### Using Plugins
|
|
96
|
-
|
|
97
|
-
Via command line:
|
|
228
|
+
```typescript
|
|
229
|
+
// api/definition/user/UpdateUserDefinition.ts
|
|
230
|
+
import { HttpOperationDefinition, HttpMethod, HttpStatusCode } from "@rexeus/typeweaver-core";
|
|
231
|
+
import { z } from "zod/v4";
|
|
232
|
+
import { sharedResponses } from "../shared/sharedResponses";
|
|
233
|
+
import { userSchema } from "./userSchema";
|
|
234
|
+
import UserNotFoundErrorDefinition from "./errors/UserNotFoundErrorDefinition";
|
|
235
|
+
import UserStatusTransitionInvalidErrorDefinition from "./errors/UserStatusTransitionInvalidErrorDefinition";
|
|
98
236
|
|
|
99
|
-
|
|
100
|
-
|
|
237
|
+
export default new HttpOperationDefinition({
|
|
238
|
+
operationId: "UpdateUser",
|
|
239
|
+
method: HttpMethod.PATCH,
|
|
240
|
+
path: "/users/:userId",
|
|
241
|
+
request: {
|
|
242
|
+
param: z.object({
|
|
243
|
+
userId: z.uuid(),
|
|
244
|
+
}),
|
|
245
|
+
// general user schema is processed via zod's pick and partial methods
|
|
246
|
+
// to match the update operation's requirements
|
|
247
|
+
body: userSchema
|
|
248
|
+
.pick({
|
|
249
|
+
name: true,
|
|
250
|
+
email: true,
|
|
251
|
+
status: true,
|
|
252
|
+
})
|
|
253
|
+
.partial(),
|
|
254
|
+
},
|
|
255
|
+
responses: [
|
|
256
|
+
{
|
|
257
|
+
statusCode: HttpStatusCode.OK,
|
|
258
|
+
description: "User successfully updated",
|
|
259
|
+
header: z.object({
|
|
260
|
+
"Content-Type": z.literal("application/json"),
|
|
261
|
+
}),
|
|
262
|
+
body: userSchema,
|
|
263
|
+
},
|
|
264
|
+
UserNotFoundErrorDefinition, // Resource specific response
|
|
265
|
+
UserStatusTransitionInvalidErrorDefinition, // Resource specific response
|
|
266
|
+
...sharedResponses, // Commonly used responses across all operations, e.g. 401, 403, 500...
|
|
267
|
+
],
|
|
268
|
+
});
|
|
101
269
|
```
|
|
102
270
|
|
|
103
|
-
|
|
271
|
+
```typescript
|
|
272
|
+
// api/definition/user/errors/UserNotFoundErrorDefinition.ts
|
|
273
|
+
import { z } from "zod/v4";
|
|
274
|
+
import { NotFoundErrorDefinition } from "../../shared";
|
|
275
|
+
|
|
276
|
+
// - uses the shared NotFoundErrorDefinition as "base" and extends it
|
|
277
|
+
// - adds a specific message and code for the user resource
|
|
278
|
+
export default NotFoundErrorDefinition.extend({
|
|
279
|
+
name: "UserNotFoundError",
|
|
280
|
+
description: "User not found",
|
|
281
|
+
body: z.object({
|
|
282
|
+
message: z.literal("User not found"),
|
|
283
|
+
code: z.literal("USER_NOT_FOUND_ERROR"),
|
|
284
|
+
actualValues: z.object({
|
|
285
|
+
userId: z.uuid(),
|
|
286
|
+
}),
|
|
287
|
+
}),
|
|
288
|
+
});
|
|
289
|
+
```
|
|
104
290
|
|
|
105
|
-
|
|
291
|
+
```typescript
|
|
292
|
+
// api/definition/user/errors/UserStatusTransitionInvalidErrorDefinition.ts
|
|
293
|
+
import { HttpResponseDefinition, HttpStatusCode } from "@rexeus/typeweaver-core";
|
|
294
|
+
import { z } from "zod/v4";
|
|
295
|
+
import { userStatusSchema } from "../userSchema";
|
|
296
|
+
|
|
297
|
+
// could also extend the shared ConflictErrorDefinition:
|
|
298
|
+
// export default ConflictErrorDefinition.extend({...});
|
|
299
|
+
|
|
300
|
+
// or in this case does not extend a BaseApiError and defines everything itself
|
|
301
|
+
export default new HttpResponseDefinition({
|
|
302
|
+
name: "UserStatusTransitionInvalidError",
|
|
303
|
+
description: "User status transition is conflicting with current status",
|
|
304
|
+
body: z.object({
|
|
305
|
+
message: z.literal("User status transition is conflicting with current status"),
|
|
306
|
+
code: z.literal("USER_STATUS_TRANSITION_INVALID_ERROR"),
|
|
307
|
+
context: z.object({
|
|
308
|
+
userId: z.uuid(),
|
|
309
|
+
currentStatus: userStatusSchema,
|
|
310
|
+
}),
|
|
311
|
+
actualValues: z.object({
|
|
312
|
+
requestedStatus: userStatusSchema,
|
|
313
|
+
}),
|
|
314
|
+
expectedValues: z.object({
|
|
315
|
+
allowedStatuses: z.array(userStatusSchema),
|
|
316
|
+
}),
|
|
317
|
+
}),
|
|
318
|
+
});
|
|
319
|
+
```
|
|
106
320
|
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
321
|
+
```typescript
|
|
322
|
+
// api/definition/shared/sharedResponses.ts
|
|
323
|
+
import ForbiddenErrorDefinition from "./ForbiddenErrorDefinition";
|
|
324
|
+
import InternalServerErrorDefinition from "./InternalServerErrorDefinition";
|
|
325
|
+
import TooManyRequestsErrorDefinition from "./TooManyRequestsErrorDefinition";
|
|
326
|
+
import UnauthorizedErrorDefinition from "./UnauthorizedErrorDefinition";
|
|
327
|
+
import UnsupportedMediaTypeErrorDefinition from "./UnsupportedMediaTypeErrorDefinition";
|
|
328
|
+
import ValidationErrorDefinition from "./ValidationErrorDefinition";
|
|
329
|
+
|
|
330
|
+
// various error responses which are relevant for every operation
|
|
331
|
+
// can be spread in the responses array of an HttpOperationDefinition
|
|
332
|
+
export const sharedResponses = [
|
|
333
|
+
ForbiddenErrorDefinition,
|
|
334
|
+
InternalServerErrorDefinition,
|
|
335
|
+
TooManyRequestsErrorDefinition,
|
|
336
|
+
UnauthorizedErrorDefinition,
|
|
337
|
+
UnsupportedMediaTypeErrorDefinition,
|
|
338
|
+
ValidationErrorDefinition,
|
|
339
|
+
];
|
|
115
340
|
```
|
|
116
341
|
|
|
117
|
-
|
|
342
|
+
### 🔧 Generate using plugins
|
|
118
343
|
|
|
119
344
|
```bash
|
|
120
|
-
|
|
345
|
+
# Generate with plugins:
|
|
346
|
+
# - Hono: to easily provide a web server
|
|
347
|
+
# - Clients: to get fitting API clients
|
|
348
|
+
npx typeweaver generate --input ./api/definition --output ./api/generated --plugins clients,hono
|
|
121
349
|
```
|
|
122
350
|
|
|
123
|
-
|
|
351
|
+
### 🌐 Create Hono web server
|
|
124
352
|
|
|
125
|
-
|
|
353
|
+
```typescript
|
|
354
|
+
// api/user-handlers.ts
|
|
355
|
+
import { HttpResponse, HttpStatusCode } from "@rexeus/typeweaver-core";
|
|
356
|
+
import {
|
|
357
|
+
type UserApiHandler,
|
|
358
|
+
type IGetUserRequest,
|
|
359
|
+
GetUserResponse,
|
|
360
|
+
GetUserSuccessResponse,
|
|
361
|
+
type ICreateUserRequest,
|
|
362
|
+
CreateUserResponse,
|
|
363
|
+
type IUpdateUserRequest,
|
|
364
|
+
UpdateUserResponse,
|
|
365
|
+
type IListUserRequest,
|
|
366
|
+
ListUserResponse,
|
|
367
|
+
} from "./generated";
|
|
368
|
+
|
|
369
|
+
export class UserHandlers implements UserApiHandler {
|
|
370
|
+
public constructor() {}
|
|
371
|
+
|
|
372
|
+
public async handleGetUserRequest(request: IGetUserRequest): Promise<GetUserResponse> {
|
|
373
|
+
// Simulate fetching user data
|
|
374
|
+
const fetchedUser = {
|
|
375
|
+
id: request.param.userId,
|
|
376
|
+
name: "John Doe",
|
|
377
|
+
email: "john.doe@example.com",
|
|
378
|
+
status: "ACTIVE",
|
|
379
|
+
createdAt: new Date("2023-01-01").toISOString(),
|
|
380
|
+
updatedAt: new Date("2023-01-01").toISOString(),
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
return new GetUserSuccessResponse({
|
|
384
|
+
statusCode: HttpStatusCode.OK,
|
|
385
|
+
header: {
|
|
386
|
+
"Content-Type": "application/json",
|
|
387
|
+
},
|
|
388
|
+
body: fetchedUser,
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
public handleCreateUserRequest(request: ICreateUserRequest): Promise<CreateUserResponse> {
|
|
393
|
+
throw new Error("Not implemented");
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
public handleUpdateUserRequest(request: IUpdateUserRequest): Promise<UpdateUserResponse> {
|
|
397
|
+
throw new Error("Not implemented");
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
public handleListUserRequest(request: IListUserRequest): Promise<ListUserResponse> {
|
|
401
|
+
throw new Error("Not implemented");
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
```
|
|
126
405
|
|
|
127
406
|
```typescript
|
|
128
|
-
|
|
407
|
+
// api/server.ts
|
|
408
|
+
import { serve } from "@hono/node-server";
|
|
409
|
+
import { Hono } from "hono";
|
|
410
|
+
// an index file exporting all generated components is automatically provided
|
|
411
|
+
import { UserHandlers } from "./user-handlers";
|
|
412
|
+
import { PostHandlers } from "./post-handlers"; // Implement similarly to UserHandlers
|
|
413
|
+
import { UserHono, PostHono } from "./generated";
|
|
414
|
+
|
|
415
|
+
const app = new Hono();
|
|
416
|
+
|
|
417
|
+
const userHandlers = new UserHandlers();
|
|
418
|
+
const postHandlers = new PostHandlers();
|
|
419
|
+
|
|
420
|
+
// you have further config options, e.g. custom error response handling
|
|
421
|
+
// (useful for mapping validation errors to your specific response format)
|
|
422
|
+
const userRouter = new UserHono({
|
|
423
|
+
requestHandlers: userHandlers,
|
|
424
|
+
});
|
|
425
|
+
const postRouter = new PostHono({
|
|
426
|
+
requestHandlers: postHandlers,
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
app.route("/", userRouter);
|
|
430
|
+
app.route("/", postRouter);
|
|
129
431
|
|
|
130
|
-
|
|
131
|
-
|
|
432
|
+
// Start server on port 3000
|
|
433
|
+
serve(
|
|
434
|
+
{
|
|
435
|
+
fetch: app.fetch,
|
|
436
|
+
port: 3000,
|
|
437
|
+
},
|
|
438
|
+
() => {
|
|
439
|
+
console.log("Server is running on http://localhost:3000");
|
|
440
|
+
}
|
|
441
|
+
);
|
|
132
442
|
```
|
|
133
443
|
|
|
134
|
-
|
|
444
|
+
```bash
|
|
445
|
+
# Start your server locally
|
|
446
|
+
tsx api/server.ts
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
### 🔗 Communicate by using Clients
|
|
135
450
|
|
|
136
451
|
```typescript
|
|
137
|
-
|
|
452
|
+
// api/client-test.ts
|
|
453
|
+
import { UserClient, GetUserRequestCommand, UserNotFoundErrorResponse } from "./generated";
|
|
454
|
+
|
|
455
|
+
const client = new UserClient({ baseUrl: "http://localhost:3000" });
|
|
456
|
+
|
|
457
|
+
try {
|
|
458
|
+
const getUserRequestCommand = new GetUserRequestCommand({ param: { userId: "123" } });
|
|
459
|
+
const result = await client.send(getUserRequestCommand);
|
|
460
|
+
|
|
461
|
+
console.log("Successfully fetched user:", result.body);
|
|
462
|
+
} catch (error) {
|
|
463
|
+
if (error instanceof UserNotFoundErrorResponse) {
|
|
464
|
+
console.error("User not found:", error.body);
|
|
465
|
+
} else {
|
|
466
|
+
console.error("Other error occurred:", error);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
```
|
|
138
470
|
|
|
139
|
-
|
|
140
|
-
|
|
471
|
+
```bash
|
|
472
|
+
# Call your created Hono server
|
|
473
|
+
tsx api/client-test.ts
|
|
141
474
|
```
|
|
142
475
|
|
|
143
|
-
## License
|
|
476
|
+
## 📄 License
|
|
144
477
|
|
|
145
|
-
|
|
478
|
+
Apache 2.0 © Dennis Wentzien 2025
|