Next.js全栈Todo应用开发

Next.js全栈Todo应用开发

课程概览

Next.js全栈Todo应用开发

连接 Vercel Postgres 数据库

在本节中,我们将学习如何配置和使用 Vercel Postgres 数据库服务,它是 Vercel 提供的全托管 PostgreSQL 数据库,专为 Next.js 应用设计,提供无缝集成体验。

Vercel Postgres 简介

Vercel Postgres 是 Vercel 与 Neon 合作推出的托管 PostgreSQL 数据库服务,具有以下特点:

  • 全托管服务,无需维护数据库服务器
  • 无服务器架构,自动扩展
  • 全球边缘缓存,提供低延迟访问
  • 与 Vercel 部署环境的无缝集成
  • 自动备份和恢复功能
  • 内置连接池和查询指标

创建 Vercel Postgres 数据库

1. 创建数据库实例

  1. 登录你的 Vercel 账号
  2. 在项目仪表板中,点击 "Storage" 标签
  3. 选择 "Connect Database" 或 "Create New"
  4. 选择 "Postgres" 数据库类型
  5. 命名你的数据库(例如 todo-app-db
  6. 选择部署区域(尽量选择靠近你的用户的区域)
  7. 点击 "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 的数据库指标监控功能:

  1. 在 Vercel 仪表板中,打开你的数据库
  2. 查看 "Metrics" 选项卡
  3. 监控连接数、查询执行时间和错误率

2. 优化查询性能

一些优化数据库性能的技巧:

  1. 使用索引:在经常查询的字段上添加索引
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]) }
  1. 优化查询:只选择需要的字段
// 只选择需要的字段 const todosWithLimitedFields = await prisma.todo.findMany({ where: { userId }, select: { id: true, title: true, completed: true, // 不包含其他字段 }, });
  1. 分页查询:使用光标分页而不是偏移分页
// 光标分页(更高效) 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 支持为每个预览部署创建数据库分支,适合团队开发:

  1. 在 Vercel 控制台数据库设置中启用"Preview Branch"
  2. 每个预览部署会自动获得独立的数据库实例
  3. 可以在预览环境中测试数据库更改而不影响生产数据

2. 数据库备份与恢复

定期备份数据是良好实践:

  1. 在 Vercel 数据库仪表板中,转到 "Backups" 标签
  2. 配置自动备份频率
  3. 需要时可以从备份中恢复数据库

数据库安全最佳实践

  1. 永不公开连接字符串:确保连接字符串只存在于环境变量中
  2. 使用最小权限账户:为不同环境使用不同权限的账户
  3. 启用 SSL 连接:确保所有连接都使用 SSL
  4. 定期审计访问日志:检查可疑活动
  5. 实施数据验证:在 API 层验证所有输入数据,防止注入攻击

通过 Vercel Postgres,你可以获得高性能、全托管的数据库服务,与 Next.js 应用无缝集成,大大简化了数据库的管理和维护工作。