こんにちは。データアナリティクス事業本部 サービスソリューション部の北川です。
Supabaseとは、firebaseの大替サービスとも言われるBaasです。データベースにRDBを採用しているのが特徴です。
Next.js v13を学んでいる時に以下の記事を見つけました。Next.jsプロジェクトから、Supabaseのデータ取得を行っており、わかりやすかったです。
Next.js v13のインプットにも、Supabaseの入門にも役に立つチュートリアルだと思います。
今回は、こちらの記事を元にNext.js v13の機能を、v12と比較したりしながら試してみました。
Supabase側の設定
Supabaseにログインし、新しいプロジェクトを作成します。
左サイドバーから[SQL Editor]に移動し、以下のコードをエディタに貼り付けます。右下の[RUN]ボタンから、クエリを実行します。
create table if not exists posts (
id uuid default uuid_generate_v4() primary key,
created_at timestamp with time zone default timezone('utc'::text, now()) not null,
title text,
content text
);
insert into posts(title, content)
values
('My first post', 'Wow! What a great post.'),
('My second post', 'This one needs a little work!');
[Table Editor]から、新規にデータが追加されているのが確認できます。
Next.js側の設定
Next.jsのプロジェクトを作成します。
npx create-next-app@latest --experimental-app supabase_next13 —ts
ちなみに、Next.jsのアプリを立ち上げた時の初期デザインが変わっていました。
yarn dev
Supabaseクライアントの作成
Supabaseをインストールします。
yarn add @supabase/supabase-js
.env.localを作成し、環境変数を追加します。 キーとURLはSupabaseの[Project Settings]→「API」から確認することができます。
NEXT_PUBLIC_SUPABASE_URL=<Project URL>
NEXT_PUBLIC_SUPABASE_ANON_KEY=<Project key>
/utils/supabase.tsを作成し、Supabaseクライアントを初期化します。
utils/supabase.ts
import { createClient } from "@supabase/supabase-js";
export default createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
);
React Server Components
Next.js v13では、appディレクトリ配下がルートになります。また、ファイルをページとして機能させるには、page.tsxを作成します。
また、getServerSideProps
、getStaticProps
は廃止され、appフォルダ配下のコンポーネントをデフォルトでReact Server Componentsとして扱います。そのため、コンポーネント内でasync、awaitの使用が可能になります。
app/static/page.tsxを作成し、以下を記述します。
app/static/page.tsx
import Link from "next/link";
import supabase from "../../utils/supabase";
export default async function Posts() {
const { data: posts } = await supabase.from("posts").select("id, title");
if (!posts) {
return <p>No posts found.</p>;
}
return posts.map((post) => (
<p key={post.id}>
<Link href={`/static/${post.id}`}>{post.title}</Link>
</p>
));
}
generateStaticParams
次に、記事の中身を見る個別ページを実装します。
ダイナミックルーティングにおけるパスのリストの生成は、generateStaticParams
を使用します。以前は、getStaticPath/getStaticPropsを使用していましたね。返り値も変わっています。
app/static/[id]/page.tsxを作成し、以下を記述します。
import supabase from "../../../utils/supabase";
import { notFound } from "next/navigation";
export async function generateStaticParams() {
const { data: posts } = await supabase.from("posts").select("id");
return posts?.map(({ id }) => ({
id,
}));
}
export default async function Post({
params: { id },
}: {
params: { id: string };
}) {
const { data: post } = await supabase
.from("posts")
.select()
.match({ id })
.single();
if (!post) {
notFound();
}
return <pre>{JSON.stringify(post, null, 2)}</pre>;
}
ブログを実装することができました。v12と比べて、記述がシンプルになったと思います。
- page.tsx
-
[id]/page.tsx
revalidateの設定
現状は、Supabaseに新規で作成した記事が反映されないので、再検証できるようにします。
Next.js v12までは、ISRはgetStaticPropsにrevalidate
を指定して実装していました。
- v12以前の記述
export async function getStaticProps() { const res = await fetch('https://.../posts') const posts = await res.json() return { props: { posts, }, revalidate: 10, // 再検証する } }
Next.js v13では、appディレクトリ配下のpage.tsx、layout.tsxページでrevalidate変数を定義し、exportするだけで機能します。
page.tsx、[id]/page.tsxをそれぞれ以下のように変更します。
import Link from "next/link";
import supabase from "../../utils/supabase";
export const revalidate = 60;
export default async function Posts() {
/*
...
*/
}
import supabase from "../../../utils/supabase";
import { notFound } from "next/navigation";
export const revalidate = 60;
export async function generateStaticParams() {
const { data: posts } = await supabase.from("posts").select("id");
return posts?.map(({ id }) => ({
id,
}));
}
export default async function Post({
params: { id },
}: {
params: { id: string };
}) {
/*
...
*/
}
これだけで、ISRを実装することができました。リクエストごとに毎回新しいデータを取得したい場合は、revalidateの値を0にすればいいだけです。
export const revalidate = 0
まとめ
今回は、SupabaseのNext.jsを使用したブログ実装を試してみました。v13は非常に大きなアップデートだったので、まだまだドキュメントを見ながらインプット中です。ちなみに、Next.jsは既にv13.1も発表されています。
ではまた。