Next.js v13をSupabaseのデータフェッチと一緒に理解する
こんにちは。データアナリティクス事業本部 サービスソリューション部の北川です。
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クライアントを初期化します。
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を作成し、以下を記述します。
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も発表されています。
ではまた。