Skip to content

Introduction

swr-openapi is a type-safe wrapper around swr.

It works by using openapi-fetch and openapi-typescript so you get all the following features:

  • ✅ No typos in URLs or params.
  • ✅ All parameters, request bodies, and responses are type-checked and 100% match your schema
  • ✅ No manual typing of your API
  • ✅ Eliminates any types that hide bugs
  • ✅ Also eliminates as type overrides that can also hide bugs
tsx
import createClient from "openapi-fetch";
import { createQueryHook } from "swr-openapi";
import type { paths } from "./my-openapi-3-schema"; // generated by openapi-typescript

const client = createClient<paths>({
  baseUrl: "https://myapi.dev/v1/",
});

const useQuery = createQueryHook(client, "my-api");

function MyComponent() {
  const { data, error, isLoading, isValidating, mutate } = useQuery(
    "/blogposts/{post_id}",
    {
      params: {
        path: { post_id: "123" },
      },
    },
  );

  if (isLoading || !data) return "Loading...";

  if (error) return `An error occured: ${error.message}`;

  return <div>{data.title}</div>;
}

Setup

Install this library along with openapi-fetch and openapi-typescript:

bash
npm i swr-openapi openapi-fetch
npm i -D openapi-typescript typescript

Highly recommended

Enable noUncheckedIndexedAccess in your tsconfig.json (docs)

Next, generate TypeScript types from your OpenAPI schema using openapi-typescript:

bash
npx openapi-typescript ./path/to/api/v1.yaml -o ./src/lib/api/v1.d.ts

Basic usage

Once types have been generated from your schema, you can create a fetch client and export wrapped swr hooks.

Wrapper hooks are provided 1:1 for each hook exported by SWR. Check out the other sections of this documentation to learn more about each one.

ts
import createClient from "openapi-fetch";
import {
  createQueryHook,
  createImmutableHook,
  createInfiniteHook,
  createMutateHook,
} from "swr-openapi";
import { isMatch } from "lodash-es";
import type { paths } from "./my-openapi-3-schema"; // generated by openapi-typescript

const client = createClient<paths>({
  baseUrl: "https://myapi.dev/v1/",
});
const prefix = "my-api";

export const useQuery = createQueryHook(client, prefix);
export const useImmutable = createImmutableHook(client, prefix);
export const useInfinite = createInfiniteHook(client, prefix);
export const useMutate = createMutateHook(
  client,
  prefix,
  isMatch, // Or any comparision function
);

TIP

You can find more information about createClient on the openapi-fetch documentation.

Then, import these hooks in your components:

tsx
import { useQuery } from "./my-api";

function MyComponent() {
  const { data, error, isLoading, isValidating, mutate } = useQuery(
    "/blogposts/{post_id}",
    {
      params: {
        path: { post_id: "123" },
      },
    },
  );

  if (isLoading || !data) return "Loading...";

  if (error) return `An error occured: ${error.message}`;

  return <div>{data.title}</div>;
}