Skip to content

Commit e0290a8

Browse files
committed
#main updated example section and expanded benchmark
1 parent 089b14f commit e0290a8

File tree

5 files changed

+221
-52
lines changed

5 files changed

+221
-52
lines changed

docs/examples.md

+195-47
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,135 @@ The types generated by openapi-typescript are universal, and can be used in a va
1111

1212
Fetching data can be done simply and safely using an **automatically-typed fetch wrapper**:
1313

14-
- [openapi-fetch](/openapi-fetch/) (recommended)
15-
- [openapi-typescript-fetch](https://www.npmjs.com/package/openapi-typescript-fetch) by [@ajaishankar](https://github.com/ajaishankar)
16-
- [feature-fetch](https://www.npmjs.com/package/feature-fetch) by [builder.group](https://github.com/builder-group)
14+
<details>
15+
<summary><a href="/openapi-fetch/">openapi-fetch</a> (recommended)</summary>
16+
17+
::: code-group
18+
19+
```ts [test/my-project.ts]
20+
import createClient from "openapi-fetch";
21+
import type { paths } from "./my-openapi-3-schema"; // generated by openapi-typescript
22+
23+
const client = createClient<paths>({ baseUrl: "https://myapi.dev/v1/" });
24+
25+
const {
26+
data, // only present if 2XX response
27+
error, // only present if 4XX or 5XX response
28+
} = await client.GET("/blogposts/{post_id}", {
29+
params: {
30+
path: { post_id: "123" },
31+
},
32+
});
33+
34+
await client.PUT("/blogposts", {
35+
body: {
36+
title: "My New Post",
37+
},
38+
});
39+
```
40+
41+
:::
42+
43+
</details>
44+
45+
<details>
46+
<summary><a href="https://www.npmjs.com/package/openapi-typescript-fetch" target="_blank" rel="noreferrer">openapi-typescript-fetch</a> by <a href="https://github.com/ajaishankar" target="_blank" rel="noreferrer">@ajaishankar</a></summary>
47+
48+
::: code-group
49+
50+
```ts [test/my-project.ts]
51+
import { Fetcher } from 'openapi-typescript-fetch';
52+
import type { paths } from './my-openapi-3-schema'; // generated by openapi-typescript
53+
54+
const fetcher = Fetcher.for<paths>();
55+
56+
// GET request
57+
const getBlogPost = fetcher.path('/blogposts/{post_id}').method('get').create();
58+
59+
try {
60+
const { status, data } = await getBlogPost({ pathParams: { post_id: '123' } });
61+
console.log(data);
62+
} catch (error) {
63+
console.error('Error:', error);
64+
}
65+
66+
// PUT request
67+
const updateBlogPost = fetcher.path('/blogposts').method('put').create();
68+
69+
try {
70+
await updateBlogPost({ body: { title: 'My New Post' } });
71+
} catch (error) {
72+
console.error('Error:', error);
73+
}
74+
```
75+
76+
:::
77+
78+
</details>
79+
80+
<details>
81+
<summary><a href="https://www.npmjs.com/package/feature-fetch" target="_blank" rel="noreferrer">feature-fetch</a> by <a href="https://github.com/builder-group" target="_blank" rel="noreferrer">builder.group</a></summary>
82+
83+
::: code-group
84+
85+
```ts [test/my-project.ts]
86+
import { createOpenApiFetchClient } from 'feature-fetch';
87+
import type { paths } from './my-openapi-3-schema'; // generated by openapi-typescript
88+
89+
// Create the OpenAPI fetch client
90+
const fetchClient = createOpenApiFetchClient<paths>({
91+
prefixUrl: 'https://myapi.dev/v1'
92+
});
93+
94+
// Send a GET request
95+
const response = await fetchClient.get('/blogposts/{post_id}', {
96+
pathParams: {
97+
post_id: '123',
98+
},
99+
});
100+
101+
// Handle the response (Approach 1: Standard if-else)
102+
if (response.isOk()) {
103+
const data = response.value.data;
104+
console.log(data); // Handle successful response
105+
} else {
106+
const error = response.error;
107+
if (error instanceof NetworkError) {
108+
console.error('Network error:', error.message);
109+
} else if (error instanceof RequestError) {
110+
console.error('Request error:', error.message, 'Status:', error.status);
111+
} else {
112+
console.error('Service error:', error.message);
113+
}
114+
}
115+
116+
// Send a PUT request
117+
const putResponse = await fetchClient.put('/blogposts', {
118+
body: {
119+
title: 'My New Post',
120+
},
121+
});
122+
123+
// Handle the response (Approach 2: Try-catch)
124+
try {
125+
const putData = putResponse.unwrap().data;
126+
console.log(putData); // Handle the successful response
127+
} catch (error) {
128+
// Handle the error
129+
if (error instanceof NetworkError) {
130+
console.error('Network error:', error.message);
131+
} else if (error instanceof RequestError) {
132+
console.error('Request error:', error.message, 'Status:', error.status);
133+
} else {
134+
console.error('Service error:', error.message);
135+
}
136+
}
137+
```
138+
139+
:::
140+
141+
</details>
142+
17143

18144
::: tip
19145

@@ -61,86 +187,108 @@ TypeChecking in server environments can be tricky, as you’re often querying da
61187

62188
:::
63189

64-
## Hono with `@blgc/openapi-router`
190+
## Hono with [`@blgc/openapi-router`](https://github.com/builder-group/community/tree/develop/packages/openapi-router)
191+
192+
Instead of manually typing each route with generics as in the [Hono example](#hono), [`@blgc/openapi-router`](https://github.com/builder-group/community/tree/develop/packages/openapi-router) wraps around the [Hono router](https://hono.dev/docs/api/routing) to deliver full typesafety and enforce your OpenAPI-Schema with validators.
193+
194+
::: tip Good To Know
195+
196+
While TypeScript types ensure compile-time safety, they don't enforce runtime schema validation. For runtime compliance, you need to integrate with validation libraries like Zod or Valibot. Although you must define the validation rules manually, they are type-safe to ensure these rules are correctly defined.
197+
198+
:::
199+
200+
::: code-group
65201

66202
```ts [src/router.ts]
67203
import { createHonoOpenApiRouter } from '@blgc/openapi-router';
68204
import { Hono } from 'hono';
69205
import { zValidator } from 'validation-adapters/zod';
70206
import * as z from 'zod';
71207

72-
import { paths } from './gen/v1';
73-
import { PetSchema } from './schemas';
208+
import { paths } from './gen/v1'; // OpenAPI-generated types
209+
import { PetSchema } from './schemas'; // Custom reusable Zod schema for validation
74210

75211
export const router = new Hono();
76212
export const openApiRouter = createHonoOpenApiRouter<paths>(router);
77213

214+
// GET /pet/{petId}
78215
openApiRouter.get('/pet/{petId}', {
79-
pathValidator: zValidator(
80-
z.object({
81-
petId: z.number()
82-
})
83-
),
84-
handler: (c) => {
85-
const { petId } = c.req.valid('param');
86-
87-
return c.json({
88-
name: 'Falko',
89-
photoUrls: []
90-
});
91-
}
216+
pathValidator: zValidator(
217+
z.object({
218+
petId: z.number() // Validate that petId is a number
219+
})
220+
),
221+
handler: (c) => {
222+
const { petId } = c.req.valid('param'); // Access validated params
223+
return c.json({ name: 'Falko', photoUrls: [] });
224+
}
92225
});
93226

227+
// POST /pet
94228
openApiRouter.post('/pet', {
95-
bodyValidator: zValidator(PetSchema),
96-
handler: (c) => {
97-
const { name, photoUrls } = c.req.valid('json');
98-
99-
return c.json({ name, photoUrls });
100-
}
229+
bodyValidator: zValidator(PetSchema), // Validate request body using PetSchema
230+
handler: (c) => {
231+
const { name, photoUrls } = c.req.valid('json'); // Access validated body data
232+
return c.json({ name, photoUrls });
233+
}
101234
});
102235
```
103236

104-
## Express with `@blgc/openapi-router`
237+
:::
238+
239+
[Full example](https://github.com/builder-group/community/tree/develop/examples/openapi-router/hono/petstore)
240+
241+
## Express with [`@blgc/openapi-router`](https://github.com/builder-group/community/tree/develop/packages/openapi-router)
242+
243+
[`@blgc/openapi-router`](https://github.com/builder-group/community/tree/develop/packages/openapi-router) wraps around the [Express router](https://expressjs.com/en/5x/api.html#router) to deliver full typesafety and enforce your OpenAPI-Schema with validators.
244+
245+
::: tip Good To Know
246+
247+
While TypeScript types ensure compile-time safety, they don't enforce runtime schema validation. For runtime compliance, you need to integrate with validation libraries like Zod or Valibot. Although you must define the validation rules manually, they are type-safe to ensure these rules are correctly defined.
248+
249+
:::
250+
251+
::: code-group
105252

106253
```ts [src/router.ts]
107254
import { createExpressOpenApiRouter } from '@blgc/openapi-router';
108255
import { Router } from 'express';
109256
import * as v from 'valibot';
110257
import { vValidator } from 'validation-adapters/valibot';
111258

112-
import { type paths } from './gen/v1';
113-
import { PetSchema } from './schemas';
259+
import { paths } from './gen/v1'; // OpenAPI-generated types
260+
import { PetSchema } from './schemas'; // Custom reusable Zod schema for validation
114261

115262
export const router: Router = Router();
116263
export const openApiRouter = createExpressOpenApiRouter<paths>(router);
117264

265+
// GET /pet/{petId}
118266
openApiRouter.get('/pet/{petId}', {
119-
pathValidator: vValidator(
120-
v.object({
121-
petId: v.number()
122-
})
123-
),
124-
handler: (req, res) => {
125-
const { petId } = req.params;
126-
127-
res.send({
128-
name: 'Falko',
129-
photoUrls: []
130-
});
131-
}
267+
pathValidator: vValidator(
268+
v.object({
269+
petId: v.number() // Validate that petId is a number
270+
})
271+
),
272+
handler: (req, res) => {
273+
const { petId } = req.params; // Access validated params
274+
res.send({ name: 'Falko', photoUrls: [] });
275+
}
132276
});
133277

278+
// POST /pet
134279
openApiRouter.post('/pet', {
135-
bodyValidator: vValidator(PetSchema),
136-
handler: (req, res) => {
137-
const { name, photoUrls } = req.body;
138-
139-
res.send({ name, photoUrls });
140-
}
280+
bodyValidator: vValidator(PetSchema), // Validate request body using PetSchema
281+
handler: (req, res) => {
282+
const { name, photoUrls } = req.body; // Access validated body data
283+
res.send({ name, photoUrls });
284+
}
141285
});
142286
```
143287

288+
:::
289+
290+
[Full example](https://github.com/builder-group/community/tree/develop/examples/openapi-router/express/petstore)
291+
144292
## Mock-Service-Worker (MSW)
145293

146294
If you are using [Mock Service Worker (MSW)](https://mswjs.io) to define your API mocks, you can use a **small, automatically-typed wrapper** around MSW, which enables you to address conflicts in your API mocks easily when your OpenAPI specification changes. Ultimately, you can have the same level of confidence in your application's API client **and** API mocks.

docs/openapi-fetch/index.md

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ openapi-fetch is a type-safe fetch client that pulls in your OpenAPI schema. Wei
1010
| :------------------------- | ---------: | :------------------------- |
1111
| openapi-fetch | `5 kB` | `300k` ops/s (fastest) |
1212
| openapi-typescript-fetch | `4 kB` | `150k` ops/s (2× slower) |
13+
| feature-fetch | `15 kB` | `300k` ops/s (not slower) |
1314
| axios | `32 kB` | `225k` ops/s (1.3× slower) |
1415
| superagent | `55 kB` | `50k` ops/s (6× slower) |
1516
| openapi-typescript-codegen | `367 kB` | `100k` ops/s (3× slower) |

docs/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"type": "module",
66
"scripts": {
77
"build": "pnpm run update-contributors && vitepress build && cp _redirects .vitepress/dist",
8-
"dev": "pnpm run update-contributors && vitepress dev",
8+
"dev": "vitepress dev",
99
"tokens": "co build",
1010
"update-contributors": "node scripts/update-contributors.js"
1111
},

packages/openapi-fetch/package.json

+4-2
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
"test:ts": "tsc --noEmit",
6262
"test:ts-no-strict": "tsc --noEmit -p test/no-strict-null-checks/tsconfig.json",
6363
"test-e2e": "playwright test",
64+
"bench:js": "vitest bench",
6465
"e2e-vite-build": "vite build test/fixtures/e2e",
6566
"e2e-vite-start": "vite preview test/fixtures/e2e",
6667
"version": "pnpm run prepare && pnpm run build"
@@ -69,15 +70,16 @@
6970
"openapi-typescript-helpers": "workspace:^"
7071
},
7172
"devDependencies": {
72-
"axios": "^1.7.2",
73+
"axios": "^1.7.4",
7374
"del-cli": "^5.1.0",
7475
"esbuild": "^0.20.2",
7576
"execa": "^8.0.1",
77+
"feature-fetch": "^0.0.15",
7678
"msw": "^2.3.1",
7779
"openapi-typescript": "workspace:^",
7880
"openapi-typescript-codegen": "^0.25.0",
7981
"openapi-typescript-fetch": "^2.0.0",
80-
"superagent": "^9.0.2",
82+
"superagent": "^10.0.1",
8183
"typescript": "^5.4.5",
8284
"vite": "^5.3.5"
8385
}

0 commit comments

Comments
 (0)