Skip to content

Commit f11e187

Browse files
committed
Middleware 3rd pass
1 parent 1590c88 commit f11e187

File tree

19 files changed

+561
-499
lines changed

19 files changed

+561
-499
lines changed

Diff for: docs/.vitepress/theme/style.css

+4
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@
9999
--vp-c-danger-2: var(--vp-c-red-2);
100100
--vp-c-danger-3: var(--vp-c-red-3);
101101
--vp-c-danger-soft: var(--vp-c-red-soft);
102+
103+
--vp-code-color: var(--vp-c-text-2);
104+
105+
--vp-c-tip-1: var(--vp-c-text-1);
102106
}
103107

104108
/**

Diff for: docs/6.x/advanced.md

+8-10
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ And the magic that produces this would live in a `test/utils.ts` file that can b
8888
::: code-group [test/utils.ts]
8989

9090
```ts
91-
import { paths } from "./api/v1/my-schema"; // generated by openapi-typescript
91+
import type { paths } from "./api/v1"; // generated by openapi-typescript
9292

9393
// Settings
9494
// ⚠️ Important: change this! This prefixes all URLs
@@ -102,15 +102,13 @@ type FilterKeys<Obj, Matchers> = {
102102
type PathResponses<T> = T extends { responses: any } ? T["responses"] : unknown;
103103
type OperationContent<T> = T extends { content: any } ? T["content"] : unknown;
104104
type MediaType = `${string}/${string}`;
105-
type MockedResponse<T, Status extends keyof T = keyof T> = FilterKeys<
106-
OperationContent<T[Status]>,
107-
MediaType
108-
> extends never
109-
? { status: Status; body?: never }
110-
: {
111-
status: Status;
112-
body: FilterKeys<OperationContent<T[Status]>, MediaType>;
113-
};
105+
type MockedResponse<T, Status extends keyof T = keyof T> =
106+
FilterKeys<OperationContent<T[Status]>, MediaType> extends never
107+
? { status: Status; body?: never }
108+
: {
109+
status: Status;
110+
body: FilterKeys<OperationContent<T[Status]>, MediaType>;
111+
};
114112

115113
/**
116114
* Mock fetch() calls and type against OpenAPI schema

Diff for: docs/6.x/cli.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ export interface paths {
7171
Which means your type lookups also have to match the exact URL:
7272

7373
```ts
74-
import { paths } from "./my-schema";
74+
import type { paths } from "./api/v1";
7575

7676
const url = `/user/${id}`;
7777
type UserResponses = paths["/user/{user_id}"]["responses"];
@@ -80,7 +80,7 @@ type UserResponses = paths["/user/{user_id}"]["responses"];
8080
But when `--path-params-as-types` is enabled, you can take advantage of dynamic lookups like so:
8181

8282
```ts
83-
import { paths } from "./my-schema";
83+
import type { paths } from "./api/v1";
8484

8585
const url = `/user/${id}`;
8686
type UserResponses = paths[url]["responses"]; // automatically matches `paths['/user/{user_id}']`

Diff for: docs/cli.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ export interface paths {
115115
Which means your type lookups also have to match the exact URL:
116116

117117
```ts
118-
import { paths } from "./my-schema";
118+
import type { paths } from "./api/v1";
119119
120120
const url = `/user/${id}`;
121121
type UserResponses = paths["/user/{user_id}"]["responses"];
@@ -124,7 +124,7 @@ type UserResponses = paths["/user/{user_id}"]["responses"];
124124
But when `--path-params-as-types` is enabled, you can take advantage of dynamic lookups like so:
125125

126126
```ts
127-
import { paths } from "./my-schema";
127+
import type { paths } from "./api/v1";
128128

129129
const url = `/user/${id}`;
130130
type UserResponses = paths[url]["responses"]; // automatically matches `paths['/user/{user_id}']`

Diff for: docs/examples.md

+8-10
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ And the magic that produces this would live in a `test/utils.ts` file that can b
136136
::: code-group [test/utils.ts]
137137

138138
```ts
139-
import { paths } from "./api/v1/my-schema"; // generated by openapi-typescript
139+
import type { paths } from "./api/v1"; // generated by openapi-typescript
140140

141141
// Settings
142142
// ⚠️ Important: change this! This prefixes all URLs
@@ -150,15 +150,13 @@ type FilterKeys<Obj, Matchers> = {
150150
type PathResponses<T> = T extends { responses: any } ? T["responses"] : unknown;
151151
type OperationContent<T> = T extends { content: any } ? T["content"] : unknown;
152152
type MediaType = `${string}/${string}`;
153-
type MockedResponse<T, Status extends keyof T = keyof T> = FilterKeys<
154-
OperationContent<T[Status]>,
155-
MediaType
156-
> extends never
157-
? { status: Status; body?: never }
158-
: {
159-
status: Status;
160-
body: FilterKeys<OperationContent<T[Status]>, MediaType>;
161-
};
153+
type MockedResponse<T, Status extends keyof T = keyof T> =
154+
FilterKeys<OperationContent<T[Status]>, MediaType> extends never
155+
? { status: Status; body?: never }
156+
: {
157+
status: Status;
158+
body: FilterKeys<OperationContent<T[Status]>, MediaType>;
159+
};
162160

163161
/**
164162
* Mock fetch() calls and type against OpenAPI schema

Diff for: docs/openapi-fetch/api.md

+53-27
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ createClient<paths>(options);
2626
The following options apply to all request methods (`.GET()`, `.POST()`, etc.)
2727

2828
```ts
29-
client.get("/my-url", options);
29+
client.GET("/my-url", options);
3030
```
3131

3232
| Name | Type | Description |
@@ -37,15 +37,14 @@ client.get("/my-url", options);
3737
| `bodySerializer` | BodySerializer | (optional) Provide a [bodySerializer](#bodyserializer) |
3838
| `parseAs` | `"json"` \| `"text"` \| `"arrayBuffer"` \| `"blob"` \| `"stream"` | (optional) Parse the response using [a built-in instance method](https://developer.mozilla.org/en-US/docs/Web/API/Response#instance_methods) (default: `"json"`). `"stream"` skips parsing altogether and returns the raw stream. |
3939
| `fetch` | `fetch` | Fetch instance used for requests (default: fetch from `createClient`) |
40-
| `middleware` | `Middleware[]` | [See docs](/openapi-fetch/middleware-auth) |
4140
| (Fetch options) | | Any valid fetch option (`headers`, `mode`, `cache`, `signal`, …) ([docs](https://developer.mozilla.org/en-US/docs/Web/API/fetch#options)) |
4241

4342
### querySerializer
4443

4544
By default, this library serializes query parameters using `style: form` and `explode: true` [according to the OpenAPI specification](https://swagger.io/docs/specification/serialization/#query). To change the default behavior, you can supply your own `querySerializer()` function either on the root `createClient()` as well as optionally on an individual request. This is useful if your backend expects modifications like the addition of `[]` for array params:
4645

4746
```ts
48-
const { data, error } = await GET("/search", {
47+
const { data, error } = await client.GET("/search", {
4948
params: {
5049
query: { tags: ["food", "california", "healthy"] },
5150
},
@@ -70,7 +69,7 @@ const { data, error } = await GET("/search", {
7069
Similar to [querySerializer](#querySerializer), bodySerializer allows you to customize how the requestBody is serialized if you don’t want the default [JSON.stringify()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) behavior. You probably only need this when using `multipart/form-data`:
7170

7271
```ts
73-
const { data, error } = await PUT("/submit", {
72+
const { data, error } = await client.PUT("/submit", {
7473
body: {
7574
name: "",
7675
query: { version: 2 },
@@ -90,31 +89,27 @@ const { data, error } = await PUT("/submit", {
9089
Middleware is an object with `onRequest()` and `onResponse()` callbacks that can observe and modify requests and responses.
9190

9291
```ts
93-
function myMiddleware(): Middleware {
94-
return {
95-
async onRequest(req, options) {
96-
// set "foo" header
97-
req.headers.set("foo", "bar");
98-
return req;
99-
},
100-
async onResponse(res, options) {
101-
const { body, ...resOptions } = res;
102-
// change status of response
103-
return new Response(body, { ...resOptions, status: 200 });
104-
},
105-
};
106-
}
107-
108-
createClient({
109-
middleware: [myMiddleware()],
110-
});
111-
```
112-
113-
::: tip
92+
import createClient from "openapi-fetch";
93+
import type { paths } from "./api/v1";
94+
95+
const myMiddleware: Middleware = {
96+
async onRequest(req, options) {
97+
// set "foo" header
98+
req.headers.set("foo", "bar");
99+
return req;
100+
},
101+
async onResponse(res, options) {
102+
const { body, ...resOptions } = res;
103+
// change status of response
104+
return new Response(body, { ...resOptions, status: 200 });
105+
},
106+
};
114107

115-
Middleware can be a simple object. But wrapping in a function like the examples show lets you optionally create multiple instances of the same logic to handle different scenarios if needed.
108+
const client = createClient<paths>({ baseUrl: "https://myapi.dev/v1/" });
116109

117-
:::
110+
// register middleware
111+
client.use(myMiddleware);
112+
```
118113

119114
### onRequest
120115

@@ -154,3 +149,34 @@ And it expects either:
154149

155150
- **If modifying the response:** A [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response)
156151
- **If not modifying:** `undefined` (void)
152+
153+
### Skipping
154+
155+
If you want to skip the middleware under certain conditions, just `return` as early as possible:
156+
157+
```ts
158+
onRequest(req) {
159+
if (req.schemaPath !== "/projects/{project_id}") {
160+
return undefined;
161+
}
162+
//
163+
}
164+
```
165+
166+
This will leave the request/response unmodified, and pass things off to the next middleware handler (if any). There’s no internal callback or observer library needed.
167+
168+
### Ejecting middleware
169+
170+
To remove middleware, call `client.eject(middleware)`:
171+
172+
```ts{9}
173+
const myMiddleware = {
174+
// …
175+
};
176+
177+
// register middleware
178+
client.use(myMiddleware);
179+
180+
// remove middleware
181+
client.eject(myMiddleware);
182+
```

Diff for: docs/openapi-fetch/examples.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Example code of using openapi-fetch with other frameworks and libraries.
88

99
## React + React Query
1010

11-
[React Query](https://tanstack.com/query/latest) is a perfect wrapper for openapi-fetch in React. At only 13 kB, it provides clientside caching and request deduping across async React components without too much client weight in return. And its type inference preserves openapi-fetch types perfectly with minimal setup.
11+
[React Query](https://tanstack.com/query/latest) is a perfect wrapper for openapi-fetch in React. At only 13 kB, it provides clientside caching without too much client weight in return. And its stellar type inference preserves openapi-fetch types perfectly with minimal setup.
1212

1313
[View a code example in GitHub](https://github.com/drwpow/openapi-typescript/tree/main/packages/openapi-fetch/examples/react-query)
1414

Diff for: docs/openapi-fetch/index.md

+9-9
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,20 @@ The syntax is inspired by popular libraries like react-query or Apollo client, b
1818

1919
```ts
2020
import createClient from "openapi-fetch";
21-
import { paths } from "./v1"; // generated from openapi-typescript
21+
import type { paths } from "./api/v1"; // generated from openapi-typescript
2222

23-
const { GET, PUT } = createClient<paths>({ baseUrl: "https://myapi.dev/v1/" });
23+
const client = createClient<paths>({ baseUrl: "https://myapi.dev/v1/" });
2424

2525
const {
2626
data, // only present if 2XX response
2727
error, // only present if 4XX or 5XX response
28-
} = await GET("/blogposts/{post_id}", {
28+
} = await client.GET("/blogposts/{post_id}", {
2929
params: {
3030
path: { post_id: "123" },
3131
},
3232
});
3333

34-
await PUT("/blogposts", {
34+
await client.PUT("/blogposts", {
3535
body: {
3636
title: "My New Post",
3737
},
@@ -102,18 +102,18 @@ The best part about using openapi-fetch over oldschool codegen is no documentati
102102

103103
```ts
104104
import createClient from "openapi-fetch";
105-
import { paths } from "./v1";
105+
import type { paths } from "./api/v1";
106106

107-
const { GET, PUT } = createClient<paths>({ baseUrl: "https://myapi.dev/v1/" });
107+
const client = createClient<paths>({ baseUrl: "https://myapi.dev/v1/" });
108108

109-
const { data, error } = await GET("/blogposts/{post_id}", {
109+
const { data, error } = await client.GET("/blogposts/{post_id}", {
110110
params: {
111111
path: { post_id: "my-post" },
112112
query: { version: 2 },
113113
},
114114
});
115115

116-
const { data, error } = await PUT("/blogposts", {
116+
const { data, error } = await client.PUT("/blogposts", {
117117
body: {
118118
title: "New Post",
119119
body: "<p>New post body</p>",
@@ -150,7 +150,7 @@ The `POST()` request required a `body` object that provided all necessary [reque
150150
All methods return an object with **data**, **error**, and **response**.
151151

152152
```ts
153-
const { data, error, response } = await GET("/url");
153+
const { data, error, response } = await client.GET("/url");
154154
```
155155

156156
| Object | Response |

0 commit comments

Comments
 (0)