Opinionated timeout tiers, retry, and exponential-backoff helpers for Directus clients. Tune by tier instead of hand-coding timeouts on every call. Works with the Directus SDK, plain fetch, or axios.
npm install directus-timeouts
The Directus SDK doesn't ship timeout or retry primitives. Every consumer reinvents:
- "What timeout do I use for this kind of query?"
- "Should I retry on 502? On
ECONNRESET?" - "How do I add backoff without pulling in a 200KB dep?"
This package gives you four tuned tiers (fast / standard / heavy / critical), a retryable-error matcher that matches what Directus actually returns under load, and a tieredFetch wrapper you can drop straight into createDirectus(...).with(rest({ fetch: tieredFetch("standard") })).
| Tier | Timeout | Use for |
|---|---|---|
fast |
8,000 | Single-record fetches, simple filters |
standard |
15,000 | Most reads |
heavy |
25,000 | Complex filters, deep relations, exports |
critical |
45,000 | File uploads, batch writes, "must succeed" |
import { createDirectus, rest, readItems } from "@directus/sdk";
import { tieredFetch, withRetry } from "directus-timeouts";
const directus = createDirectus(process.env.DIRECTUS_URL!).with(
rest({ fetch: tieredFetch("standard") }),
);
// Add retry + backoff to any call:
const posts = await withRetry(() => directus.request(readItems("posts")));import { tieredFetch } from "directus-timeouts";
const f = tieredFetch("heavy");
const res = await f("https://cms.example.com/items/big-export");import axios from "axios";
import { createAxiosConfig } from "directus-timeouts";
await axios.get("/items/posts", createAxiosConfig("fast"));getTimeout(tier?: TimeoutTier, overrides?): number
backoffDelay(attempt: number, options?: RetryOptions): number
isRetryableError(error: unknown): boolean
withRetry<T>(fn: () => Promise<T>, options?: RetryOptions & { onRetry? }): Promise<T>
tieredFetch(tier?: TimeoutTier, options?: { overrides?; baseFetch? }): typeof fetch
createAxiosConfig(tier?, headers?): { timeout, headers }
DEFAULT_TIMEOUTS // { fast, standard, heavy, critical }
DEFAULT_RETRY // { maxAttempts, baseDelayMs, exponentialBackoff, jitter }isRetryableError returns true for:
AbortErrorECONNABORTED,ECONNRESET,ETIMEDOUT- Errors whose message contains
timeout/network/fetch failed - HTTP
429(rate limit) - HTTP
5xx
Everything else (including 4xx) is non-retryable and propagated immediately.
Delay = baseDelayMs * 2^(attempt-1) ± up to 20% jitter, so a thundering herd doesn't synchronize.
MIT