Skip to main content
Version: 6.6


Managers are singletons that orchestrate the complex asynchronous behavior of rest-hooks. Several managers are provided by rest-hooks and used by default; however there is nothing stopping other compatible managers to be built that expand the functionality. We encourage PRs or complimentary libraries!

While managers often have complex internal state and methods - the exposed interface is quite simple. Because of this, it is encouraged to keep any supporting state or methods marked at protected by typescript. Managers have three exposed pieces - the constructor to build initial state and take any parameters; a simple cleanup() method to tear down any dangling pieces like setIntervals() or unresolved Promises; and finally getMiddleware() - providing the mechanism to hook into the flux data flow.

type Dispatch<R extends React.Reducer<any, any>> = (action: React.ReducerAction<R>) => Promise<void>;

type Middleware = <R extends React.Reducer<any, A>, A extends Actions>({
}: MiddlewareAPI<R>) => (
next: Dispatch<R>,
) => Dispatch<R>;

interface Manager {
getMiddleware<T extends Manager>(this: T): Middleware;
cleanup(): void;
init?: (state: State<any>) => void;


getMiddleware() returns a function that very similar to a redux middleware. The only differences is that the next() function returns a Promise. This promise resolves when the reducer update is committed when using <CacheProvider />. This is necessary since the commit phase is asynchronously scheduled. This enables building managers that perform work after the DOM is updated and also with the newly computed state.

Since redux is fully synchronous, an adapter must be placed in front of Rest Hooks style middleware to ensure they can consume a promise. Conversely, redux middleware must be changed to pass through promises.

Middlewares will intercept actions that are dispatched and then potentially dispatch their own actions as well. To read more about middlewares, see the redux documentation.


Provides any cleanup of dangling resources after manager is no longer in use.


Called with initial state after provider is mounted. Can be useful to run setup at start that relies on state actually existing.

Provided managers:

Control flow

Managers live in the CacheProvider centralized store. They orchestrate complex control flows by interfacing via intercepting and dispatching actions, as well as reading the internal state.

Manager flux flow

Middleware logging

this.middleware = ({ getState }) => (next) => async (action) => {
console.log('before', action, getState());
await next(action);
console.log('after', action, getState())

Middleware data stream

import type { Manager, Middleware } from '@rest-hooks/core';
import type { EndpointInterface } from '@rest-hooks/endpoint';

export default class StreamManager implements Manager
protected declare middleware: Middleware;
protected declare websocket: Websocket;
protected declare endpoints: Record<string, EndpointInterface>;

constructor(url: string, endpoints: Record<string, EndpointInterface>) {
this.websocket = new Websocket(url);
this.endpoints = endpoints;

this.middleware = ({ controller, getState }) => {
this.websocket.onmessage = (event) => {
controller.receive(this.endpoints[event.type], ...event.args,;
return (next) => async (action) => next(action);

cleanup() {

getMiddleware<T extends StreamManager>(this: T) {
return this.middleware;

Controller.receive() updates the Rest Hooks store with