🚀

Supabase + SWR / TanStack Queryをもっと快適に!Supabase Cache Helpersのすすめ

に公開

TL;DR

SupabaseとSWR / TanStack Queryを組み合わせて使うなら、Supabase Cache Helpersの導入がおすすめ!

  • キャッシュキーが自動生成される
  • ミューテーション後のキャッシュ更新が自動
  • SWR / TanStack Queryどちらでも使える

本記事では、このライブラリを使うことで得られるメリットや、導入〜実装の具体例までをわかりやすく紹介します。

https://supabase-cache-helpers.vercel.app/

Supabase Cache Helpersを使用した場合のデータ取得の実装例(SWR)
import { useQuery } from "@supabase-cache-helpers/postgrest-swr";
import { supabase } from "../lib/supabaseClient";

export default function PostList() {
  const {
    data: posts,
    error,
    isLoading,
  } = useQuery(supabase.from("posts").select());

  if (isLoading) return <div>Loading posts...</div>;
  if (error) {
    return <div>Error:{error.message}</div>;
  }
  if (!posts || posts.length === 0) return <div>No posts found.</div>;

  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
}

Supabase JavaScript Client Libraryだけを使用した場合

https://github.com/supabase/supabase-js

初期化

import { createClient } from "@supabase/supabase-js";
import { Database } from "./database.types";

const supabase = createClient<Database>(
  process.env.SUPABASE_URL,
  process.env.SUPABASE_ANON_KEY,
);

データの取得

const { data, error } = await supabase.from("posts").select();

データの挿入

const { data, error } = await supabase
  .from("posts")
  .insert({ title: "New Post" })
  .select();

クライアント側でのデータ取得の課題

以下のようにuseEffectを使う方法は一般的ですが、React公式、Next.js公式いずれも推奨していません[1][2]

useEffect(() => {
  const fetch = async () => {
    const { data } = await supabase.from("posts").select();
    setPosts(data || []);
  };
  fetch();
}, []);

SWR / TanStack Queryとは

以下のコードは、APIからpostsデータを取得し、

  • data: 取得したデータ
  • error: エラーが発生した場合の内容
  • isLoading: ローディング中かどうか

をそれぞれ管理できる、SWR / TanStack Queryの基本パターンです。

// SWR
const { data, error, isLoading } = useSWR("/api/posts", fetcher);

// TanStack Query
const { data, error, isLoading } = useQuery({
  queryKey: ["posts"],
  queryFn: getPosts,
});

Supabase JavaScript Client Library + SWR / TanStack Query

SupabaseクライアントとSWR / TanStack Queryを組み合わせると、以下のようになります。

// SWR
useSWR("posts", async () => {
  const { data, error } = await supabase.from("posts").select();
  if (error) throw error;
  return data;
});

// TanStack Query
useQuery({
  queryKey: ["posts"],
  queryFn: async () => {
    const { data, error } = await supabase.from("posts").select();
    if (error) throw error;
    return data;
  },
});

ただし、キャッシュキーの管理や、ミューテーション後のキャッシュ更新は自分で行う必要があります。

ミューテーション後のキャッシュ更新の実装例(TanStack Query)
const mutation = useMutation({
  mutationFn: insertPost,
  onSuccess: () => {
    queryClient.invalidateQueries(["posts"]);
  },
});

mutation.mutate({ title: "New Post" });

Supabase Cache Helpersの紹介

Supabase Cache Helpersは、SupabaseクライアントとSWR / TanStack Queryとの統合をより便利にするためのライブラリです。

以下のような特徴があります。

  • キャッシュキーを自動生成(設計不要)
  • ミューテーション時のキャッシュ更新も自動
  • SWR / TanStack Queryどちらでも使える

また、Supabase公式でも紹介されています。

  • Supabase公式ブログ

https://supabase.com/blog/react-query-nextjs-app-router-cache-helpers

  • Supabase GitHub Discussion #3145

https://github.com/orgs/supabase/discussions/3145

実装例(データ取得)

// SWR
import { useQuery } from "@supabase-cache-helpers/postgrest-swr";

const { data, error, isLoading } = useQuery(supabase.from("posts").select());

// TanStack Query
import { useQuery } from "@supabase-cache-helpers/postgrest-react-query";

const { data, error, isLoading } = useQuery(supabase.from("posts").select());

実装例(データ挿入)

// SWR
import { useInsertMutation } from "@supabase-cache-helpers/postgrest-swr";

const { trigger: insert } = useInsertMutation(
  supabase.from("posts"),
  ["id"], // Primary key
  "id", // Column to return
  { onSuccess: () => console.log("Success!") },
);

// TanStack Query
import { useInsertMutation } from "@supabase-cache-helpers/postgrest-react-query";

const { mutateAsync: insert } = useInsertMutation(
  supabase.from("posts"),
  ["id"], // Primary key
  "id", // Column to return
  { onSuccess: () => console.log("Success!") },
);

データ取得の実装比較(SWR)

実装コードの行数も約半分に短縮でき、シンプルになります。

Supabase JavaScript Client LibraryとuseEffectを使用した場合の実装例
import { useEffect, useState } from "react";
import { supabase } from "../lib/supabaseClient";
import { Post } from "../types";

export default function PostList() {
  const [posts, setPosts] = useState<Post[]>([]);
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    const fetchPosts = async () => {
      try {
        setIsLoading(true);
        setError(null);

        const { data, error } = await supabase.from("posts").select();

        if (error) {
          throw error;
        }

        setPosts(data || []);
      } catch (error) {
        const errorMessage =
          error instanceof Error ? error.message : "Unknown error occurred";
        setError(errorMessage);
        setPosts([]);
      } finally {
        setIsLoading(false);
      }
    };

    fetchPosts();
  }, []);

  if (isLoading) return <div>Loading...</div>;
  if (error) {
    return <div>Error:{error.message}</div>;
  }
  if (posts.length === 0) return <div>No posts found.</div>;

  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
}
Supabase Cache Helpersを使用した場合のデータ取得の実装例
import { useQuery } from "@supabase-cache-helpers/postgrest-swr";
import { supabase } from "../lib/supabaseClient";

export default function PostList() {
  const {
    data: posts,
    error,
    isLoading,
  } = useQuery(supabase.from("posts").select());

  if (isLoading) return <div>Loading posts...</div>;
  if (error) {
    return <div>Error:{error.message}</div>;
  }
  if (!posts || posts.length === 0) return <div>No posts found.</div>;

  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
}

まとめ

  • Supabase + SWR / TanStack Queryを組み合わせて便利に
  • ただしキャッシュキーやinvalidateは手動対応が必要
  • Supabase Cache Helpersを使えば、それらを自動化でき、実装が大幅に簡潔になる
脚注
  1. React公式: SWR / React Query / React Router 6.4+の利用を推奨 ↩︎

  2. Next.js公式: SWRやReact Queryなどのコミュニティ製ライブラリを使うことを推奨 ↩︎

Discussion