参考:https://www.prisma.io/docs/getting-started/prisma-orm/quickstart/postgresql
1、安装依赖
xxxxxxxxxx21npm install prisma @types/node @types/pg --save-dev 2npm install @prisma/client @prisma/adapter-pg pg dotenv2、初始化prisma orm
xxxxxxxxxx11npx prisma init --datasource-provider postgresql --output ../generated/prisma3、编辑.env文件,添加数据库地址
我使用的是neon,它提供5G的存储空间,够用了。使用的是google账号登录的。

每一个新项目最好创建一个新数据库,因为prisma操作会同步数据库,如果使用现有的数据库,会造成冲突。那么就点击create database,会创建一个新数据库。

将这个地址粘贴到.env文件里面即可。

4、在prisma/schema.prisma文件里面,定义data model
5、创建和应用迁移
只要model变更了,就执行这两个命令:
xxxxxxxxxx21npx prisma migrate dev --name init2npx prisma generate6、初始化prisma client
创建lib/prisma.ts
xxxxxxxxxx101import "dotenv/config";2import { PrismaPg } from '@prisma/adapter-pg'3import { PrismaClient } from '../generated/prisma/client'45const connectionString = `${process.env.DATABASE_URL}`67const adapter = new PrismaPg({ connectionString })8const prisma = new PrismaClient({ adapter })910export { prisma }7、接下来就可以在lib文件夹里面写相关的数据库操作代码了,先引入定义好的prismaClient,然后正常写代码。
8、添加模拟数据
注意:
postgresql里面的字段名要采用下划线的形式,不要采用小驼峰的形式,下划线是SQL规范。在prisma里面定义的时候也要注意。
但是这样做之后,在组件代码里面使用字段名有不方便了,JS里面使用下划线比较少见,更多的是小驼峰的形式,这该怎么办呢?
prisma有两个方法来帮助我们,
@@map()方法和@map()方法,在定义表的时候,在表的最后面加上表的复数形式@@map("comments"),这样prisma在postgresql创建表的时候,就会自动将数据表的名称变为comments,而不是大驼峰的形式。
https://www.prisma.io/docs/orm/prisma-schema/data-model/models#defining-models
✅
@@map——模型映射(Model/Table Mapping)作用:指定 Prisma model 对应数据库中的表名。
如果 Prisma 模型名是大驼峰(默认是),但你希望数据库用复数表名或蛇形命名,则使用
@@map.简单来讲,
@@map()里面的参数就是定义model名称的复数形式,你指定是什么就是什么,如果不知道英文单词的复数,就搜索一下即可。
✅
@map——字段映射(Field Mapping)作用:指定 Prisma 字段对应数据库中的实际列名。
默认情况下 Prisma 会直接使用字段名作为数据库列名。如果你使用了小驼峰、想让数据库是下划线,就要用
@map。比如说createdAt,用的时候是这样用,但是在定义的时候,postgresql里面需要定义为下划线的形式,就需要这样指定:createdAt DateTime @default(now()) @map("created_at")。下面是一个model样板。
xxxxxxxxxx151model User {2id String @id @default(cuid())3email String @unique4name String?5image String?6role String @default("USER") // USER / ADMIN7createdAt DateTime @default(now()) @map("created_at")8updatedAt DateTime @updatedAt @map("updated_at")910articles Article[]11comments Comment[]12photos Photo[]1314@@map("users")15}
使用https://mockaroo.com/来创建模拟数据。

需要注意的是datetime,要选择SQL datetime这种类型,否则格式不对。

这里就是SQL的插入语句,复制

在编辑器中修改表名,粘贴到neno SQL Editor里面执行即可。这种方法我试过了,太复杂了,所以还是使用本地编辑器导入csv文件来做吧。
neon的链接地址:
xxxxxxxxxx11DATABASE_URL='postgresql://neondb_owner:npg_PdcV96hSGZFR@ep-misty-meadow-a8g8kxvi-pooler.eastus2.azure.neon.tech/neondb?sslmode=require&channel_binding=require'➤ Host(服务器地址)
xxxxxxxxxx11ep-msty-meadow-a8g8kxvi-pooler.eastus2.azure.neon.tech
➤ Port
Neon 默认 PostgreSQL 端口是:
xxxxxxxxxx115432
➤ Database
xxxxxxxxxx11neondb
➤ Username
xxxxxxxxxx11neondb_owner
➤ Password
xxxxxxxxxx11PdcV96hSGZFR
粘贴进去即可连接。然后导入数据。


注意:
Prisma 遵循 camelCase (驼峰命名法) 定义模型字段,但大多数传统的 PostgreSQL 数据库默认使用 snake_case (下划线命名法) 作为物理列名。所以我在添加数据的时候,如果是这样的语句,会报错
createat column does not exist:xxxxxxxxxx11insert into public."Product" (id, name, description, price, image, stock, sales, category, createAt, updateAt) values (1, 'Portable Solar Generator', 'Eco-friendly generator for outdoor adventures.', 399.99, 'http://dummyimage.com/185x100.png/5fa2dd/ffffff', '67989', '3837', 'Outdoor', '2025-03-12 15:38:53', '2025-07-14 17:13:57');所以需要改为这样
craete_at,update_at,也就是说凡是有小驼峰命名的地方,都要改为下划线命名:xxxxxxxxxx11insert into public."Product" (id, name, description, price, image, stock, sales, category, create_at, update_at) values (1, 'Portable Solar Generator', 'Eco-friendly generator for outdoor adventures.', 399.99, 'http://dummyimage.com/185x100.png/5fa2dd/ffffff', '67989', '3837', 'Outdoor', '2025-03-12 15:38:53', '2025-07-14 17:13:57');
✅ 原因 1:Neon Serverless 会自动断开空闲连接
Neon 是 serverless Postgres,为了节省资源,它会自动关闭长时间空闲的连接。
默认情况(Neon 免费 tier):
| 行为 | Neon 的做法 |
|---|---|
| 连接 5 分钟无活动 | 会自动关闭连接 |
| 空闲到达阈值 | 会 suspend compute(自动休眠) |
| 再次连接 | 需要唤醒 → 所以你看到“重新连接”提示 |
这就是为什么 DBeaver 停在那里不动一会儿之后,连接突然失效。
✅ 原因 2:你使用的是 Neon 的 \pooler\ 端点
你现在的连接串是这样的:
xxxxxxxxxx11...-pooler.eastus2.azure.neon.tech
Neon 的 Pooler 也有自己的超时机制:
| 机制 | 描述 |
|---|---|
pooler_idle_timeout | 连接一段时间不活跃会被关闭 |
pooler_max_client_conn | 超过总连接数会丢弃/挤掉连接 |
➡ 所以 DBeaver 会被踢掉是完全正常的。
✅ 原因 3:DBeaver 默认不会自动保持心跳
DBeaver 对 PostgreSQL 不会默认发心跳包(KeepAlive)。
因此:
= 连接被断开,必须重新连接。

nextjs的Image组件,不能直接访问外部图片链接,需要在next.config.ts里面进行配置:

项目在prisma配置好之后,就要配置better-auth,因为里面有User表,要按照它的来做,所以对后面的很多表都有影响,最先来做是最好的。
Better-Auth 的核心思想是将认证逻辑(Auth Handler, Server)和用户界面逻辑(Auth Client, React Hooks)分离,并利用 Next.js 的路由和中间件进行集成。
我先把整个流程讲解一下,后面再讲解具体操作:
1、数据库模型定义 (Prisma Schema Definition)
这是整个认证系统的基石,作用是告诉 Better-Auth 数据应该以何种结构存储。
| 流程步骤 | 文件示例 | 作用 |
|---|---|---|
| 定义认证相关模型 | prisma/schema.prisma | 🌟 数据结构约定:你定义的 User, Session, Account, 和 Verification 四个核心模型,是 Better-Auth 适配器(Prisma Adapter)用来进行所有数据库操作的蓝图。它决定了用户的身份信息、登录状态、社交账号绑定(GitHub等)以及邮箱验证信息将如何与 PostgreSQL 交互和存储。 |
为什么它很重要?
Better-Auth 依赖于这些特定的模型来处理认证流程:
id, name, email)。providerId 和 accountId),以及邮箱密码的哈希值。为什么按照它的方式来创建这些表呢?我想创建自己的User表不行吗?因为better-auth的原理是这样的:
Better-Auth 的核心实现原理是基于 Tokenless Session Management (无令牌会话管理),结合 Prisma 数据库适配器 来实现全栈身份验证。它遵循了现代 Web 应用中常见的 Session Cookie 机制。
Better-Auth 的三大核心原理
1. Tokenless Session Management(无令牌会话管理)
Better-Auth 避免使用 JWT (JSON Web Tokens) 这种需要在每个请求中传输大量用户数据的传统方式。它的原理更接近于传统的基于 Cookie 的会话:
- 登录时生成会话: 用户登录成功后,Better-Auth 会在数据库的
Session表中创建一个唯一的会话记录。- 颁发安全 Cookie: Better-Auth 然后会创建一个安全的、加密的 Session ID,并将其作为 Cookie 发送到用户的浏览器。这个 Cookie 只包含一个指向数据库中会话记录的随机 ID。
- 验证会话: 在后续的请求中,服务器(包括 Next.js 的服务端组件或 API 路由)通过读取这个 Cookie 中的 ID,然后去数据库中查找完整的会话记录。如果记录存在且未过期,则用户身份得到验证。
- 优势: 这种方式的安全性更高,因为用户数据(如角色、权限)只存储在服务器端的数据库中,而不是暴露在客户端的 Cookie 中。如果需要吊销会话或更改用户权限,可以直接在数据库中操作,即时生效。
2. Prisma Adapter(数据库适配器)
Better-Auth 通过
prismaAdapter将其认证流程与您的 PostgreSQL 数据库连接起来:
- 抽象化操作: Better-Auth 核心库不需要知道您的数据库是 PostgreSQL 还是 MySQL,它只依赖于适配器提供的一组标准 CRUD (增删改查) 函数。
- 模型映射: 适配器负责将 Better-Auth 内部需要的操作(如
createUser,createSession)转换为 Prisma Client 能够理解的、针对您的User,Session,Account等模型的具体查询。3. Next.js 集成机制
Better-Auth 利用 Next.js 的特点,实现服务端和客户端的无缝衔接:
- API Route Handler (
[...all]/route.ts): 这是认证逻辑的入口。Better-Auth 的核心处理函数 (auth.handler) 被封装在 Next.js 的 API 路由中。所有登录、登出、OAuth 回调等操作都通过这个单一路由进行处理。- React Hooks (
useSession): 在客户端,useSessionHook 通过调用 Better-Auth 的 Session API 路由来获取当前会话状态。它管理了 Loading 状态和会话的刷新,使得在 React 组件中获取用户状态变得非常简单。- 服务端 (
auth.api.getSession): 在 Next.js 的 Server Components 或 Middleware 中,可以直接访问原始的 Request Headers 来读取 Session Cookie,然后服务器直接进行数据库查询验证,确保了高性能的 SSR/SSG。简而言之,Better-Auth 的实现原理就是:利用安全的 Session Cookie 作为密钥,通过 Prisma 适配器连接到您的数据库,在服务器端进行身份验证和数据管理,并在 Next.js 的 Server/Client 组件中提供便捷的 Hooks 来消费这些认证状态。
2、后端配置与数据库集成 (Server Setup)
| 流程步骤 | 文件示例 | 作用 |
|---|---|---|
| 初始化 Better-Auth 实例 | lib/auth.ts | 🌟 定义认证规则:这是 Better-Auth 的核心,负责告诉系统如何工作。它配置了:1. 数据库适配器(如 Prisma),用于存储用户、Session 和 Account 数据。2. 认证策略(如 Email/Password, GitHub, Google),用于定义用户登录的方式。 |
3、API 路由集成 (API Handler)
| 流程步骤 | 文件示例 | 作用 |
|---|---|---|
| 创建认证 API 路由 | app/api/auth/[...all]/route.ts | 🌟 处理所有认证请求:这个 Catch-all 路由([...all])将所有发送到 /api/auth/* 的请求(如登录、注册、OAuth 回调、登出等)转发给 Better-Auth 的 Handler 进行处理。它将 Better-Auth 的核心逻辑暴露给 Next.js 应用程序。 |
因为打开页面的时候,nav或header上面的auth-user-menu组件会使用useSession()来查看是否登录了,所以会触发/api/auth/get-session接口调用,这个是better-auth帮我们做的。还有登录、登出的接口,都是这样的形式:/api/auth/xxxxxxxx,所以这里就需要有这个文件,让触发这些接口的时候,转交给better-auth进行处理。
4、前端客户端配置 (Client Setup)
| 流程步骤 | 文件示例 | 作用 |
|---|---|---|
| 创建 Auth Client 实例 | lib/auth-client.ts | 🌟 封装前端交互方法:基于后端配置,创建一个客户端实例。它导出了供 React 组件使用的便捷方法,例如 signIn, signUp, signOut 以及最重要的 useSession Hook。登录、登出、注册、查看是否登录,用到的就是这些方法。这里配置好了之后,在各个组件里面引入使用即可,非常方便。 |
5、前端组件与会话管理 (Usage & Session Management)
| 流程步骤 | 文件示例 | 作用 |
|---|---|---|
| 组件中使用 Hook | app/page.tsx (Client Component) | 🌟 管理用户状态和 UI 切换:通过 useSession() Hook,组件可以获取当前用户的登录状态、用户信息(session.user)和加载状态。这是实现“登录/登出”按钮切换、显示用户名称、以及在客户端保护路由的基础。 |
| 服务端获取 Session | app/dashboard/page.tsx (Server Component) | 🌟 在服务端渲染时获取用户信息:在 Next.js 13/14 的服务端组件中,可以直接调用 auth.api.getSession(),通过读取 Request Headers 中的 Cookie 来预先获取会话信息,实现高效的 SSR 或 SSG。 |
6、编写登录界面
上面的1-4步做好之后,其余的都好说了。
①创建一个登录组件,就是显示登录、登出的button样式的组件,放到nav栏上。里面使用useSession()来查看是否已登录。如果点击登录,就跳转到/sign-in页面;如果点击登出,就调用auth-client里面暴露的signOut方法即可。
②编写app/(auth)/sign-in/page.tsx页面,这个页面UI按照需求编写即可,完全可以是一个普通的form页面。重点是提交表单的时候,要调用auth-client暴露的signIn方法来登录。
③在sign-in页面中,可以加一个跳转链接,跳转到app/(auth)/sign-out/page.tsx页面,这个页面UI也是一个普通的form页面。重点是调用auth-client暴露的signUp方法来注册。
参考https://www.better-auth.com/docs/installation,但是没有专门的nextjs相关的,所以需要结合AI来做。
1、安装依赖npm install better-auth
2、设置环境变量
在.env里面添加:
xxxxxxxxxx21BETTER_AUTH_SECRET=EdIDdrNFl6KnGkF0Hh3WC00lx8h8a3tg2BETTER_AUTH_URL=http://localhost:3000如果需要添加github登录,需要获取GITHUB OAuth凭证后添加:
去 GitHub Developer Settings -> New OAuth App。
Homepage URL: http://localhost:3000
Authorization callback URL: http://localhost:3000/api/auth/callback/github
获取 Client ID 和 Client Secret。
xxxxxxxxxx31# GitHub OAuth2GITHUB_CLIENT_ID=你的Client_ID3GITHUB_CLIENT_SECRET=你的Client_Secret3、创建better-auth实例,配置数据库及prisma适配器
创建/app/lib/auth.ts文件。加入 邮箱密码、GitHub 登录支持和 Prisma 适配器配置。
xxxxxxxxxx251// app/lib/auth.ts23import { betterAuth } from "better-auth";4import { prismaAdapter } from "better-auth/adapters/prisma";5import { prisma } from "./lib/prisma";67export const auth = betterAuth({8 // 配置数据库和prisma适配器9 database: prismaAdapter(prisma, {10 provider: "postgresql", 11 }),12 // 允许邮箱密码登录13 emailAndPassword: { 14 enabled: true, // 启用邮箱密码15 },16 // 社交账号支持,这里启用了github账号的支持17 socialProviders: { 18 github: { 19 clientId: process.env.GITHUB_CLIENT_ID as string, 20 clientSecret: process.env.GITHUB_CLIENT_SECRET as string, 21 }, 22 },23 // 配置密钥24 secret: process.env.BETTER_AUTH_SECRET,25});4、创建better-auth相关的数据库
先安装依赖pnpm add -D @better-auth/cli,这个依赖是用来生成ORM schema的。
然后使用npx @better-auth/cli generate命令来生成model,这个会在现有的schema.prisma里面追加model,可以根据项目需要添加一些字段,但生成的字段最好全部保留。
如果不想使用这种方式,想直接定义model,可以参考:https://www.better-auth.com/docs/concepts/database#core-schema,按照里面来定义即可。
5、API路由集成
创建/app/api/auth/[...all]/route.ts文件,将代码copy进去即可。这个文件就是帮助我们处理better-auth相关的接口。

6、创建better-auth客户端实例
client实例,可以帮助我们与auth服务进行交互,实例上的signIn方法可以帮助我们登录,signOut方法可以帮助我们登出,useSession方法帮助查询是否已登录等等。
这样我们就不用自己定义API接口了,直接使用这些方法即可。
xxxxxxxxxx91// app/lib/auth-client.ts23import { createAuthClient } from "better-auth/react";45export const authClient = createAuthClient({6 baseURL: process.env.BETTER_AUTH_URL,7});89export const { signIn, signOut, useSession, signUp } = authClient;7、编写登录功能
better-auth客户端实例的方法,使用范例在这里:https://www.better-auth.com/docs/basic-usage。
①创建一个登录组件,就是显示登录、登出的button样式的组件,放到nav栏上。里面使用useSession()来查看是否已登录。如果点击登录,就跳转到/sign-in页面;如果点击登出,就调用auth-client里面暴露的signOut方法即可。
②编写app/(auth)/sign-in/page.tsx页面,这个页面UI按照需求编写即可,完全可以是一个普通的form页面。重点是提交表单的时候,要调用auth-client暴露的signIn方法来登录。
③在sign-in页面中,可以加一个跳转链接,跳转到app/(auth)/sign-out/page.tsx页面,这个页面UI也是一个普通的form页面。重点是调用auth-client暴露的signUp方法来注册。
因为nextjs只能做一些简单项目,所以不要担心更复杂的权限,先把这种权限掌握好即可。
使用next-themes库。设置一个 ThemeProvider 组件来包裹您的应用,并使用 useTheme 钩子来切换主题。这个库能自动处理系统偏好设置,并防止页面闪烁。
1、首先在app/layout.tsx里面添加ThemeProvider。

2、编写一个toggle-mode组件,用来切换主题

3、在nav或header上面添加toggle-mode组件,因为这个组件使用了Provider,所以看上去是在别处操作,但是会作用到全局。这就是Provider的好处。

疑问:next-themes是怎么设置dark mode的呢?
xxxxxxxxxx71<ThemeProvider2 attribute="class"3 defaultTheme="system"4 enableSystem5 disableTransitionOnChange>6 {children}7</ThemeProvider>next-themes 的核心功能是根据当前选择的主题(例如 'light', 'dark', 或 'system')来动态地给根 HTML 元素 (<html>) 添加或移除一个 CSS class。
配置 attribute: 当您在 ThemeProvider 中设置 attribute="class" 时,next-themes 知道要操作 class 属性。
应用 Class:
<html> 标签上添加 class="dark"。dark class,或者根据配置添加 class="light"。CSS/Tailwind 的响应: 您的 CSS 样式(特别是使用 Tailwind CSS 时)会监听这个 class。例如,Tailwind CSS 的 dark: 变体样式仅在父元素或祖先元素有 .dark class 时才会被应用。