Published on

SWR

Authors
  • avatar
    Name
    Shelton Ma
    Twitter

1. Install

pnpm add swr

2. Fetcher

// src/lib/fetch.ts

export default async function fetcher(...args: Parameters<typeof fetch>) {
  const res = await fetch(...args);
  return res.json();
}

3. Hooks

1. Fetch Data

// src/features/documents/hooks/use-get-document.ts
import fetcher from "@/lib/fetch";
import useSWR from "swr";

export const useGetDocuments = () => {
  const { data, error, isLoading } = useSWR("/api/document", fetcher);
  return { data, error, isLoading };
};

2. Mutate

// src/features/documents/hooks/use-create-document.ts
import { documents } from "@/db/schema";
import { InferInsertModel, InferSelectModel } from "drizzle-orm";
import { useState } from "react";
import { mutate } from "swr";

type CreateDocumentData = InferInsertModel<typeof documents>;

type DocumentData = InferSelectModel<typeof documents>;

type Options = {
  onSuccess?: (data: DocumentData) => void;
  onError?: (error: Error) => void;
  onSettled?: () => void;
};

export const useCreateDocument = () => {
  const [status, setStatus] = useState<
    "success" | "error" | "settled" | "pending" | null
  >(null);

  const createDocument = async (
    data: CreateDocumentData,
    options?: Options
  ) => {
    try {
      setStatus("pending");
      const response = await fetch("/api/documents", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify(data),
      });

      if (!response.ok) {
        const errorData = await response.json();
        throw new Error(errorData.message || "Failed to create document");
      }

      const result = await response.json();
      mutate("/api/documents");

      options?.onSuccess?.(result?.document);
      setStatus("success");
      return result?.document;
    } catch (error) {
      setStatus("error");
      options?.onError?.(
        error instanceof Error ? error : new Error("An error occurred")
      );
    } finally {
      setStatus("settled");
      options?.onSettled?.();
    }
  };
  return { createDocument, status };
};

3. Add types to hooks

import fetcher from "@/lib/fetch";
import { DocumentData } from "../types";
import useSWR from "swr";

export const useGetDocuments = () => {
  const { data, error, isLoading } = useSWR<{ documents: DocumentData[] }>("/api/documents", fetcher);

  return {
    data: data.documents,
    isLoading: isLoading,
    error
  };
};

4. Pagination

import fetcher from "@/lib/fetch";
import useSWR from "swr";
import { DocumentData } from "../types";

export const useGetDocuments = ({
  page = 1,
  pageSize = 10,
  sort = "updatedAt",
  sortOrder = "desc",
}: {
  page?: number;
  pageSize?: number;
  sort?: string;
  sortOrder?: string;
}) => {
  const { data, error, isLoading } = useSWR<{
    documents: DocumentData[];
    totalPages: number;
    totalDocuments: number;
    currentPage: number;
    pageSize: number;
  }>(
    `/api/documents?page=${page}&pageSize=${pageSize}&sort=${sort}&sortOrder=${sortOrder}`,
    fetcher
  );
  return { data, error, isLoading };
};

Revalidate

mutate("/api/documents");

mutate(
  (key: string) =>
    typeof key === "string" && key.startsWith("/api/documents")
);