Next.js + Neon + Prismaでユーザー登録を実装!サーバーレスデータベースを活用した簡単な認証システム
はじめに
前回まではサーバーアクションと連携し入力フォームの入力内容を渡す処理を作成しました。
今回は受け取った入力内容をもとにサーバーアクションでデータベースへの登録処理を実装します。
使用するデータベースの紹介、データベースにアクセスするためのライブラリの紹介などしつつ、登録までの流れをご説明いたします。
前回の記事ではサーバーアクションが100%エラーを返す状態で終わらせてしまいましたが、こちらもデータベースへの登録後に成功したメッセージを返すよう修正していきます。
今回のゴール
実施にユーザー登録をアカウント登録画面から行います。下記のように「アカウントを登録しました」と表示されるところまで実装していきます。
また、登録したユーザーを使ってログインする処理は少し先の記事となるため、今回は登録できたことを確認するために、データベースに直接アクセスする手順もご紹介します。
データベースNeonについて
NeonはPostgresをサーバーレスで提供しているサービスとなります。
サーバーレスでデータベースを提供する企業では他にも、MySQLベースのPlanetScale
やPostgresベースのSupabase
などが有名です。それらに比べると比較的新しいNeon
で今回は実装してみます。
Neon
は2024年9月現在、1アカウントにつき1データベースを無料で提供しています。そのため、検証用としても始めやすくなっています。こちらでご紹介したPlanetScale
も以前は1データベースを無料で提供していたのですが、最近有料に切り替わりました。そういう意味ではNeon
も今後、無料から有料に切り替えるかもしれません。
ORM(Object-Relational Mapping)ツールPrismaについて
Next.jsのサーバーアクションからデータベースへの接続はPrismaを使っていきます。
Prisma自体様々な製品を提供しておりますがその中で今回使用する2つの製品をご紹介します。
- ORM: NodeやTypeScriptベースのバックエンドサービスで使用可能
- Studio: ローカルからデータベースをブラウザで参照
対応しているデータベースやフレームワークの一覧はこちらのページにまとまっております。
Neon
だけでなく、先ほどご紹介したデータベースであるPlanetScale
やSupabase
も対応しています。そのため今回はNeon
をデータベースとして実装を進めていきますが、Prisma
を使うのであれば、PlanetScale
やSupabase
でも大体同じような実装が可能となります。
また、Prisma
はTypeScript
と密接に連携しており、データベーススキーマに基づいて自動生成された型定義を使うことで、型安全なクエリが可能です。これにより、クエリの構築時にタイプミスや構文エラーを事前に検出でき、開発者の生産性が向上します。
読みやすいデータモデル Prismaのスキーマは直感的で、JavaScriptやTypeScriptを普段使用している人が読める方法でデータベースのテーブルを宣言できます。 手作業でモデルを定義したり、既存のデータベースの構造(テーブルやカラムの情報)を自動的に読み取って、Prismaスキーマを生成する
VS CodeのPrisma拡張機能と一緒に使うことでデータを取得する際に使用可能なAPIを候補として出してくれる自動補完などもあるため、実装しやすくなっております。
データベースの準備
まずはNeon
でアカウントを登録します。
こちらのページでサインアップをします。
無料で作成できるデータベースの名前とプロジェクト名とリージョンを指定します。
以上でデータベースの作成は終了です。
次に作成したデータベースにアクセスするための情報を確認するため、今回使用するPrisma
を選択して、.env
を参照します。
DATABASE_URL=
で始まる記述がありますので、これをプロジェクトのトップに.env
ファイルを作成した上で貼り付けます。
Prismaの準備
次にPrisma
を使えるよう準備していきます。
基本的な流れはPrisma
公式にもありますのでこちらを参考にしつつ進めていきます。
スキーマ定義を生成するライブラリのインストール
下記のコマンドを実行します。
$ npm install prisma --save-dev
$ npx prisma
コマンド使用例が紹介されていればOKです。
◭ Prisma is a modern DB toolkit to query, migrate and model your database (https://prisma.io)
Usage
$ prisma [command]
Commands
init Set up Prisma for your app
generate Generate artifacts (e.g. Prisma Client)
db Manage your database schema and lifecycle
migrate Migrate your database
studio Browse your data with Prisma Studio
validate Validate your Prisma schema
format Format your Prisma schema
version Displays Prisma version info
debug Displays Prisma debug info
(※以下省略)
スキーマ定義の作成
先ほどのコマンド使用例で記載のあるinit
でPrismaをセットアップします。
$ npx prisma init
$ npx prisma init
✔ Your Prisma schema was created at prisma/schema.prisma
You can now open it in your favorite editor.
warn You already have a .gitignore file. Don't forget to add `.env` in it to not commit any private information.
Next steps:
1. Set the DATABASE_URL in the .env file to point to your existing database. If your database has no tables yet, read https://pris.ly/d/getting-started
2. Set the provider of the datasource block in schema.prisma to match your database: postgresql, mysql, sqlite, sqlserver, mongodb or cockroachdb.
3. Run prisma db pull to turn your database schema into a Prisma schema.
4. Run prisma generate to generate the Prisma Client. You can then start querying your database.
5. Tip: Explore how you can extend the ORM with scalable connection pooling, global caching, and real-time database events. Read: https://pris.ly/cli/beyond-orm
More information in our documentation:
https://pris.ly/d/getting-started
すると、prisma
フォルダ配下にschema.prisma
ファイルが生成され、下記のような記述があるかと思います。
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
このファイルに早速モデルを書いてみます。
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
+ model User {
+ id String @id @default(cuid())
+ name String
+ email String @unique
+ password String
+ }
User
というモデル名で作成します。id
は自動で採番するよう定義し、email
はユニークであるよう定義しています。
スキーマ定義から型の生成
TypeScriptで使用するための型を下記コマンドで生成します。
$ npx prisma generate
$ npx prisma generate
Environment variables loaded from .env
Prisma schema loaded from prisma/schema.prisma
✔ Generated Prisma Client (v5.18.0) to ./node_modules/@prisma/client in 73ms
Start by importing your Prisma Client (See: http://pris.ly/d/importing-client)
Tip: Need your database queries to be 1000x faster? Accelerate offers you that and more: https://pris.ly/tip-2-accelerate
型を使用しつつデータ取得処理を書いていくのですが、Prismaクライアントのインストールを行なってからとなりますので、今はこのままでOKです。
スキーマ定義をデータベースに反映
データベースにスキーマ定義を反映させることでUser
というテーブルが作成されます。
$ npx prisma db push
Prisma Studioでデータベースを確認
データベースにテーブルができたことを確認します。
$ npx prisma studio
モデル名にUser
があるのでクリック
スキーマ定義で設定したid
とかname
があることが確認できます。
Next.jsからデータベースにアクセスするPrismaクライアントのインストール
下記のコマンドを実行します。
$ npm install @prisma/client
Next.jsでPrismaクライアントを使う際に必要な設定
ローカル環境で開発していると下記のようなメッセージが出ることがあります。
warn(prisma-client) There are already 10 instances of Prisma Client actively running.
npm run dev
で開発している場合、コードを書き換えるたびにNext.jsが自動で読み取って最新の状態でブラウザに表示されます。このタイミングでデータベースのコネクションも都度新しく作られてしまうため、上記のメッセージが表示されます。
ローカルで開発する際のためにlib
フォルダ配下に、下記のファイルを定義します。
import { PrismaClient } from '@prisma/client'
const prismaClientSingleton = () => {
return new PrismaClient()
}
declare const globalThis: {
prismaGlobal: ReturnType<typeof prismaClientSingleton>;
} & typeof global;
const prisma = globalThis.prismaGlobal ?? prismaClientSingleton()
export default prisma
if (process.env.NODE_ENV !== 'production') globalThis.prismaGlobal = prisma
このファイルで定義したprisma
を使ってデータベースにアクセスしていきます。
Next.jsからデータベースにアクセスしユーザーの登録をする
ようやく長いセットアップが終わりました。
ここからサーバーアクションにコードを書いてデータベースに登録する作業を進めていきます。
パスワードのハッシュ化
今回の認証方式ではパスワードをデータベースに保存し、ログインする際にはパスワードを受け取って、保存されているパスワードと一致していることを確認しアクセス許可をします。
実装としてはシンプルなため初めて認証の仕組みを作るにはお勧めですがパスワードの管理が重要となります。
今回はパスワードをハッシュ化した上で保存します。
その後、ログイン時にはログイン画面でパスワードをユーザーが入力するため、そのパスワードをハッシュ化した上で、ハッシュ化されたデータベース上のパスワードと一致するか確認するという手段を取ります。
任意の文字列をハッシュ化するライブラリをインストールします。
$ npm install bcryptjs
$ npm install -save-dev @types/bcryptjs
サーバーアクションの修正
サーバアクションを下記の通りに書き換えます。(修正箇所多いのでそのままコピペしていただくのがいいかと思います。)
'use server'
import { PrismaClientKnownRequestError } from '@prisma/client/runtime/library'
import bcryptjs from 'bcryptjs'
import type { z } from 'zod'
import db from '@/lib/db'
import { RegisterSchema } from '@/schema'
export const register = async (values: z.infer<typeof RegisterSchema>) => {
const validatedFields = RegisterSchema.safeParse(values)
if (!validatedFields.success) {
return { error: '入力内容を修正してください' }
}
const { name, email, password } = validatedFields.data
const hashedPassword = await bcryptjs.hash(password, 10)
try {
await db.user.create({
data: {
name,
email,
password: hashedPassword,
},
})
return { success: 'アカウントを登録しました' }
} catch (e) {
if (e instanceof PrismaClientKnownRequestError) {
if (e.code === 'P2002') {
return { error: 'このメールアドレスは既に登録されています' }
}
}
console.log(e)
return { error: 'エラーが発生しました' }
}
}
コードの解説をしていきます。
import bcryptjs from 'bcryptjs'
(※一部省略)
const { name, email, password } = validatedFields.data
const hashedPassword = await bcrypt.hash(password, 10)
Zodを使って受け取ったデータのスキーマチェックが通った後に、必要な値を取り出します。
その中からパスワードに関してはbcryptjs
を使ってハッシュ化されたパスワードを取得します。
import db from '@/lib/db'
(※一部省略)
try {
await db.user.create({
data: {
name,
email,
password: hashedPassword,
},
})
return { success: 'アカウントを登録しました' }
}
実際にデータベースに登録する処理です。名前とメールアドレスとハッシュ化されたパスワードで登録します。VS Codeで実際にコードを書くとき、db.
とすると使用できるAPIやモデルが候補として出てくるため非常に便利です。
データベース登録でエラーが発生しなければ、入力画面に対して「アカウントを登録しました」のメッセージを返します。
import { PrismaClientKnownRequestError } from '@prisma/client/runtime/library'
catch (e) {
if (e instanceof PrismaClientKnownRequestError) {
if (e.code === 'P2002') {
return { error: 'このメールアドレスは既に登録されています' }
}
}
console.log(e)
return { error: 'エラーが発生しました' }
}
データベース登録でエラーが発生した場合の処理となります。先ほどモデルで定義したようにメールアドレスはユニークである必要がありますので、万が一、同じメールアドレスで登録されそうになった場合は「このメールアドレスは既に登録されています」というエラーを返すようにしています。
データベースに関するエラーは多岐に渡り他にもユーザーに通知した方がいいエラーなどあるかもしれません。下記のドキュメントにはエラーコードとエラー内容がまとまっていますのでこちらから必要なエラーを見つけて、条件分岐に追加してもいいかと思います。
今回は上記のエラー以外は全て「エラーが発生しました」とだけ返すようにしました。
入力フォームの修正
サーバーアクションで成功した時のメッセージを返せるように修正したので、アカウント登録画面の入力フォームでは成功した時のメッセージを表示できるよう修正します。
(※一部省略)
const onSubmit = async (values: z.infer<typeof RegisterSchema>) => {
setError('')
setSuccess('')
setTransition(async () => {
try {
const response = await register(values)
if (response.error) {
setError(response.error)
} else {
- // setSuccess(response.success)
+ setSuccess(response.success)
}
} catch (e) {
setError('エラーが発生しました')
}
})
}
(※一部省略)
動作確認
実施にユーザー登録をアカウント登録画面から行います。
下記のように「アカウントを登録しました」と表示されればOKです。
Prisma Studioでは登録された内容が確認できます。
$ npx prisma studio
名前、メールアドレスが入力内容通りに登録されており、パスワードは入力内容とは違う文字列が入っていることが確認できます。
試しに先ほど使用したメールアドレスを使って再度「アカウント作成」を行います。
「このメールアドレスは既に登録されています」のメッセージが表示されれば想定通りの挙動となります。
さいごに
今回はデータベースとORMツールのセットアップを行なった上でNext.jsのサーバーアクションからデータベースへの登録処理を行いました。また、Prisma Studioという便利なツールを使いつつデータベースの内容を見れるようにしました。
次回以降は登録した情報を今度は取得できるようにしつつ、ログインができるように実装して行きます。
次の記事はこちら
関連する技術ブログ
チャットアプリ(画像・PDF送信、ビデオ通話機能付き)
お客様固有の要件を除き一般的なチャットアプリに求められる最低限の機能を実装しデモアプリとしてご紹介いたします。
shinagawa-web.com
管理ダッシュボード機能(グラフ表示、データ取り込み)
一般的な家計簿アプリとして求められる最低限の機能を実装しデモアプリとしてご紹介いたします。
shinagawa-web.com
Next.jsとAuth.jsで認証機能を実装するチュートリアル
Next.jsでアプリケーションを作る時に必要となる認証機能をどのように実装するかをご紹介する記事となります。アカウント登録から始まり、ログイン、ログアウト、ページごとのアクセス制御、OAuth、二要素認証、パスワードリセットなど認証に関連する様々な機能をコードベースでご紹介します。
shinagawa-web.com
Next.jsでのメール認証処理の実装ガイド:アカウント登録からトークン検証まで
Next.jsを活用したメール認証の実装方法を解説。アカウント登録時のトークン発行から、Sendgridを使ったメール送信処理まで、具体的な手順を紹介します。
shinagawa-web.com
ユーザー向けパスワードリセット機能の実装方法:トークン発行からメール送信、セキュリティ対策まで
このガイドでは、ユーザーのパスワードリセット機能を実装するためのステップを詳しく解説します。トークンの生成、リセットリンクのメール送信、そしてセキュリティ対策まで、効果的な実装方法を紹介します。
shinagawa-web.com
Next.jsでの二要素認証(2FA)実装ガイド:メール認証を使ったセキュリティ強化
このガイドでは、Next.jsを使用して二要素認証(2FA)を実装する方法を解説します。メールで送信されるワンタイムトークンを利用したログインプロセスを実装し、セキュリティを強化するためのステップバイステップの方法を紹介します。
shinagawa-web.com
フロントエンドのテスト自動化戦略:Jest・Playwright・MSW を活用したユニット・E2E・API テスト最適化
フロントエンド開発において、品質を担保しながら効率的に開発を進めるためには、適切なテストの自動化が不可欠です。本記事では、Jest や Vitest を活用したユニットテストの導入・強化、React Testing Library や Storybook との統合によるコンポーネントテストの最適化、Playwright / Cypress を用いた E2E テストの拡充について詳しく解説します。さらに、Supertest や MSW を活用した API テストの自動化、Faker / GraphQL Mock によるモックデータの整理、CI/CD パイプラインにおける並列実行やキャッシュ活用による最適化など、テストを効果的に運用するための手法を紹介。また、Codecov / SonarQube によるテストカバレッジの可視化や、フィーチャーフラグを考慮したテスト戦略の策定についても解説し、実践的なアプローチを提案します。テストの信頼性と効率を向上させ、開発プロセスを強化したいフロントエンドエンジニア必見の内容です。
shinagawa-web.com
動画配信サービス(クレカ決済、視聴履歴機能付き)
お客様固有の要件を除き一般的な動画配信プラットフォームに求められる最低限の機能を実装しデモアプリとしてご紹介いたします。
shinagawa-web.com
弊社の技術支援サービス
無駄なコストを削減し、投資対効果を最大化する
クラウド費用の高騰、不要なSaaSの乱立、開発工数の増加――これらの課題に悩んでいませんか?本サービスでは、クラウドコストの最適化、開発効率向上、技術選定の最適化 を通じて、単なるコスト削減ではなく、ROIを最大化する最適解 をご提案します。
shinagawa-web.com
最新技術の導入・検証を支援するPoCサービス
Remix、React Server Components、TypeScript移行、クラウドサービス比較、マイクロサービス、サーバーレス、デザインシステムなど、最新技術のPoC(概念実証)を通じて、最適な技術選定と導入を支援します。貴社の開発課題に合わせた検証・実装で、ビジネスの成長を加速させます。
shinagawa-web.com
開発生産性を最大化するための支援サービス
開発チームの生産性向上、コードの品質管理、インフラの最適化まで、様々な側面からサポートします。コードベースのリファクタリングから、テスト自動化、オンボーディング強化まで、プロジェクトの成功に必要なすべての支援を提供。御社の開発現場が効率的に機能するように、技術的な障害を取り除き、スムーズな開発を実現します。
shinagawa-web.com
開発品質向上支援 – 効率的で安定したプロダクトを実現
フロントエンドからバックエンド、データベースまで、開発プロセス全体を最適化し、安定したプロダクト作りをサポートします。コードレビューの仕組み、型定義の強化、E2Eテスト環境の構築など、開発の各ステップにおけるベストプラクティスを導入することで、より効率的でバグの少ない、そしてユーザー満足度の高いサービス提供を支援します。
shinagawa-web.com
Webアプリのセキュリティ強化支援
Webアプリの脆弱性対策からインフラのセキュリティ強化まで、包括的なセキュリティ支援を提供。OWASP Top 10対策、JWT認証の最適化、APIのアクセス制御、依存パッケージの監査、セキュアコーディングの標準化など、実践的なアプローチで開発現場の安全性を向上させます。
shinagawa-web.com
目次
お問い合わせ