d2l-fetch: Fetch Middleware Wrapper

raw JSON →
2.7.0 verified Thu Apr 23 auth: no javascript

d2l-fetch is a JavaScript library that provides a middleware layer for the standard `window.fetch` API. It enables developers to intercept and modify requests and responses before they reach the network or are returned to the caller. This allows for centralized handling of concerns like authentication, caching, logging, and error handling. The current stable version is 2.7.0. While the release cadence is not explicitly stated, the package appears to be actively maintained, with regular updates. Its key differentiator lies in its straightforward, chainable middleware pattern, offering a flexible way to extend `fetch` behavior without deep polyfilling or direct modification of `window.fetch` itself, making it suitable for environments where `fetch` is natively available.

error ReferenceError: d2lfetch is not defined
cause `d2lfetch` was not correctly imported or the global script failed to load before being accessed.
fix
If using ES modules, ensure import { d2lfetch } from 'd2l-fetch/src/index.js'; is at the top of your file. If using a global script, verify the <script> tag's src path is correct and that it has loaded before d2lfetch is accessed.
error TypeError: Cannot read properties of undefined (reading 'headers') (or similar for Request/Response properties)
cause A middleware function either did not receive the `request` object as its first argument, or a subsequent middleware returned something other than a `Request` or `Response` object to the next function.
fix
Ensure middleware functions are defined as (request, next) => {...} and that next(request) is called. If you modify the request/response, ensure you return a valid Request or Response object or a Promise resolving to one.
error Unhandled Promise Rejection: TypeError: Failed to fetch (or network error)
cause Often indicates an issue in a middleware preventing the `window.fetch` call from being executed, or a misconfigured request, or a network issue.
fix
Review middleware functions for early exits that might be unintentionally preventing window.fetch from running. Check the request object's URL, headers, and method within a middleware to ensure it's well-formed before next(request) is called.
gotcha When using middleware, ensure each middleware function explicitly returns the result of `next(request)` or a Promise representing an early exit. Failing to return `next(request)` will cause the middleware chain to break and the `window.fetch` call to not be executed, often leading to unresolved promises or unexpected behavior in subsequent middleware.
fix Always include `return next(request);` at the end of middleware functions that should continue the chain, or `return Promise.resolve(myResponse);` for early exits.
gotcha Mixing global script imports (`<script type="module" src="...">`) with ES module imports (`import { d2lfetch } from '...'`) can lead to two separate instances of `d2lfetch` being created in the application, resulting in middleware being registered with one instance but `fetch` being called on another, or global scope pollution conflicts.
fix Choose one import method (either global script or ES module import) and stick to it consistently throughout your application to ensure a single, shared `d2lfetch` instance.
gotcha Early exiting from the middleware chain by returning a Promise (e.g., `Promise.resolve(new Response())`) will prevent all subsequent middleware and the underlying `window.fetch` call from executing. While useful for caching or mock responses, ensure this is the intended behavior.
fix Carefully design your middleware order. Place early-exit middleware (like caching) strategically so that they only intercept requests where an early exit is appropriate and desired.
npm install d2l-fetch
yarn add d2l-fetch
pnpm add d2l-fetch

This quickstart demonstrates how to register two middleware functions (authentication and logging) with `d2lfetch` and then use `d2lfetch.fetch` to make an API request, showing how the middleware intercepts and modifies the request/response flow.

import { d2lfetch } from 'd2l-fetch/src/index.js';

const authMiddleware = (request, next) => {
  console.log('Auth Middleware: Adding Authorization header...');
  request.headers.set('Authorization', `Bearer ${process.env.AUTH_TOKEN ?? 'your-jwt-here'}`);
  return next(request);
};

const loggingMiddleware = async (request, next) => {
  console.log(`Logging Middleware: Request to ${request.url}`);
  try {
    const response = await next(request);
    console.log(`Logging Middleware: Received response for ${request.url} with status ${response.status}`);
    return response;
  } catch (error) {
    console.error(`Logging Middleware: Request to ${request.url} failed:`, error);
    throw error;
  }
};

d2lfetch.use({ name: 'authMiddleware', fn: authMiddleware });
d2lfetch.use({ name: 'loggingMiddleware', fn: loggingMiddleware });

async function fetchData() {
  try {
    const request = new Request('https://api.example.com/data', {
      method: 'GET',
      headers: { 'Content-Type': 'application/json' }
    });
    const response = await d2lfetch.fetch(request);
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    const data = await response.json();
    console.log('Fetched data:', data);
    return data;
  } catch (error) {
    console.error('Failed to fetch data:', error);
    throw error;
  }
}

// Example usage (replace with actual API endpoint and token)
fetchData();