Flexible, modular pipeline orchestrator for REST APIs — retry with backoff, caching, rate limiting, streaming, plugin system, and Vue/React integrations.

A plain fetch call with error handling works fine until your project needs retries, caching, rate limits, and token refresh all at once — rest-pipeline-js pulls all of that into one configurable pipeline instead of scattered ad-hoc code for every case.
A shopper taps "Pay" twice because the network stalls — idempotency keys guarantee the retry doesn't create a second order, and an offline queue delivers the operation once the connection comes back if it drops entirely.
One request returns a list of objects, the next pulls metrics for each one, a third compares them to the previous period, and each step's result shapes the parameters of the next. The whole chain is described as a single pipeline, where each step automatically receives what it needs from the one before it, instead of requests nested inside each other and data passed around by hand.
Confirming a reservation means checking slot availability, pricing in a discount, reserving the spot, and sending a notification — if one step fails, you need to know exactly what already happened and retry just the stuck step instead of starting the whole flow over.
A validation schema (Zod, Yup, or Valibot) catches a malformed response before it reaches the rest of your code, and the process itself gets a chance to fall back to a default value instead of aborting entirely.

Create clients with retries (exponential backoff, Retry‑After, jitter), caching (TTL, stale‑while‑revalidate, invalidation), rate limiting (local and distributed), automatic authentication with token refresh, circuit breaker, request cancellation by key, idempotency, and an offline queue for mutating operations.

Build flexible pipelines from sequential and parallel stage groups with conditions, before/after/errorHandler hooks, shared context (sharedData), pause/resume, state export/import, rerunning individual steps, and nested sub‑pipelines. Control execution with AbortSignal and collect metrics.

Use stream stages for SSE and any AsyncIterable with real‑time chunk handling (onChunk) and cancellation support. WebSocket stages provide persistent connections with onOpen/onMessage/onClose/onError callbacks, automatic closing by condition, timeouts, and integration with the abort signal.

Vue composables and React hooks (usePipelineRun, usePipelineProgress) let you run pipelines and track progress in components. All hooks are SSR‑safe. Plugins, global middleware, metrics, and tracing (W3C traceparent, OpenTelemetry) are easily attached without changing stage logic.

Offline queue, idempotency keys, and step recovery via recoverStep ensure reliability. Schema validation (Zod, Yup, Valibot) and custom HTTP adapters (fetch, edge) adapt the library to any environment. A built‑in mock adapter simplifies testing without a real backend.

Ready‑made paginate, paginateAll, and flattenPages helpers save you from hand‑writing pagination loops — cursor‑based or offset‑based, including automatic stopping on a condition. Pipelines can also be built with a fluent createPipeline().pipe().step() API, with types inferred automatically.
Set up the REST client once — from there it handles retries, caching, and token refresh on its own.
import { createRestClient } from 'rest-pipeline-js'
const client = createRestClient({
baseURL: 'https://api.example.com',
retry: { attempts: 2, delayMs: 500, backoffMultiplier: 2 },
cache: { enabled: true, ttlMs: 60000 },
auth: {
getToken: async () => localStorage.getItem('token') ?? '',
onUnauthorized: async () => {
// refresh the token here
},
},
})
const user = await client.get('/users/1') Chain steps into a pipeline — each one gets the previous result through sharedData.
import { PipelineOrchestrator } from 'rest-pipeline-js'
const orchestrator = new PipelineOrchestrator({
config: {
stages: [
{
key: 'fetchUser',
request: ({ sharedData }) => client.get(`/users/${sharedData.userId}`),
},
{
key: 'processData',
request: ({ prev }) => ({ ...prev.data, processed: true }),
},
],
},
sharedData: { userId: 42 },
})
const result = await orchestrator.run()
console.log(result.success, result.stageResults)After a run of consecutive failures, the client stops hitting the dead API and rejects requests locally right away — then probes on its own to see if it's back.
import { createRestClient, CircuitOpenError } from 'rest-pipeline-js'
const client = createRestClient({
baseURL: 'https://api.example.com',
circuitBreaker: {
failureThreshold: 5, // open after 5 consecutive failures
openMs: 30_000, // stay open for 30s before probing again
isFailure: (error) => error.status === undefined || error.status >= 500, // ignore 4xx
},
})
try {
await client.get('/flaky-endpoint')
} catch (err) {
if (err instanceof CircuitOpenError) {
// rejected locally — no network call was made
}
}