はじめに
この記事では、supabaseのauthenticationを使ったログイン機能とprismaの使い方の個人的な備忘録です
supabaseでログイン機能はできたけど、どうやってリレーションするとか認証機能作るとかわからない人にオススメです
また、セキュリティ面なども考慮したつもりですが、怪しいところ等ありましたらコメントで教えていただけると幸いです!
完成イメージ
ログイン中のユーザー表示やログイン中のユーザーの投稿のみを表示したりします!
コードはこちら
supabaseとは
SupabaseはFirebaseに代わるオープンソースです。
Postgresデータベース、認証、インスタントAPI、エッジファンクション、リアルタイムサブスクリプション、ストレージ、Vectorエンベッディングでプロジェクトを始めましょう。
prismaとは
Prisma は、チームがデータベースを操作し操作するための最高のエクスペリエンスを提供します。接続プーリング、キャッシュ、リアルタイム データベース サブスクリプションなどの複雑な作業も、当社の製品を使用すると簡単に行えます。 アプリケーションを構築し、すべてがスムーズに実行されるように最適化し、ユーザーと要件に合わせて成長します。
supabaseアカウント作成・プロジェクト作成
公式ページのStart your projectからアカウントを作成します
アカウントが作成できたらDashboardからNew projectを作成します
NameとDatabase Passwordは自分で適当に決めておっけです
あとでDatabase Passwordは使うのでメモしておきましょう!
Create new projectをクリックすると新しいプロジェクトを作り始めます
この画面ではちょっと待機します(自分の場合は1分程度で作成完了しました)
一応今回使う左のメニュー説明しときます
Table Editor
→このあと作成するprismaで作ったデータベース表示
SQL Editor
→ログイン処理後のprismaとのやりとりを記述(supabaseのpostgreSQLを直接操作できます)
Authenitication
→ログイン機能
Storage
→ユーザー画像を登録
Logs
→エラーが出た時にみる
Project Settings
→Nextプロジェクトとsupabaseを繋ぐURLとか見ます
終わり
Next.jsプロジェクト作成
ここからはNext.jsのプロジェクトを作成していじっていきます
npx create-next-app@latest
バージョンは、14.1.2
です
詳細はこんな感じで作りました
(自分の好きなように作って良いかと思います)
✔ What is your project named? … qiita-test-app
✔ Would you like to use TypeScript? … No / Yes
✔ Would you like to use ESLint? … No / Yes
✔ Would you like to use Tailwind CSS? … No / Yes
✔ Would you like to use `src/` directory? … No / Yes
✔ Would you like to use App Router? (recommended) … No / Yes
✔ Would you like to customize the default import alias (@/*)? … No / Yes
✔ What import alias would you like configured? … @/*
Prismaの設定
ディレクトリを移動して必要なライブラリをインストールします
cd qiita-test-app/
npm i prisma --save-dev
npm i @prisma/client
npx prisma init
新しくschema.prismaというファイルが作成されているかと思います
データベースに新しいテーブルを作成したいときは、schema.prismaを変更します
今回はユーザー情報を保持するテーブルとブログ投稿用のPostテーブルを作成し、リレーションしておきます
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id Int @id @default(autoincrement())
auth_id String @unique
email String @unique
name String?
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
title String
content String?
published Boolean @default(false)
author User? @relation(fields: [authorId], references: [id])
authorId Int?
}
次にsupabaseと繋ぐためのURLを持ってきます
.env
ファイルを作成します
DATABASE_URL="*************"
DATABASE_URL
は、project settingsのDatabaseから取得できます
こんな感じになってると思います(多少違うかもしれません)
postgres://postgres.aiolelfrdszjtreachau:[YOUR-PASSWORD]@aws-0-ap-northeast-1.pooler.supabase.com:5432/postgres
[YOUR-PASSWORD]はsupabaseのプロジェクトを作成したときのパスワードに置き換えてください
パスワードが123456なら次のようにしてください
postgres://postgres.aiolelfrdszjtreachau:123456@aws-0-ap-northeast-1.pooler.supabase.com:5432/postgres
envファイルを作成できたらmigrateしてsupabaseのデータベースにテーブルを追加します
npx prisma migrate dev --name init
うまく作成が完了するとsupabaseのDatabaseからUserとPostと_prisma_migrationsというテーブルが作成できていることが確認できます
supabaseの設定
ここまでで、prismaを介してsupabaseのデータベースを利用できるようになっています
しかし、supabase側でAutheniticationからユーザー登録してもTable Editorからは確認できません
どういうことかというと、supabaseの機能を使ってアカウントを作ってもその情報がprismaで作ったデータベースに保存されないよってことです
まず、AutheniticationのProvidersからConfirm emailをオフにしておきます
次にAutheniticationによるsign upがされたときに、Userにデータを追加するように設定します
SQL Editorで次のコードを実行します
create function public.handle_new_user()
returns trigger
language plpgsql
security definer set search_path = public
as $$
begin
insert into public."User" (auth_id, email)
values (new.id, new.email);
return new;
end;
$$;
create trigger on_auth_user_created
after insert on auth.users
for each row execute procedure public.handle_new_user();
Runを押して、Success. No rows returned
と出力されればおっけです
これでsign upされたらuserが追加されるようになるはずです
Autheniticationでsign upしてみてエラーが出るようでしたら、LogsのAuthからエラーを確認してみてください
ここまでsupabase, prismaはほぼほぼ終了です!あとはNext.jsいじっていきます〜〜
Nextプロジェクトの設定
とりあえず必要なライブラリをインストールしておきます
npm i @supabase/supabase-js
envに必要なコードを追加します
+ NEXT_PUBLIC_SUPABASE_URL="***************"
+ NEXT_PUBLIC_SUPABASE_API_KEY="***************"
これらはsupabaseのProject Settings→APIから確認できます
supabase clientを作成します
これはenvに書いたコードを使ってnext側からsupabaseにアクセスするためのものです
import { createClient } from "@supabase/supabase-js";
export const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL as string,
process.env.NEXT_PUBLIC_SUPABASE_API_KEY as string
);
ログインフォームを作る前にまずは認証関連を扱うカスタムフックスを作成します
"use client";
import { supabase } from "@/utils/supabase";
import { useEffect, useState } from "react";
import { Session } from "@supabase/supabase-js";
import { UserType } from "@/types/user";
export default function useUser() {
const [session, setSession] = useState<Session | null>(null);
const [user, setUser] = useState<UserType | null>(null);
useEffect(() => {
const { data: authListener } = supabase.auth.onAuthStateChange(
(event, session) => {
setSession(session);
}
);
return () => {
authListener.subscription.unsubscribe();
};
}, []);
useEffect(() => {
const setupUser = async () => {
if (session?.user.id) {
const response = await fetch(`/api/user/${session.user.id}`);
if (response.ok) {
const data = await response.json();
setUser(data.user);
} else {
console.error("Failed to fetch user data");
}
}
};
setupUser();
}, [session]);
function signUp({ email, password }: { email: string; password: string }) {
supabase.auth.signUp({ email, password });
}
function signIn({ email, password }: { email: string; password: string }) {
supabase.auth.signInWithPassword({ email, password });
}
function signOut() {
supabase.auth.signOut();
}
return { session, user, signUp, signIn, signOut };
}
このカスタムフックスで、ユーザーのセッション、情報、サインアップ処理、サインイン処理、サインアウト処理を一括で行います
サインアップ処理に関しては、supabaseのAuthenticationで行っており先ほど作ったutils/supabase.ts
に設定が書かれています
その後、サインアップしたユーザーの情報をprismaを通じて取得します
これはsrc/app/api/user/[auth_id]/route.ts
に設定したAPIを使ってsupabaseのAuthenticationでサインアップしたユーザーidをprismaで作成したUserテーブルのauth_idと検索してデータを取得しています
import { NextResponse } from "next/server";
import { PrismaClient } from "@prisma/client";
export const prisma = new PrismaClient() as any;
export async function doConnect() {
try {
await prisma.$connect();
} catch (error) {
return Error("DB接続に失敗しました");
}
}
export const GET = async (req: Request, res: NextResponse) => {
const auth_id: string = req.url.split("/user/")[1];
console.log(auth_id);
try {
await doConnect();
const user = await prisma.user.findUnique({
where: { auth_id },
include: {
posts: true,
},
});
return NextResponse.json({ message: "Success", user }, { status: 200 });
} catch (error) {
return NextResponse.json({ message: "Error", error }, { status: 500 });
} finally {
await prisma.$disconnect();
}
};
このカスタムフックスさえ作成できてしまえばあとはフォームを作るだけです
フォームにはreact-hook-formを使っています
npm i react-hook-form
コードは長くなるのでgithubを見てみてください
ログイン中のユーザーの情報等は、カスタムフックスから全て取得できるので、自由にカスタマイズしてみてください
const { session, user, signUp, signIn, signOut } = useUser();
基本的には、session
がnull
の時は未ログインで、ログイン中の時はuser
にユーザー情報が入ってるだけです
これを使って投稿機能も作成していますが、記事が長くなったし他の記事と対して変わらないので説明は飛ばします〜〜
UserとPostがリレーションされているので、
投稿する時にはauthoerIdを一緒に投稿することと、
投稿を表示するときは、ログイン中のuser情報の中にその人のposts情報が入っていること
に注意してください
終わり
終わりです
あざした〜〜