Next.js Supabase Prisma を使用してアプリを作る 4/6 覚書
気になったところだけ書くので実際にされる方は↑から進めてください。
Next.js とのやりとり
SSRを使用した Next.js のデータクエリ
npm run dev
さっき入れたレコードが入ってないことを確認。
↓
なぜ入ってないのか。
↓
index.js を見るとローカルの data.json からデータを取得していることがわかる。
json にデータを渡すために SSR を使用する。
SSR: サーバー側からリクエストごとにページを事前にレンダリングする機能
既存のコードにSSR機能をもたせる。
// 既存のコード.
export default function Home() {
return (
<Layout>
<h1 className="text-xl font-medium text-gray-800">
Top-rated places to stay
</h1>
<p className="text-gray-500">
Explore some of the best places in the world
</p>
<div className="mt-8">
<Grid homes={homes} />
</div>
</Layout>
);
}
追加
// インスタンス化
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()
export async function getServerSideProps() {
// この関数内で prisma を使用して Supabase からデータをフェッチ.
const homes = await prisma.home.findMany();
return {
props: {
homes: JSON.parse(JSON.stringify(homes)),
}
};
}
// homesを渡す
export default function Home({ homes = [] }) {
return (
<Layout>
<h1 className="text-xl font-medium text-gray-800">
Top-rated places to stay
</h1>
<p className="text-gray-500">
Explore some of the best places in the world
</p>
<div className="mt-8">
<Grid homes={homes} />
</div>
</Layout>
);
}
PrismaClient を使用して Next.js からデータを作成
Prisma Studio から作成したが、アプリからも登録できるようにしていく。
フォームを追加して新しいレコードを作成
フォーム作成。
import Layout from '@/components/Layout';
import ListingForm from '@/components/ListingForm';
const Create = () => {
const addHome = () => null;
return (
<Layout>
<div className="max-w-screen-sm mx-auto">
<h1 className="text-xl font-medium text-gray-800">List your home</h1>
<p className="text-gray-500">
Fill out the form below to list a new home.
</p>
<div className="mt-8">
<ListingForm
buttonText="Add home"
redirectPath="/"
onSubmit={addHome}
/>
</div>
</div>
</Layout>
);
};
export default Create;
Form とか作ったことないので ListingForm も確認しておく.
formik でフォーム作成、 yup でバリデーションの組み合わせがいいらしい。
API エンドポイントを作成
エンドポイント: APIにアクセスするためのURIこのこと
Next.js プロジェクト内にAPIエンドポイントを作成し、
DBにレコードを作成するためのHTTPリクエストを処理していく
APIエンドポイントは pages/api
内にファイルを配置する
pages
└ api
└ homes.js
APIルートでリクエストを処理するには、パラメーターを受け取る関数をエクスポートする必要がある。
リクエストハンドラーとか呼ばれてるらしい。
export default async function handler(req, res) {}
ついでにAPIルートのドキュメントも読んでおく: https://nextjs-ja-translation-docs.vercel.app/docs/api-routes/introduction
- req: http.IncomingMessageのインスタンスと、組み込みミドルウェア
- レスポンスステータス、ヘッダー、データ等にアクセスするために使用
- 受け取ったリクエストをパースする
- res: http.ServerResponseのインスタンスと、ヘルパー関数。
- HTTPサーバーによって内部的に作成される
- res.status など
POST リクエスト処理
HTTPリクエストのメソッドを確認して処理を分ける。
export default async function handler(req, res) {
// POSTメソッドであれば.
if (req.method === 'POST') {
// TODO
}
// それ以外.
else {
res.setHeader('Allow', ['POST']);
res
.status(405)
.json({ message: `HTTP method ${req.method} is not supported.` });
}
}
- 405 method not allowed
- https://developer.mozilla.org/ja/docs/Web/HTTP/Status/405
- 対象のリソースでは対応してないことを示す。
PrismaClient で新しいレコードを作成
Prisma Client を使用してHTTPリクエストから受け取ったデータで、新しいレコードを作成していく。
req.body からプロパティ取得。
create メソッドを使用してクエリ作成。
クエリは新しく生成された home レコードを返却するため、クライアンとに情報を返す。
res.status(200).json(home);
APIエンドポイントの呼び出し
axios が使えるらしい。
npm i axios
エンドポイントからデータ取得.
const addHome = data => axios.post('/api/homes', data);
Next.js と DB のやりとりまとめ
必要なフォームを作成する
↓
フォームの onSubmit を使用して addHome
関数を呼び出す
↓
addHome
はフォームで入力された data を引数として持つ
axios によって data 情報を持った POST リクエストが作成される
↓
api
> homes.js
の handler
内で POST リクエストが処理されレスポンスが作成される.
こいつが肝っぽい。
-
req.body
からPOSTの値を取得. -
await prisma.home.create()
を使用してDBに登録. -
res.status(200).json(home)
で新しく作成された home レコードを返す
わかってないことろ
-
res.status(200).json(home)
は新しく追加されたデータを返すが、今までのデータはどこで取得しているのか。
↓
解決
index.js で最初に取得していた。 -
index.js から呼び出されるコンポーネントの実装方法
- index.js > Grid.js > Card.js 等となっててわかりにくい
- 後で確認する
静的生成(SSG)を使用したNext.jsのデータのクエリ
Next.jsでのページの事前レンダリング
SSG の説明
必要なHTMLがビルド時に生成されるらしい。
データを使用した静的生成
getStaticProps()
を使用する。
SSR と似ててデータ取得の方法はこんな感じ。
// データ取得.
export async function getStaticProps() {
const data = ...
return {
props: //...
}
}
// 実際にレンダリング
export default function MyPage(props) { ... }
動的ルートを作成する
pages の下に[xxxx].js
を配置するやつ。
表示部分はは長いので省略.
export async function getStaticPaths() {
const prisma = new PrismaClient();
const homes = await prisma.home.findMany({
// id から各レコードのフィールドを取得する.
select: { id: true },
});
return {
paths: homes.map(home => ({
params: { id: home.id },
})),
fallback: false,
};
}
getStaticPaths() と getStaticProps()
- getStaticPaths
- ページ生成用。のイメージ。
- ビルド時に実行される。
- 事前にパスを設定しておく。
- falback はないときの対応。
- ページに動的ルートと
getStaticProps
が使用されている場合、パスのリストを定義する必要がある。 - 指定しておくことでパスを事前に生成できる。
-
getStaticPaths
とgetStaticProps
は一緒に使うもの。
- getStaticProps
-
getStaticProps
はデータフェッチ(外部データ等の取得)に使う。- const res = await fetch('https://.../posts') 等
- そして props としてページコンポーネントにデータを渡す
- 動的ルート(pages 以下)でのみ使用できる。
公式の例
-
export async function getStaticPaths() {
return {
paths: [
{ params: { ... } }
],
fallback: true // false or 'blocking'
};
}
export async function getStaticProps({ params }) {
// params.id (現在のページのID) から データを取得.
const home = await prisma.home.findUnique({
where: { id: params.id },
});
// 現在のページのデータがあれば.
if (home) {
return {
// パースしたデータを取得.
props: JSON.parse(JSON.stringify(home)),
};
}
// これはなにやってるんだ.
return {
redirect: {
destination: '/',
permanent: false,
},
};
}
めっちゃ遅いとおもったら開発では毎回 getStaticProps が実行されてるらしい。
インクリメンタル静的生成を有効にする
- ISRとは
- https://nextjs.org/docs/basic-features/data-fetching/incremental-static-regeneration
- 段階的な静的サイト生成
- どうやらキャッシュやら使って早くなるらしい
- revalidate で時間設定ができる
この教材ではなぜか使われてない。
getStaticPaths で fallback: true を設定することでリクエストページのフォールバックバージョンが提供される。自動的に getStaticProps が呼び出されてデータが取得される。
そしてめっちゃはやくなった。
SupabaseStorageからファイルを保存して提供する
ファイルアップロード アプリからファイルにアクセス
Storage から設定を行う.
外部ファイルを読み取るには next.config.js に設定が必要
module.exports = {
images: {
domains: ['ホスト名'],
}
}
セキュリティルールを追加
CRUDのルールを特定のバケットに適応できる
アプリからファイルをアップロード
APIエンドポイントの作成
export default async function handler(req, res) {}
の出番
SupabaseJS クライアントが必要
npm i @supabase/supabase-js
カスタムAPI構成
APIルートで config をエクスポートすることで独自の設定ができる
export const config = {
api: {
bodyParser: {
sizeLimit: '10mb',
},
},
};
Discussion