如何将 Prisma ORM 与 Better-Auth 和 Next.js 结合使用
介绍
¥Introduction
Better-Auth 是一款面向 Web 应用的现代开源身份验证解决方案。它基于 TypeScript、React 和 Prisma 构建,以提供简单且可扩展的身份验证体验。
¥Better-Auth is a modern, open-source authentication solution for web applications. It's built with TypeScript, React, and Prisma to provide a simple and extensible auth experience.
在本指南中,你将把 Better-Auth 连接到一个全新的 Next.js 应用中,并将用户持久化到 Prisma Postgres 数据库中。你可以在 GitHub 上找到本指南的完整示例。
¥In this guide, you'll wire Better-Auth into a brand-new Next.js app and persist users in a Prisma Postgres database. You can find a complete example of this guide on GitHub.
先决条件
¥Prerequisites
-
熟悉 Next.js 应用路由和 Prisma
¥Basic familiarity with Next.js App Router and Prisma
1. 设置你的项目
¥ Set up your project
创建一个新的 Next.js 应用:
¥Create a new Next.js application:
npx create-next-app@latest betterauth-nextjs-prisma
它会提示你自定义设置。选择默认值:
¥It will prompt you to customize your setup. Choose the defaults:
-
你想使用 TypeScript 吗?
Yes
¥Would you like to use TypeScript?
Yes
-
你想使用 ESLint 吗?
Yes
¥Would you like to use ESLint?
Yes
-
你想使用 Tailwind CSS 吗?
Yes
¥Would you like to use Tailwind CSS?
Yes
-
你想将代码放在
src/
目录中吗?Yes
¥Would you like your code inside a
src/
directory?Yes
-
你想使用 App Router 吗?
Yes
¥Would you like to use App Router?
Yes
-
你想使用 Turbopack 吗?
Yes
¥Would you like to use Turbopack?
Yes
-
你想自定义导入别名(默认为
@/*
)吗?No
¥Would you like to customize the import alias (
@/*
by default)?No
导航到项目目录:
¥Navigate to the project directory:
cd betterauth-nextjs-prisma
这些选择将创建一个现代的 Next.js 项目,其中 TypeScript 确保类型安全,ESLint 确保代码质量,Tailwind CSS 进行样式设置。使用 src/
目录和 App Router 是新 Next.js 应用的常见约定。
¥These selections will create a modern Next.js project with TypeScript for type safety, ESLint for code quality, and Tailwind CSS for styling. Using the src/
directory and the App Router are common conventions for new Next.js applications.
2. 设置 Prisma
¥ Set up Prisma
接下来,你将 Prisma 添加到你的项目中以管理你的数据库。
¥Next, you'll add Prisma to your project to manage your database.
2.1.安装 Prisma 及其依赖
¥2.1. Install Prisma and dependencies
安装必要的 Prisma 软件包。根据你将 Prisma Postgres 与 Accelerate 还是其他数据库一起使用,依赖略有不同。
¥Install the necessary Prisma packages. The dependencies differ slightly depending on whether you use Prisma Postgres with Accelerate or another database.
- Prisma Postgres (recommended)
- Other databases
npm install prisma --save-dev
npm install @prisma/extension-accelerate @prisma/client
npm install prisma --save-dev
npm install @prisma/client
安装完成后,请在你的项目中初始化 Prisma:
¥Once installed, initialize Prisma in your project:
npx prisma init --db --output ../src/generated/prisma
在设置 Prisma Postgres 数据库时,你需要回答几个问题。选择离你最近的区域,并为数据库选择一个容易记住的名称,例如 "我的 Better-Auth 项目"
¥You'll need to answer a few questions while setting up your Prisma Postgres database. Select the region closest to your location and a memorable name for your database like "My Better-Auth Project"
这将造成:
¥This will create:
-
一个包含
schema.prisma
文件的prisma
目录。¥A
prisma
directory with aschema.prisma
file. -
一个 Prisma Postgres 数据库。
¥A Prisma Postgres database.
-
一个位于项目根目录、包含
DATABASE_URL
文件的.env
文件。¥A
.env
file containing theDATABASE_URL
at the project root. -
用于生成的 Prisma 客户端的
output
目录,用作better-auth/generated/prisma
。¥An
output
directory for the generated Prisma Client asbetter-auth/generated/prisma
.
2.2.配置 Prisma 客户端生成器
¥2.2. Configure the Prisma client generator
运行以下命令创建数据库表并生成 Prisma 客户端:
¥Run the following command to create the database tables and generate the Prisma Client:
npx prisma generate
2.3.设置全局 Prisma 客户端
¥2.3. Set up a global Prisma client
在其中创建一个 /lib
目录和一个 prisma.ts
文件。此文件将用于创建和导出你的 Prisma 客户端实例。
¥Create a /lib
directory and a prisma.ts
file inside it. This file will be used to create and export your Prisma Client instance.
mkdir -p src/lib
touch src/lib/prisma.ts
按如下方式设置 Prisma 客户端:
¥Set up the Prisma client like this:
- Prisma Postgres (recommended)
- Other databases
import { PrismaClient } from "@/generated/prisma";
import { withAccelerate } from "@prisma/extension-accelerate";
const globalForPrisma = global as unknown as {
prisma: PrismaClient;
};
const prisma =
globalForPrisma.prisma || new PrismaClient().$extends(withAccelerate());
if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma;
export default prisma;
import { PrismaClient } from "@/generated/prisma";
const globalForPrisma = global as unknown as {
prisma: PrismaClient;
};
const prisma =
globalForPrisma.prisma || new PrismaClient()
if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma;
export default prisma;
我们建议使用连接池(例如 Prisma 加速)来高效管理数据库连接。
¥We recommend using a connection pooler (like Prisma Accelerate) to manage database connections efficiently.
如果你选择不使用 PrismaClient
,请避免在长期存在的环境中全局实例化 PrismaClient
。请改为按请求创建并释放客户端,以防止耗尽数据库连接。
¥If you choose not to use one, avoid instantiating PrismaClient
globally in long-lived environments. Instead, create and dispose of the client per request to prevent exhausting your database connections.
3. 设置 Better-Auth
¥ Set up Better-Auth
现在是时候集成 Better-Auth 进行身份验证了。
¥Now it's time to integrate Better-Auth for authentication.
3.1.安装并配置 Better-Auth
¥3.1. Install and configure Better-Auth
首先,安装 Better-Auth 核心包:
¥First, install the Better-Auth core package:
npm install better-auth
接下来,生成一个 Better-Auth 将用于签署身份验证令牌的安全密钥。这可确保你的令牌不会被篡改。
¥Next, generate a secure secret that Better-Auth will use to sign authentication tokens. This ensures your tokens cannot be messed with.
npx @better-auth/cli@latest secret
复制生成的密钥,并将其与应用的 URL 一起添加到你的 .env
文件中:
¥Copy the generated secret and add it, along with your application's URL, to your .env
file:
# Better-Auth
BETTER_AUTH_SECRET=your-generated-secret
BETTER_AUTH_URL=http://localhost:3000
# Prisma
DATABASE_URL="your-database-url"
现在,在 src/lib/auth.ts
处为 Better-Auth 创建一个配置文件:
¥Now, create a configuration file for Better-Auth at src/lib/auth.ts
:
touch src/lib/auth.ts
在此文件中,你将配置 Better-Auth 以使用 Prisma 适配器,这允许它将用户和会话数据持久化到数据库中。你还将启用电子邮件和密码身份验证。
¥In this file, you'll configure Better-Auth to use the Prisma adapter, which allows it to persist user and session data in your database. You will also enable email and password authentication.
import { betterAuth } from 'better-auth'
import { prismaAdapter } from 'better-auth/adapters/prisma'
import prisma from '@/lib/prisma'
export const auth = betterAuth({
database: prismaAdapter(prisma, {
provider: 'postgresql',
}),
})
Better-Auth 还支持其他登录方式,例如社交登录(Google、GitHub 等),你可以在其 documentation 中探索这些方式。
¥Better-Auth also supports other sign-in methods like social logins (Google, GitHub, etc.), which you can explore in their documentation.
import { betterAuth } from 'better-auth'
import { prismaAdapter } from 'better-auth/adapters/prisma'
import prisma from '@/lib/prisma'
export const auth = betterAuth({
database: prismaAdapter(prisma, {
provider: 'postgresql',
}),
emailAndPassword: {
enabled: true,
},
})
如果你的应用在 3000
以外的端口上运行,则必须将其添加到 auth.ts
配置中的 trustedOrigins
中,以避免身份验证请求期间出现 CORS 错误。
¥If your application runs on a port other than 3000
, you must add it to the trustedOrigins
in your auth.ts
configuration to avoid CORS errors during authentication requests.
import { betterAuth } from 'better-auth'
import { prismaAdapter } from 'better-auth/adapters/prisma'
import prisma from '@/lib/prisma'
export const auth = betterAuth({
database: prismaAdapter(prisma, {
provider: 'postgresql',
}),
emailAndPassword: {
enabled: true,
},
trustedOrigins: ['http://localhost:3001'],
})
3.2.将 Better-Auth 模型添加到架构
¥3.2. Add Better-Auth models to your schema
Better-Auth 提供了一个 CLI 命令,可以自动将必要的身份验证模型(User
、Session
、Account
和 Verification
)添加到你的 schema.prisma
文件中。
¥Better-Auth provides a CLI command to automatically add the necessary authentication models (User
, Session
, Account
, and Verification
) to your schema.prisma
file.
运行以下命令:
¥Run the following command:
npx @better-auth/cli generate
它会请求确认是否覆盖你现有的 Prisma 模式。选择 y
。
¥It will ask for confirmation to overwrite your existing Prisma schema. Select y
.
这将添加以下模型:
¥This will add the following models:
model User {
id String @id
name String
email String
emailVerified Boolean
image String?
createdAt DateTime
updatedAt DateTime
sessions Session[]
accounts Account[]
@@unique([email])
@@map("user")
}
model Session {
id String @id
expiresAt DateTime
token String
createdAt DateTime
updatedAt DateTime
ipAddress String?
userAgent String?
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([token])
@@map("session")
}
model Account {
id String @id
accountId String
providerId String
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
accessToken String?
refreshToken String?
idToken String?
accessTokenExpiresAt DateTime?
refreshTokenExpiresAt DateTime?
scope String?
password String?
createdAt DateTime
updatedAt DateTime
@@map("account")
}
model Verification {
id String @id
identifier String
value String
expiresAt DateTime
createdAt DateTime?
updatedAt DateTime?
@@map("verification")
}
3.3.迁移数据库
¥3.3. Migrate the database
使用架构中的新模型,你需要更新数据库。运行迁移以创建相应的表:
¥With the new models in your schema, you need to update your database. Run a migration to create the corresponding tables:
npx prisma migrate dev --name add-auth-models
4. 设置 API 路由
¥ Set up the API routes
Better-Auth 需要一个 API 端点来处理登录、注册和退出等身份验证请求。你将在 Next.js 中创建一个 catch-all API 路由来处理发送到 /api/auth/[...all]
的所有请求。
¥Better-Auth needs an API endpoint to handle authentication requests like sign-in, sign-up, and sign-out. You'll create a catch-all API route in Next.js to handle all requests sent to /api/auth/[...all]
.
首先,创建必要的目录和文件:
¥First, create the necessary directory and file:
mkdir -p "src/app/api/auth/[...all]"
touch "src/app/api/auth/[...all]/route.ts"
将以下代码添加到新创建的 route.ts
文件中。此代码使用 Better-Auth 的助手来创建与 Next.js 兼容的 GET
和 POST
请求处理程序。
¥Add the following code to the newly created route.ts
file. This code uses a helper from Better-Auth to create Next.js-compatible GET
and POST
request handlers.
import { auth } from "@/lib/auth";
import { toNextJsHandler } from "better-auth/next-js";
export const { POST, GET } = toNextJsHandler(auth);
接下来,你需要一个客户端实用程序来从你的 React 组件与这些端点进行交互。创建一个新的文件 src/lib/auth-client.ts
:
¥Next, you'll need a client-side utility to interact with these endpoints from your React components. Create a new file src/lib/auth-client.ts
:
touch src/lib/auth-client.ts
添加以下代码,用于创建你将在 UI 中使用的 React 钩子和函数:
¥Add the following code, which creates the React hooks and functions you'll use in your UI:
import { createAuthClient } from 'better-auth/react'
export const { signIn, signUp, signOut, useSession } = createAuthClient()
5. 设置你的页面
¥ Set up your pages
现在,让我们构建用于身份验证的用户界面。创建用于注册、登录和受保护仪表板的页面:
¥Now, let's build the user interface for authentication. Create the pages for signing up, signing in, and a protected dashboard:
mkdir -p src/app/{sign-up,sign-in,dashboard}
touch src/app/{sign-up,sign-in,dashboard}/page.tsx
5.1.注册页面
¥5.1. Sign up page
首先,在 src/app/sign-up/page.tsx
中创建基本的 SignUpPage
组件。这将为你的页面设置主容器和标题。
¥First, create the basic SignUpPage
component in src/app/sign-up/page.tsx
. This sets up the main container and a title for your page.
"use client";
export default function SignUpPage() {
return (
<main className="max-w-md mx-auto p-6 space-y-4 text-white">
<h1 className="text-2xl font-bold">Sign Up</h1>
</main>
);
}
接下来,从 React 和 Next.js 导入必要的钩子来管理状态和导航。初始化路由和一个状态变量以保存任何潜在的错误消息。
¥Next, import the necessary hooks from React and Next.js to manage state and navigation. Initialize the router and a state variable to hold any potential error messages.
"use client";
import { useState } from "react";
import { useRouter } from "next/navigation";
export default function SignUpPage() {
const router = useRouter();
const [error, setError] = useState<string | null>(null);
return (
<main className="max-w-md mx-auto p-6 space-y-4 text-white">
<h1 className="text-2xl font-bold">Sign Up</h1>
</main>
);
}
现在,从 Better-Auth 客户端导入 signUp
函数并添加 handleSubmit
函数。此函数在表单提交时触发,并调用 Better-Auth 提供的 signUp.email
方法,传递用户的名称、电子邮件和密码。
¥Now, import the signUp
function from your Better-Auth client and add the handleSubmit
function. This function is triggered on form submission and calls the signUp.email
method provided by Better-Auth, passing the user's name, email, and password.
"use client";
import { useState } from "react";
import { useRouter } from "next/navigation";
//add-next-lin
import { signUp } from "@/lib/auth-client";
export default function SignUpPage() {
const router = useRouter();
const [error, setError] = useState<string | null>(null);
async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
setError(null);
const formData = new FormData(e.currentTarget);
const res = await signUp.email({
name: formData.get("name") as string,
email: formData.get("email") as string,
password: formData.get("password") as string,
});
if (res.error) {
setError(res.error.message || "Something went wrong.");
} else {
router.push("/dashboard");
}
}
return (
<main className="max-w-md mx-auto p-6 space-y-4 text-white">
<h1 className="text-2xl font-bold">Sign Up</h1>
</main>
);
}
要将任何问题告知用户,请添加一个元素,当 error
状态不为空时,该元素有条件地进行渲染。
¥To inform the user of any issues, add an element that conditionally renders when the error
state is not null.
"use client";
import { useState } from "react";
import { useRouter } from "next/navigation";
import { signUp } from "@/lib/auth-client";
export default function SignUpPage() {
const router = useRouter();
const [error, setError] = useState<string | null>(null);
async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
setError(null);
const formData = new FormData(e.currentTarget);
const res = await signUp.email({
name: formData.get("name") as string,
email: formData.get("email") as string,
password: formData.get("password") as string,
});
if (res.error) {
setError(res.error.message || "Something went wrong.");
} else {
router.push("/dashboard");
}
}
return (
<main className="max-w-md mx-auto p-6 space-y-4 text-white">
<h1 className="text-2xl font-bold">Sign Up</h1>
{error && <p className="text-red-500">{error}</p>}
</main>
);
}
最后,添加 HTML 表单,其中包含用于输入用户名、电子邮件和密码的字段以及提交按钮。
¥Finally, add the HTML form with input fields for the user's name, email, and password, and a submit button.
"use client";
import { useState } from "react";
import { useRouter } from "next/navigation";
import { signUp } from "@/lib/auth-client";
export default function SignUpPage() {
const router = useRouter();
const [error, setError] = useState<string | null>(null);
async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
setError(null);
const formData = new FormData(e.currentTarget);
const res = await signUp.email({
name: formData.get("name") as string,
email: formData.get("email") as string,
password: formData.get("password") as string,
});
if (res.error) {
setError(res.error.message || "Something went wrong.");
} else {
router.push("/dashboard");
}
}
return (
<main className="max-w-md mx-auto p-6 space-y-4 text-white">
<h1 className="text-2xl font-bold">Sign Up</h1>
{error && <p className="text-red-500">{error}</p>}
<form onSubmit={handleSubmit} className="space-y-4">
<input
name="name"
placeholder="Full Name"
required
className="w-full rounded-md bg-neutral-900 border border-neutral-700 px-3 py-2"
/>
<input
name="email"
type="email"
placeholder="Email"
required
className="w-full rounded-md bg-neutral-900 border border-neutral-700 px-3 py-2"
/>
<input
name="password"
type="password"
placeholder="Password"
required
minLength={8}
className="w-full rounded-md bg-neutral-900 border border-neutral-700 px-3 py-2"
/>
<button
type="submit"
className="w-full bg-white text-black font-medium rounded-md px-4 py-2 hover:bg-gray-200"
>
Create Account
</button>
</form>
</main>
);
}
5.2.登录页面
¥5.2. Sign in page
对于登录页面,请从 src/app/sign-in/page.tsx
中的基本结构开始。
¥For the sign-in page, start with the basic structure in src/app/sign-in/page.tsx
.
"use client";
export default function SignInPage() {
return (
<main className="max-w-md h-screen flex items-center justify-center flex-col mx-auto p-6 space-y-4 text-white">
<h1 className="text-2xl font-bold">Sign In</h1>
</main>
);
}
现在,添加状态和路由钩子,类似于注册页面。
¥Now, add the state and router hooks, similar to the sign-up page.
"use client";
import { useState } from "react";
import { useRouter } from "next/navigation";
export default function SignInPage() {
const router = useRouter();
const [error, setError] = useState<string | null>(null);
return (
<main className="max-w-md h-screen flex items-center justify-center flex-col mx-auto p-6 space-y-4 text-white">
<h1 className="text-2xl font-bold">Sign In</h1>
</main>
);
}
添加 handleSubmit
函数,这次导入并使用 Better-Auth 中的 signIn.email
方法。
¥Add the handleSubmit
function, this time importing and using the signIn.email
method from Better-Auth.
"use client";
import { useState } from "react";
import { useRouter } from "next/navigation";
import { signIn } from "@/lib/auth-client";
export default function SignInPage() {
const router = useRouter();
const [error, setError] = useState<string | null>(null);
async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
setError(null);
const formData = new FormData(e.currentTarget);
const res = await signIn.email({
email: formData.get("email") as string,
password: formData.get("password") as string,
});
if (res.error) {
setError(res.error.message || "Something went wrong.");
} else {
router.push("/dashboard");
}
}
return (
<main className="max-w-md h-screen flex items-center justify-center flex-col mx-auto p-6 space-y-4 text-white">
<h1 className="text-2xl font-bold">Sign In</h1>
</main>
);
}
添加条件错误消息显示。
¥Add the conditional error message display.
"use client";
import { useState } from "react";
import { useRouter } from "next/navigation";
import { signIn } from "@/lib/auth-client";
export default function SignInPage() {
const router = useRouter();
const [error, setError] = useState<string | null>(null);
async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
setError(null);
const formData = new FormData(e.currentTarget);
const res = await signIn.email({
email: formData.get("email") as string,
password: formData.get("password") as string,
});
if (res.error) {
setError(res.error.message || "Something went wrong.");
} else {
router.push("/dashboard");
}
}
return (
<main className="max-w-md h-screen flex items-center justify-center flex-col mx-auto p-6 space-y-4 text-white">
<h1 className="text-2xl font-bold">Sign In</h1>
{error && <p className="text-red-500">{error}</p>}
</main>
);
}
最后,添加用于输入电子邮件和密码的表单字段以及登录按钮。
¥Finally, add the form fields for email and password and a sign-in button.
"use client";
import { useState } from "react";
import { useRouter } from "next/navigation";
import { signIn } from "@/lib/auth-client";
export default function SignInPage() {
const router = useRouter();
const [error, setError] = useState<string | null>(null);
async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
setError(null);
const formData = new FormData(e.currentTarget);
const res = await signIn.email({
email: formData.get("email") as string,
password: formData.get("password") as string,
});
if (res.error) {
setError(res.error.message || "Something went wrong.");
} else {
router.push("/dashboard");
}
}
return (
<main className="max-w-md h-screen flex items-center justify-center flex-col mx-auto p-6 space-y-4 text-white">
<h1 className="text-2xl font-bold">Sign In</h1>
{error && <p className="text-red-500">{error}</p>}
<form onSubmit={handleSubmit} className="space-y-4">
<input
name="email"
type="email"
placeholder="Email"
required
className="w-full rounded-md bg-neutral-900 border border-neutral-700 px-3 py-2"
/>
<input
name="password"
type="password"
placeholder="Password"
required
className="w-full rounded-md bg-neutral-900 border border-neutral-700 px-3 py-2"
/>
<button
type="submit"
className="w-full bg-white text-black font-medium rounded-md px-4 py-2 hover:bg-gray-200"
>
Sign In
</button>
</form>
</main>
);
}
5.3.仪表板页面
¥5.3. Dashboard page
这是经过身份验证的用户的受保护页面。从 src/app/dashboard/page.tsx
中的基本组件开始。
¥This is the protected page for authenticated users. Start with the basic component in src/app/dashboard/page.tsx
.
"use client";
export default function DashboardPage() {
return (
<main className="max-w-md h-screen flex items-center justify-center flex-col mx-auto p-6 space-y-4 text-white">
<h1 className="text-2xl font-bold">Dashboard</h1>
</main>
);
}
从 Better-Auth 客户端导入 useSession
钩子。此钩子是管理客户端身份验证状态的关键。它提供会话数据和待处理状态。
¥Import the useSession
hook from your Better-Auth client. This hook is the key to managing authentication state on the client side. It provides the session data and a pending status.
"use client";
import { useRouter } from "next/navigation";
import { useSession } from "@/lib/auth-client";
export default function DashboardPage() {
const router = useRouter();
const { data: session, isPending } = useSession();
return (
<main className="max-w-md h-screen flex items-center justify-center flex-col mx-auto p-6 space-y-4 text-white">
<h1 className="text-2xl font-bold">Dashboard</h1>
</main>
);
}
要保护此路由,请使用 useEffect
钩子。此操作会检查会话是否已加载(!isPending
)以及是否存在经过身份验证的用户(!session?.user
)。如果两者都是,则会将用户重定向到登录页面。
¥To protect this route, use a useEffect
hook. This effect checks if the session has loaded (!isPending
) and if there is no authenticated user (!session?.user
). If both are true, it redirects the user to the sign-in page.
"use client";
import { useRouter } from "next/navigation";
import { useSession } from "@/lib/auth-client";
import { useEffect } from "react";
export default function DashboardPage() {
const router = useRouter();
const { data: session, isPending } = useSession();
//add-start: redirect if not signed in
useEffect(() => {
if (!isPending && !session?.user) {
router.push("/sign-in");
}
}, [isPending, session, router]);
return (
<main className="max-w-md h-screen flex items-center justify-center flex-col mx-auto p-6 space-y-4 text-white">
<h1 className="text-2xl font-bold">Dashboard</h1>
</main>
);
}
为了提供更好的用户体验,请在验证会话时添加加载和重定向状态。
¥To provide a better user experience, add loading and redirecting states while the session is being verified.
"use client";
import { useRouter } from "next/navigation";
import { useSession } from "@/lib/auth-client";
import { useEffect } from "react";
export default function DashboardPage() {
const router = useRouter();
const { data: session, isPending } = useSession();
useEffect(() => {
if (!isPending && !session?.user) {
router.push("/sign-in");
}
}, [isPending, session, router]);
//add-start: loading and redirect states
if (isPending)
return <p className="text-center mt-8 text-white">Loading...</p>;
if (!session?.user)
return <p className="text-center mt-8 text-white">Redirecting...</p>;
return (
<main className="max-w-md h-screen flex items-center justify-center flex-col mx-auto p-6 space-y-4 text-white">
<h1 className="text-2xl font-bold">Dashboard</h1>
</main>
);
}
最后,如果用户已通过身份验证,则从 session
对象显示其名称和电子邮件地址。此外,导入 signOut
函数并添加一个调用该函数的按钮,允许用户注销。
¥Finally, if the user is authenticated, display their name and email from the session
object. Also, import the signOut
function and add a button that calls it, allowing the user to log out.
"use client";
import { useRouter } from "next/navigation";
import { useSession, signOut } from "@/lib/auth-client";
import { useEffect } from "react";
export default function DashboardPage() {
const router = useRouter();
const { data: session, isPending } = useSession();
useEffect(() => {
if (!isPending && !session?.user) {
router.push("/sign-in");
}
}, [isPending, session, router]);
if (isPending)
return <p className="text-center mt-8 text-white">Loading...</p>;
if (!session?.user)
return <p className="text-center mt-8 text-white">Redirecting...</p>;
//add-start: destructure user from session
const { user } = session;
return (
<main className="max-w-md h-screen flex items-center justify-center flex-col mx-auto p-6 space-y-4 text-white">
<h1 className="text-2xl font-bold">Dashboard</h1>
<p>Welcome, {user.name || "User"}!</p>
<p>Email: {user.email}</p>
{/* add-start: sign out button */}
<button
onClick={() => signOut()}
className="w-full bg-white text-black font-medium rounded-md px-4 py-2 hover:bg-gray-200"
>
Sign Out
</button>
</main>
);
}
5.4.主页
¥5.4. Home page
最后,更新主页,以便轻松导航至登录和注册页面。将 src/app/page.tsx
的内容替换为以下内容:
¥Finally, update the home page to provide simple navigation to the sign-in and sign-up pages. Replace the contents of src/app/page.tsx
with the following:
"use client";
import { useRouter } from "next/navigation";
export default function Home() {
const router = useRouter();
return (
<main className="flex items-center justify-center h-screen bg-neutral-950 text-white">
<div className="flex gap-4">
<button
onClick={() => router.push("/sign-up")}
className="bg-white text-black font-medium px-6 py-2 rounded-md hover:bg-gray-200">
Sign Up
</button>
<button
onClick={() => router.push("/sign-in")}
className="border border-white text-white font-medium px-6 py-2 rounded-md hover:bg-neutral-800">
Sign In
</button>
</div>
</main>
);
}
6. 测试一下
¥ Test it out
你的应用现已完全配置。
¥Your application is now fully configured.
-
启动开发服务器进行测试:
¥Start the development server to test it:
npm run dev
-
在浏览器中导航到
http://localhost:3000
。你应该看到带有 "注册" 和 "登录" 按钮的主页。¥Navigate to
http://localhost:3000
in your browser. You should see the home page with "Sign Up" and "Sign In" buttons. -
点击“注册”,创建一个新账户,你将被重定向到仪表板。然后你可以退出并重新登录。
¥Click on Sign Up, create a new account, and you should be redirected to the dashboard. You can then sign out and sign back in.
-
要直接在数据库中查看用户数据,你可以使用 Prisma Studio。
¥To view the user data directly in your database, you can use Prisma Studio.
npx prisma studio
-
这将在你的浏览器中打开一个新标签页,你可以在其中查看
User
、Session
和Account
表及其内容。¥This will open a new tab in your browser where you can see the
User
,Session
, andAccount
tables and their contents.
恭喜!现在,你拥有一个使用 Better-Auth、Prisma 和 Next.js 构建的功能齐全的身份验证系统。
¥Congratulations! You now have a fully functional authentication system built with Better-Auth, Prisma, and Next.js.
下一步
¥Next steps
-
添加对社交登录或魔术链接的支持
¥Add support for social login or magic links
-
实现密码重置和电子邮件验证
¥Implement password reset and email verification
-
添加用户个人资料和账户管理页面
¥Add user profile and account management pages
-
部署到 Vercel 并保护你的环境变量
¥Deploy to Vercel and secure your environment variables
-
使用自定义应用模型扩展你的 Prisma 模式
¥Extend your Prisma schema with custom application models
延伸阅读
¥Further reading
Stay connected with Prisma
Continue your Prisma journey by connecting with our active community. Stay informed, get involved, and collaborate with other developers:
- Follow us on X for announcements, live events and useful tips.
- Join our Discord to ask questions, talk to the community, and get active support through conversations.
- Subscribe on YouTube for tutorials, demos, and streams.
- Engage on GitHub by starring the repository, reporting issues, or contributing to an issue.