Next.js全栈Todo应用开发
连接 Vercel Postgres 数据库
在本节中,我们将学习如何配置和使用 Vercel Postgres 数据库服务,它是 Vercel 提供的全托管 PostgreSQL 数据库,专为 Next.js 应用设计,提供无缝集成体验。
Vercel Postgres 简介
Vercel Postgres 是 Vercel 与 Neon 合作推出的托管 PostgreSQL 数据库服务,具有以下特点:
- 全托管服务,无需维护数据库服务器
- 无服务器架构,自动扩展
- 全球边缘缓存,提供低延迟访问
- 与 Vercel 部署环境的无缝集成
- 自动备份和恢复功能
- 内置连接池和查询指标
创建 Vercel Postgres 数据库
1. 创建数据库实例
- 登录你的 Vercel 账号
- 在项目仪表板中,点击 "Storage" 标签
- 选择 "Connect Database" 或 "Create New"
- 选择 "Postgres" 数据库类型
- 命名你的数据库(例如
todo-app-db
) - 选择部署区域(尽量选择靠近你的用户的区域)
- 点击 "Create" 创建数据库
2. 获取连接信息
创建数据库后,Vercel 会自动为你的项目环境生成密钥和连接信息。你可以在数据库页面查看:
- 数据库名称、主机、端口
- 用户名和密码
- 连接字符串
- 连接池配置
3. 环境变量配置
Vercel 会自动将数据库连接信息添加到项目的环境变量中:
POSTGRES_URL
: 主连接字符串POSTGRES_URL_NON_POOLING
: 非连接池连接字符串POSTGRES_USER
: 数据库用户名POSTGRES_PASSWORD
: 数据库密码POSTGRES_HOST
: 数据库主机POSTGRES_DATABASE
: 数据库名称
这些环境变量会自动在你的 Vercel 部署环境中可用。
配置 Prisma 使用 Vercel Postgres
1. 更新 Prisma 配置
修改 prisma/schema.prisma
文件,将数据源配置为使用环境变量:
// prisma/schema.prisma generator client { provider = "prisma-client-js" } datasource db { provider = "postgresql" url = env("POSTGRES_PRISMA_URL") // 使用连接池的 URL directUrl = env("POSTGRES_URL_NON_POOLING") // 直接连接 URL,用于数据库迁移 } // 模型定义...
2. 创建数据库连接配置
创建一个工具函数来配置数据库连接:
// lib/db.ts import { PrismaClient } from "@prisma/client"; // 防止开发环境热重载创建多个实例 const globalForPrisma = global as unknown as { prisma: PrismaClient }; export const prisma = globalForPrisma.prisma || new PrismaClient({ log: process.env.NODE_ENV === "development" ? ["query", "error", "warn"] : ["error"], }); if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma;
3. 本地开发环境配置
为本地开发环境创建 .env
文件,使用 Vercel 提供的连接字符串:
# .env
POSTGRES_PRISMA_URL="postgres://username:password@host:port/database?pgbouncer=true"
POSTGRES_URL_NON_POOLING="postgres://username:password@host:port/database"
你可以从 Vercel 仪表板复制这些连接字符串。
初始化和迁移数据库
1. 创建和应用迁移
使用 Prisma 的迁移工具初始化数据库:
# 创建迁移 npx prisma migrate dev --name init # 部署迁移(生产环境) npx prisma migrate deploy
2. 设置自动迁移
在 Vercel 部署时,你可以通过构建命令自动运行迁移:
// package.json { "scripts": { "vercel-build": "prisma migrate deploy && next build", "postinstall": "prisma generate" } }
这将确保每次部署时数据库模式都会更新。
使用 Vercel Postgres 数据库
1. 基本 CRUD 操作
使用 Prisma Client 操作数据库:
// app/api/todos/route.ts import { prisma } from "@/lib/db"; import { NextResponse } from "next/server"; import { auth } from "@/auth"; export async function GET() { const session = await auth(); if (!session?.user) { return NextResponse.json({ error: "未授权" }, { status: 401 }); } const todos = await prisma.todo.findMany({ where: { userId: session.user.id, }, orderBy: { createdAt: "desc", }, }); return NextResponse.json(todos); }
2. 使用 Prisma 事务
对于需要原子性的操作,使用事务:
// 创建用户和他的第一个待办事项 async function createUserWithTodo( name: string, email: string, todoTitle: string ) { return prisma.$transaction(async (tx) => { // 创建用户 const user = await tx.user.create({ data: { name, email, }, }); // 创建待办事项 const todo = await tx.todo.create({ data: { title: todoTitle, userId: user.id, }, }); return { user, todo }; }); }
高级数据库操作
1. 复杂查询和聚合
Prisma 支持复杂查询和聚合:
// 获取用户的待办事项统计 async function getTodoStats(userId: string) { const stats = await prisma.todo.groupBy({ by: ["completed"], where: { userId, }, _count: { _all: true, }, }); // 处理结果 const completed = stats.find((s) => s.completed)?._count._all || 0; const incomplete = stats.find((s) => !s.completed)?._count._all || 0; // 计算完成率 const total = completed + incomplete; const completionRate = total > 0 ? (completed / total) * 100 : 0; return { total, completed, incomplete, completionRate: Math.round(completionRate), }; }
2. 处理关系查询
Prisma 使关系查询变得简单:
// 获取用户及其所有待办事项 async function getUserWithTodos(userId: string) { return prisma.user.findUnique({ where: { id: userId }, include: { todos: { orderBy: { createdAt: "desc" }, }, }, }); }
监控和优化数据库性能
1. 监控查询性能
使用 Vercel 的数据库指标监控功能:
- 在 Vercel 仪表板中,打开你的数据库
- 查看 "Metrics" 选项卡
- 监控连接数、查询执行时间和错误率
2. 优化查询性能
一些优化数据库性能的技巧:
- 使用索引:在经常查询的字段上添加索引
model Todo { id String @id @default(cuid()) title String userId String completed Boolean @default(false) createdAt DateTime @default(now()) user User @relation(fields: [userId], references: [id]) @@index([userId]) @@index([completed]) }
- 优化查询:只选择需要的字段
// 只选择需要的字段 const todosWithLimitedFields = await prisma.todo.findMany({ where: { userId }, select: { id: true, title: true, completed: true, // 不包含其他字段 }, });
- 分页查询:使用光标分页而不是偏移分页
// 光标分页(更高效) const todosPage = await prisma.todo.findMany({ where: { userId }, take: 10, cursor: cursor ? { id: cursor } : undefined, orderBy: { createdAt: "desc" }, }); // 获取下一页的光标 const nextCursor = todosPage.length === 10 ? todosPage[9].id : null;
3. 连接池配置
使用连接池可以优化数据库连接管理:
// prisma/schema.prisma datasource db { provider = "postgresql" url = env("POSTGRES_PRISMA_URL") // 使用带连接池的URL }
使用 Vercel Postgres 的团队协作
1. 数据库分支
Vercel 支持为每个预览部署创建数据库分支,适合团队开发:
- 在 Vercel 控制台数据库设置中启用"Preview Branch"
- 每个预览部署会自动获得独立的数据库实例
- 可以在预览环境中测试数据库更改而不影响生产数据
2. 数据库备份与恢复
定期备份数据是良好实践:
- 在 Vercel 数据库仪表板中,转到 "Backups" 标签
- 配置自动备份频率
- 需要时可以从备份中恢复数据库
数据库安全最佳实践
- 永不公开连接字符串:确保连接字符串只存在于环境变量中
- 使用最小权限账户:为不同环境使用不同权限的账户
- 启用 SSL 连接:确保所有连接都使用 SSL
- 定期审计访问日志:检查可疑活动
- 实施数据验证:在 API 层验证所有输入数据,防止注入攻击
通过 Vercel Postgres,你可以获得高性能、全托管的数据库服务,与 Next.js 应用无缝集成,大大简化了数据库的管理和维护工作。