Next.js、Prisma 和 MySQL 实践示例

1. 环境设置

a. 创建 Next.js 项目

首先,使用下面的命令创建一个新的 Next.js 应用:

npx create-next-app@latest my-next-prisma-app  

cd my-next-prisma-app  

b. 安装依赖

安装 Prisma 和 MySQL 驱动程序:

npm install @prisma/client prisma mysql  

c. 初始化 Prisma

初始化 Prisma,并创建一个 Prisma 配置文件:

npx prisma init  

这将创建一个 prisma 文件夹,其中包含 schema.prisma 文件。

d. 环境变量

在 .env 文件中配置数据库连接字符串:

DATABASE_URL="mysql://user:password@localhost:3306/mydb?connection_limit=10"  

2. 设置 Prisma 模型

在 prisma/schema.prisma 文件中定义数据模型。例如,一个简单的用户模型:

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  
  createdAt DateTime @default(now())  
  updatedAt DateTime @updatedAt  
}  

运行以下命令生成数据库迁移并更新数据库结构:

npx prisma migrate dev --name init  

npx prisma generate

查看数据库状态(可选):

可以使用以下命令查看数据库的当前状态:

npx prisma studio  

3. 使用中间件处理连接

为了避免连接泄漏,使用 Prisma 中间件来处理连接。可以创建一个 db/prisma.ts 文件:

// data/prisma.ts  
import { PrismaClient } from '@prisma/client';

class PrismaInstance {
  private static instance: PrismaClient | undefined;

  private constructor() { }

  public static getInstance(): PrismaClient {
    if (!PrismaInstance.instance) {
      PrismaInstance.instance = new PrismaClient();
    }
    return PrismaInstance.instance;
  }
}

// 导出 Prisma 实例获取方法
export const prisma = PrismaInstance.getInstance();

在上面的代码中,确保多个请求不会重复创建 Prisma 实例。

4. 创建 API 路由

在 pages/api 文件夹中创建 API 路由。例如,创建 users/route.ts 文件处理用户的 CRUD 操作:

// pages/api/users/route.ts  
import { NextRequest, NextResponse } from 'next/server';
import { prisma } from '@/db/prisma';

export async function GET(request: NextRequest) {
  const { searchParams } = new URL(request.url);
  const id = searchParams.get('id');

  if (id) {
    // 获取特定用户
    const user = await prisma.user.findUnique({
      where: { id: Number(id) },
    });

    if (!user) {
      return NextResponse.json({ error: 'User not found' }, { status: 404 });
    }

    return NextResponse.json(user);
  } else {
    // 获取所有用户
    const users = await prisma.user.findMany();
    return NextResponse.json(users);
  }
}

export async function POST(request: NextRequest) {
  const { name, email } = await request.json();
  const newUser = await prisma.user.create({
    data: { name, email },
  });
  return NextResponse.json(newUser, { status: 201 });
}

export async function PUT(request: NextRequest) {
  const { searchParams } = new URL(request.url);
  const id = searchParams.get('id');

  if (!id) {
    return NextResponse.json({ error: 'User ID is required' }, { status: 400 });
  }

  const { name, email } = await request.json();
  const updatedUser = await prisma.user.update({
    where: { id: Number(id) },
    data: { name, email },
  });
  return NextResponse.json(updatedUser);
}

export async function DELETE(request: NextRequest) {
  const { searchParams } = new URL(request.url);
  const id = searchParams.get('id');

  if (!id) {
    return NextResponse.json({ error: 'User ID is required' }, { status: 400 });
  }

  await prisma.user.delete({
    where: { id: Number(id) },
  });
  return new NextResponse(null, { status: 204 });
}

5. 创建前端页面

可以在 app/page.tsx 中创建一个简单的用户列表和表单来添加用户:

// app/page.tsx  
'use client';

import { useEffect, useState } from 'react';

interface User {
  id: string;
  name: string;
  email: string;
}

const Home = () => {
  const [users, setUsers] = useState<User[]>([]);
  const [name, setName] = useState('');
  const [email, setEmail] = useState('');

  const fetchUsers = async () => {
    try {
      const response = await fetch('/api/users');
      if (!response.ok) {
        throw new Error('Failed to fetch users');
      }
      const data = await response.json();
      setUsers(data);
    } catch (error) {
      console.error('Error fetching users:', error);
    }
  };

  const addUser = async (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    try {
      const response = await fetch('/api/users', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({ name, email }),
      });
      if (!response.ok) {
        throw new Error('Failed to add user');
      }
      setName('');
      setEmail('');
      fetchUsers();
    } catch (error) {
      console.error('Error adding user:', error);
    }
  };

  useEffect(() => {
    fetchUsers();
  }, []);

  return (
    <div className="p-4 max-w-2xl mx-auto">
      <h1 className="text-3xl font-bold mb-6 text-center">User List</h1>
      <form onSubmit={addUser} className="mb-8 flex flex-col sm:flex-row gap-4">
        <input
          type="text"
          value={name}
          onChange={(e) => setName(e.target.value)}
          placeholder="Name"
          required
          className="flex-grow p-2 border rounded focus:outline-none focus:ring-2 focus:ring-blue-500"
        />
        <input
          type="email"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
          placeholder="Email"
          required
          className="flex-grow p-2 border rounded focus:outline-none focus:ring-2 focus:ring-blue-500"
        />
        <button
          type="submit"
          className="p-2 bg-blue-500 text-white rounded hover:bg-blue-600 transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-blue-700"
        >
          Add User
        </button>
      </form>
      {users.length > 0 ? (
        <ul className="space-y-2">
          {users.map((user) => (
            <li key={user.id} className="p-3 bg-gray-100 rounded shadow">
              <span className="font-semibold">{user.name}</span> - {user.email}
            </li>
          ))}
        </ul>
      ) : (
        <p className="text-center text-gray-500">No users found. Add a user to get started!</p>
      )}
    </div>
  );
};

export default Home;


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值