0
# Response Handling
1
2
Enhanced response promises with body method shortcuts and automatic error handling for non-2xx status codes. Ky extends the standard Response object with convenient methods for accessing response data.
3
4
## Capabilities
5
6
### Response Promise Interface
7
8
The ResponsePromise extends the standard Promise with body method shortcuts that automatically set appropriate Accept headers.
9
10
```typescript { .api }
11
interface ResponsePromise<T = unknown> extends Promise<KyResponse<T>> {
12
/** Get response body as ArrayBuffer */
13
arrayBuffer(): Promise<ArrayBuffer>;
14
/** Get response body as Blob */
15
blob(): Promise<Blob>;
16
/** Get response body as FormData */
17
formData(): Promise<FormData>;
18
/** Get response body as raw bytes (when supported by runtime) */
19
bytes(): Promise<Uint8Array>;
20
/** Get response body as JSON with type safety */
21
json<J = T>(): Promise<J>;
22
/** Get response body as text string */
23
text(): Promise<string>;
24
}
25
```
26
27
**Usage Examples:**
28
29
```typescript
30
import ky from "ky";
31
32
// JSON response with automatic type inference
33
interface User {
34
id: number;
35
name: string;
36
email: string;
37
}
38
39
const user = await ky.get("https://api.example.com/user/123").json<User>();
40
console.log(user.name); // TypeScript knows this is a string
41
42
// Text response
43
const csvData = await ky.get("https://api.example.com/data.csv").text();
44
console.log(csvData);
45
46
// Binary data as ArrayBuffer
47
const fileBuffer = await ky.get("https://api.example.com/file.pdf").arrayBuffer();
48
const uint8Array = new Uint8Array(fileBuffer);
49
50
// Blob for file handling
51
const imageBlob = await ky.get("https://api.example.com/image.jpg").blob();
52
const imageUrl = URL.createObjectURL(imageBlob);
53
54
// FormData response
55
const formResponse = await ky.get("https://api.example.com/form-data").formData();
56
console.log(formResponse.get("field-name"));
57
58
// Raw bytes (when supported)
59
if (typeof Response.prototype.bytes === "function") {
60
const rawBytes = await ky.get("https://api.example.com/binary").bytes();
61
console.log(rawBytes instanceof Uint8Array); // true
62
}
63
```
64
65
### Enhanced Response Object
66
67
Ky responses extend the standard Response with enhanced JSON methods.
68
69
```typescript { .api }
70
interface KyResponse<T = unknown> extends Response {
71
/** Enhanced JSON method with type safety */
72
json<J = T>(): Promise<J>;
73
}
74
```
75
76
**Usage Examples:**
77
78
```typescript
79
import ky from "ky";
80
81
// Access full response object
82
const response = await ky.get("https://api.example.com/data");
83
84
// Check response properties
85
console.log("Status:", response.status);
86
console.log("Status Text:", response.statusText);
87
console.log("Headers:", Object.fromEntries(response.headers));
88
console.log("OK:", response.ok);
89
90
// Process response based on content type
91
const contentType = response.headers.get("content-type");
92
93
if (contentType?.includes("application/json")) {
94
const data = await response.json();
95
console.log("JSON data:", data);
96
} else if (contentType?.includes("text/")) {
97
const text = await response.text();
98
console.log("Text data:", text);
99
} else {
100
const buffer = await response.arrayBuffer();
101
console.log("Binary data:", buffer.byteLength, "bytes");
102
}
103
```
104
105
### Automatic Header Handling
106
107
Response body methods automatically set appropriate Accept headers for content negotiation.
108
109
**Usage Examples:**
110
111
```typescript
112
import ky from "ky";
113
114
// Automatically sets Accept: application/json
115
const jsonData = await ky.get("https://api.example.com/data").json();
116
117
// Automatically sets Accept: text/*
118
const textData = await ky.get("https://api.example.com/text").text();
119
120
// Automatically sets Accept: multipart/form-data
121
const formData = await ky.get("https://api.example.com/form").formData();
122
123
// Automatically sets Accept: */*
124
const binaryData = await ky.get("https://api.example.com/binary").arrayBuffer();
125
const blobData = await ky.get("https://api.example.com/file").blob();
126
127
// Manual Accept header override
128
const customData = await ky.get("https://api.example.com/custom", {
129
headers: { "Accept": "application/vnd.api+json" }
130
}).json();
131
```
132
133
### JSON Response Special Cases
134
135
Ky handles JSON responses with special behaviors for edge cases.
136
137
**Usage Examples:**
138
139
```typescript
140
import ky from "ky";
141
142
// Empty response (status 204) returns empty string
143
const noContent = await ky.delete("https://api.example.com/resource/123").json();
144
console.log(noContent); // ""
145
146
// Empty body returns empty string instead of throwing parse error
147
const emptyResponse = await ky.get("https://api.example.com/empty").json();
148
console.log(emptyResponse); // ""
149
150
// Custom JSON parsing with error handling
151
const safeClient = ky.create({
152
parseJson: (text) => {
153
if (!text.trim()) {
154
return null;
155
}
156
try {
157
return JSON.parse(text);
158
} catch (error) {
159
console.warn("JSON parse error:", error);
160
return { error: "Invalid JSON", originalText: text };
161
}
162
}
163
});
164
165
const safeData = await safeClient.get("https://api.example.com/maybe-json").json();
166
```
167
168
### Response with TypeScript Generics
169
170
Leverage TypeScript generics for type-safe response handling.
171
172
**Usage Examples:**
173
174
```typescript
175
import ky from "ky";
176
177
// Generic at call site
178
interface ApiResponse<T> {
179
data: T;
180
status: string;
181
timestamp: string;
182
}
183
184
interface Product {
185
id: number;
186
name: string;
187
price: number;
188
}
189
190
// Type the entire response
191
const productResponse = await ky.get("https://api.example.com/products/123")
192
.json<ApiResponse<Product>>();
193
194
console.log(productResponse.data.name); // TypeScript knows this is string
195
console.log(productResponse.data.price); // TypeScript knows this is number
196
197
// Generic at instance level
198
const typedClient = ky.create({ prefixUrl: "https://api.example.com" });
199
200
const users = await typedClient.get("users").json<User[]>();
201
const user = await typedClient.get("users/123").json<User>();
202
const stats = await typedClient.get("stats").json<{ count: number; average: number }>();
203
204
// Conditional response types
205
type ApiResult<T> = {
206
success: true;
207
data: T;
208
} | {
209
success: false;
210
error: string;
211
};
212
213
const result = await ky.post("https://api.example.com/process", {
214
json: { input: "data" }
215
}).json<ApiResult<{ output: string }>>();
216
217
if (result.success) {
218
console.log(result.data.output); // TypeScript knows this exists
219
} else {
220
console.error(result.error); // TypeScript knows this exists
221
}
222
```
223
224
### Response Streaming
225
226
Handle streaming responses with progress tracking.
227
228
**Usage Examples:**
229
230
```typescript
231
import ky from "ky";
232
233
// Stream large file with progress
234
const downloadLargeFile = async () => {
235
const response = await ky.get("https://api.example.com/large-file.zip", {
236
onDownloadProgress: (progress, chunk) => {
237
console.log(`Downloaded: ${Math.round(progress.percent * 100)}%`);
238
console.log(`Chunk size: ${chunk.length} bytes`);
239
}
240
});
241
242
return response.arrayBuffer();
243
};
244
245
// Process streaming JSON responses
246
const processStreamingData = async () => {
247
const response = await ky.get("https://api.example.com/streaming-data");
248
249
const reader = response.body?.getReader();
250
const decoder = new TextDecoder();
251
252
if (reader) {
253
while (true) {
254
const { done, value } = await reader.read();
255
if (done) break;
256
257
const chunk = decoder.decode(value, { stream: true });
258
console.log("Received chunk:", chunk);
259
}
260
}
261
};
262
```
263
264
## Types
265
266
```typescript { .api }
267
interface ResponsePromise<T = unknown> extends Promise<KyResponse<T>> {
268
arrayBuffer(): Promise<ArrayBuffer>;
269
blob(): Promise<Blob>;
270
formData(): Promise<FormData>;
271
bytes(): Promise<Uint8Array>;
272
json<J = T>(): Promise<J>;
273
text(): Promise<string>;
274
}
275
276
interface KyResponse<T = unknown> extends Response {
277
json<J = T>(): Promise<J>;
278
}
279
280
interface KyRequest<T = unknown> extends Request {
281
json<J = T>(): Promise<J>;
282
}
283
```