Skip to main content

Middleware

When we make requests a lot of the time there is some consistent logic between all of our endpoints such as authentication or error handling. This is where middleware come in!

Defining & Adding Middleware

Middleware are objects whose keys are event names and values are listeners. Listeners may be synchronous or asynchronous.

/auth-middleware.ts
import { RequestMiddleware } from "api-def";

const getAuthToken = (): Promise<string> => {
return /*... */;
};

/*
Add auth header to requests
*/
const AuthMiddleware = (): RequestMiddleware => {
return {
// called before a send occurs
beforeSend: async (context) => {
const authToken = await getAuthToken();

if (authToken) {
context.updateHeaders({
Authorization: `Bearer ${authToken}`,
});
}
},
};
};

export default AuthMiddleware;

Now that we have our fancy new middleware we can add it to our api:

/api.ts
import { Api } from "api-def";

const API = new Api({
name: "My Backend",
baseUrl: "http://localhost:5000/v1",
middleware: [AuthMiddleware()],
});

export default API;

Events

NameUsage
beforeSendCalled before send.
successWhen the request was a success.
errorThe request failed. May be called multiple times due to retries
unrecoverableErrorThe request failed after retries are exhausted or shouldRetry returns false.

Event Results

Your event listeners can also return special objects that will trigger certain things

Respond

Applicable in the beforeSend event.

Your middleware can respond instead of going to the network, as used by the built-in client cache middleware.

return {
type: "respond",
response: {
status: 200,
data: /* ... */,
headers: {}
}
};

Retry

Applicable in the error event.

Trigger a retry despite the retry option. Be careful of causing infinite loops with this!

return {
type: "retry"
};

Endpoint middleware

Pass middleware to an individual endpoint when it should run after API-level middleware:

const endpoint = API.endpoint().build({
id: "fetch_data",
method: "get",
path: "/data",
middleware: [AuthMiddleware()],
});

Request mutation

Middleware can update the request before the backend runs. These changes affect only the current request:

const rewriteRequest: RequestMiddleware = {
beforeSend: (context) => {
context.updateBaseUrl("https://backup.example.com");
context.updatePath("/health");
context.updateMethod("get");
context.updateBody({ source: "fallback" });
},
};

Use context.requestConfig to inspect the merged API, endpoint, and request configuration. context.state is not a public property; middleware-only state is available at context.requestConfig.state.