Skip to content

Integrations & Testing

HTTP Adapter (custom fetch / edge environments)

Replace the built-in axios client with any HTTP implementation:

js
const fetchAdapter = {
  async request(config) {
    const url = `${config.baseURL ?? ''}${config.url ?? ''}`
    const res = await fetch(url, {
      method: config.method ?? 'GET',
      body: config.data ? JSON.stringify(config.data) : undefined,
      headers: { 'Content-Type': 'application/json', ...config.headers },
      signal: config.signal,
    })
    const data = await res.json()
    return {
      data,
      status: res.status,
      statusText: res.statusText,
      headers: Object.fromEntries(res.headers.entries()),
    }
  },
}

const client = createRestClient({
  baseURL: 'https://api.example.com',
  adapter: fetchAdapter,
  // Auth, interceptors, sanitizeHeaders, metrics still work on top of the adapter
  auth: { getToken: async () => token },
})
ts
type HttpAdapter = {
  request<T = unknown>(config: RestRequestConfig & { baseURL?: string }): Promise<ApiResponse<T>>
}

When adapter is set, createRestClient() never calls axios.create() — the built-in axios instance simply isn't constructed, so adapter-only usage (e.g. in Cloudflare Workers / Deno) doesn't pay for it.

Vue integration

vue
<script setup>
import {
  PipelineOrchestrator,
  usePipelineProgressVue,
  usePipelineRunVue,
} from 'rest-pipeline-js/vue'

const orchestrator = new PipelineOrchestrator({
  config: {
    stages: [/* ... */],
  },
})
const progress = usePipelineProgressVue(orchestrator)
const { run, running, result, error, abort, pause, resume, rerunStep } =
  usePipelineRunVue(orchestrator)
</script>

<template>
  <div>
    <div>Current stage: {{ progress.currentStage }}</div>
    <button @click="run()" :disabled="running">Start</button>
    <button @click="abort()" :disabled="!running">Abort</button>
    <button @click="pause()">Pause</button>
    <button @click="resume()">Resume</button>
    <div v-if="result">Done: {{ result }}</div>
    <div v-if="error">Error: {{ error.message }}</div>
  </div>
</template>

Composables (import from rest-pipeline-js/vue):

ComposableReturnsDescription
usePipelineProgressVue(orchestrator)Ref<PipelineProgress>Reactive progress
usePipelineRunVue(orchestrator){ run, running, result, error, stageResults, abort, pause, resume, rerunStep, clearStageResults }Run pipeline and get reactive state
usePipelineStepEventVue(orchestrator, stepKey, eventType)Ref<any>Last payload for a specific step event
usePipelineLogsVue(orchestrator)Ref<log[]>Reactive logs
useRerunPipelineStepVue(orchestrator)functionBound rerunStep
useRestClientVue(config)ComputedRef<RestClient>Reactive REST client
usePipelineStageResultVue(orchestrator, stepKey)Ref<PipelineStepResult | null>Reactive result of a single stage

React integration

jsx
import { useRef } from 'react'
import {
  PipelineOrchestrator,
  usePipelineProgressReact,
  usePipelineRunReact,
} from 'rest-pipeline-js/react'

const orchestrator = new PipelineOrchestrator({
  config: {
    stages: [/* ... */],
  },
})

export function PipelineComponent() {
  const progress = usePipelineProgressReact(orchestrator)
  const [run, { running, result, error, abort, pause, resume, rerunStep, clearStageResults }] =
    usePipelineRunReact(orchestrator)

  return (
    <div>
      <div>Current stage: {progress.currentStage}</div>
      <button onClick={() => run()} disabled={running}>
        Start
      </button>
      <button onClick={() => abort()} disabled={!running}>
        Abort
      </button>
      <button onClick={() => pause()}>Pause</button>
      <button onClick={() => resume()}>Resume</button>
      {result && <div>Done: {JSON.stringify(result)}</div>}
      {error && <div>Error: {error.message}</div>}
    </div>
  )
}

Hooks (import from rest-pipeline-js/react):

HookReturnsDescription
usePipelineProgressReact(orchestrator)PipelineProgressReactive progress
usePipelineRunReact(orchestrator)[run, { running, result, error, stageResults, abort, pause, resume, rerunStep, clearStageResults }]Run pipeline and get state
usePipelineStepEventReact(orchestrator, stepKey, eventType)anyLast payload for a specific step event
usePipelineLogsReact(orchestrator)log[]Reactive logs
useRerunPipelineStepReact(orchestrator)functionBound rerunStep
useRestClientReact(config)RestClientMemoized REST client — recreated when config is a new object reference; memoize it yourself (useMemo/useState/module-level constant) to avoid recreating it every render
usePipelineStageResultReact(orchestrator, stepKey)PipelineStepResult | nullResult of a single stage

Testing

createMockAdapter() (from the separate rest-pipeline-js/testing entry point, so it never ships in a production bundle) replaces the network with a set of route definitions — for testing code that uses createRestClient() or PipelineOrchestrator without hitting a real backend:

ts
import { createRestClient } from 'rest-pipeline-js'
import { createMockAdapter } from 'rest-pipeline-js/testing'

const adapter = createMockAdapter([
  { method: 'GET', url: '/users/1', respond: { data: { id: 1, name: 'Ada' } } },

  // Dynamic response — reads the request to build the reply
  {
    method: 'POST',
    url: '/orders',
    respond: (info) => ({ data: { id: 42, ...(info.data as object) }, status: 201 }),
  },

  // Sequence of responses, one per matching call — exercise retry logic:
  // first two attempts fail, third succeeds. Repeats the last entry once exhausted.
  {
    method: 'GET',
    url: '/flaky',
    respond: [{ error: true, status: 503 }, { error: true, status: 503 }, { data: { ok: true } }],
  },
])

const client = createRestClient({ baseURL: 'https://api.example.com', adapter })

const user = await client.get('/users/1')
// adapter.calls — history of every request handled, in order; assert on it in your tests
expect(adapter.calls).toHaveLength(1)
  • url matches by substring (string) or .test() (RegExp) against the relative URL (config.url, e.g. /users/1), not the full URL. Omit method to match any method.
  • A response with status >= 400 rejects by default (matching what axios/fetch do), thrown as Error with .status and .response.status/.response.data/.response.headers set — enough for retry.retriableStatus, circuitBreaker, and error interceptors to work against. Override with an explicit error: true/false on the response spec.
  • delayMs on a response simulates network latency with a real setTimeout (works fine under vi.useFakeTimers()/similar).
  • No route matching a request throws immediately with a clear message, instead of hanging — a test with a missing route setup fails loudly.
  • adapter.reset() clears calls and rewinds any array-respond sequences back to their start, without touching the routes themselves.

See examples/mock-adapter.ts for a fuller example, including use with PipelineOrchestrator via httpConfig.