Middleware Architecture

Overview

The Middleware Architecture module is responsible for enabling a flexible and extensible mechanism to enhance and customize the behavior of core SWR hooks. It allows the composition of multiple middleware functions that can intercept, modify, or augment the data fetching lifecycle, cache management, and state updates in a declarative and reusable manner.

This architecture solves the problem of maintaining a clean and modular core while providing advanced features (such as devtools integration, data preloading, subscriptions, or optimistic updates) without cluttering the main hook logic. Middleware can be composed and layered, allowing developers to easily add, remove, or customize functionality in a predictable way.


Core Concepts

Middleware Composition

Middleware functions wrap around the core SWR hook or another middleware-enhanced hook, creating a pipeline through which hook invocations flow. Each middleware has access to the hook's arguments, internal state, and can inject additional hooks or side-effects.

The key idea is to treat middleware as higher-order functions that take a hook and return a new hook with enhanced capabilities.

Built-in Middlewares

The project defines a set of built-in middleware implementations bundled as presets, which provide essential features such as:

These built-in middleware are composed and exported together to be automatically applied or selectively included.


How Middleware Works

Middleware in this system is implemented as a function that takes an SWR hook (useSWR) and returns a new SWR hook wrapping the original. This wrapping hook internally manages the middleware chain by extending the use array in the hook's configuration.

Middleware Application via withMiddleware

The utility function withMiddleware is the primary mechanism to create a middleware-enhanced hook. It normalizes the hook arguments and appends the new middleware into the configuration's use array, effectively layering the middleware.

Simplified Illustration from withMiddleware.ts

export const withMiddleware = (
  useSWR: SWRHook,
  middleware: Middleware
): SWRHook => {
  return <Data = any, Error = any>(...args) => {
    const [key, fn, config] = normalize(args)
    const uses = (config.use || []).concat(middleware)
    return useSWR<Data, Error>(key, fn, { ...config, use: uses })
  }
}

This design allows middleware to be layered dynamically and composed in a declarative manner.


Interaction with Other Modules

Middleware interacts closely with the core SWR hook implementation and other utility modules:

The middleware chain is respected and executed in sequence, with each middleware able to operate on the SWR hook's arguments and state.


Built-in Middleware Preset

The file middleware-preset.ts exports a predefined array of middleware combining devtools and preloading capabilities:

import { use as devtoolsUse } from './devtools'
import { middleware as preload } from './preload'

export const BUILT_IN_MIDDLEWARE = devtoolsUse.concat(preload)

This preset can be applied to SWR hooks to automatically include these commonly used enhancements.


Design Patterns and Unique Approaches


Mermaid Diagram: Middleware Composition Flow

sequenceDiagram
  participant C as Component
  participant MH as Middleware-Enhanced Hook
  participant MW as Middleware Chain
  participant SWR as Core useSWR Hook

  C->>MH: Calls hook with key, fetcher, config
  MH->>MW: Adds middleware to config.use array
  MW->>SWR: Invokes core useSWR with updated config
  SWR-->>MW: Returns SWR response (data, error, mutate, etc.)
  MW-->>MH: Middleware chain processes response
  MH-->>C: Returns final enhanced SWR response

This sequence illustrates how the component calls a hook wrapped by middleware, which composes middleware functions and ultimately invokes the core useSWR hook. The middleware chain can intercept, modify, or extend the response before it reaches the component.


Summary

The Middleware Architecture in this project provides a clean, extensible, and composable pattern to enhance SWR hooks. Through higher-order wrapping and declarative composition, middleware enables modular addition of features like devtools integration, data preloading, and custom behaviors without complicating the core logic. The utility withMiddleware facilitates easy middleware application by normalizing arguments and managing middleware arrays, while built-in middleware presets bundle common enhancements for streamlined use. This design supports flexible feature expansion and maintains separation of concerns across the data fetching lifecycle.