@kubb/plugin-client 5.5.7 → 5.5.8

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,56 @@
1
+ # Validate requests and responses
2
+
3
+ `@kubb/plugin-client` does not validate on its own. With `validator` set, it passes the schemas to your client, which runs them.
4
+
5
+ ::code-group
6
+
7
+ ```typescript [kubb.config.ts]
8
+ import { defineConfig } from 'kubb/config'
9
+ import { pluginTs } from '@kubb/plugin-ts'
10
+ import { pluginZod } from '@kubb/plugin-zod'
11
+ import { pluginClient } from '@kubb/plugin-client'
12
+
13
+ export default defineConfig({
14
+ input: './petStore.yaml',
15
+ output: { path: './src/gen' },
16
+ plugins: [
17
+ pluginTs(),
18
+ pluginZod(),
19
+ pluginClient({ importPath: '../../../client', validator: 'zod' }),
20
+ ],
21
+ })
22
+ ```
23
+
24
+ ::
25
+
26
+ Each generated call now includes the schemas:
27
+
28
+ ```typescript
29
+ request({
30
+ method: 'POST',
31
+ url: '/pet',
32
+ validator: { response: addPetResponseSchema, error: addPetErrorSchema },
33
+ ...config,
34
+ })
35
+ ```
36
+
37
+ Add a `validator` field to your `RequestConfig`, then run the schemas in `client`. Zod schemas follow [Standard Schema](https://standardschema.dev), so `~standard.validate` works with any compatible library:
38
+
39
+ ```typescript [src/client.ts]
40
+ type Result = { value: unknown; issues?: undefined } | { issues: ReadonlyArray<unknown> }
41
+ type Schema = { '~standard': { validate: (value: unknown) => Result | Promise<Result> } }
42
+
43
+ export type RequestConfig = {
44
+ // ...
45
+ validator?: { request?: Schema; response?: Schema; error?: Schema }
46
+ }
47
+
48
+ // inside client(), after reading the body
49
+ const result = await config.validator?.response?.['~standard'].validate(body)
50
+ if (result && 'issues' in result && result.issues) throw new Error('Response validation failed')
51
+ const validatedBody = result && 'value' in result ? result.value : body
52
+ ```
53
+
54
+ Return `validatedBody` as `data`, so a schema that transforms the value takes effect. Without a response schema, `validatedBody` is the original `body`.
55
+
56
+ Set `validator: { request: 'zod' }` or `{ response: 'zod' }` to pass one direction only. Without `pluginZod()` in the plugins list, generation stops with an error.
@@ -0,0 +1,91 @@
1
+ # Write your client
2
+
3
+ The generated functions import your module through [`importPath`](https://kubb.dev/extensions/kubb-labs/plugin-client/options#importpath). That module is the only runtime code the generated functions call, and you own it.
4
+
5
+ ## Required exports
6
+
7
+ | Export | Kind | Purpose |
8
+ | --------------- | -------- | -------------------------------------------------------------------------------------------- |
9
+ | `client` | function | Sends the request. Every generated function calls it, unless a call passes its own `client`. |
10
+ | `Options` | type | The grouped options object a generated function accepts. |
11
+ | `RequestResult` | type | What a generated function resolves to. |
12
+
13
+ If your spec has server-sent event operations, also export `toEventStream` and the `EventStreamResult` and `SuccessOf` types.
14
+
15
+ ## What a generated function looks like
16
+
17
+ ```typescript
18
+ import type { Options, RequestResult } from '../../../client'
19
+ import type { GetPetByIdOptions, GetPetByIdResponses } from '../../models/pet/GetPetById'
20
+ import { client } from '../../../client'
21
+
22
+ export function getPetById<ThrowOnError extends boolean = true>(
23
+ options: Options<GetPetByIdOptions, ThrowOnError>,
24
+ ): Promise<RequestResult<GetPetByIdResponses, ThrowOnError>> {
25
+ const { client: request = client, ...config } = options
26
+
27
+ return request({
28
+ method: 'GET',
29
+ url: '/pet/{petId}',
30
+ security: [{ type: 'apiKey', name: 'api_key', in: 'header' }, { type: 'oauth2' }],
31
+ ...config,
32
+ throwOnError: config.throwOnError ?? true,
33
+ }) as Promise<RequestResult<GetPetByIdResponses, ThrowOnError>>
34
+ }
35
+ ```
36
+
37
+ Your `client` receives `method`, the `url` template with `{param}` placeholders, `security`, the grouped `path`, `query`, `headers`, and `body`, and `throwOnError`. It returns a promise.
38
+
39
+ ## A minimal client
40
+
41
+ The client below uses `fetch`. Replace the transport with the HTTP library you use.
42
+
43
+ ```typescript [src/client.ts]
44
+ export type RequestConfig = {
45
+ method: 'GET' | 'PUT' | 'PATCH' | 'POST' | 'DELETE' | 'OPTIONS' | 'HEAD'
46
+ url: string
47
+ baseURL?: string
48
+ headers?: object
49
+ path?: object
50
+ query?: object
51
+ body?: unknown
52
+ signal?: AbortSignal
53
+ throwOnError?: boolean
54
+ security?: Array<{ type: string; name?: string; in?: string }>
55
+ client?: typeof client
56
+ }
57
+
58
+ type DataShape = { body?: unknown; headers?: unknown; path?: unknown; query?: unknown }
59
+
60
+ export type Options<TData extends DataShape, ThrowOnError extends boolean = true> = Omit<RequestConfig, keyof DataShape | 'url' | 'method'> &
61
+ TData & { throwOnError?: ThrowOnError }
62
+
63
+ export type RequestResult<TResponses, ThrowOnError extends boolean = true> = ThrowOnError extends true
64
+ ? { data: TResponses[keyof TResponses]; error: undefined; response: Response }
65
+ : { data: TResponses[keyof TResponses]; error: undefined; response: Response } | { data: undefined; error: {}; response: Response }
66
+
67
+ // Set this to your API host. `new URL()` needs an absolute URL.
68
+ export const settings = { baseURL: 'https://petstore3.swagger.io/api/v3' }
69
+
70
+ export async function client(config: RequestConfig) {
71
+ const path: Record<string, unknown> = { ...config.path }
72
+ const url = new URL((config.baseURL ?? settings.baseURL) + config.url.replace(/\{(\w+)\}/g, (_, key) => encodeURIComponent(String(path[key]))))
73
+
74
+ const response = await fetch(url, {
75
+ method: config.method,
76
+ headers: config.headers as Record<string, string>,
77
+ body: config.body === undefined ? undefined : JSON.stringify(config.body),
78
+ signal: config.signal,
79
+ })
80
+ const body = await response.json().catch(() => undefined)
81
+
82
+ if (response.ok) return { data: body, error: undefined, response }
83
+ if (config.throwOnError ?? true) throw new Error(`Request failed with status ${response.status}`)
84
+ return { data: undefined, error: body ?? { status: response.status }, response }
85
+ }
86
+ ```
87
+
88
+ The [example project](https://github.com/kubb-labs/plugins/tree/main/examples/client) has a fuller version with a base URL setter, query handling, and an async auth hook. The types in this sketch are minimal. Add status-aware response types when you need them.
89
+
90
+ > [!TIP]
91
+ > Kubb does not depend on retries, interceptors, or request signing. Add them to `client` when you need them.
@@ -0,0 +1,79 @@
1
+ # Options
2
+
3
+ Pass these options to `pluginClient()`. Shared options link to [Shared plugin options](https://kubb.dev/docs/5.x/reference/plugin-options).
4
+
5
+ ## Options overview
6
+
7
+ | Option | Purpose | Default |
8
+ | ------------------------------------------------------------------------- | ---------------------------------------------------------------------- | ------------------------------------------------ |
9
+ | [`importPath`](#importpath) | Import specifier of your client module. | Required |
10
+ | [`output`](https://kubb.dev/docs/5.x/reference/plugin-options#output) | Where files are written: `path`, `mode`, `barrel`, `banner`, `footer`. | `{ path: 'clients', barrel: { type: 'named' } }` |
11
+ | [`group`](https://kubb.dev/docs/5.x/reference/plugin-options#group) | Split output by tag or path: `type`, `name`. | None |
12
+ | [`throwOnErrorDefault`](#throwonerrordefault) | Default `throwOnError` value passed to your client. | `true` |
13
+ | [`validator`](#validator) | Pass Zod schemas to your client. | `false` |
14
+ | [`include`](https://kubb.dev/docs/5.x/reference/plugin-options#include) | Keep only operations that match. | None |
15
+ | [`exclude`](https://kubb.dev/docs/5.x/reference/plugin-options#exclude) | Skip operations that match. | `[]` |
16
+ | [`override`](https://kubb.dev/docs/5.x/reference/plugin-options#override) | Apply different options per pattern. | `[]` |
17
+ | [`resolver`](https://kubb.dev/docs/5.x/reference/plugin-options#resolver) | Customize generated names and file paths. | `resolverClient` |
18
+ | [`macros`](https://kubb.dev/docs/5.x/reference/plugin-options#macros) | Rewrite AST nodes before printing. | `[]`, run after the built-in client macros |
19
+
20
+ ## Option details
21
+
22
+ ### importPath
23
+
24
+ Import specifier of your client module. The plugin writes it into every generated import exactly as given, so it must resolve from the generated file. Use a relative path, a package name, or an alias.
25
+
26
+ | | |
27
+ | -------- | -------- |
28
+ | Type | `string` |
29
+ | Required | `true` |
30
+
31
+ ```typescript
32
+ pluginClient({ importPath: '../../../client' }) // relative to src/gen/clients/<tag>/
33
+ pluginClient({ importPath: '@my-org/api-client' }) // a package
34
+ ```
35
+
36
+ With `group: { type: 'tag' }` and `output.path: 'clients'`, a generated file sits in `src/gen/clients/<tag>/`, so `../../../client` points at `src/client.ts`. Without `group` it is `../../client`.
37
+
38
+ The module must export `client`, plus the `Options` and `RequestResult` types. See [write your client](https://kubb.dev/extensions/kubb-labs/plugin-client/guides/write-your-client).
39
+
40
+ ### throwOnErrorDefault
41
+
42
+ Default for the `throwOnError` field that every generated function passes to your client. A call that sets `throwOnError` itself wins. Your client decides what the flag does: the [example client](https://kubb.dev/extensions/kubb-labs/plugin-client/guides/write-your-client) throws on a non-2xx status when it is `true` and returns the error as a value when it is `false`.
43
+
44
+ | | |
45
+ | -------- | --------- |
46
+ | Type | `boolean` |
47
+ | Required | `false` |
48
+ | Default | `true` |
49
+
50
+ The setting applies to the whole plugin, so you cannot set it in `override`.
51
+
52
+ ### validator
53
+
54
+ Passes Zod schemas from `@kubb/plugin-zod` to your client on `config.validator`. The plugin does not run the schemas. Your client reads `config.validator` and runs them. See [validate requests and responses](https://kubb.dev/extensions/kubb-labs/plugin-client/guides/validate-requests-and-responses).
55
+
56
+ | | |
57
+ | -------- | --------------------------------------------------------- |
58
+ | Type | `false \| 'zod' \| { request?: 'zod'; response?: 'zod' }` |
59
+ | Required | `false` |
60
+ | Default | `false` |
61
+
62
+ ::field-group
63
+
64
+ :::field{name="false"}
65
+ Default value. Passes nothing.
66
+ :::
67
+
68
+ :::field{name="'zod'"}
69
+ Passes the response and error schemas.
70
+ :::
71
+
72
+ :::field{name="{ request?: 'zod', response?: 'zod' }"}
73
+ Opts in per direction.
74
+ :::
75
+
76
+ ::
77
+
78
+ > [!IMPORTANT]
79
+ > Add `pluginZod()` to the plugins list when `validator` is set. Generation stops with an error if it is missing.
@@ -0,0 +1,47 @@
1
+ # Client
2
+
3
+ Generates typed operation functions from your OpenAPI spec that call a client module you write. You decide the transport, authentication, and error handling, and the generated code has no Fetch, axios, or Kubb runtime dependency.
4
+
5
+ Each function takes grouped `path`, `query`, `headers`, and `body` parameters and passes the operation's auth requirements to your client in a `security` list. A single call can pass its own `client` to replace the default.
6
+
7
+ Use it to connect the generated functions to an HTTP layer you already have, or to sign requests. For a ready-made client, use `@kubb/plugin-fetch` or `@kubb/plugin-axios` instead.
8
+
9
+ ## Setup
10
+
11
+ - Set `importPath` to your client module. It is required. The module must export `client` and the `Options` and `RequestResult` types.
12
+ - Add `@kubb/plugin-ts` for the operation types.
13
+ - Add `@kubb/plugin-zod` when you set `validator`. Generation fails without it.
14
+
15
+ ## Options worth knowing
16
+
17
+ - `importPath` is written into every generated import as given, so it must resolve from the generated file. A relative path, a package name, or an alias works.
18
+ - `validator: 'zod'` passes request and response schemas from `@kubb/plugin-zod` to your client.
19
+ - `throwOnErrorDefault` is the `throwOnError` value each call passes to your client. The default is `true`.
20
+
21
+ ## Example
22
+
23
+ ```typescript [kubb.config.ts]
24
+ import { defineConfig } from 'kubb/config'
25
+ import { pluginTs } from '@kubb/plugin-ts'
26
+ import { pluginClient } from '@kubb/plugin-client'
27
+
28
+ export default defineConfig({
29
+ input: './petStore.yaml',
30
+ output: { path: './src/gen' },
31
+ plugins: [
32
+ pluginTs(),
33
+ pluginClient({
34
+ output: { path: 'clients', mode: 'directory', barrel: { type: 'named' } },
35
+ group: { type: 'tag' },
36
+ // resolved from each generated file: src/gen/clients/<tag>/ to src/client.ts
37
+ importPath: '../../../client',
38
+ }),
39
+ ],
40
+ })
41
+ ```
42
+
43
+ ## See also
44
+
45
+ - [`@kubb/plugin-fetch`](https://kubb.dev/extensions/kubb-labs/plugin-fetch)
46
+ - [`@kubb/plugin-axios`](https://kubb.dev/extensions/kubb-labs/plugin-axios)
47
+ - [Example project](https://github.com/kubb-labs/plugins/tree/main/examples/client)
@@ -10,9 +10,16 @@
10
10
  "light": "https://kubb.dev/feature/javascript.svg"
11
11
  },
12
12
  "tags": ["api-client", "http-client", "codegen", "openapi"],
13
- "docs": "https://kubb.dev/plugins/plugin-client",
13
+ "docs": "https://kubb.dev/extensions/kubb-labs/plugin-client",
14
+ "maintainers": [
15
+ {
16
+ "name": "Stijn Van Hulle",
17
+ "github": "stijnvanhulle"
18
+ }
19
+ ],
20
+ "dependencies": ["plugin-ts"],
14
21
  "compatibility": {
15
- "kubb": ">=5.8.0",
22
+ "kubb": ">=5.8.3",
16
23
  "node": ">=22"
17
24
  },
18
25
  "options": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubb/plugin-client",
3
- "version": "5.5.7",
3
+ "version": "5.5.8",
4
4
  "description": "Generate typed API operations against your own client module.",
5
5
  "keywords": [
6
6
  "api-client",
@@ -11,7 +11,7 @@
11
11
  "swagger",
12
12
  "typescript"
13
13
  ],
14
- "homepage": "https://kubb.dev/plugins/plugin-client",
14
+ "homepage": "https://kubb.dev/extensions/kubb-labs/plugin-client",
15
15
  "license": "MIT",
16
16
  "author": "stijnvanhulle",
17
17
  "repository": {
@@ -23,7 +23,7 @@
23
23
  "src",
24
24
  "dist",
25
25
  "kubb-extension.json",
26
- "OVERVIEW.md",
26
+ "docs",
27
27
  "!/**/**.test.**",
28
28
  "!/**/__tests__/**",
29
29
  "!/**/__snapshots__/**"
@@ -47,17 +47,17 @@
47
47
  "registry": "https://registry.npmjs.org/"
48
48
  },
49
49
  "dependencies": {
50
- "@kubb/plugin-ts": "^5.0.8",
51
- "@kubb/plugin-zod": "^5.4.6"
50
+ "@kubb/plugin-ts": "^5.0.9",
51
+ "@kubb/plugin-zod": "^5.4.7"
52
52
  },
53
53
  "devDependencies": {
54
54
  "@internals/client": "0.0.0",
55
55
  "@internals/shared": "0.0.0",
56
- "kubb": "^5.8.1",
56
+ "kubb": "^5.8.3",
57
57
  "typescript": "^6.0.3"
58
58
  },
59
59
  "peerDependencies": {
60
- "kubb": "^5.8.1"
60
+ "kubb": "^5.8.3"
61
61
  },
62
62
  "engines": {
63
63
  "node": ">=22"
package/src/plugin.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { createGroupConfig } from '@internals/shared'
2
- import { addGenerator, definePlugin, Resolver, setMacros, setResolver } from 'kubb/kit'
2
+ import { addGenerator, defineExtension, Resolver, setMacros, setResolver } from 'kubb/kit'
3
3
  import { compatibility } from '../kubb-extension.json'
4
4
  import { version } from '../package.json'
5
5
  import { pluginTsName } from '@kubb/plugin-ts'
@@ -23,7 +23,8 @@ export const pluginClientName = 'plugin-client' satisfies PluginClient['name']
23
23
  * pluginClient({ importPath: '../api/client' })
24
24
  * ```
25
25
  */
26
- export const pluginClient = definePlugin<PluginClient>({
26
+ export const pluginClient = defineExtension<PluginClient>({
27
+ kind: 'plugin',
27
28
  meta: { name: pluginClientName, version, compatibility },
28
29
  dependencies: (options) => ({ [pluginTsName]: { optional: true }, [pluginZodName]: { optional: !options.validator } }),
29
30
  setup(options) {
package/OVERVIEW.md DELETED
@@ -1,17 +0,0 @@
1
- # Client
2
-
3
- Generates typed operation functions from your OpenAPI spec that call a client module you write. You decide the transport, authentication, and error handling, and the generated code has no Fetch, axios, or Kubb runtime dependency.
4
-
5
- Use it for signed requests, a shared client, or an existing HTTP layer. For a ready-made client, use `@kubb/plugin-fetch` or `@kubb/plugin-axios` instead.
6
-
7
- ## Setup
8
-
9
- - Set `importPath` to your client module. It is required. The module must export `client` and the `Options` and `RequestResult` types.
10
- - Add `@kubb/plugin-ts` for the operation types.
11
- - Add `@kubb/plugin-zod` when you set `validator`. Generation fails without it.
12
-
13
- ## Options worth knowing
14
-
15
- - `importPath` is written into every generated import as given, so it must resolve from the generated file. A relative path, a package name, or an alias works.
16
- - `validator: 'zod'` passes request and response schemas from `@kubb/plugin-zod` to your client.
17
- - `throwOnErrorDefault` is the `throwOnError` value each call passes to your client. The default is `true`.