Skip to content

Commit a2ff3a0

Browse files
authored
release 0.7.x (#17)
1 parent 872db55 commit a2ff3a0

11 files changed

Lines changed: 341 additions & 119 deletions

File tree

docs/v1/api-reference.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# API Reference
2+
3+
## createClient
4+
5+
Creates a configured HTTP client.
6+
7+
## Client methods
8+
9+
- `get`
10+
- `post`
11+
- `put`
12+
- `patch`
13+
- `delete`
14+
15+
## Configuration
16+
17+
- `baseUrl`
18+
- `timeout`
19+
- `retry`
20+
- `auth`
21+
- `hooks`
22+
23+
## Hooks
24+
25+
- `beforeRequest`
26+
- `afterResponse`
27+
- `onError`
28+
- `onRetry`
29+
30+
## Errors
31+
32+
- `HttpError`
33+
- `NetworkError`
34+
- `TimeoutError`
35+
- `RequestAbortedError`

docs/v1/create-client.md

Lines changed: 96 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,21 @@
22

33
Use `createClient` to create a reusable HTTP client instance.
44

5+
It provides a consistent way to configure:
6+
7+
- base URL and default headers
8+
- timeouts and retries
9+
- auth
10+
- lifecycle hooks
11+
- request observability metadata
12+
513
## Basic client
614

715
```ts
816
import { createClient } from '@dfsync/client';
917

1018
const client = createClient({
11-
baseURL: 'https://api.example.com',
19+
baseUrl: 'https://api.example.com',
1220
});
1321
```
1422

@@ -59,6 +67,20 @@ type ClientConfig = {
5967
};
6068
```
6169

70+
Hook configuration supports:
71+
72+
- `beforeRequest`
73+
- `afterResponse`
74+
- `onError`
75+
- `onRetry`
76+
77+
Retry configuration supports:
78+
79+
- retry attempts
80+
- retry conditions
81+
- backoff strategy
82+
- `Retry-After` handling
83+
6284
## HTTP methods
6385

6486
The client provides a predictable set of methods:
@@ -146,6 +168,10 @@ type RequestOptions = {
146168
};
147169
```
148170

171+
`requestId` can be provided explicitly when you want to correlate logs or trace a request across services.
172+
173+
Request-level `retry` overrides client-level retry settings.
174+
149175
## Low-level request
150176

151177
```ts
@@ -155,13 +181,13 @@ const result = await client.request({
155181
body: {
156182
type: 'user.created',
157183
},
158-
headers: {
159-
'x-request-id': 'req-123',
160-
},
184+
requestId: 'req-123',
161185
timeout: 3000,
162186
});
163187
```
164188

189+
If both `requestId` and `x-request-id` header are provided, `x-request-id` takes precedence.
190+
165191
### Request Config
166192

167193
```ts
@@ -180,14 +206,26 @@ type RequestConfig = {
180206

181207
## Request context
182208

183-
Each request is executed within a request context that contains:
209+
Each request attempt is executed within a request context that contains:
184210

185-
- `requestId` — unique identifier for the request
186-
- `attempt` — current retry attempt
211+
- `requestId` — stable identifier for the full request lifecycle
212+
- `attempt` — current retry attempt (zero-based)
213+
- `maxAttempts` — total number of allowed attempts, including the initial request
187214
- `signal` — AbortSignal for cancellation
188215
- `startedAt` — request start timestamp
189216

190-
This context is available in all lifecycle hooks.
217+
Completed attempts may also expose:
218+
219+
- `endedAt` — request end timestamp
220+
- `durationMs` — total duration for the current attempt
221+
222+
Retry-specific contexts may also expose:
223+
224+
- `retryDelayMs`
225+
- `retryReason`
226+
- `retrySource`
227+
228+
This context is available through lifecycle hooks.
191229

192230
## Request ID
193231

@@ -196,9 +234,12 @@ Each request has a `requestId` that is:
196234
- automatically generated by default
197235
- can be overridden per request
198236
- propagated via the `x-request-id` header
237+
- remains stable across retries
199238

200239
This allows tracing requests across services.
201240

241+
It also makes retries easier to correlate in logs and monitoring systems.
242+
202243
### Example
203244

204245
```ts
@@ -275,3 +316,50 @@ If request body is a string, the client:
275316

276317
- sends it as-is
277318
- does not force a `content-type`
319+
320+
## Retry observability
321+
322+
Retries can be observed using lifecycle hooks.
323+
324+
```ts
325+
const client = createClient({
326+
baseUrl: 'https://api.example.com',
327+
retry: {
328+
attempts: 2,
329+
retryOn: ['5xx', '429'],
330+
backoff: 'exponential',
331+
baseDelayMs: 300,
332+
},
333+
hooks: {
334+
onRetry({ requestId, attempt, maxAttempts, retryDelayMs, retryReason, retrySource }) {
335+
console.log({
336+
requestId,
337+
attempt,
338+
maxAttempts,
339+
retryDelayMs,
340+
retryReason,
341+
retrySource,
342+
});
343+
},
344+
},
345+
});
346+
```
347+
348+
This is useful for logging, monitoring, and debugging retry behavior.
349+
350+
## Retry-After support
351+
352+
When a retryable response includes a `Retry-After` header, `@dfsync/client` uses that value before falling back to the configured backoff strategy.
353+
354+
Supported formats:
355+
356+
- seconds
357+
- HTTP-date
358+
359+
If the header value is invalid, `@dfsync/client` falls back to normal retry backoff.
360+
361+
## Related guides
362+
363+
- See **Hooks** for lifecycle hooks and observability metadata
364+
- See **Retry** for retry conditions, backoff, and `Retry-After`
365+
- See **Errors** for failure behavior and error types

docs/v1/getting-started.md

Lines changed: 16 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -31,37 +31,27 @@ The client focuses on predictable behavior, extensibility, and a clean developer
3131
```ts
3232
import { createClient } from '@dfsync/client';
3333

34-
type User = {
35-
id: string;
36-
name: string;
37-
};
38-
3934
const client = createClient({
4035
baseUrl: 'https://api.example.com',
4136
timeout: 5000,
37+
retry: {
38+
attempts: 2,
39+
retryOn: ['5xx', 'network-error'],
40+
},
41+
hooks: {
42+
onRetry: ({ requestId, retryReason, retryDelayMs }) => {
43+
console.log(`[${requestId}] retrying in ${retryDelayMs}ms`, retryReason);
44+
},
45+
},
4246
});
4347

44-
const user = await client.get<User>('/users/1');
48+
const data = await client.get('/health');
4549
```
4650

47-
## How requests work
48-
49-
A request in `@dfsync/client` follows a predictable lifecycle:
50-
51-
1. create request context
52-
2. build final URL from `baseUrl`, `path`, and query params
53-
3. merge client and request headers
54-
4. apply authentication
55-
5. attach request metadata (e.g. `x-request-id`)
56-
6. run `beforeRequest` hooks
57-
7. send request with `fetch`
58-
8. retry on failure (if configured)
59-
9. parse response (JSON, text, or `undefined`)
60-
10. run `afterResponse` or `onError` hooks
61-
62-
## Runtime requirements
63-
64-
- Node.js >= 20
65-
- a working fetch implementation
51+
This gives you:
6652

67-
If you do not pass a custom `fetch`, the client uses `globalThis.fetch`.
53+
- timeouts
54+
- retries
55+
- structured errors
56+
- request lifecycle hooks
57+
- built-in retry observability

0 commit comments

Comments
 (0)