Installation
npm install @hey-api/client-ky ky
pnpm add @hey-api/client-ky ky
yarn add @hey-api/client-ky ky
bun add @hey-api/client-ky ky
Ky is a peer dependency and must be installed separately.
Basic Usage
1
Create a client instance
import { createClient } from '@hey-api/client-ky';
const client = createClient({
baseUrl: 'https://api.example.com',
});
2
Make API calls
const { data, error } = await client.get({
url: '/users/{id}',
path: { id: 123 },
});
if (error) {
console.error('Error:', error);
} else {
console.log('User:', data);
}
Configuration
The Ky client extends Ky configuration with OpenAPI TypeScript features:Client Options
import { createClient } from '@hey-api/client-ky';
import ky from 'ky';
const client = createClient({
// Base URL for all requests
baseUrl: 'https://api.example.com',
// Custom Ky instance (optional)
ky: ky.create({
timeout: 5000,
}),
// Default headers
headers: {
'Content-Type': 'application/json',
'X-API-Key': 'your-api-key',
},
// Response parsing format
parseAs: 'json', // 'json' | 'text' | 'blob' | 'arrayBuffer' | 'formData' | 'stream' | 'auto'
// Response style
responseStyle: 'fields', // 'fields' | 'data'
// Error handling
throwOnError: false,
// Timeout (milliseconds)
timeout: 10000,
// Retry configuration
retry: {
limit: 2,
methods: ['get', 'put', 'head', 'delete', 'options', 'trace'],
statusCodes: [408, 413, 429, 500, 502, 503, 504],
},
// Authentication
auth: async (auth) => getToken(),
// Body serialization
bodySerializer: (body) => JSON.stringify(body),
// Query serialization
querySerializer: {
array: { style: 'form', explode: true },
object: { style: 'deepObject', explode: true },
},
// Request validator
requestValidator: async (data) => validateRequest(data),
// Response transformer
responseTransformer: async (data) => transformResponse(data),
// Response validator
responseValidator: async (data) => validateResponse(data),
// Additional Ky options
kyOptions: {
throwHttpErrors: false,
retry: 0,
},
// Standard fetch options
credentials: 'include',
mode: 'cors',
cache: 'default',
});
Custom Ky Instance
Use an existing Ky instance:import { createClient } from '@hey-api/client-ky';
import ky from 'ky';
// Create custom Ky instance
const kyInstance = ky.create({
timeout: 5000,
headers: {
'X-Custom-Header': 'value',
},
hooks: {
beforeRequest: [
(request) => {
console.log('Request:', request.url);
},
],
},
});
// Use with OpenAPI TypeScript client
const client = createClient({
baseUrl: 'https://api.example.com',
ky: kyInstance,
});
HTTP Methods
- GET
- POST
- PUT
- PATCH
- DELETE
const { data, error, response } = await client.get({
url: '/users',
query: {
page: 1,
limit: 10,
},
});
if (!error) {
console.log('Users:', data);
console.log('Status:', response.status);
}
const { data, error } = await client.post({
url: '/users',
body: {
name: 'John Doe',
email: 'john@example.com',
},
});
const { data } = await client.put({
url: '/users/{id}',
path: { id: 123 },
body: {
name: 'Jane Doe',
},
});
const { data } = await client.patch({
url: '/users/{id}',
path: { id: 123 },
body: {
email: 'jane@example.com',
},
});
const { data } = await client.delete({
url: '/users/{id}',
path: { id: 123 },
});
Retry Configuration
Ky has built-in retry support:Default Retry
const client = createClient({
baseUrl: 'https://api.example.com',
retry: {
limit: 2, // Maximum retry attempts
methods: ['get', 'put', 'head', 'delete', 'options', 'trace'],
statusCodes: [408, 413, 429, 500, 502, 503, 504],
},
});
Custom Retry Logic
const client = createClient({
baseUrl: 'https://api.example.com',
kyOptions: {
retry: {
limit: 3,
methods: ['get', 'post'],
statusCodes: [408, 413, 429, 500, 502, 503, 504],
afterStatusCodes: [413, 429, 503],
},
},
});
Per-Request Retry
const { data } = await client.get({
url: '/users',
retry: {
limit: 5,
statusCodes: [429, 503],
},
});
Timeout Configuration
Global Timeout
const client = createClient({
baseUrl: 'https://api.example.com',
timeout: 10000, // 10 seconds
});
Per-Request Timeout
const { data } = await client.get({
url: '/users',
timeout: 5000, // 5 seconds
});
Disable Timeout
const { data } = await client.get({
url: '/users',
timeout: false,
});
Response Styles
Fields Style (Default)
const { data, error, request, response } = await client.get({
url: '/users',
responseStyle: 'fields',
});
if (error) {
console.error('Error:', error);
} else {
console.log('Data:', data);
console.log('Status:', response.status);
}
Data Style
const data = await client.get({
url: '/users',
responseStyle: 'data',
});
if (data) {
console.log('Users:', data);
}
Authentication
Bearer Token
const client = createClient({
baseUrl: 'https://api.example.com',
auth: async (auth) => {
if (auth.scheme === 'bearer') {
return await getAccessToken();
}
},
});
API Key
const client = createClient({
baseUrl: 'https://api.example.com',
auth: async (auth) => {
if (auth.type === 'apiKey') {
return process.env.API_KEY;
}
},
});
// Per-request security
const { data } = await client.get({
url: '/protected',
security: [
{
type: 'apiKey',
in: 'header',
name: 'X-API-Key',
},
],
});
Interceptors
Add request, response, and error interceptors:Request Interceptor
client.interceptors.request.use((request, options) => {
console.log('Request:', request.url);
// Add custom header
request.headers.set('X-Request-ID', generateRequestId());
return request;
});
Response Interceptor
client.interceptors.response.use((response, request, options) => {
console.log('Response:', response.status);
return response;
});
Error Interceptor
client.interceptors.error.use((error, response, request, options) => {
if (response?.status === 429) {
console.warn('Rate limited, will retry...');
}
return error;
});
Server-Sent Events
Stream real-time data:const stream = await client.sse.get({
url: '/events',
onSseEvent: (event) => {
console.log('Event:', event.data);
},
onSseError: (error) => {
console.error('Stream error:', error);
},
sseMaxRetryAttempts: 3,
sseMaxRetryDelay: 5000,
});
// Close the stream
stream.close();
Request Cancellation
const controller = new AbortController();
const promise = client.get({
url: '/users',
signal: controller.signal,
});
// Cancel the request
controller.abort();
Response Parsing
Auto (Default)
const { data } = await client.get({
url: '/users',
parseAs: 'auto', // Infers from Content-Type
});
Specific Format
// JSON
const { data } = await client.get({
url: '/users',
parseAs: 'json',
});
// Text
const { data } = await client.get({
url: '/document',
parseAs: 'text',
});
// Blob
const { data } = await client.get({
url: '/image.png',
parseAs: 'blob',
});
// Stream
const { data } = await client.get({
url: '/large-file',
parseAs: 'stream',
});
Advanced Features
Preflight Requests
import { createClient } from '@hey-api/client-ky';
import ky from 'ky';
const kyInstance = ky.create({
hooks: {
beforeRequest: [
async (request, options) => {
// Send preflight request
await fetch(request.url, { method: 'OPTIONS' });
},
],
},
});
const client = createClient({
baseUrl: 'https://api.example.com',
ky: kyInstance,
});
Response Hooks
import ky from 'ky';
const kyInstance = ky.create({
hooks: {
afterResponse: [
async (request, options, response) => {
// Log response time
const responseTime = response.headers.get('X-Response-Time');
console.log('Response time:', responseTime);
return response;
},
],
},
});
const client = createClient({
baseUrl: 'https://api.example.com',
ky: kyInstance,
});
Error Hooks
import ky from 'ky';
import { HTTPError } from 'ky';
const kyInstance = ky.create({
hooks: {
beforeError: [
(error) => {
const { response } = error;
if (response && response.body) {
error.message = `${error.message} (${response.status})`;
}
return error;
},
],
},
});
TypeScript Types
import type {
Client,
Config,
RequestOptions,
RequestResult,
ResponseStyle,
RetryOptions,
} from '@hey-api/client-ky';
// Custom retry configuration
const retryConfig: RetryOptions = {
limit: 3,
methods: ['get', 'post'],
statusCodes: [408, 429, 500, 502, 503, 504],
};
const client = createClient({
baseUrl: 'https://api.example.com',
retry: retryConfig,
});
Comparison with Fetch Client
| Feature | Ky Client | Fetch Client |
|---|---|---|
| Dependencies | Requires Ky | Zero dependencies |
| Browser Support | Modern browsers | Modern browsers |
| Retry Support | Built-in | Manual |
| Timeout Support | Built-in | Manual |
| Request Hooks | Ky hooks | Interceptors only |
| Response Hooks | Ky hooks | Interceptors only |
| Bundle Size | ~5KB | ~2KB |
Next Steps
Fetch Client
Zero-dependency alternative
ofetch Client
Universal fetch wrapper
Retry Configuration
Configure automatic retries
Authentication
Set up API authentication