Docs Plugins

Creating Plugins

axios-retryer should grow through plugins, not through one-off flags in the core. Use a single plugin architecture across the repo so every plugin stays predictable to read, test, and extend.

💡
Recommended pattern

Keep the top-level plugin class as an orchestrator. Put public contracts in types/, defaults and validation in configs/, errors in errors/, pure helpers in utils/, and stateful collaborators in managers/, storage/, or interceptors/ only when needed.

When to create a plugin

  • Create a plugin when the feature is optional, cross-cutting, or can be expressed through events and request lifecycle hooks.
  • Do not add niche feature flags to RetryManagerOptions when the behavior belongs in a plugin.
  • Keep the core focused on orchestration and shared infrastructure.

Single recommended folder structure

Use this as the default shape for new plugins. Omit folders you do not need, but do not invent a new structure for each plugin.

src/plugins/YourPlugin/
  YourPlugin.ts
  index.ts
  types/
    index.ts
  configs/
    index.ts
  errors/
    YourPluginError.ts
    index.ts
  utils/
    *.ts
    index.ts
  managers/
    *.ts
  storage/
    *.ts
  interceptors/
    RequestInterceptor.ts
    ResponseInterceptor.ts
    ErrorInterceptor.ts

Responsibility split

  • YourPlugin.ts: lifecycle wiring, interceptor registration, event emission, public methods, teardown.
  • index.ts: public exports and the optional createYourPlugin(...) factory only.
  • types/: options, events, adapter contracts, state records, request metadata types.
  • configs/: defaults, normalization, and validation.
  • errors/: plugin-specific error classes.
  • utils/: pure helpers with explicit return types and no shared mutable state.
  • managers/ or storage/: stateful subsystems with narrow interfaces.
  • interceptors/: request/response/error phase boundaries when lifecycle logic becomes substantial.

Minimal plugin skeleton

import type { PluginContext, RetryPlugin } from 'axios-retryer';

export interface YourPluginEvents {
  onSomethingHappened?: (payload: { requestId: string }) => void;
}

export interface YourPluginOptions {
  enabled?: boolean;
}

export class YourPlugin implements RetryPlugin<YourPluginEvents> {
  public name = 'YourPlugin';
  public version = '1.0.0';
  public readonly _events?: Readonly<YourPluginEvents>;

  private context!: PluginContext<YourPluginEvents>;

  constructor(private readonly options: Required<YourPluginOptions>) {}

  public initialize(context: PluginContext<YourPluginEvents>): void {
    this.context = context;
  }

  public onBeforeDestroyed(): void {
    // Clean up listeners, timers, interceptors, and state here.
  }
}

Current plugin contract

New plugins should target the current RetryPlugin<TPluginEvents> interface from src/types/plugins.ts. The _events marker is important because it preserves event inference when users call manager.use(plugin).

export interface RetryPlugin<TPluginEvents extends object = {}> {
  name: string;
  version: string;
  readonly _events?: Readonly<TPluginEvents>;
  initialize: (context: PluginContext<TPluginEvents>) => void;
  onBeforeDestroyed?: (context: PluginContext<TPluginEvents>) => void;
}

Current PluginContext contract

initialize(context) receives the full plugin-facing manager bridge. Design plugin behavior around this existing surface before adding new APIs.

export interface PluginContext<TPluginEvents extends object = {}> {
  readonly axiosInstance: AxiosInstance;
  getLogger(): Logger;
  on(...): void;
  off(...): boolean;
  emit(...): void;
  triggerAndEmit(...): void;
  cancelRequest(requestId: string): void;
  cancelAllRequests(): void;
  cancelQueuedRequests(): void;
  registerQueueGate(name: string, canProcess: (request: AxiosRequestConfig) => boolean): void;
  unregisterQueueGate(name: string): boolean;
  refreshQueue(): void;
  registerMetricsRecorder(recorder: MetricsRecorder | null): void;
  getTimerStats(): { activeTimers: number; activeRetryTimers: number };
  releaseRequestTracking(config: AxiosRequestConfig): void;
}
  • Use axiosInstance to register Axios interceptors owned by the plugin.
  • Use on, off, emit, and triggerAndEmit for event-driven behavior.
  • Use queue APIs such as registerQueueGate and refreshQueue for gating or coordination.
  • Use registerMetricsRecorder only when the plugin is responsible for manager-visible metrics.
  • Use releaseRequestTracking only for advanced request lifecycle ownership, such as refresh/replay flows.

Public entrypoint shape

The package entrypoint should stay small and predictable. Export the class, public errors, public types, and optional factory function from index.ts.

export { YourPlugin } from './YourPlugin';
export { YourPluginError } from './errors';
export type { YourPluginEvents, YourPluginOptions } from './types';

import { YourPlugin, type YourPluginOptions } from './YourPlugin';

export function createYourPlugin(options?: YourPluginOptions): YourPlugin {
  return new YourPlugin(options);
}

Best practices

  • Emit plugin behavior at the outer boundary with context.triggerAndEmit(...) instead of coupling the core to plugin internals.
  • Keep side effects near boundaries and keep helper logic deterministic.
  • Use explicit return types and never use any.
  • If a file grows beyond roughly 200 lines, extract a focused collaborator.
  • Split request, response, and error handling when a plugin starts mixing lifecycle phases.
  • Write focused tests for config validation, side effects, and plugin-specific state transitions.

Suggested workflow

  1. Read the plugin contract in src/types/plugins.ts.
  2. Map the feature to the existing PluginContext methods before adding implementation details.
  3. Inspect an existing plugin before inventing a new shape.
  4. Define public events and options first.
  5. Implement defaults and validation in configs/.
  6. Keep the plugin class focused on orchestration.
  7. Extract pure logic and stateful boundaries as soon as they stop being trivial.
  8. Document the plugin on this website when its public behavior is user-facing.
Plugins Overview API Reference