CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-pollyjs--adapter-fetch

Fetch adapter for Polly.JS that enables seamless recording and replaying of HTTP interactions using the browser's native fetch API.

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

index.mddocs/

@pollyjs/adapter-fetch

The @pollyjs/adapter-fetch package provides a fetch adapter that enables seamless recording and replaying of HTTP interactions using the browser's native fetch API and Request/Response objects. It integrates with Polly.JS to intercept fetch calls, preserve request/response data, and replay interactions for deterministic testing.

Package Information

  • Package Name: @pollyjs/adapter-fetch
  • Package Type: npm
  • Language: JavaScript (with TypeScript definitions)
  • Installation: npm install @pollyjs/adapter-fetch -D

Core Imports

import FetchAdapter from '@pollyjs/adapter-fetch';

For CommonJS:

const FetchAdapter = require('@pollyjs/adapter-fetch');

Basic Usage

import { Polly } from '@pollyjs/core';
import FetchAdapter from '@pollyjs/adapter-fetch';

// Register the adapter with Polly
Polly.register(FetchAdapter);

// Create a Polly instance that uses the fetch adapter
const polly = new Polly('Recording Name', {
  adapters: ['fetch']
});

// The adapter will now intercept all fetch calls
const response = await fetch('/api/users');
const data = await response.json();

// Stop recording/replaying
await polly.stop();

Capabilities

FetchAdapter Class

The main adapter class that extends the base Polly.JS Adapter to provide fetch API interception and request/response handling.

/**
 * Fetch adapter that patches the global fetch function and Request constructor
 * to enable recording and replaying of HTTP interactions.
 */
export default class FetchAdapter extends Adapter {
  /**
   * Static identifier for the adapter
   * @returns {string} Always returns 'fetch'
   */
  static get id(): string;

  /**
   * Default configuration options for the adapter
   * @returns {object} Configuration object with context property
   */
  get defaultOptions(): {
    context?: any; // Global context object (default: global)
  };

  /**
   * Called when adapter connects to Polly instance.
   * Patches global fetch and Request constructor.
   */
  onConnect(): void;

  /**
   * Called when adapter disconnects from Polly instance.
   * Restores original global fetch and Request constructor.
   */
  onDisconnect(): void;

  /**
   * Called when a request is being processed.
   * Handles abort signal setup and event listeners.
   * @param {object} pollyRequest - The Polly request object
   */
  onRequest(pollyRequest: object): void;

  /**
   * Makes the actual fetch request and processes the response.
   * Handles binary data conversion and buffer compatibility.
   * @param {object} pollyRequest - The Polly request object  
   * @returns {Promise<object>} Response object with statusCode, headers, body, encoding
   */
  async onFetchResponse(pollyRequest: object): Promise<{
    statusCode: number;
    headers: object;
    body: string;
    encoding?: string;
  }>;

  /**
   * Called when responding to the original caller.
   * Handles response construction and error scenarios.
   * @param {object} pollyRequest - The Polly request object
   * @param {Error} error - Any error that occurred during processing
   */
  onRespond(pollyRequest: object, error?: Error): void;
}

Configuration Options

The adapter accepts configuration options through the Polly instance:

interface AdapterOptions {
  /**
   * Global context object containing fetch, Request, Response, Headers.
   * Defaults to the global object (window in browsers, global in Node.js).
   */
  context?: {
    fetch: Function;
    Request: new (url: string | Request, options?: RequestInit) => Request;
    Response: new (body?: any, options?: ResponseInit) => Response;
    Headers: new (headers?: HeadersInit) => Headers;
  };
}

Usage Example:

const polly = new Polly('Recording', {
  adapters: ['fetch'],
  adapterOptions: {
    fetch: {
      context: window // Explicitly specify the global context
    }
  }
});

Features

Request Interception

The adapter patches the global fetch function to intercept all fetch calls, supporting:

  • URL objects: Accepts both string URLs and URL instances
  • Request objects: Supports fetch calls with Request instances
  • All fetch options: Headers, method, body, credentials, etc.
  • Request cloning: Maintains fetch API clone semantics

Response Handling

Comprehensive response processing including:

  • Binary data support: Handles both text and binary response bodies
  • Encoding preservation: Uses base64 encoding for binary content
  • Header serialization: Converts Headers instances to plain objects
  • Status code mapping: Preserves HTTP status codes and status text
  • Empty response handling: Correctly handles 204 No Content responses

Abort Signal Support

Full AbortController/AbortSignal integration:

const controller = new AbortController();

// This request can be aborted
fetch('/api/data', { signal: controller.signal });

// Abort the request
controller.abort(); // Throws DOMException with name 'AbortError'

Cross-Environment Compatibility

The adapter works across different JavaScript environments:

  • Browser environments: Uses window.fetch and related globals
  • Node.js environments: Works with fetch polyfills or native Node.js fetch
  • Different contexts: Supports custom global contexts for testing scenarios

Error Handling

Robust error handling for various scenarios:

  • Missing globals: Validates fetch, Request, Response, Headers are available
  • Concurrent adapters: Prevents multiple fetch adapters from running simultaneously
  • Network errors: Properly propagates fetch network failures
  • Abort scenarios: Handles request cancellation with proper DOMException
  • Buffer compatibility: Manages ArrayBuffer differences across contexts

Internal Implementation Details

The adapter uses several internal symbols and patterns:

  • Symbol-based marking: Uses symbols to mark patched globals and prevent double-patching
  • Argument preservation: Stores original Request constructor arguments for proper cloning
  • Lazy execution: Operations are queued and executed when needed
  • Response URL fixing: Manually sets response.url since Response constructor doesn't allow it

Error Types

/**
 * Errors that may be thrown by the adapter
 */
interface AdapterErrors {
  /** Thrown when required globals (fetch, Request, etc.) are not found */
  AssertionError: Error;
  
  /** Thrown when request is aborted via AbortSignal */
  AbortError: DOMException;
  
  /** Network or other fetch-related errors */
  TypeError: Error;
}

Common Error Scenarios:

// Missing globals
// Error: "fetch global not found."

// Concurrent adapters  
// Error: "Running concurrent fetch adapters is unsupported, stop any running Polly instances."

// Aborted request
// DOMException: "The user aborted a request." (name: 'AbortError')

Advanced Usage Patterns

Custom Context

For testing or specialized environments:

const customGlobals = {
  fetch: mockFetch,
  Request: MockRequest,
  Response: MockResponse,
  Headers: MockHeaders
};

const polly = new Polly('Test', {
  adapters: ['fetch'],
  adapterOptions: {
    fetch: {
      context: customGlobals
    }
  }
});

Integration with Testing

Common pattern for test suites:

import { Polly } from '@pollyjs/core';
import FetchAdapter from '@pollyjs/adapter-fetch';

// Register once in test setup
Polly.register(FetchAdapter);

describe('API Tests', () => {
  let polly;

  beforeEach(() => {
    polly = new Polly('API Test', {
      adapters: ['fetch'],
      recordIfMissing: false // Use existing recordings
    });
  });

  afterEach(async () => {
    await polly.stop();
  });

  it('fetches user data', async () => {
    const response = await fetch('/api/users/1');
    const user = await response.json();
    
    expect(user.id).toBe(1);
  });
});

Install with Tessl CLI

npx tessl i tessl/npm-pollyjs--adapter-fetch

docs

index.md

tile.json