Generate swr
hooks using OpenAPI schemas
npm install swr-openapi swr openapi-fetch
npm install --save-dev openapi-typescript typescript
Follow openapi-typescript directions to generate TypeScript definitions for each service being used.
Here is an example of types being generated for a service via the command line:
npx openapi-typescript "https://sandwiches.example/openapi/json" --output ./types/sandwich-schema.ts
Then, create an openapi-fetch client and initialize swr hooks for the API:
// sandwich-api.ts
import { createClient } from "openapi-fetch";
import { createHooks } from "swr-openapi";
import type * as SandwichesSchema from "./types/sandwich-schema";
export const sandwichesApi = createClient<SandwichesSchema.paths>({
baseUrl: "https://sandwiches.example",
});
export const {
use: useSandwiches,
useInfinite: useSandwichesInfinite,
} = createHooks(sandwichesApi, "sandwich-api");
import { useSandwiches } from "./sandwich-api";
export function MySandwiches() {
// Fetch a single sandwich (uses useSWR)
const { data: sandwiches } = useSandwiches("/sandwiches/{sandwichId}", {
params: {
path: {
sandwichId: "sandwich-123",
},
},
});
// Fetch multiple pages of sandwiches (uses useSWRInfinite)
const {
data: pages,
size,
setSize,
} = useSandwichesInfinite("/sandwiches", (index, previous) => {
if (!previous.hasMore) {
return null;
}
return {
params: {
query: {
limit: 25,
offset: 25 * index,
},
},
};
});
}
- Parameters:
api
: An openapi-fetch client.keyPrefix
: A string to differentiate this API from others. This helps avoid swr cache collisions when using multiple APIs that may have identical paths.
- Returns:
use
: A wrapper overuseSWR
bound to the given api.useInfinite
: A wrapper overuseSWRInfinite
bound to the given api.
Depending on your project preferences, there are different ways to export the return value of createHooks
. Here are two examples:
Option 1: Destructure the return value and rename the destructured properties
// sandwich-api.ts
export const {
use: useSandwiches,
useInfinite: useSandwichesInfinite
} = createHooks(sandwichesApi, "sandwich-api");
// some-component.tsx
import { useSandwiches } from "./sandwich-api";
export function SomeComponent() {
const { data, error, isLoading } = useSandwiches("/sandwiches/{sandwichId}", {
params: {
path: {
sandwichId: "sandwich-123",
},
},
});
}
Option 2: Don't destructure the return value
// sandwich-api.ts
export const sandwiches = createHooks(sandwichesApi, "sandwich-api");
// some-component.tsx
import { sandwiches } from "./sandwich-api";
export function SomeComponent() {
const { data, error, isLoading } = sandwiches.use(
"/sandwiches/{sandwichId}",
{
params: {
path: {
sandwichId: "sandwich-123",
},
},
},
);
}
function use(
path: Path,
options: Options | null,
swrConfig?: Config,
): SWRResponse;
- Parameters:
path
: The GET endpoint to request.options
: Either- An openapi-fetch
FetchOptions
object for the given path. null
to support conditional fetching.
- An openapi-fetch
swrConfig
(optional): Configuration options foruseSWR
.
- Returns:
const { data, error, isLoading, mutate, revalidate } = use(
"/sandwiches/{sandwichId}",
{
params: {
path: {
sandwichId: "sandwich-123",
},
},
},
);
function useInfinite(
path: Path,
getOptionsFn: SWRInfiniteKeyLoader<Data, Options | null>,
swrConfig?: Config,
): SWRInfiniteResponse;
- Parameters:
path
: The GET endpoint to request.getOptionsFn
: An swrgetKey
function that accepts the index and the previous page data, returning either an openapi-fetchFetchOptions
object for loading the next page ornull
, once there are no more pages.swrConfig
(optional): Configuration options foruseSWRInfinite
.
- Returns:
const {
data: sandwichPages,
error,
isLoading,
isValidating,
mutate,
size,
setSize,
} = useInfinite("/sandwiches", (index, previousPage) => {
if (!previousPage.hasMore) {
return null;
}
return {
params: {
query: {
limit: 25,
offset: 25 * index,
},
},
};
});