|
1 | 1 | --- |
2 | | -title: Ky client |
3 | | -description: Ky client for Hey API. Compatible with all our features. |
| 2 | +title: Ky v1 Client |
| 3 | +description: Generate a type-safe Ky v1 client from OpenAPI with the Ky client for openapi-ts. Fully compatible with validators, transformers, and all core features. |
4 | 4 | --- |
5 | 5 |
|
6 | 6 | <script setup lang="ts"> |
7 | | -import FeatureStatus from '@components/FeatureStatus.vue'; |
| 7 | +import AuthorsList from '@components/AuthorsList.vue'; |
| 8 | +import Heading from '@components/Heading.vue'; |
| 9 | +import VersionLabel from '@components/VersionLabel.vue'; |
| 10 | +import { sebastiaanWouters } from '@data/people.js'; |
8 | 11 | </script> |
9 | 12 |
|
10 | | -# Ky <span data-soon>soon</span> |
11 | | - |
12 | | -<FeatureStatus issueNumber=2794 name="Ky" /> |
| 13 | +<Heading> |
| 14 | + <h1>Ky<span class="sr-only"> v1</span></h1> |
| 15 | + <VersionLabel value="v1" /> |
| 16 | +</Heading> |
13 | 17 |
|
14 | 18 | ### About |
15 | 19 |
|
16 | 20 | [Ky](https://github.com/sindresorhus/ky) is a tiny and elegant JavaScript HTTP client based on the Fetch API. |
17 | 21 |
|
| 22 | +The Ky client for Hey API generates a type-safe client from your OpenAPI spec, fully compatible with validators, transformers, and all core features. |
| 23 | + |
| 24 | +### Collaborators |
| 25 | + |
| 26 | +<AuthorsList :people="[sebastiaanWouters]" /> |
| 27 | + |
| 28 | +## Features |
| 29 | + |
| 30 | +- seamless integration with `@hey-api/openapi-ts` ecosystem |
| 31 | +- type-safe response data and errors |
| 32 | +- response data validation and transformation |
| 33 | +- access to the original request and response |
| 34 | +- granular request and response customization options |
| 35 | +- minimal learning curve thanks to extending the underlying technology |
| 36 | +- support bundling inside the generated output |
| 37 | + |
| 38 | +## Installation |
| 39 | + |
| 40 | +In your [configuration](/openapi-ts/get-started), add `@hey-api/client-ky` to your plugins and you'll be ready to generate client artifacts. :tada: |
| 41 | + |
| 42 | +::: code-group |
| 43 | + |
| 44 | +```js [config] |
| 45 | +export default { |
| 46 | + input: 'hey-api/backend', // sign up at app.heyapi.dev |
| 47 | + output: 'src/client', |
| 48 | + plugins: ['@hey-api/client-ky'], // [!code ++] |
| 49 | +}; |
| 50 | +``` |
| 51 | + |
| 52 | +```sh [cli] |
| 53 | +npx @hey-api/openapi-ts \ |
| 54 | + -i hey-api/backend \ |
| 55 | + -o src/client \ |
| 56 | + -c @hey-api/client-ky # [!code ++] |
| 57 | +``` |
| 58 | + |
| 59 | +::: |
| 60 | + |
| 61 | +## Configuration |
| 62 | + |
| 63 | +The Ky client is built as a thin wrapper on top of Ky, extending its functionality to work with Hey API. If you're already familiar with Ky, configuring your client will feel like working directly with Ky. |
| 64 | + |
| 65 | +When we installed the client above, it created a [`client.gen.ts`](/openapi-ts/output#client) file. You will most likely want to configure the exported `client` instance. There are two ways to do that. |
| 66 | + |
| 67 | +### `setConfig()` |
| 68 | + |
| 69 | +This is the simpler approach. You can call the `setConfig()` method at the beginning of your application or anytime you need to update the client configuration. You can pass any Ky configuration option to `setConfig()`, and even your own [`ky`](#custom-instance) instance. |
| 70 | + |
| 71 | +```js |
| 72 | +import { client } from 'client/client.gen'; |
| 73 | + |
| 74 | +client.setConfig({ |
| 75 | + baseUrl: 'https://example.com', |
| 76 | +}); |
| 77 | +``` |
| 78 | + |
| 79 | +The disadvantage of this approach is that your code may call the `client` instance before it's configured for the first time. Depending on your use case, you might need to use the second approach. |
| 80 | + |
| 81 | +### Runtime API |
| 82 | + |
| 83 | +Since `client.gen.ts` is a generated file, we can't directly modify it. Instead, we can tell our configuration to use a custom file implementing the Runtime API. We do that by specifying the `runtimeConfigPath` option. |
| 84 | + |
| 85 | +```js |
| 86 | +export default { |
| 87 | + input: 'hey-api/backend', // sign up at app.heyapi.dev |
| 88 | + output: 'src/client', |
| 89 | + plugins: [ |
| 90 | + { |
| 91 | + name: '@hey-api/client-ky', |
| 92 | + runtimeConfigPath: './src/hey-api.ts', // [!code ++] |
| 93 | + }, |
| 94 | + ], |
| 95 | +}; |
| 96 | +``` |
| 97 | + |
| 98 | +In our custom file, we need to export a `createClientConfig()` method. This function is a simple wrapper allowing us to override configuration values. |
| 99 | + |
| 100 | +::: code-group |
| 101 | + |
| 102 | +```ts [hey-api.ts] |
| 103 | +import type { CreateClientConfig } from './client/client.gen'; |
| 104 | + |
| 105 | +export const createClientConfig: CreateClientConfig = (config) => ({ |
| 106 | + ...config, |
| 107 | + baseUrl: 'https://example.com', |
| 108 | +}); |
| 109 | +``` |
| 110 | + |
| 111 | +::: |
| 112 | + |
| 113 | +With this approach, `client.gen.ts` will call `createClientConfig()` before initializing the `client` instance. If needed, you can still use `setConfig()` to update the client configuration later. |
| 114 | + |
| 115 | +### `createClient()` |
| 116 | + |
| 117 | +You can also create your own client instance. You can use it to manually send requests or point it to a different domain. |
| 118 | + |
| 119 | +```js |
| 120 | +import { createClient } from './client/client'; |
| 121 | + |
| 122 | +const myClient = createClient({ |
| 123 | + baseUrl: 'https://example.com', |
| 124 | +}); |
| 125 | +``` |
| 126 | + |
| 127 | +You can also pass this instance to any SDK function through the `client` option. This will override the default instance from `client.gen.ts`. |
| 128 | + |
| 129 | +```js |
| 130 | +const response = await getFoo({ |
| 131 | + client: myClient, |
| 132 | +}); |
| 133 | +``` |
| 134 | + |
| 135 | +### SDKs |
| 136 | + |
| 137 | +Alternatively, you can pass the client configuration options to each SDK function. This is useful if you don't want to create a client instance for one-off use cases. |
| 138 | + |
| 139 | +```js |
| 140 | +const response = await getFoo({ |
| 141 | + baseUrl: 'https://example.com', // <-- override default configuration |
| 142 | +}); |
| 143 | +``` |
| 144 | + |
| 145 | +## Interceptors |
| 146 | + |
| 147 | +Interceptors (middleware) can be used to modify requests before they're sent or responses before they're returned to your application. |
| 148 | + |
| 149 | +They can be added with `use`, removed with `eject`, and updated wth `update`. The `use` and `update` methods will return the ID of the interceptor for use with `eject` and `update`. Ky does not have the interceptor functionality, so we implement our own. |
| 150 | + |
| 151 | +### Example: Request interceptor |
| 152 | + |
| 153 | +::: code-group |
| 154 | + |
| 155 | +```js [use] |
| 156 | +import { client } from 'client/client.gen'; |
| 157 | + |
| 158 | +async function myInterceptor(request) { |
| 159 | + // do something |
| 160 | + return request; |
| 161 | +} |
| 162 | + |
| 163 | +interceptorId = client.interceptors.request.use(myInterceptor); |
| 164 | +``` |
| 165 | + |
| 166 | +```js [eject] |
| 167 | +import { client } from 'client/client.gen'; |
| 168 | + |
| 169 | +// eject by ID |
| 170 | +client.interceptors.request.eject(interceptorId); |
| 171 | + |
| 172 | +// eject by reference |
| 173 | +client.interceptors.request.eject(myInterceptor); |
| 174 | +``` |
| 175 | + |
| 176 | +```js [update] |
| 177 | +import { client } from 'client/client.gen'; |
| 178 | + |
| 179 | +async function myNewInterceptor(request) { |
| 180 | + // do something |
| 181 | + return request; |
| 182 | +} |
| 183 | + |
| 184 | +// update by ID |
| 185 | +client.interceptors.request.update(interceptorId, myNewInterceptor); |
| 186 | + |
| 187 | +// update by reference |
| 188 | +client.interceptors.request.update(myInterceptor, myNewInterceptor); |
| 189 | +``` |
| 190 | + |
| 191 | +::: |
| 192 | + |
| 193 | +### Example: Response interceptor |
| 194 | + |
| 195 | +::: code-group |
| 196 | + |
| 197 | +```js [use] |
| 198 | +import { client } from 'client/client.gen'; |
| 199 | + |
| 200 | +async function myInterceptor(response) { |
| 201 | + // do something |
| 202 | + return response; |
| 203 | +} |
| 204 | + |
| 205 | +interceptorId = client.interceptors.response.use(myInterceptor); |
| 206 | +``` |
| 207 | + |
| 208 | +```js [eject] |
| 209 | +import { client } from 'client/client.gen'; |
| 210 | + |
| 211 | +// eject by ID |
| 212 | +client.interceptors.response.eject(interceptorId); |
| 213 | + |
| 214 | +// eject by reference |
| 215 | +client.interceptors.response.eject(myInterceptor); |
| 216 | +``` |
| 217 | + |
| 218 | +```js [update] |
| 219 | +import { client } from 'client/client.gen'; |
| 220 | + |
| 221 | +async function myNewInterceptor(response) { |
| 222 | + // do something |
| 223 | + return response; |
| 224 | +} |
| 225 | + |
| 226 | +// update by ID |
| 227 | +client.interceptors.response.update(interceptorId, myNewInterceptor); |
| 228 | + |
| 229 | +// update by reference |
| 230 | +client.interceptors.response.update(myInterceptor, myNewInterceptor); |
| 231 | +``` |
| 232 | + |
| 233 | +::: |
| 234 | + |
| 235 | +::: tip |
| 236 | +To eject, you must provide the ID or reference of the interceptor passed to `use()`, the ID is the value returned by `use()` and `update()`. |
| 237 | +::: |
| 238 | + |
| 239 | +## Auth |
| 240 | + |
| 241 | +The SDKs include auth mechanisms for every endpoint. You will want to configure the `auth` field to pass the right token for each request. The `auth` field can be a string or a function returning a string representing the token. The returned value will be attached only to requests that require auth. |
| 242 | + |
| 243 | +```js |
| 244 | +import { client } from 'client/client.gen'; |
| 245 | + |
| 246 | +client.setConfig({ |
| 247 | + auth: () => '<my_token>', // [!code ++] |
| 248 | + baseUrl: 'https://example.com', |
| 249 | +}); |
| 250 | +``` |
| 251 | + |
| 252 | +If you're not using SDKs or generating auth, using interceptors is a common approach to configuring auth for each request. |
| 253 | + |
| 254 | +```js |
| 255 | +import { client } from 'client/client.gen'; |
| 256 | + |
| 257 | +client.interceptors.request.use((request, options) => { |
| 258 | + request.headers.set('Authorization', 'Bearer <my_token>'); // [!code ++] |
| 259 | + return request; |
| 260 | +}); |
| 261 | +``` |
| 262 | + |
| 263 | +## Build URL |
| 264 | + |
| 265 | +If you need to access the compiled URL, you can use the `buildUrl()` method. It's loosely typed by default to accept almost any value; in practice, you will want to pass a type hint. |
| 266 | + |
| 267 | +```ts |
| 268 | +type FooData = { |
| 269 | + path: { |
| 270 | + fooId: number; |
| 271 | + }; |
| 272 | + query?: { |
| 273 | + bar?: string; |
| 274 | + }; |
| 275 | + url: '/foo/{fooId}'; |
| 276 | +}; |
| 277 | + |
| 278 | +const url = client.buildUrl<FooData>({ |
| 279 | + path: { |
| 280 | + fooId: 1, |
| 281 | + }, |
| 282 | + query: { |
| 283 | + bar: 'baz', |
| 284 | + }, |
| 285 | + url: '/foo/{fooId}', |
| 286 | +}); |
| 287 | +console.log(url); // prints '/foo/1?bar=baz' |
| 288 | +``` |
| 289 | + |
| 290 | +## Custom Instance |
| 291 | + |
| 292 | +You can provide a custom `ky` instance. This is useful if you need to extend the default instance with extra functionality, or replace it altogether. |
| 293 | + |
| 294 | +```js |
| 295 | +import { client } from 'client/client.gen'; |
| 296 | + |
| 297 | +client.setConfig({ |
| 298 | + ky: ky.create({ |
| 299 | + /* custom `ky` instance */ |
| 300 | + }), |
| 301 | +}); |
| 302 | +``` |
| 303 | + |
| 304 | +You can use any of the approaches mentioned in [Configuration](#configuration), depending on how granular you want your custom instance to be. |
| 305 | + |
| 306 | +## API |
| 307 | + |
| 308 | +You can view the complete list of options in the [UserConfig](https://github.com/hey-api/openapi-ts/blob/main/packages/openapi-ts/src/plugins/@hey-api/client-ky/types.d.ts) interface. |
| 309 | + |
| 310 | +<!--@include: ../../partials/examples.md--> |
18 | 311 | <!--@include: ../../partials/sponsors.md--> |
0 commit comments