Skip to content

Commit 872db55

Browse files
authored
release 0.6.x (#14)
1 parent 6768e43 commit 872db55

File tree

9 files changed

+192
-81
lines changed

9 files changed

+192
-81
lines changed

docs/v1/create-client.md

Lines changed: 65 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ type ClientConfig = {
5555
hooks?: {
5656
// see Hooks section
5757
};
58+
retry?: RetryConfig;
5859
};
5960
```
6061

@@ -141,6 +142,7 @@ type RequestOptions = {
141142
timeout?: number;
142143
retry?: RetryConfig;
143144
signal?: AbortSignal;
145+
requestId?: string;
144146
};
145147
```
146148

@@ -172,32 +174,91 @@ type RequestConfig = {
172174
timeout?: number;
173175
retry?: RetryConfig;
174176
signal?: AbortSignal;
177+
requestId?: string;
175178
};
176179
```
177180

181+
## Request context
182+
183+
Each request is executed within a request context that contains:
184+
185+
- `requestId` — unique identifier for the request
186+
- `attempt` — current retry attempt
187+
- `signal` — AbortSignal for cancellation
188+
- `startedAt` — request start timestamp
189+
190+
This context is available in all lifecycle hooks.
191+
192+
## Request ID
193+
194+
Each request has a `requestId` that is:
195+
196+
- automatically generated by default
197+
- can be overridden per request
198+
- propagated via the `x-request-id` header
199+
200+
This allows tracing requests across services.
201+
202+
### Example
203+
204+
```ts
205+
await client.get('/users', {
206+
requestId: 'req_123',
207+
});
208+
```
209+
210+
You can also override the header directly:
211+
212+
```ts
213+
await client.get('/users', {
214+
headers: {
215+
'x-request-id': 'custom-id',
216+
},
217+
});
218+
```
219+
220+
## Request cancellation
221+
222+
Requests can be cancelled using `AbortSignal`:
223+
224+
```ts
225+
const controller = new AbortController();
226+
227+
const promise = client.get('/users', {
228+
signal: controller.signal,
229+
});
230+
231+
controller.abort();
232+
```
233+
234+
Cancellation is treated differently from timeouts:
235+
236+
- timeout → `TimeoutError`
237+
- manual cancellation → `RequestAbortedError`
238+
178239
## Header behavior
179240

180-
Headers are merged in this order:
241+
Headers are resolved as part of the request lifecycle in the following order:
181242

182243
1. default headers
183244
2. client headers
184245
3. request headers
185246
4. auth modifications
186247
5. beforeRequest hook modifications
187248

188-
That means request-level headers override client-level headers, and auth can still overwrite auth-related header values.
249+
This means request-level headers override client-level headers, and auth can still overwrite auth-related header values.
189250

190251
## Timeout behavior
191252

192-
Timeout is resolved in this order:
253+
Timeout is resolved as part of the request lifecycle:
193254

194255
1. request-level timeout
195256
2. client-level timeout
196257
3. default timeout: `5000`
197258

198259
## Response parsing
199260

200-
Responses are parsed automatically:
261+
Responses are parsed automatically during the response phase:
201262

202263
- `application/json` → parsed JSON
203264
- other content types → text

docs/v1/errors.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@
44

55
## Error classes
66

7-
- `DfsyncError`
8-
- `HttpError`
9-
- `NetworkError`
10-
- `TimeoutError`
7+
- `DfsyncError` - basic Error class
8+
- `HttpError` — non-2xx responses
9+
- `NetworkError` — network failures
10+
- `TimeoutError` — request timed out
11+
- `RequestAbortedError` — request was cancelled
12+
13+
- This allows you to handle failures more precisely.
1114

1215
## Base error
1316

@@ -167,9 +170,7 @@ if (error instanceof HttpError) {
167170
Errors thrown inside:
168171

169172
- custom auth
170-
171173
- `beforeRequest`
172-
173174
- `afterResponse`
174175

175176
are rethrown as-is.

docs/v1/getting-started.md

Lines changed: 23 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Getting Started
22

3-
`@dfsync/client` is a lightweight TypeScript HTTP client designed for reliable service-to-service communication.
3+
`@dfsync/client` is a lightweight HTTP client built around a predictable request lifecycle for service-to-service communication in Node.js.
44

55
It provides sensible defaults for:
66

@@ -11,19 +11,20 @@ It provides sensible defaults for:
1111

1212
The client focuses on predictable behavior, extensibility, and a clean developer experience.
1313

14-
## What you get
14+
## Main features
15+
16+
- predictable request lifecycle
17+
- request ID propagation (`x-request-id`)
18+
- request cancellation via `AbortSignal`
19+
- built-in retry with configurable policies
20+
- lifecycle hooks: `beforeRequest`, `afterResponse`, `onError`
1521

1622
- typed responses
17-
- simple client creation
18-
- request timeout support
1923
- automatic JSON parsing
20-
- text response support
21-
- consistent error handling with structured error classes
22-
- auth support: `bearer`, `API key` and custom
23-
- lifecycle hooks: `beforeRequest`, `afterResponse`, `onError`
24+
- consistent error handling
25+
26+
- auth support: bearer, API key, custom
2427
- support for `GET`, `POST`, `PUT`, `PATCH`, and `DELETE`
25-
- retry policies
26-
- custom `fetch` support
2728

2829
## Quick example
2930

@@ -41,53 +42,22 @@ const client = createClient({
4142
});
4243

4344
const user = await client.get<User>('/users/1');
44-
45-
console.log(user.id);
46-
console.log(user.name);
4745
```
4846

4947
## How requests work
5048

51-
A request in `@dfsync/client` goes through the following lifecycle:
52-
53-
1. Build request URL
54-
55-
The final URL is constructed from `baseUrl`, `path`, and optional query parameters.
56-
57-
2. Merge headers
58-
59-
Default headers, client-level headers, and request-level headers are combined.
60-
61-
3. Apply authentication
62-
63-
The configured auth strategy (Bearer, API key, or custom) is applied to the request.
64-
65-
4. Run `beforeRequest` hooks
66-
67-
Hooks can modify the request before it is sent.
68-
69-
5. Execute the HTTP request
70-
71-
The request is sent using the Fetch API.
72-
73-
6. Retry if necessary
74-
75-
If the request fails with a retryable error, it may be retried according to the configured retry policy.
76-
77-
7. Parse the response
78-
79-
The response body is parsed automatically:
80-
- JSON → parsed object
81-
- text → string
82-
- `204 No Content``undefined`
83-
84-
8. Handle errors
85-
86-
Non-success responses and network failures are converted into structured errors.
87-
88-
9. Run response hooks
89-
- `afterResponse` runs for successful responses
90-
- `onError` runs when an error occurs
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
9161

9262
## Runtime requirements
9363

docs/v1/hooks.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,24 @@ Each hook can be:
1515

1616
Hooks run sequentially in the order you provide them.
1717

18+
## Request metadata
19+
20+
Hooks receive a rich lifecycle context, including request metadata and execution details.
21+
22+
```ts
23+
const client = createClient({
24+
baseUrl: 'https://api.example.com',
25+
hooks: {
26+
beforeRequest: (ctx) => {
27+
console.log(ctx.requestId, ctx.attempt);
28+
},
29+
onError: (ctx) => {
30+
console.error(ctx.requestId, ctx.error);
31+
},
32+
},
33+
});
34+
```
35+
1836
## beforeRequest
1937

2038
Use `beforeRequest` to mutate headers or the final request URL before `fetch` is called.

src/components/Features/Features.tsx

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import HubIcon from '@mui/icons-material/Hub';
2-
import SecurityIcon from '@mui/icons-material/Security';
2+
import AutorenewIcon from '@mui/icons-material/Autorenew';
33
import LockIcon from '@mui/icons-material/Lock';
44
import ReplayIcon from '@mui/icons-material/Replay';
55
import DeviceHubIcon from '@mui/icons-material/DeviceHub';
@@ -35,23 +35,23 @@ const items = [
3535
description: 'Built-in request lifecycle hooks like beforeRequest, afterResponse, and onError.',
3636
},
3737
{
38-
icon: <SecurityIcon fontSize="large" />,
39-
title: 'Production-oriented',
40-
description:
41-
'Designed for reliability, clear request behavior, and maintainable service communication.',
38+
icon: <AutorenewIcon fontSize="large" />,
39+
title: 'Predictable lifecycle',
40+
description: 'Every request follows a clear and controllable lifecycle.',
4241
},
4342
];
4443

4544
export const Features = () => {
4645
return (
47-
<Container maxWidth="lg" sx={{ pb: { xs: 8, md: 12 } }}>
46+
<Container maxWidth="lg" sx={{ py: { xs: 2, md: 4 } }}>
4847
<Stack spacing={2} sx={{ mb: 5 }}>
4948
<Typography variant="h2" sx={{ fontSize: { xs: '2rem', md: '3rem' } }}>
5049
Why @dfsync/client
5150
</Typography>
5251
<Typography color="text.secondary" sx={{ maxWidth: 720 }}>
53-
A lightweight HTTP client for service-to-service communication, with sensible defaults,
54-
authentication strategies, lifecycle hooks, and retry support.
52+
A lightweight HTTP client with a predictable request lifecycle for service-to-service
53+
communication with sensible defaults, authentication strategies, lifecycle hooks, and
54+
retry support.
5555
</Typography>
5656
</Stack>
5757

src/components/Hero/Hero.tsx

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export const Hero = () => {
1111
<Box
1212
component="section"
1313
sx={{
14-
py: { xs: 6, md: 12 },
14+
py: { xs: 2, md: 4 },
1515
background: 'radial-gradient(circle at top, rgba(56,189,248,0.15), transparent 40%)',
1616
}}
1717
>
@@ -56,9 +56,9 @@ export const Hero = () => {
5656
lineHeight: 1.6,
5757
}}
5858
>
59-
The first package, <strong>@dfsync/client</strong>, provides a lightweight and
60-
reliable HTTP client for service-to-service communication in Node.js, with built-in
61-
retry, authentication, and lifecycle hooks.
59+
The first package, <strong>@dfsync/client</strong>, is a lightweight HTTP client built
60+
around a predictable request lifecycle for service-to-service communication in
61+
Node.js.
6262
</Typography>
6363
</Box>
6464

@@ -114,7 +114,7 @@ export const Hero = () => {
114114
width: '100%',
115115
p: { xs: 3, md: 4 },
116116
borderRadius: 1,
117-
bgcolor: 'background.paper',
117+
backgroundColor: 'background.paper',
118118
border: '1px solid',
119119
borderColor: 'divider',
120120
overflowX: 'auto',
@@ -130,14 +130,17 @@ export const Hero = () => {
130130
color: 'text.primary',
131131
}}
132132
>
133-
{`import { createClient } from "@dfsync/client";
133+
{`import { createClient } from '@dfsync/client';
134134
135135
const client = createClient({
136-
baseURL: "https://api.example.com",
137-
retry: { attempts: 3 }
136+
baseURL: 'https://api.example.com',
137+
retry: { attempts: 3 },
138138
});
139139
140-
const users = await client.get("/users");`}
140+
const users = await client.get('/users', {
141+
requestId: 'req_123',
142+
});
143+
`}
141144
</Typography>
142145
</Box>
143146
</Stack>

0 commit comments

Comments
 (0)