diff --git a/docs/v1/getting-started.md b/docs/v1/getting-started.md
index 6a8d3e6..825a19f 100644
--- a/docs/v1/getting-started.md
+++ b/docs/v1/getting-started.md
@@ -21,6 +21,7 @@ The client focuses on predictable behavior, extensibility, and a clean developer
- structured error classes
- auth support
- lifecycle hooks
+- retry support
- custom `fetch` support
## Quick example
@@ -46,16 +47,46 @@ console.log(user.name);
## How requests work
-A request in `@dfsync/client` follows this flow:
+A request in `@dfsync/client` goes through the following lifecycle:
-1. build final URL from `baseUrl`, `path`, and optional query params
-2. merge default, client-level, and request-level headers
-3. apply auth configuration
-4. run `beforeRequest` hooks
-5. send request with `fetch`
-6. parse response as JSON, text, or `undefined` for `204`
-7. throw structured errors for failed requests
-8. run `afterResponse` or `onError` hooks
+1. Build request URL
+
+ The final URL is constructed from `baseUrl`, `path`, and optional query parameters.
+
+2. Merge headers
+
+ Default headers, client-level headers, and request-level headers are combined.
+
+3. Apply authentication
+
+ The configured auth strategy (Bearer, API key, or custom) is applied to the request.
+
+4. Run `beforeRequest` hooks
+
+ Hooks can modify the request before it is sent.
+
+5. Execute the HTTP request
+
+ The request is sent using the Fetch API.
+
+6. Retry if necessary
+
+ If the request fails with a retryable error, it may be retried according to the configured retry policy.
+
+7. Parse the response
+
+ The response body is parsed automatically:
+ - JSON → parsed object
+ - text → string
+ - `204 No Content` → `undefined`
+
+8. Handle errors
+
+ Non-success responses and network failures are converted into structured errors.
+
+9. Run response hooks
+ - `afterResponse` runs for successful responses
+ - `onError` runs when an error occurs
## Runtime requirements
diff --git a/docs/v1/retry.md b/docs/v1/retry.md
index e69de29..fd8b6ff 100644
--- a/docs/v1/retry.md
+++ b/docs/v1/retry.md
@@ -0,0 +1,179 @@
+# Retry
+
+`@dfsync/client` supports configurable retry policies for transient failures.
+
+Retries are useful when communicating with external services that may temporarily fail or return `5xx` responses.
+
+The retry behavior can be configured globally for the client or overridden per request.
+
+---
+
+## Basic retry configuration
+
+```ts
+import { createClient } from '@dfsync/client';
+
+const client = createClient({
+ baseUrl: 'https://api.example.com',
+ retry: {
+ attempts: 2,
+ },
+});
+```
+
+If a retryable error occurs, the request will be retried up to the configured number of attempts.
+
+## Retry conditions
+
+By default, retries happen for:
+
+- network errors
+- HTTP 5xx responses
+
+Example:
+
+```ts
+const client = createClient({
+ baseUrl: 'https://api.example.com',
+ retry: {
+ attempts: 3,
+ },
+});
+```
+
+## Retry backoff
+
+Two retry strategies are supported:
+
+### Fixed delay
+
+```ts
+const client = createClient({
+ baseUrl: 'https://api.example.com',
+ retry: {
+ attempts: 3,
+ backoff: 'fixed',
+ baseDelayMs: 300,
+ },
+});
+```
+
+Retry delays:
+
+```bash
+300ms
+300ms
+300ms
+```
+
+### Exponential backoff
+
+```ts
+const client = createClient({
+ baseUrl: 'https://api.example.com',
+ retry: {
+ attempts: 3,
+ backoff: 'exponential',
+ baseDelayMs: 300,
+ },
+});
+```
+
+Retry delays:
+
+```bash
+300ms
+600ms
+1200ms
+```
+
+If `attempts` is `0` (default), no retries are performed and retry delays are ignored.
+
+## Retry methods
+
+By default retries apply to:
+
+- `GET`
+- `PUT`
+- `DELETE`
+
+POST requests are **not retried by default**.
+
+Example enabling POST retries:
+
+```ts
+const client = createClient({
+ baseUrl: 'https://api.example.com',
+ retry: {
+ attempts: 2,
+ retryMethods: ['GET', 'POST'],
+ },
+});
+```
+
+## Retry conditions configuration
+
+You can control which errors trigger retries.
+
+Supported conditions:
+
+- network-error
+- 5xx
+- 429
+
+Example:
+
+```ts
+const client = createClient({
+ baseUrl: 'https://api.example.com',
+ retry: {
+ attempts: 2,
+ retryOn: ['network-error', '5xx', '429'],
+ },
+});
+```
+
+## Per-request retry override
+
+Request-level configuration overrides the client configuration.
+
+```ts
+await client.get('/users', {
+ retry: {
+ attempts: 1,
+ },
+});
+```
+
+## Retry and hooks
+
+Hooks behave as follows when retries are enabled:
+
+| Hook | Behavior |
+| --------------- | ------------------------------------- |
+| `beforeRequest` | executed on every retry attempt |
+| `afterResponse` | executed only on successful response |
+| `onError` | executed once after the final failure |
+
+Example:
+
+```ts
+const client = createClient({
+ baseUrl: 'https://api.example.com',
+ retry: { attempts: 2 },
+ hooks: {
+ onError(ctx) {
+ console.error(ctx.error);
+ },
+ },
+});
+```
+
+## Summary
+
+Retry is designed for **safe and predictable service-to-service communication** and works well for:
+
+- microservices
+- external APIs
+- background workers
+- integration services
diff --git a/index.html b/index.html
index 0acd386..1276cab 100644
--- a/index.html
+++ b/index.html
@@ -4,7 +4,7 @@
-
dfsync — Reliable HTTP client for Node.js Microservices
+ dfsync — Reliable HTTP communication for Node.js environments.
,
- title: 'Auth & lifecycle hooks',
- description:
- 'Built-in support for bearer tokens, API keys, and request lifecycle hooks like beforeRequest, afterResponse, and onError.',
+ icon: ,
+ title: 'Auth support',
+ description: 'Built-in support for bearer tokens, API keys, and custom auth flows.',
+ },
+ {
+ icon: ,
+ title: 'Retry support',
+ description: 'Built-in configurable retry policies for transient failures.',
+ },
+ {
+ icon: ,
+ title: 'Lifecycle hooks',
+ description: 'Built-in request lifecycle hooks like beforeRequest, afterResponse, and onError.',
},
{
icon: ,
@@ -36,11 +47,11 @@ export const Features = () => {
- Why dfsync
+ Why @dfsync/client
- A focused foundation for dependable HTTP communication between services — with sensible
- defaults, auth strategies, and lifecycle hooks.
+ A lightweight HTTP client for service-to-service communication, with sensible defaults,
+ authentication strategies, lifecycle hooks, and retry support.
diff --git a/src/components/Footer/Footer.tsx b/src/components/Footer/Footer.tsx
index 7e1a8c7..329fd05 100644
--- a/src/components/Footer/Footer.tsx
+++ b/src/components/Footer/Footer.tsx
@@ -31,7 +31,7 @@ export const Footer = () => {
npm
{
}
diff --git a/src/components/Hero/Hero.tsx b/src/components/Hero/Hero.tsx
index 00fa94e..d304482 100644
--- a/src/components/Hero/Hero.tsx
+++ b/src/components/Hero/Hero.tsx
@@ -10,13 +10,13 @@ export const Hero = () => {
-
+
{
maxWidth: 900,
}}
>
- Reliable HTTP communication for modern services.
+ Reliable toolkit for service-to-service communication
-
@@ -52,8 +48,9 @@ export const Hero = () => {
lineHeight: 1.6,
}}
>
- dfsync provides a unified HTTP client with sensible defaults for service-to-service
- communication across microservices, internal APIs, workers, and external integrations.
+ The first package, @dfsync/client, provides a lightweight and
+ reliable HTTP client for service-to-service communication in Node.js, with built-in
+ retry, authentication, and lifecycle hooks.
@@ -113,6 +110,7 @@ export const Hero = () => {
const client = createClient({
baseURL: "https://api.example.com",
+ retry: { attempts: 3 }
});
const users = await client.get("/users");`}
diff --git a/src/components/ProjectBadges/ProjectBadges.tsx b/src/components/ProjectBadges/ProjectBadges.tsx
index 09dc19d..67d0704 100644
--- a/src/components/ProjectBadges/ProjectBadges.tsx
+++ b/src/components/ProjectBadges/ProjectBadges.tsx
@@ -1,7 +1,6 @@
import { Stack, Box, Link } from '@mui/material';
const packageName = '@dfsync/client';
-const repoName = 'dfsyncjs/dfsync';
export const ProjectBadges = () => {
return (
@@ -22,7 +21,7 @@ export const ProjectBadges = () => {
/>
- {/* npm weekly downloads */}
+ {/* npm downloads */}
{
>
-
- {/* github stars */}
-
-
-
-
- {/* LICENSE
-
-
-
- */}
);
};
diff --git a/src/content/docsContent.ts b/src/content/docsContent.ts
index 9b5af15..94c667d 100644
--- a/src/content/docsContent.ts
+++ b/src/content/docsContent.ts
@@ -5,6 +5,7 @@ export const docsContent = {
'create-client': () => import('../../docs/v1/create-client.md?raw'),
auth: () => import('../../docs/v1/auth.md?raw'),
hooks: () => import('../../docs/v1/hooks.md?raw'),
+ retry: () => import('../../docs/v1/retry.md?raw'),
errors: () => import('../../docs/v1/errors.md?raw'),
examples: () => import('../../docs/v1/examples.md?raw'),
},
diff --git a/src/content/docsNavigation.ts b/src/content/docsNavigation.ts
index 9e4c608..594033d 100644
--- a/src/content/docsNavigation.ts
+++ b/src/content/docsNavigation.ts
@@ -4,6 +4,7 @@ export const docsNavigation = [
{ label: 'Create Client', slug: 'create-client' },
{ label: 'Auth', slug: 'auth' },
{ label: 'Hooks', slug: 'hooks' },
+ { label: 'Retry', slug: 'retry' },
{ label: 'Errors', slug: 'errors' },
{ label: 'Examples', slug: 'examples' },
] as const;
diff --git a/src/pages/Docs/DocsPage.tsx b/src/pages/Docs/DocsPage.tsx
index 59750ba..8c1f59d 100644
--- a/src/pages/Docs/DocsPage.tsx
+++ b/src/pages/Docs/DocsPage.tsx
@@ -142,13 +142,63 @@ export function DocsPage() {
'& pre': {
overflowX: 'auto',
p: 2,
- borderRadius: 0.5,
+ borderRadius: 1,
bgcolor: 'background.default',
border: '1px solid',
borderColor: 'divider',
+ mb: 3,
},
+
+ '& pre code': {
+ fontFamily: 'ui-monospace, SFMono-Regular, Menlo, monospace',
+ fontSize: 14,
+ lineHeight: 1.7,
+ backgroundColor: 'transparent',
+ padding: 0,
+ borderRadius: 0,
+ border: 'none',
+ },
+
'& code': {
fontFamily: 'ui-monospace, SFMono-Regular, Menlo, monospace',
+ fontSize: '0.9em',
+ px: 0.75,
+ py: 0.25,
+ borderRadius: 0.5,
+ bgcolor: 'background.default',
+ border: '1px solid',
+ borderColor: 'divider',
+ },
+ '& table': {
+ width: '100%',
+ borderCollapse: 'collapse',
+ mb: 3,
+ overflow: 'hidden',
+ borderRadius: 2,
+ border: '1px solid',
+ borderColor: 'divider',
+ },
+ '& thead': {
+ bgcolor: 'background.default',
+ },
+ '& th': {
+ textAlign: 'left',
+ px: 2,
+ py: 1.5,
+ borderBottom: '1px solid',
+ borderColor: 'divider',
+ fontWeight: 700,
+ },
+ '& td': {
+ px: 2,
+ py: 1.5,
+ borderBottom: '1px solid',
+ borderColor: 'divider',
+ color: 'text.secondary',
+ verticalAlign: 'top',
+ },
+ '& tr:last-of-type td': {
+ borderBottom: 'none',
},
'& a': {
color: 'primary.main',