@kubb/plugin-fetch 5.5.8 → 5.5.10

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.
@@ -0,0 +1,77 @@
1
+ # Authenticate requests
2
+
3
+ Set an `auth` resolver on the generated client. Operations use the [security schemes](https://spec.openapis.org/oas/v3.1.0#security-scheme-object) declared in your OpenAPI spec. Requests remain unauthenticated until you provide credentials.
4
+
5
+ ## Set the auth resolver
6
+
7
+ Set the resolver for all secured operations:
8
+
9
+ ```typescript
10
+ import { client } from './gen/.kubb/client'
11
+
12
+ client.setConfig({
13
+ auth: () => localStorage.getItem('token') ?? undefined,
14
+ })
15
+ ```
16
+
17
+ `auth` takes a token string or a function. The function can be async and returns a token, or `undefined` to skip that scheme. When an operation accepts several schemes, the runtime tries each until one returns a token.
18
+
19
+ ## Return the right token per scheme
20
+
21
+ Use the scheme passed to the resolver to select a credential:
22
+
23
+ ```typescript
24
+ type Auth = {
25
+ type: 'http' | 'apiKey' | 'oauth2' | 'openIdConnect'
26
+ scheme?: 'bearer' | 'basic'
27
+ name?: string
28
+ in?: 'header' | 'query' | 'cookie'
29
+ }
30
+ ```
31
+
32
+ The scheme decides where the runtime puts the token:
33
+
34
+ | Scheme in the spec | `Auth` object | Where the token goes |
35
+ | ------------------ | ---------------------------------------- | ------------------------------- |
36
+ | `http` bearer | `{ type: 'http', scheme: 'bearer' }` | `Authorization: Bearer <token>` |
37
+ | `http` basic | `{ type: 'http', scheme: 'basic' }` | `Authorization: Basic <token>` |
38
+ | `apiKey` header | `{ type: 'apiKey', name, in: 'header' }` | request header named `name` |
39
+ | `apiKey` query | `{ type: 'apiKey', name, in: 'query' }` | query parameter named `name` |
40
+ | `apiKey` cookie | `{ type: 'apiKey', name, in: 'cookie' }` | `Cookie` header |
41
+ | `oauth2` | `{ type: 'oauth2' }` | `Authorization: Bearer <token>` |
42
+ | `openIdConnect` | `{ type: 'openIdConnect' }` | `Authorization: Bearer <token>` |
43
+
44
+ > [!NOTE]
45
+ > For basic auth, return the raw `username:password` string. The runtime base64-encodes it and writes the `Basic` prefix, so you never build the header yourself.
46
+
47
+ When a spec mixes schemes, branch on the `Auth` object and return the matching credential:
48
+
49
+ ```typescript
50
+ client.setConfig({
51
+ auth: (auth) => (auth.type === 'apiKey' ? apiKey : accessToken),
52
+ })
53
+ ```
54
+
55
+ ## Use a separate client per environment
56
+
57
+ To give each environment its own token, such as one per tenant in a multi-tenant app, build an isolated client with `createClient`:
58
+
59
+ ```typescript
60
+ import { createClient } from './gen/.kubb/client'
61
+
62
+ const tenant = createClient({
63
+ baseURL: 'https://tenant.example.com',
64
+ auth: () => tenantToken,
65
+ })
66
+
67
+ const { data } = await getPetById({ path: { petId: 1 }, client: tenant })
68
+ ```
69
+
70
+ To override the client for one request, pass `auth` on that single call, which suits a one-off token refresh. An explicit `headers` value you set on a call always wins over the resolved token.
71
+
72
+ For authentication outside OpenAPI security schemes, use an async request interceptor to sign requests or supply custom headers.
73
+
74
+ ## See also
75
+
76
+ - [Add interceptors](https://kubb.dev/extensions/kubb-labs/plugin-fetch/guides/interceptors)
77
+ - [Set the base URL](https://kubb.dev/extensions/kubb-labs/plugin-fetch/guides/base-url)
@@ -0,0 +1,63 @@
1
+ # Set the base URL
2
+
3
+ Set `baseURL` on the client plugin or on the generated client.
4
+
5
+ > [!NOTE]
6
+ > OpenAPI server URLs do not configure the client automatically. The `server` option of `adapterOas` only sets document metadata. It does not set `baseURL`.
7
+
8
+ ## Use the baseURL option
9
+
10
+ Pass `baseURL` to `pluginFetch` to set the generated default. Add `pluginTs` for the operation types.
11
+
12
+ ```typescript [kubb.config.ts]
13
+ import { defineConfig } from 'kubb/config'
14
+ import { pluginTs } from '@kubb/plugin-ts'
15
+ import { pluginFetch } from '@kubb/plugin-fetch'
16
+
17
+ export default defineConfig({
18
+ input: './petStore.yaml',
19
+ output: { path: './src/gen' },
20
+ plugins: [
21
+ pluginTs(),
22
+ pluginFetch({ baseURL: 'https://localhost:8080/api/v1' }),
23
+ ],
24
+ })
25
+ ```
26
+
27
+ A value containing a `${...}` interpolation stays dynamic. The plugin emits it as a template literal in the generated client config, so `baseURL: '${process.env.API_URL}'` reads the environment variable when the app runs instead of baking in the build-time value.
28
+
29
+ ## Set it at runtime
30
+
31
+ Use `client.setConfig` to configure all operations at startup:
32
+
33
+ ```typescript
34
+ import { client } from './gen/.kubb/client'
35
+
36
+ client.setConfig({ baseURL: import.meta.env.VITE_API_URL })
37
+ ```
38
+
39
+ Use `createClient` for an isolated client, then pass it to an operation:
40
+
41
+ ```typescript
42
+ import { createClient } from './gen/.kubb/client'
43
+ import { getPetById } from './gen/clients/getPetById'
44
+
45
+ const staging = createClient({ baseURL: 'https://staging.petstore.swagger.io/v2' })
46
+
47
+ const { data } = await getPetById({ path: { petId: 1 }, client: staging })
48
+ ```
49
+
50
+ Pass `baseURL` on a single call to override the client for that one request:
51
+
52
+ ```typescript
53
+ import { getPetById } from './gen/clients/getPetById'
54
+
55
+ const { data } = await getPetById({ path: { petId: 1 }, baseURL: 'https://localhost:3000' })
56
+ ```
57
+
58
+ A `baseURL` set on the call wins over `createClient`, which wins over `setConfig`, which wins over the build-time value.
59
+
60
+ ## See also
61
+
62
+ - [Authenticate requests](https://kubb.dev/extensions/kubb-labs/plugin-fetch/guides/authentication)
63
+ - [Use a custom transport](https://kubb.dev/extensions/kubb-labs/plugin-fetch/guides/transport)
@@ -0,0 +1,147 @@
1
+ # Call operations
2
+
3
+ The generated client accepts grouped request options on every operation and returns a typed `RequestResult` by default.
4
+
5
+ ## Call an operation
6
+
7
+ Pass parameters under `path`, `query`, `headers`, `cookies`, or `body`, matching the OpenAPI operation.
8
+
9
+ ```typescript
10
+ import { getPetById } from './gen/clients/getPetById'
11
+
12
+ const { data } = await getPetById({ path: { petId: 1 } })
13
+ // ^ the parsed pet, typed from the 200 response
14
+ ```
15
+
16
+ Query, header, and cookie parameters sit under their own keys, and a request body goes under
17
+ `body`:
18
+
19
+ ```typescript
20
+ import { searchPets } from './gen/clients/searchPets'
21
+ import { updatePet } from './gen/clients/updatePet'
22
+
23
+ await searchPets({
24
+ query: { status: 'available', category: 'dogs', limit: 10, offset: 0 },
25
+ })
26
+
27
+ await updatePet({
28
+ path: { petId: '123' },
29
+ headers: { 'X-Request-ID': 'req-123456' },
30
+ body: { name: 'Updated name', status: 'sold' },
31
+ })
32
+ ```
33
+
34
+ Each key is optional and only appears when the operation declares it. Call an operation with no parameters with an empty object: `getStatus({})`.
35
+
36
+ ## Read the result
37
+
38
+ A resolved call returns a `RequestResult` discriminated by the numeric `status`:
39
+
40
+ ```typescript
41
+ type RequestResult = {
42
+ status: number
43
+ data: TData // the parsed success body, undefined on an error result
44
+ error: TError // the parsed error body, undefined on a success result
45
+ contentType: string | undefined // the negotiated response media type
46
+ request: Request // the native request
47
+ response: Response // the native response
48
+ }
49
+ ```
50
+
51
+ When `throwOnError` is `true` (the generated default), a resolved call is always a success, so you
52
+ read `data` straight away:
53
+
54
+ ```typescript
55
+ const { data, status, response } = await getPetById({ path: { petId: 1 } })
56
+
57
+ console.info(status) // 200
58
+ console.info(response.headers.get('x-ratelimit-remaining'))
59
+ ```
60
+
61
+ When an operation documents more than one success status, narrow on `status` to reach the body
62
+ for that case, and TypeScript follows the check:
63
+
64
+ ```typescript
65
+ const result = await getPetById({ path: { petId: 1 } })
66
+
67
+ if (result.status === 200) {
68
+ console.info(result.data.name)
69
+ }
70
+ ```
71
+
72
+ ## Unwrap the success body
73
+
74
+ Call `.unwrap()` to return the success body. Awaiting the operation directly returns the full result.
75
+
76
+ ```typescript
77
+ import { getPetById } from './gen/clients/getPetById'
78
+
79
+ const pet = await getPetById({ path: { petId: 1 } }).unwrap()
80
+ // ^ the parsed pet, not the full RequestResult
81
+ ```
82
+
83
+ With `throwOnError: true`, a non-2xx response throws the same `ResponseError` a plain `await` would. With `throwOnError: false`, `.unwrap()` rejects with the parsed error body instead:
84
+
85
+ ```typescript
86
+ try {
87
+ const pet = await getPetById({ path: { petId: 1 }, throwOnError: false }).unwrap()
88
+ } catch (error) {
89
+ // the parsed error body, not a ResponseError
90
+ console.error(error)
91
+ }
92
+ ```
93
+
94
+ ## Set the content type
95
+
96
+ When an operation accepts or returns more than one media type, set `contentType` on the call. A
97
+ bare string sets the request content type. The object form also sends an `Accept` header for the
98
+ response:
99
+
100
+ ```typescript
101
+ await uploadAvatar({
102
+ path: { petId: '123' },
103
+ body: avatarBlob,
104
+ contentType: 'image/png',
105
+ })
106
+
107
+ await getPet({
108
+ path: { petId: '123' },
109
+ contentType: { request: 'application/json', response: 'application/xml' },
110
+ })
111
+ ```
112
+
113
+ An operation that declares a single content type has it built in, so a multipart upload such as `uploadFile({ path: { petId: '123' }, body: { file: pngBlob } })` needs only the body.
114
+
115
+ ## Pass native client options
116
+
117
+ Pass `options` on an operation for native fetch settings, such as `cache`, `mode`, `redirect`, `keepalive`, `duplex`, or `next`:
118
+
119
+ ```typescript [src/app.ts]
120
+ import { getPetById } from './gen/clients/getPetById'
121
+
122
+ await getPetById({ path: { petId: 1 }, options: { cache: 'no-store' } })
123
+ ```
124
+
125
+ Set `client.setConfig({ options })` for shared defaults. Per-call options take precedence. Kubb keeps control of serialization and HTTP error handling. See [Use a custom transport](https://kubb.dev/extensions/kubb-labs/plugin-fetch/guides/transport).
126
+
127
+ ## Build a URL without sending
128
+
129
+ Use `client.getUrl` to build a URL with the same base URL, path interpolation, and query serialization as a request:
130
+
131
+ ```typescript
132
+ import { client } from './gen/.kubb/client'
133
+
134
+ const url = client.getUrl({
135
+ url: '/pets/{petId}',
136
+ path: { petId: 1 },
137
+ query: { fields: 'name' },
138
+ })
139
+ // https://api.example.com/v1/pets/1?fields=name
140
+ ```
141
+
142
+ ## See also
143
+
144
+ - [Handle errors](https://kubb.dev/extensions/kubb-labs/plugin-fetch/guides/error-handling)
145
+ - [Configure serialization](https://kubb.dev/extensions/kubb-labs/plugin-fetch/guides/serialization)
146
+ - [Set the base URL](https://kubb.dev/extensions/kubb-labs/plugin-fetch/guides/base-url)
147
+ - [Server-sent events](https://kubb.dev/extensions/kubb-labs/plugin-fetch/guides/server-sent-events)
@@ -0,0 +1,124 @@
1
+ # Handle errors
2
+
3
+ Handle failures from the client Kubb generates. Catch a `ResponseError`, switch to a returned error result with `throwOnError`, and tell a network failure apart from a 4xx or 5xx response.
4
+
5
+ The generated client throws a `ResponseError` on non-2xx responses by default:
6
+
7
+ ```typescript
8
+ import { getPetById } from './gen/clients/getPetById'
9
+ import { ResponseError } from './gen/.kubb/client'
10
+
11
+ try {
12
+ const { data } = await getPetById({ path: { petId: 1 } })
13
+ console.info(data.name)
14
+ } catch (error) {
15
+ if (ResponseError.is(error)) {
16
+ console.error(error.status) // 404
17
+ console.error(error.data) // the parsed error body
18
+ }
19
+ }
20
+ ```
21
+
22
+ The `message` holds the HTTP method and the URL without its query string, so query credentials stay out of logs. For example: `GET https://api.example.com/pets/1 failed with status 404 Not Found`.
23
+
24
+ The error exposes these fields:
25
+
26
+ ```typescript
27
+ class ResponseError extends Error {
28
+ static is(error: unknown): error is ResponseError<unknown, unknown, unknown>
29
+ data: TError // the parsed error body
30
+ status: number
31
+ statusText: string
32
+ contentType: string | undefined
33
+ request: Request
34
+ response: Response
35
+ }
36
+ ```
37
+
38
+ Each generated package bundles its own `ResponseError` class, so narrow with `ResponseError.is(error)` instead of `instanceof`. `is()` checks the error name.
39
+
40
+ ## Return the error instead
41
+
42
+ Pass `throwOnError: false` and the call resolves for every documented status. The result is a
43
+ discriminated union: `data` is set on success and `error` on failure. Branch on `status`,
44
+ because an empty error body can leave `error` empty or undefined.
45
+
46
+ ```typescript
47
+ const result = await getPetById({ path: { petId: 1 }, throwOnError: false })
48
+
49
+ if (result.status === 200) {
50
+ console.info(result.data.name)
51
+ }
52
+ if (result.status !== 200) {
53
+ console.error(result.status, result.error)
54
+ }
55
+ ```
56
+
57
+ ## Set the generated default
58
+
59
+ Set `throwOnErrorDefault` on the client plugin to choose how generated operations handle non-2xx
60
+ responses by default. Each call can still override that setting with `throwOnError`:
61
+
62
+ ```typescript
63
+ import { pluginFetch } from '@kubb/plugin-fetch'
64
+
65
+ pluginFetch({ throwOnErrorDefault: false })
66
+
67
+ // resolves with an error result
68
+ const list = await searchPets({ query: { status: 'available' } })
69
+
70
+ // opt one call back into throwing
71
+ const pet = await getPetById({ path: { petId: 1 }, throwOnError: true })
72
+ ```
73
+
74
+ `client.setConfig({ throwOnError })` does not change this default, because each generated
75
+ operation passes its own value. Set it on the plugin.
76
+
77
+ ## Network failures still throw
78
+
79
+ > [!IMPORTANT]
80
+ > `throwOnError` only applies when the server answers. A dropped connection, a DNS failure, or an
81
+ > aborted `AbortSignal` always rejects. A `ResponseError` means the server answered with a non-2xx
82
+ > status, and a `ParseError` means a body failed validation. Any other error usually means the
83
+ > request never completed.
84
+
85
+ ```typescript
86
+ try {
87
+ const { data } = await getPetById({ path: { petId: 1 }, throwOnError: false })
88
+ use(data)
89
+ } catch (error) {
90
+ // not a ResponseError: the request never completed
91
+ console.error('request failed to send', error)
92
+ }
93
+ ```
94
+
95
+ Pass an `AbortSignal` to cancel a request. The call rejects with the abort reason.
96
+
97
+ ## Validation failures
98
+
99
+ When you turn on the `validator` option, a body that does not match its Zod schema throws a
100
+ `ParseError` instead of returning. It carries the schema's raw `issues`:
101
+
102
+ ```typescript
103
+ import { ParseError } from './gen/.kubb/standardSchema'
104
+
105
+ try {
106
+ const { data } = await getPetById({ path: { petId: 1 } })
107
+ use(data)
108
+ } catch (error) {
109
+ if (error instanceof ParseError) {
110
+ console.error(error.issues) // [{ message, path }]
111
+ }
112
+ }
113
+ ```
114
+
115
+ A `ParseError` reports schema validation issues. A `ResponseError` reports a non-2xx status. With `throwOnError: false`, the error body is checked against its own error schema.
116
+
117
+ Calling `.unwrap()` on a `throwOnError: false` call turns that same `error` into a rejection, so a
118
+ `try`/`catch` works there too.
119
+
120
+ ## See also
121
+
122
+ - [Call operations](https://kubb.dev/extensions/kubb-labs/plugin-fetch/guides/calling-operations)
123
+ - [Configure serialization](https://kubb.dev/extensions/kubb-labs/plugin-fetch/guides/serialization)
124
+ - [Add interceptors](https://kubb.dev/extensions/kubb-labs/plugin-fetch/guides/interceptors)
@@ -0,0 +1,78 @@
1
+ # Add interceptors
2
+
3
+ Register handlers on `client.interceptors.request`, `.response`, or `.error` to apply behavior across calls. Handlers receive the resolved request and result objects.
4
+
5
+ ## Run before the send
6
+
7
+ Request handlers run after URL construction, serialization, and authentication. Return the modified request:
8
+
9
+ ```typescript
10
+ import { client } from './gen/.kubb/client'
11
+
12
+ client.interceptors.request.use((request) => {
13
+ request.headers['X-Request-ID'] = crypto.randomUUID()
14
+ return request
15
+ })
16
+ ```
17
+
18
+ The handler receives a `ResolvedRequest` (`url`, `method`, `headers`, `body`, `signal`,
19
+ `credentials`, `options`, `responseType`).
20
+
21
+ ## Run after the send
22
+
23
+ Response handlers run before calls resolve, and before the status split and deserialization, so
24
+ `data` is the raw body. The handler receives a `TransportResult` (`data`, `status`, `statusText`,
25
+ `headers`, `contentType`, `request`, `response`):
26
+
27
+ ```typescript
28
+ client.interceptors.response.use((result) => {
29
+ console.info(`${result.status} ${result.request.url}`)
30
+ return result
31
+ })
32
+ ```
33
+
34
+ ## React to an error
35
+
36
+ Use an error handler to react to HTTP errors on the throwing path. The handler receives the
37
+ `ResponseError`:
38
+
39
+ ```typescript
40
+ client.interceptors.error.use((error) => {
41
+ if (error.status === 401) scheduleTokenRefresh()
42
+ return error
43
+ })
44
+ ```
45
+
46
+ This channel only fires on the throw path. When you read with `throwOnError: false`, a documented
47
+ non-2xx response resolves and no error interceptor runs, so inspect the returned `error` on the
48
+ result instead. A transport failure (no response) still throws. The error handler does not run,
49
+ because there is no result to pass it.
50
+
51
+ ## Add, replace, and remove handlers
52
+
53
+ `use` registers a handler and returns an id. Pass that id to `eject` to remove it, or to `update`
54
+ to swap its function in place.
55
+
56
+ > [!NOTE]
57
+ > Handlers run in the order you register them.
58
+
59
+ ```typescript
60
+ const id = client.interceptors.request.use((request) => request)
61
+
62
+ client.interceptors.request.update(id, (request) => {
63
+ request.headers['X-Trace'] = 'on'
64
+ return request
65
+ })
66
+
67
+ client.interceptors.request.eject(id)
68
+ ```
69
+
70
+ ## Where to set them
71
+
72
+ Register handlers on the shared `client` for all operations, or on a separate `createClient` instance for isolated calls. Interceptors are instance-level settings.
73
+
74
+ ## See also
75
+
76
+ - [Handle errors](https://kubb.dev/extensions/kubb-labs/plugin-fetch/guides/error-handling)
77
+ - [Authenticate requests](https://kubb.dev/extensions/kubb-labs/plugin-fetch/guides/authentication)
78
+ - [Use a custom transport](https://kubb.dev/extensions/kubb-labs/plugin-fetch/guides/transport)
@@ -0,0 +1,120 @@
1
+ # Configure serialization
2
+
3
+ The generated client encodes parameters using OpenAPI `style` and `explode`, serializes request bodies by content type, and decodes responses by media type. Each generated operation carries its parameter styles and content types, so most requests need no extra setup.
4
+
5
+ ## Override parameter serialization
6
+
7
+ Check the [default parameter styles and encoding](https://kubb.dev/docs/5.x/reference/serialization#parameter-styles) before replacing a serializer.
8
+
9
+ Override `serializer.query`, `.body`, or `.path` on the client. Omitted functions retain their defaults:
10
+
11
+ ```typescript
12
+ import { client } from './gen/.kubb/client'
13
+ import qs from 'qs'
14
+
15
+ client.setConfig({
16
+ serializer: {
17
+ query: (params) => qs.stringify(params, { arrayFormat: 'brackets' }),
18
+ },
19
+ })
20
+ ```
21
+
22
+ A serializer set this way runs for every call, but you can pass `serializer` on a single call to
23
+ override only that request.
24
+
25
+ ## Encode request bodies
26
+
27
+ The request content type decides how the body is encoded. The default serializer handles the
28
+ common types: a plain object becomes JSON, `multipart/form-data` becomes `FormData`, and
29
+ `application/x-www-form-urlencoded` becomes `URLSearchParams`. Binary and already-encoded bodies
30
+ (`FormData`, `URLSearchParams`, `Blob`, `ArrayBuffer`, typed arrays, and strings) pass through
31
+ untouched.
32
+
33
+ When an operation declares a single request content type, Kubb sets it on the generated function,
34
+ so you pass only the body. For an operation that accepts more than one, set `contentType` on the
35
+ call.
36
+
37
+ > [!NOTE]
38
+ > When the body is `FormData`, the runtime removes any `Content-Type` header so the transport sets
39
+ > it with the multipart boundary. You do not need to set `multipart/form-data` yourself, and a
40
+ > value you set is dropped for that request.
41
+
42
+ To encode a content type the default serializer does not handle, register a codec for that media
43
+ type. `codecs` is keyed by content type. Each entry takes a `serialize` for the request body, a
44
+ `deserialize` for the response, or both:
45
+
46
+ ```typescript
47
+ import { client } from './gen/.kubb/client'
48
+ import { stringify } from 'yaml'
49
+
50
+ client.setConfig({
51
+ codecs: {
52
+ 'application/yaml': { serialize: (body) => stringify(body) },
53
+ },
54
+ })
55
+ ```
56
+
57
+ ## Decode responses
58
+
59
+ The runtime reads the response `Content-Type` and decodes the body by it: JSON is parsed, text
60
+ stays a string, and a binary type becomes a `Blob`. The negotiated media type is on the result as
61
+ `contentType`, so a `switch (result.contentType)` narrows `data` for an operation that returns
62
+ more than one.
63
+
64
+ To decode a media type the runtime does not handle, register a codec's `deserialize` for it.
65
+ `deserialize` receives the raw body and the content type and returns the parsed value. It runs
66
+ before validation, so the schema checks the parsed value:
67
+
68
+ ```typescript
69
+ import { client } from './gen/.kubb/client'
70
+
71
+ client.setConfig({
72
+ codecs: {
73
+ 'application/xml': { deserialize: (raw) => new DOMParser().parseFromString(raw as string, 'application/xml') },
74
+ },
75
+ })
76
+ ```
77
+
78
+ Like the other config, `codecs` can also be passed on a single call, and a per-call entry merges
79
+ over the client one for that content type.
80
+
81
+ When the runtime picks the wrong parse mode because a response omits its `Content-Type` or sets a
82
+ misleading one, force the mode with `responseType` on the call. It accepts `'json'`, `'text'`,
83
+ `'blob'`, `'arraybuffer'`, and `'document'`:
84
+
85
+ ```typescript
86
+ const { data } = await downloadInvoice({ path: { id: '123' }, responseType: 'blob' })
87
+ // data is a Blob even when the server leaves Content-Type unset
88
+ ```
89
+
90
+ [Server-sent events](https://kubb.dev/extensions/kubb-labs/plugin-fetch/guides/server-sent-events) covers `responseType: 'stream'`.
91
+
92
+ For bidirectional formats, supply both `serialize` and `deserialize` in the media type codec, then set the operation's request and response `contentType`.
93
+
94
+ ## Validate responses
95
+
96
+ Enable `validator` on the client plugin and register `pluginZod` in the same configuration. Validation is disabled by default.
97
+
98
+ ```typescript
99
+ import { pluginFetch } from '@kubb/plugin-fetch'
100
+
101
+ pluginFetch({ validator: 'zod' })
102
+ ```
103
+
104
+ `'zod'` validates the success response body, and the error body when a non-2xx call does not
105
+ throw. Use the object form to opt in per direction, where `request` validates the request
106
+ body before the call goes out:
107
+
108
+ ```typescript
109
+ pluginFetch({ validator: { request: 'zod', response: 'zod' } })
110
+ ```
111
+
112
+ With a validator set, Kubb passes the matching schema to each generated call, and the runtime
113
+ parses the body through it. A body that does not match throws a `ParseError` with the schema's
114
+ `issues`.
115
+
116
+ ## See also
117
+
118
+ - [Call operations](https://kubb.dev/extensions/kubb-labs/plugin-fetch/guides/calling-operations)
119
+ - [Handle errors](https://kubb.dev/extensions/kubb-labs/plugin-fetch/guides/error-handling)
120
+ - [Server-sent events](https://kubb.dev/extensions/kubb-labs/plugin-fetch/guides/server-sent-events)