🚀
Supabase + SWR / TanStack Queryをもっと快適に!Supabase Cache Helpersのすすめ
TL;DR
SupabaseとSWR / TanStack Queryを組み合わせて使うなら、Supabase Cache Helpersの導入がおすすめ!
- キャッシュキーが自動生成される
- ミューテーション後のキャッシュ更新が自動
- SWR / TanStack Queryどちらでも使える
本記事では、このライブラリを使うことで得られるメリットや、導入〜実装の具体例までをわかりやすく紹介します。
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だけを使用した場合
初期化
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公式ブログ
- Supabase GitHub Discussion #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を使えば、それらを自動化でき、実装が大幅に簡潔になる
Discussion