通常我们都想开发一个全栈的项目,例如一个个人的技术博客等等,但是有可能受限于不会后端语言,也不懂数据库这些的操作,所以也就只能想想了。
Prisma 和 Next.js 的出现,的确大大简化了全栈开发的流程,特别是对于那些不熟悉后端语言和数据库操作的开发者。它们的设计理念和集成能力,使得开发者可以专注于业务逻辑和用户体验,而不需要深入理解传统的后端开发细节。
对于不熟悉数据库操作的开发者,Prisma 提供了一种简单、直观的方式来进行数据库交互。
-
自动生成的数据库模型:Prisma 允许你在 schema.prisma 文件中定义数据模型,通过简单的 DSL(领域特定语言)来描述表和字段的关系,不需要编写复杂的 SQL。
-
类型安全:Prisma 生成的数据库客户端自动带有类型支持(TypeScript),这意味着在开发时可以获得智能提示和类型检查,减少了错误的可能性。
-
数据库迁移管理:Prisma 的迁移工具会自动生成数据库迁移文件,只需执行简单的命令,Prisma 会帮你自动创建、修改数据库表的结构,这避免了手动管理迁移文件的麻烦。
通过这些特性,开发者不需要深入理解 SQL 或复杂的数据库操作,也能轻松管理数据结构和数据操作。
而 Next.js 提供了 App Router 和 API 路由,让前端开发者也可以轻松实现后端功能,形成一个真正的全栈项目。:Next.js 内置了 API 路由,允许开发者在 /api 文件夹中创建服务器端函数。这意味着在一个 Next.js 项目中,开发者可以通过简单的函数实现数据处理逻辑,构建后端接口,而不需要搭建额外的后端框架。
最主要的还是 Prisma 可以直接与 Next.js 集成,并且能够在 Next.js 的 API 路由中直接调用。这样,开发者可以通过 Next.js 提供的 API 路由,使用 Prisma 操作数据库,实现后端数据接口,而不需要后端语言的知识。
编写 Docker 创建 mysql 服务
首先我们创建一个简单的 NextJs 服务,这个跟着官网来进行就行:
npx create-next-app@latest
创建完成之后我们需要在项目的根目录里面创建一个 docker-compose.yml 文件并编写如下命令:
version: "3.9"
services:
mysql:
image: mysql:latest
container_name: moment-mysql
environment:
MYSQL_ROOT_PASSWORD: rootpassword # Root 用户的密码
MYSQL_DATABASE: moment # 初始化数据库名称
MYSQL_USER: moment # 数据库用户
MYSQL_PASSWORD: moment # 用户的密码
ports:
- "3306:3306" # 映射 MySQL 的端口
volumes:
- mysql_data:/var/lib/mysql # 数据持久化
volumes:
mysql_data:
编写完成之后我们执行如下命令:
docker compose up -d
最后可以看到我们的 Docker 镜像创建成功了:
初始化 Prisma
前面的步骤我们已经成功创建了一个 mysql 的本地服务,接下来我们要初始化 prisma 了:
pnpm add prisma @prisma/client
首先我们需要安装相关的依赖包,安装完成之后执行如下命令:
npx prisma init
完成之后它会给我们创建了这些文件:
并且在终端里会有以下的输出:
首先我们要修改我们这两个文件,如下图所示:
最终 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 = "mysql"
url = env("DATABASE_URL")
}
model User {
id Int @id @default(autoincrement())
name String
email String @unique
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
author User? @relation(fields: [authorId], references: [id])
authorId Int?
}
在这个模型中:
-
User 有 id、name 和 email 字段,并且和 Post 表存在一对多的关系。
-
Post 包含 id、title、content、published 等字段,并通过 authorId 与 User 表建立关系。
定义好数据模型后,运行 Prisma 的迁移命令,将模型转换为数据库中的表结构。
npx prisma migrate dev --name init
此命令会生成迁移文件,并在数据库中创建 User 和 Post 表。
上图表示我们初始化成功了:
也为我们成功地创建了两个表了。
在 NextJs 中应用
首先我们要在 utils 目录下创建一个 prisma 的实例,并且后续都是使用一个实例:
import { PrismaClient } from "@prisma/client";
const globalForPrisma = global;
export const prisma = globalForPrisma.prisma || new PrismaClient();
if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma;
编写完成之后,我们在 app 目录下创建一个 api 文件,并创建一个 user/router.ts 文件,并编写如下代码:
import { prisma } from "@/utils";
import { NextRequest, NextResponse } from "next/server";
interface User {
id: number;
name: string;
email: string;
createdAt: Date;
}
export async function GET(request: NextRequest): Promise<NextResponse> {
try {
// 获取所有用户
const users: User[] = await prisma.user.findMany();
return NextResponse.json(users, { status: 200 });
} catch (error) {
console.error("Failed to fetch users:", error);
return NextResponse.json(
{ error: "Failed to fetch users" },
{ status: 500 }
);
}
}
export async function POST(request: NextRequest): Promise<NextResponse> {
try {
const body = await request.json();
const { name, email }: { name: string; email: string } = body;
// 创建新的用户
const newUser = await prisma.user.create({
data: {
name,
email,
},
});
return NextResponse.json(newUser, { status: 201 });
} catch (error) {
console.error("Failed to create user:", error);
return NextResponse.json(
{ error: "Failed to create user" },
{ status: 500 }
);
}
}
在上面的代码中我们定义了两个请求,如下:
-
GET 请求:用于获取所有用户数据。通过 prisma.user.findMany() 查询数据库中的所有用户,并返回用户列表。如果查询失败,则返回一个错误消息。
-
POST 请求:用于创建新用户。将请求体中的 name 和 email 解析出来,通过 prisma.user.create() 在数据库中创建新的用户,并返回该用户的数据。如果创建失败,则返回一个错误消息。
这个时候我们简单的后端 api 就编写完成了,我们需要在前端中调用该 api 并编写一个简答的页面;
"use client";
import { useState, useEffect, FormEvent } from "react";
interface User {
id: number;
name: string;
email: string;
createdAt: string;
}
export default function UsersPage() {
const [users, setUsers] = useState<User[]>([]);
const [name, setName] = useState<string>("");
const [email, setEmail] = useState<string>("");
const [error, setError] = useState<string | null>(null);
// 获取所有用户
const fetchUsers = async () => {
try {
const res = await fetch("/api/users");
if (!res.ok) throw new Error("Failed to fetch users");
const data = await res.json();
setUsers(data);
} catch (error) {
console.error(error);
setError("Failed to fetch users");
}
};
// 创建新用户
const createUser = async (e: FormEvent) => {
e.preventDefault();
try {
const res = await fetch("/api/users", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name, email }),
});
if (!res.ok) throw new Error("Failed to create user");
const newUser = await res.json();
setUsers((prevUsers) => [...prevUsers, newUser]);
setName("");
setEmail("");
setError(null);
} catch (error) {
console.error(error);
setError("Failed to create user");
}
};
useEffect(() => {
fetchUsers();
}, []);
return (
<div>
<h1>Users</h1>
{error && <p style={{ color: "red" }}>{error}</p>}
<form onSubmit={createUser}>
<input
type="text"
placeholder="Name"
value={name}
onChange={(e) => setName(e.target.value)}
required
/>
<input
type="email"
placeholder="Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
/>
<button type="submit">Create User</button>
</form>
<ul>
{users.map((user) => (
<li key={user.id}>
<strong>{user.name}</strong> - {user.email} (Created at:{" "}
{new Date(user.createdAt).toLocaleString()})
</li>
))}
</ul>
</div>
);
}
我们添加用户之后,数据也被我们添加到 mysql 数据库中永久存储了。
总结
Prisma 结合 Next.js 做全栈开发,可以高效管理数据库,提供类型安全的数据操作,简化前后端的交互。Next.js 内置 API 路由,无需额外的后端框架就能实现完整的全栈功能。Prisma 的迁移工具和自动生成的查询接口让数据库管理更加便捷,适合快速迭代和扩展应用。
最后再来提一下这两个开源项目,它们都是我们目前正在维护的开源项目:
如果你想参与进来开发或者想进群学习,可以添加我微信 yunmz777,后面还会有很多需求,等这个项目完成之后还会有很多新的并且很有趣的开源项目等着你。