Prisma 无论是结合 NextJs 还是 NestJs 都太好用了,妥妥的 Node 全栈开发利器 🥰🥰🥰

5,636 阅读6分钟

通常我们都想开发一个全栈的项目,例如一个个人的技术博客等等,但是有可能受限于不会后端语言,也不懂数据库这些的操作,所以也就只能想想了。

Prisma 和 Next.js 的出现,的确大大简化了全栈开发的流程,特别是对于那些不熟悉后端语言和数据库操作的开发者。它们的设计理念和集成能力,使得开发者可以专注于业务逻辑和用户体验,而不需要深入理解传统的后端开发细节。

对于不熟悉数据库操作的开发者,Prisma 提供了一种简单、直观的方式来进行数据库交互。

  1. 自动生成的数据库模型:Prisma 允许你在 schema.prisma 文件中定义数据模型,通过简单的 DSL(领域特定语言)来描述表和字段的关系,不需要编写复杂的 SQL。

  2. 类型安全:Prisma 生成的数据库客户端自动带有类型支持(TypeScript),这意味着在开发时可以获得智能提示和类型检查,减少了错误的可能性。

  3. 数据库迁移管理: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 镜像创建成功了:

20241105092540

初始化 Prisma

前面的步骤我们已经成功创建了一个 mysql 的本地服务,接下来我们要初始化 prisma 了:

 pnpm add prisma @prisma/client

首先我们需要安装相关的依赖包,安装完成之后执行如下命令:

npx prisma init

完成之后它会给我们创建了这些文件:

20241105092908

并且在终端里会有以下的输出:

20241105092934

首先我们要修改我们这两个文件,如下图所示:

20241105093102

最终 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?
}

在这个模型中:

  1. User 有 id、name 和 email 字段,并且和 Post 表存在一对多的关系。

  2. Post 包含 id、title、content、published 等字段,并通过 authorId 与 User 表建立关系。

定义好数据模型后,运行 Prisma 的迁移命令,将模型转换为数据库中的表结构。

npx prisma migrate dev --name init

此命令会生成迁移文件,并在数据库中创建 User 和 Post 表。

20241105095032

上图表示我们初始化成功了:

20241105095120

也为我们成功地创建了两个表了。

在 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 }
    );
  }
}

在上面的代码中我们定义了两个请求,如下:

  1. GET 请求:用于获取所有用户数据。通过 prisma.user.findMany() 查询数据库中的所有用户,并返回用户列表。如果查询失败,则返回一个错误消息。

  2. 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>
  );
}

20241105100201

我们添加用户之后,数据也被我们添加到 mysql 数据库中永久存储了。

20241105100235

总结

Prisma 结合 Next.js 做全栈开发,可以高效管理数据库,提供类型安全的数据操作,简化前后端的交互。Next.js 内置 API 路由,无需额外的后端框架就能实现完整的全栈功能。Prisma 的迁移工具和自动生成的查询接口让数据库管理更加便捷,适合快速迭代和扩展应用。

最后再来提一下这两个开源项目,它们都是我们目前正在维护的开源项目:

如果你想参与进来开发或者想进群学习,可以添加我微信 yunmz777,后面还会有很多需求,等这个项目完成之后还会有很多新的并且很有趣的开源项目等着你。