rest-pipeline-js
Flexible, modular pipeline orchestrator for REST APIs — sequential and parallel stages, retry with backoff, response caching, rate limiting, auth provider, stream stages (SSE / AsyncIterable), plugin system, and Vue / React integrations — all with a single dependency (axios).
Features
createRestClient()— full-featured HTTP client built on top of axios: retry with exponential backoff andRetry-Aftersupport, response caching with a pluggableCacheStorebackend (incl. targetedinvalidateCache()), rate limiting (concurrency + req/interval) with a pluggable distributedRateLimiterStore, circuit breaker with a pluggable distributedCircuitBreakerStore, auth provider with automatic 401 refresh and optional token caching, request cancellation by key, custom HTTP adapters- Request tracing — W3C
traceparentheader generation plus aTracingProviderhook (duck-typed against OpenTelemetry'sSpanAPI) for wiring in a real tracing backend - Idempotency keys —
Idempotency-Keyheader on mutating requests, manual or auto-generated per logical request across retry attempts - Offline queue — queue mutating requests made while offline and replay them (same idempotency key on every attempt) once back online; pluggable persistence,
isOnline/onOnlineChangefor non-browser environments PipelineOrchestrator— sequential and parallel stage execution; each stage hascondition,before,request,after,validateInput,validateOutput,errorHandlerhooks (all receive the pipeline'sAbortSignal);sharedDatapool shared across all stages- Error recovery —
errorHandlercan returnrecoverStep(data)to turn a failed stage back into a successful one and keep the pipeline going, instead of only transforming the error - Global middleware —
beforeEach/afterEach/onErrorhooks that apply to every stage without modifying individual configs - Parallel groups — multiple stages run concurrently via
Promise.all, or through a bounded pool viaconcurrency; single failure stops the group - Pause / Resume / Abort —
pause()waits after the current stage;resume()continues;abort()cancels the current HTTP request and propagates itsAbortSignalinto every stage hook so customrequest/before/afterfunctions can cancel their own work too - Export / Import state — serialize
stageResults+ logs to a plain object; restore on the next page load - Stream stages —
stream: async function*for SSE / anyAsyncIterable;onChunkcallback in real time; abort-aware - WebSocket stages — a stage over a persistent connection (
onOpen/onMessage/onClose/onError,closeOn); pluggablecreateWebSocket(defaults toglobalThis.WebSocket) for Node <22/edge runtimes - Pipeline metrics & run correlation —
onPipelineStart,onPipelineEnd,onStepDurationcallbacks, plus arunId(also ongetRunId(), log entries, and step events) shared by every callback/event from the same run createPipeline()/pipe()builder — short factory and fluent builder API for common patterns; in TypeScript,pipe().step()chains inferprev's type from the previous step automaticallyvalidatePipelineConfig()— catch duplicate keys, empty keys, type errors before runtime- Plugin system — install reusable behavior (logging, analytics, etc.); cleanup via
destroy() - Persist adapter — pluggable save/load interface; auto-save after each stage
- Log sanitization — mask sensitive headers (
authorization,x-api-key,cookie, …) in metrics callbacks, on by default - Vue integration —
usePipelineRunVue,usePipelineProgressVue, and more (import fromrest-pipeline-js/vue) - React integration —
usePipelineRunReact,usePipelineProgressReact, and more (import fromrest-pipeline-js/react) paginate()/paginateAll()/flattenPages()— iterate a paginated API (cursor- or offset/limit-based) as anAsyncGenerator<T[]>, standalone or as aStreamStageConfigsourcecreateMockAdapter()(separaterest-pipeline-js/testingentry point) — route-basedHttpAdapterfor testing code that uses this package without a real backend, with call history and sequenced responses for exercising retry- Tree-shakeable —
sideEffects: false; Vue and React entry points are code-split
Installation
npm install rest-pipeline-jsPeer dependencies for framework integrations:
# Vue
npm install vue@>=3.3
# React
npm install react@>=19 react-dom@>=19CDN usage
No bundler, no Node — a single <script> tag pulls in the core module (PipelineOrchestrator / createRestClient / everything under the core entry point, no Vue/React) with axios bundled in, so nothing else needs to be loaded separately. Built as a self-contained IIFE that exposes a window.RestPipeline global — a plain <script> tag, no CommonJS/AMD loader support:
<script src="https://unpkg.com/rest-pipeline-js/dist/umd/rest-pipeline.umd.min.js"></script>
<script>
const { createRestClient, PipelineOrchestrator } = RestPipeline
const client = createRestClient({ baseURL: 'https://api.example.com' })
const pipeline = new PipelineOrchestrator({
config: {
stages: [{ key: 'user', request: () => client.get('/me') }],
},
})
pipeline.run().then((result) => console.log(result))
</script>Pin a version for production use (rest-pipeline-js@2.1.0/dist/umd/...) — the unpinned URL above always resolves to the latest release. jsDelivr works the same way: https://cdn.jsdelivr.net/npm/rest-pipeline-js/dist/umd/rest-pipeline.umd.min.js.
An unminified build with a source map (rest-pipeline.umd.js) is also published, for debugging.
Quick start
import { createRestClient, PipelineOrchestrator } from 'rest-pipeline-js'
// 1. Create a REST client
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 token */
},
},
})
const res = await client.get('/users/1')
// 2. Run a pipeline
const orchestrator = new PipelineOrchestrator({
config: {
stages: [
{
key: 'fetchUser',
request: async ({ sharedData }) => client.get(`/users/${sharedData.userId}`),
},
{
key: 'processData',
request: async ({ prev }) => ({ ...prev.data, processed: true }),
},
],
},
sharedData: { userId: 42 },
})
const result = await orchestrator.run()
console.log(result.success, result.stageResults)