You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React组件用Prisma展示数据报错:PrismaClient无法在浏览器运行

解决React组件中使用Prisma Client报错"Uncaught Error: PrismaClient is unable to be run in the browser."

问题背景

在React应用的Table组件中直接导入Prisma Client查询数据库,触发错误:Uncaught Error: PrismaClient is unable to be run in the browser.,无法正常展示数据。

错误原因

Prisma Client是仅服务端可用的ORM工具,它需要直接访问数据库文件/连接字符串,而前端代码运行在用户浏览器中,既无法直接连接服务端数据库,也会暴露敏感的数据库配置信息,因此Prisma禁止在浏览器环境中初始化Client。

解决方案

必须通过服务端API接口中转数据:前端发送HTTP请求到服务端,服务端用Prisma操作数据库后返回结果给前端。以下是具体实现步骤:


1. 创建服务端API路由

以Next.js的API路由为例(如果用Express等框架,逻辑类似),在pages/api/historico.js文件中编写接口:

import { prisma } from '../../prisma/prismaScript';

export default async function handler(req, res) {
  // 获取历史数据
  if (req.method === 'GET') {
    try {
      const historico = await prisma.historico.findMany({
        include: { Barcaca: true, Inspetor: true } // 按需关联查询关联表数据
      });
      res.status(200).json(historico);
    } catch (error) {
      res.status(500).json({ error: '获取数据失败' });
    }
  } 
  // 新增数据
  else if (req.method === 'POST') {
    try {
      const newItem = await prisma.historico.create({
        data: req.body
      });
      res.status(201).json(newItem);
    } catch (error) {
      res.status(500).json({ error: '新增数据失败' });
    }
  }
}

2. 修改前端Table组件

移除Prisma导入,改用fetch请求服务端API获取和提交数据,同时修复状态初始化的问题:

import React, { useState, useEffect, Fragment } from "react";
import ReadOnlyRow from "./ReadOnlyRow";
import "./table.css"

const Table = () => {
  const [contacts, setContacts] = useState([]);
  const [addFormData, setAddFormData] = useState({
    data: "",
    barcaca: "",
    inspecao: "",
    inspetor: "",
    anotacoes: "",
    relatorio: ""
  });

  // 组件挂载时获取数据
  useEffect(() => {
    const fetchHistorico = async () => {
      try {
        const response = await fetch('/api/historico');
        const data = await response.json();
        setContacts(data);
      } catch (error) {
        console.error('获取数据失败:', error);
      }
    };
    fetchHistorico();
  }, []);

  const handleAddFormChange = (event) => {
    const fieldName = event.target.getAttribute("name");
    const fieldValue = event.target.value;
    setAddFormData(prev => ({ ...prev, [fieldName]: fieldValue }));
  };

  const handleAddFormSubmit = async (event) => {
    event.preventDefault();

    try {
      const response = await fetch('/api/historico', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          Data: addFormData.data,
          BarcacaId: addFormData.barcaca,
          Inspecao: addFormData.inspecao,
          InspetorId: addFormData.inspetor,
          Anotacoes: parseInt(addFormData.anotacoes),
          Relatorio: addFormData.relatorio
        })
      });

      if (response.ok) {
        const newItem = await response.json();
        setContacts(prev => [...prev, newItem]);
        // 重置表单
        setAddFormData({
          data: "",
          barcaca: "",
          inspecao: "",
          inspetor: "",
          anotacoes: "",
          relatorio: ""
        });
      }
    } catch (error) {
      console.error('新增数据失败:', error);
    }
  };

  return (
    <div className="app-container">
      <form>
        <table className="table table-hover">
          <thead>
            <tr className="table-primary">
              <th>Data</th>
              <th>Barcaça</th>
              <th>Inspeção</th>
              <th>Inspetor</th>
              <th>Anotações</th>
              <th>Relatório</th>
            </tr>
          </thead>
          <tbody>
            {contacts.map((contact) => (
              <Fragment key={contact.ID}>
                  <ReadOnlyRow
                    contact={contact} 
                  />
              </Fragment>
            ))}
          </tbody>
        </table>
      </form>

      <h2>Add a Contact</h2>
      <form onSubmit={handleAddFormSubmit}>
        <input
          type="date"
          name="data"
          required
          placeholder="DD/MM/AAAA"
          value={addFormData.data}
          onChange={handleAddFormChange}
        />
        <input
          type="text"
          name="barcaca"
          required
          placeholder="Barcaça ID"
          value={addFormData.barcaca}
          onChange={handleAddFormChange}
        />
        <input
          type="text"
          name="inspecao"
          required
          placeholder="Inspeção"
          value={addFormData.inspecao}
          onChange={handleAddFormChange}
        />
        <input
          type="text"
          name="inspetor"
          required
          placeholder="Inspetor ID"
          value={addFormData.inspetor}
          onChange={handleAddFormChange}
        />
        <input
          type="number"
          name="anotacoes"
          required
          placeholder="Anotações"
          value={addFormData.anotacoes}
          onChange={handleAddFormChange}
        />
        <input
          type="text"
          name="relatorio"
          required
          placeholder="Relatório"
          value={addFormData.relatorio}
          onChange={handleAddFormChange}
        />
        <button type="submit">Adicionar</button>
      </form>
    </div>
  );
};

export default Table;

关键注意事项

  • 前端永远不能直接操作数据库,所有数据库操作必须通过服务端API完成
  • Prisma Schema中的字段名是大写开头(如Data、BarcacaId),前端提交数据时要严格对应字段名
  • 如果需要处理文件上传,需修改API支持multipart/form-data格式,当前示例简化为文本输入
  • 生产环境需为API添加身份验证,防止非法访问数据库

内容的提问来源于stack exchange,提问作者Hugo Machado Fabris

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 13:50:39