Middleware
Middleware lets you execute logic before and after SWRV composables. If there are multiple middleware, each one wraps the next middleware, and the last middleware wraps the original SWRV composable.
Usage
Register middleware either per composable call or through SWRVConfig:
const logger = (useSWRVNext) => (key, fetcher, config) => {
console.log("swrv key", key);
return useSWRVNext(key, fetcher, config);
};<script setup lang="ts">
import useSWRV, { SWRVConfig } from "swrv";
const logger = (useSWRVNext) => (key, fetcher, config) => {
console.log("swrv key", key);
return useSWRVNext(key, fetcher, config);
};
useSWRV("/api/user", fetcher, { use: [logger] });
</script>
<template>
<SWRVConfig :value="{ use: [logger] }">
<App />
</SWRVConfig>
</template>API
A middleware receives the next SWRV composable and returns a wrapped version of it:
const middleware = (useSWRVNext) => (key, fetcher, config) => {
// Before composable runs...
return useSWRVNext(key, fetcher, config);
// After composable runs...
};Unlike React, Vue does not have the same lint rule around function naming for this pattern. The important part is that middleware stays a plain function and composables are still called inside setup scope.
See TypeScript for the exported middleware helper types.
The wrapped call still receives the original public key shape. That matters for:
- tuple keys
- object keys
- function keys
useSWRVInfiniteuseSWRVMutationuseSWRVSubscription
See Arguments, Pagination, Mutation, and Subscription for those API surfaces.
Middleware can inspect or replace the fetcher, inject options, or wrap the returned response.
Extend
Middleware extends exactly like regular config options. For example:
<script setup lang="ts">
import { SWRVConfig } from "swrv";
const a = (useSWRVNext) => (key, fetcher, config) => useSWRVNext(key, fetcher, config);
const b = (useSWRVNext) => (key, fetcher, config) => useSWRVNext(key, fetcher, config);
const c = (useSWRVNext) => (key, fetcher, config) => useSWRVNext(key, fetcher, config);
</script>
<template>
<SWRVConfig :value="{ use: [a] }">
<SWRVConfig :value="{ use: [b] }">
<Child />
</SWRVConfig>
</SWRVConfig>
</template>useSWRV(key, fetcher, { use: [c] });
// equivalent composition order: [a, b, c]Middleware can also replace the fetcher or inject config:
const withTiming = (useSWRVNext) => (key, fetcher, config) => {
const timedFetcher =
fetcher &&
(async (...args: Parameters<typeof fetcher>) => {
const startedAt = performance.now();
try {
return await fetcher(...args);
} finally {
console.log("request time", performance.now() - startedAt);
}
});
return useSWRVNext(key, timedFetcher ?? fetcher, config);
};Multiple middleware
Each middleware wraps the next middleware. For example:
useSWRV(key, fetcher, { use: [a, b, c] });Execution order is:
enter a
enter b
enter c
useSWRV()
exit c
exit b
exit aThat matches SWR's composition model. Middleware order also follows config boundaries:
- parent
SWRVConfigmiddleware - nested
SWRVConfigmiddleware - per-call middleware
This makes it easy to keep broad behavior at the app boundary and local behavior at the composable call.
Examples
Request logger
const logger = (useSWRVNext) => (key, fetcher, config) => {
const loggedFetcher =
fetcher &&
(async (...args: Parameters<typeof fetcher>) => {
console.log("SWRV request:", key);
return fetcher(...args);
});
return useSWRVNext(key, loggedFetcher ?? fetcher, config);
};Every time the request is fired, it prints the SWRV key to the console:
SWRV request: /api/user1
SWRV request: /api/user2Keep previous result
SWR’s middleware docs show a custom "laggy" middleware that keeps previous data during key changes. In SWRV, the preferred solution is the built-in keepPreviousData option:
useSWRV(key, fetcher, {
keepPreviousData: true,
});Serialize object keys
SWR’s middleware docs also show a custom key-serialization middleware. In SWRV, object and tuple keys are already serialized internally, so you usually do not need custom middleware for that.
See Arguments for the built-in key behavior.
Set shared defaults
const noFocusRevalidate = (useSWRVNext) => (key, fetcher, config) => {
return useSWRVNext(key, fetcher, {
...config,
revalidateOnFocus: false,
});
};SWR’s middleware examples sometimes demonstrate features that SWRV already supports directly:
- use
keepPreviousDatainstead of custom “laggy” middleware (details) - rely on built-in key serialization for object keys (details)
- use
swrv/immutableor the namedimmutablemiddleware for immutable resources (details)
That keeps middleware focused on cross-cutting concerns such as logging, auth, analytics, or debugging.
