Skip to content

Commit acf4c6d

Browse files
committed
update docs for release 0.8.x
1 parent 8ac6dc2 commit acf4c6d

11 files changed

Lines changed: 448 additions & 28 deletions

docs/v1/api-reference.md

Lines changed: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,58 @@
44

55
Creates a configured HTTP client.
66

7+
```ts
8+
import { createClient } from '@dfsync/client';
9+
```
10+
711
## Client methods
812

9-
- `get`
10-
- `post`
11-
- `put`
12-
- `patch`
13-
- `delete`
13+
```text
14+
client.get(path, options?)
15+
client.delete(path, options?)
16+
17+
client.post(path, body?, options?)
18+
client.put(path, body?, options?)
19+
client.patch(path, body?, options?)
20+
21+
client.request(config)
22+
```
1423

1524
## Configuration
1625

1726
- `baseUrl`
1827
- `timeout`
28+
- `headers`
29+
- `fetch`
1930
- `retry`
2031
- `auth`
2132
- `hooks`
33+
- `validateResponse`
34+
35+
## Request options
36+
37+
- `query`
38+
- `headers`
39+
- `timeout`
40+
- `retry`
41+
- `signal`
42+
- `requestId`
43+
- `idempotencyKey`
44+
- `validateResponse`
45+
46+
## Retry
47+
48+
- `attempts`
49+
- `backoff`
50+
- `baseDelayMs`
51+
- `retryOn`
52+
- `retryMethods`
53+
54+
## Response validation
55+
56+
```ts
57+
type ResponseValidator<TData = unknown> = (data: TData) => boolean | void | Promise<boolean | void>;
58+
```
2259

2360
## Hooks
2461

@@ -32,4 +69,22 @@ Creates a configured HTTP client.
3269
- `HttpError`
3370
- `NetworkError`
3471
- `TimeoutError`
72+
- `ValidationError`
3573
- `RequestAbortedError`
74+
75+
## Exported types
76+
77+
- `AuthConfig`
78+
- `Client`
79+
- `ClientConfig`
80+
- `RetryConfig`
81+
- `RetryCondition`
82+
- `RetryBackoff`
83+
- `ResponseValidator`
84+
- `BeforeRequestContext`
85+
- `AfterResponseContext`
86+
- `ErrorContext`
87+
- `RetryContext`
88+
- `HooksConfig`
89+
- `RequestConfig`
90+
- `RequestOptions`

docs/v1/create-client.md

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ It provides a consistent way to configure:
99
- auth
1010
- lifecycle hooks
1111
- request observability metadata
12+
- response validation
1213

1314
## Basic client
1415

@@ -64,6 +65,7 @@ type ClientConfig = {
6465
// see Hooks section
6566
};
6667
retry?: RetryConfig;
68+
validateResponse?: ResponseValidator;
6769
};
6870
```
6971

@@ -100,7 +102,7 @@ client.request(config)
100102

101103
- `get` and `delete` do not accept body
102104
- `post`, `put`, and `patch` accept request body as the second argument
103-
- `options` is used for headers, query, timeout, retry, and other settings
105+
- `options` is used for headers, query, timeout, retry, validation, idempotency keys, and other settings
104106

105107
## GET request
106108

@@ -165,13 +167,17 @@ type RequestOptions = {
165167
retry?: RetryConfig;
166168
signal?: AbortSignal;
167169
requestId?: string;
170+
idempotencyKey?: string;
171+
validateResponse?: ResponseValidator;
168172
};
169173
```
170174

171175
`requestId` can be provided explicitly when you want to correlate logs or trace a request across services.
172176

173177
Request-level `retry` overrides client-level retry settings.
174178

179+
Request-level `validateResponse` overrides client-level response validation.
180+
175181
## Low-level request
176182

177183
```ts
@@ -201,6 +207,8 @@ type RequestConfig = {
201207
retry?: RetryConfig;
202208
signal?: AbortSignal;
203209
requestId?: string;
210+
idempotencyKey?: string;
211+
validateResponse?: ResponseValidator;
204212
};
205213
```
206214

@@ -258,6 +266,55 @@ await client.get('/users', {
258266
});
259267
```
260268

269+
## Idempotency key
270+
271+
Use `idempotencyKey` for non-idempotent operations that may be retried safely.
272+
273+
```ts
274+
await client.post(
275+
'/payments',
276+
{ amount: 100 },
277+
{
278+
idempotencyKey: 'payment-123',
279+
},
280+
);
281+
```
282+
283+
This adds:
284+
285+
```http
286+
idempotency-key: payment-123
287+
```
288+
289+
If both `idempotencyKey` and an explicit `idempotency-key` header are provided, the explicit header takes precedence.
290+
291+
`POST` and `PATCH` requests are retried only when the method is included in `retry.retryMethods` and the request provides `idempotencyKey`.
292+
293+
## Response validation
294+
295+
Use `validateResponse` to validate parsed response data before it is returned.
296+
297+
```ts
298+
const client = createClient({
299+
baseUrl: 'https://api.example.com',
300+
validateResponse(data) {
301+
return typeof data === 'object' && data !== null && 'id' in data;
302+
},
303+
});
304+
```
305+
306+
You can override validation per request:
307+
308+
```ts
309+
await client.get('/users/1', {
310+
validateResponse(data) {
311+
return typeof data === 'object' && data !== null && 'email' in data;
312+
},
313+
});
314+
```
315+
316+
Returning `false` throws `ValidationError`. Returning `true` or `undefined` passes validation.
317+
261318
## Request cancellation
262319

263320
Requests can be cancelled using `AbortSignal`:
@@ -361,5 +418,6 @@ If the header value is invalid, `@dfsync/client` falls back to normal retry back
361418
## Related guides
362419

363420
- See **Hooks** for lifecycle hooks and observability metadata
421+
- See **Response Handling** for parsing and response validation
364422
- See **Retry** for retry conditions, backoff, and `Retry-After`
365423
- See **Errors** for failure behavior and error types

docs/v1/errors.md

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@
88
- `HttpError` — non-2xx responses
99
- `NetworkError` — network failures
1010
- `TimeoutError` — request timed out
11+
- `ValidationError` — response validation failed
1112
- `RequestAbortedError` — request was cancelled
1213

13-
- This allows you to handle failures more precisely.
14+
This allows you to handle failures more precisely.
1415

1516
## Base error
1617

@@ -114,14 +115,68 @@ try {
114115

115116
Properties:
116117

117-
- `code``"NETWORK_ERROR"`
118+
- `code``"TIMEOUT_ERROR"`
118119
- `timeout`
119120
- optional `cause`
120121

122+
## ValidationError
123+
124+
Thrown when a successful response fails `validateResponse`.
125+
126+
```ts
127+
import { ValidationError } from '@dfsync/client';
128+
129+
try {
130+
await client.get('/users/1');
131+
} catch (error) {
132+
if (error instanceof ValidationError) {
133+
console.error(error.data);
134+
console.error(error.response.status);
135+
}
136+
}
137+
```
138+
139+
Properties:
140+
141+
- `code``"VALIDATION_ERROR"`
142+
- `data`
143+
- `response`
144+
145+
Validation failures are not retried.
146+
147+
## RequestAbortedError
148+
149+
Thrown when the request is cancelled by an external `AbortSignal`.
150+
151+
```ts
152+
import { RequestAbortedError } from '@dfsync/client';
153+
154+
const controller = new AbortController();
155+
156+
const promise = client.get('/users', {
157+
signal: controller.signal,
158+
});
159+
160+
controller.abort();
161+
162+
try {
163+
await promise;
164+
} catch (error) {
165+
if (error instanceof RequestAbortedError) {
166+
console.error('Request was cancelled');
167+
}
168+
}
169+
```
170+
171+
Properties:
172+
173+
- `code``"REQUEST_ABORTED"`
174+
- optional `cause`
175+
121176
## Error handling example
122177

123178
```ts
124-
import { HttpError, NetworkError, TimeoutError } from '@dfsync/client';
179+
import { HttpError, NetworkError, TimeoutError, ValidationError } from '@dfsync/client';
125180

126181
try {
127182
const result = await client.get('/users/1');
@@ -143,6 +198,11 @@ try {
143198
throw error;
144199
}
145200

201+
if (error instanceof ValidationError) {
202+
console.error('Unexpected response payload:', error.data);
203+
throw error;
204+
}
205+
146206
throw error;
147207
}
148208
```

docs/v1/examples.md

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
## Basic client
44

55
```ts
6+
import { createClient } from '@dfsync/client';
7+
68
const client = createClient({
7-
baseURL: 'https://api.example.com',
9+
baseUrl: 'https://api.example.com',
810
});
911
```
1012

@@ -49,10 +51,53 @@ const singleUser = await client.request<User>({
4951

5052
```ts
5153
const client = createClient({
52-
baseURL: 'https://api.example.com',
54+
baseUrl: 'https://api.example.com',
55+
auth: {
56+
type: 'bearer',
57+
token: 'TOKEN',
58+
},
59+
});
60+
```
61+
62+
## Response validation
63+
64+
```ts
65+
import { ValidationError } from '@dfsync/client';
66+
67+
const client = createClient({
68+
baseUrl: 'https://api.example.com',
69+
validateResponse(data) {
70+
return typeof data === 'object' && data !== null && 'id' in data;
71+
},
72+
});
73+
74+
try {
75+
const user = await client.get('/users/1');
76+
console.log(user);
77+
} catch (error) {
78+
if (error instanceof ValidationError) {
79+
console.error(error.data);
80+
}
81+
}
82+
```
83+
84+
## Safe POST retry
5385

54-
auth: async ({ request }) => {
55-
request.headers.set('Authorization', 'Bearer TOKEN');
86+
```ts
87+
const client = createClient({
88+
baseUrl: 'https://api.example.com',
89+
retry: {
90+
attempts: 2,
91+
retryMethods: ['POST'],
92+
retryOn: ['5xx'],
5693
},
5794
});
95+
96+
const payment = await client.post(
97+
'/payments',
98+
{ amount: 100 },
99+
{
100+
idempotencyKey: 'payment-123',
101+
},
102+
);
58103
```

docs/v1/getting-started.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,15 @@ The client focuses on predictable behavior, extensibility, and a clean developer
1717
- request ID propagation (`x-request-id`)
1818
- request cancellation via `AbortSignal`
1919
- built-in retry with configurable policies
20-
- lifecycle hooks: `beforeRequest`, `afterResponse`, `onError`
20+
- lifecycle hooks: `beforeRequest`, `afterResponse`, `onError`, `onRetry`
2121

2222
- typed responses
2323
- automatic JSON parsing
24+
- response validation with `ValidationError`
2425
- consistent error handling
2526

2627
- auth support: bearer, API key, custom
28+
- idempotency key support for safer retries
2729
- support for `GET`, `POST`, `PUT`, `PATCH`, and `DELETE`
2830

2931
## Quick example

0 commit comments

Comments
 (0)