LoginSignup
5
3

Next.jsでsupabaseとprismaを使ってログイン機能作ってみた

Posted at

はじめに

この記事では、supabaseのauthenticationを使ったログイン機能とprismaの使い方の個人的な備忘録です
supabaseでログイン機能はできたけど、どうやってリレーションするとか認証機能作るとかわからない人にオススメです
また、セキュリティ面なども考慮したつもりですが、怪しいところ等ありましたらコメントで教えていただけると幸いです!

完成イメージ

image.png
image.png
image.png
image.png
ログイン中のユーザー表示やログイン中のユーザーの投稿のみを表示したりします!

コードはこちら

supabaseとは

SupabaseはFirebaseに代わるオープンソースです。
Postgresデータベース、認証、インスタントAPI、エッジファンクション、リアルタイムサブスクリプション、ストレージ、Vectorエンベッディングでプロジェクトを始めましょう。

prismaとは

Prisma は、チームがデータベースを操作し操作するための最高のエクスペリエンスを提供します。接続プーリング、キャッシュ、リアルタイム データベース サブスクリプションなどの複雑な作業も、当社の製品を使用すると簡単に行えます。
アプリケーションを構築し、すべてがスムーズに実行されるように最適化し、ユーザーと要件に合わせて成長します。

supabaseアカウント作成・プロジェクト作成

公式ページのStart your projectからアカウントを作成します
Screenshot 2024-03-06 13.52.34.png

アカウントが作成できたらDashboardからNew projectを作成します
Screenshot 2024-03-06 13.56.55.png

NameとDatabase Passwordは自分で適当に決めておっけです
あとでDatabase Passwordは使うのでメモしておきましょう!
image.png

Create new projectをクリックすると新しいプロジェクトを作り始めます
この画面ではちょっと待機します(自分の場合は1分程度で作成完了しました)
image.png

作成が完了するとHome画面になってるかと思います
image.png

一応今回使う左のメニュー説明しときます
Table Editor→このあと作成するprismaで作ったデータベース表示
SQL Editor→ログイン処理後のprismaとのやりとりを記述(supabaseのpostgreSQLを直接操作できます)
Authenitication→ログイン機能
Storage→ユーザー画像を登録
Logs→エラーが出た時にみる
Project Settings→Nextプロジェクトとsupabaseを繋ぐURLとか見ます

image.png

終わり

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テーブルを作成し、リレーションしておきます

prisma/schema.prisma
// 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ファイルを作成します

.env
DATABASE_URL="*************"

DATABASE_URLは、project settingsのDatabaseから取得できます
Screenshot 2024-03-06 18.37.47.png

こんな感じになってると思います(多少違うかもしれません)
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というテーブルが作成できていることが確認できます
image.png

supabaseの設定

ここまでで、prismaを介してsupabaseのデータベースを利用できるようになっています
しかし、supabase側でAutheniticationからユーザー登録してもTable Editorからは確認できません
どういうことかというと、supabaseの機能を使ってアカウントを作ってもその情報がprismaで作ったデータベースに保存されないよってことです

まず、AutheniticationのProvidersからConfirm emailをオフにしておきます
Screenshot 2024-03-06 22.45.03.png

次に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と出力されればおっけです
image.png

これでsign upされたらuserが追加されるようになるはずです
Autheniticationでsign upしてみてエラーが出るようでしたら、LogsのAuthからエラーを確認してみてください
image.png

ここまでsupabase, prismaはほぼほぼ終了です!あとはNext.jsいじっていきます〜〜

Nextプロジェクトの設定

とりあえず必要なライブラリをインストールしておきます

npm i @supabase/supabase-js

envに必要なコードを追加します

.env
+ NEXT_PUBLIC_SUPABASE_URL="***************"
+ NEXT_PUBLIC_SUPABASE_API_KEY="***************"

これらはsupabaseのProject Settings→APIから確認できます
Screenshot 2024-03-10 5.23.44.png

supabase clientを作成します
これはenvに書いたコードを使ってnext側からsupabaseにアクセスするためのものです

utils/supabase.ts
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
);

ログインフォームを作る前にまずは認証関連を扱うカスタムフックスを作成します

src/hooks/useUser.ts
"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と検索してデータを取得しています

src/app/api/user/[auth_id]/route.ts
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();

基本的には、sessionnullの時は未ログインで、ログイン中の時はuserにユーザー情報が入ってるだけです

これを使って投稿機能も作成していますが、記事が長くなったし他の記事と対して変わらないので説明は飛ばします〜〜
UserとPostがリレーションされているので、
投稿する時にはauthoerIdを一緒に投稿することと、
投稿を表示するときは、ログイン中のuser情報の中にその人のposts情報が入っていること
に注意してください

終わり

終わりです
あざした〜〜

5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3