SvelteKit中如何在lib组件调用Prisma增删改函数?
解决SvelteKit弹窗组件调用Prisma的方案
核心逻辑
Prisma只能在服务端上下文(比如+page.server.js、+server.js)中运行,前端组件(lib目录下的组件属于客户端/通用组件)无法直接导入使用。必须通过服务端动作(Form Actions)或者API端点中转数据库操作,这是唯一合规的实现路径。
方式一:使用SvelteKit Form Actions(推荐)
这是SvelteKit原生推荐的表单处理方案,无需额外编写API路由,直接在页面的服务端文件中定义动作即可。
1. 在+page.server.js中定义增删改动作
import { prisma } from '$lib/prisma'; // 假设你的Prisma实例存放在lib/prisma.js import { fail } from '@sveltejs/kit'; // 原有加载areas的逻辑保留 export async function load() { const areas = await prisma.area.findMany(); return { areas }; } // 定义Form Actions处理数据库操作 export const actions = { createComment: async ({ request }) => { const data = await request.formData(); const author = data.get('author'); const comment = data.get('comment'); const areaId = data.get('areaId'); if (!author || !comment || !areaId) { return fail(400, { error: '必填项不能为空' }); } try { await prisma.comment.create({ data: { author, comment, areaId: parseInt(areaId) } }); return { success: true }; } catch (err) { return fail(500, { error: '创建评论失败' }); } }, editComment: async ({ request }) => { const data = await request.formData(); const id = data.get('id'); const author = data.get('author'); const comment = data.get('comment'); if (!id || !author || !comment) { return fail(400, { error: '必填项不能为空' }); } try { await prisma.comment.update({ where: { id: parseInt(id) }, data: { author, comment } }); return { success: true }; } catch (err) { return fail(500, { error: '编辑评论失败' }); } }, deleteComment: async ({ request }) => { const data = await request.formData(); const id = data.get('id'); if (!id) { return fail(400, { error: '缺少评论ID' }); } try { await prisma.comment.delete({ where: { id: parseInt(id) } }); return { success: true }; } catch (err) { return fail(500, { error: '删除评论失败' }); } } };
2. 修改弹窗组件(Modal.svelte)的提交逻辑
替换原有空实现的handleSave、handleEdit、handleDelete,改为调用上面定义的服务端动作:
<script> import { showModalBackdrop, modalAreaIdStore, modalCommentIdStore, refreshComments, commentToEditStore } from '$lib/stores'; import Swal from 'sweetalert2'; let author = ''; let comment = ''; let showConfirmDeleteButton = false; let files = { accepted: [] }; function handleFilesSelect(event) { files = event.detail; } function clearForm() { author = ''; comment = ''; showConfirmDeleteButton = false; files = { accepted: [] }; } function getComment() { if ($commentToEditStore) { author = $commentToEditStore.author; comment = $commentToEditStore.comment; } } // 通用提交函数 async function submitAction(action, formData) { const response = await fetch('/?/'+action, { method: 'POST', body: formData }); const result = await response.json(); if (result.success) { // 重置状态并刷新数据 $modalAreaIdStore = 0; $modalCommentIdStore = 0; $showModalBackdrop = false; $refreshComments = !$refreshComments; Swal.fire('操作成功'); } else { Swal.fire(result.error); } } const handleSave = () => { if (!author || !comment) { Swal.fire('作者和评论内容不能为空'); return; } const formData = new FormData(); formData.append('author', author); formData.append('comment', comment); formData.append('areaId', $modalAreaIdStore); submitAction('createComment', formData); }; const handleEdit = () => { if (!author || !comment) { Swal.fire('作者和评论内容不能为空'); return; } const formData = new FormData(); formData.append('id', $commentToEditStore.entry_id); formData.append('author', author); formData.append('comment', comment); submitAction('editComment', formData); }; async function handleDelete(id) { const formData = new FormData(); formData.append('id', id); submitAction('deleteComment', formData); } $: $commentToEditStore, getComment(); $: $showModalBackdrop, clearForm(); </script> <!-- 原有弹窗HTML结构无需修改,仅按钮点击逻辑已在script中更新 --> <div class="{$showAddComment ? '' : 'hidden'} fixed top-1/4 left-1/2 -translate-x-1/2 -translate-y-1/2 w-1/3 border-2 rounded-lg bg-slate-100 font-bold addCommentModal" > <div class="m-4"> <div class="text-lg pb-4 border-b-2"> <h1>{$modalAreaIdStore != 0 ? '添加安全项:' : '编辑安全项:'}</h1> </div> <div> <label for="author">作者</label> <input type="text" class="bg-slate-100 w-full rounded-lg p-2 outline outline-blue-500" bind:value={author} /> </div> <div> <label for="comment">评论</label> <textarea name="comment" id="comment" cols="22" rows="5" class="bg-slate-100 w-full border rounded-lg focus:border-white p-2" bind:value={comment} /> </div> <div> <Dropzone on:drop={handleFilesSelect} /> <ol> {#each files.accepted as item} <li>{item.name}</li> <li>{item.size}</li> {/each} </ol> </div> <div> {#if $modalAreaIdStore === 0} <button class="bg-red-500 hover:bg-red-700 text-white py-2 px-4 rounded" on:click={() => (showConfirmDeleteButton = true)} > 删除 </button> {#if showConfirmDeleteButton} <button class="" on:click={() => handleDelete($commentToEditStore.entry_id)}>确认删除</button> {/if} {/if} <div class="float-right"> <button class="m-2 bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded" on:click={$modalCommentIdStore === 0 ? handleSave : handleEdit} > 保存 </button> <button class="m-2 bg-slate-300 hover:bg-slate-500 font-bold py-2 px-4 rounded" on:click={() => { $modalAreaIdStore = 0; $modalCommentIdStore = 0; $showModalBackdrop = false; }} > 取消 </button> </div> </div> </div> </div>
方式二:创建API端点(+server.js)
如果偏好RESTful API模式,可以单独创建API路由处理数据库操作:
1. 创建routes/api/comments/+server.js
import { prisma } from '$lib/prisma'; import { json, error } from '@sveltejs/kit'; export async function POST({ request }) { const { action, data } = await request.json(); try { switch(action) { case 'create': await prisma.comment.create({ data }); return json({ success: true }); case 'edit': await prisma.comment.update({ where: { id: data.id }, data: { author: data.author, comment: data.comment } }); return json({ success: true }); case 'delete': await prisma.comment.delete({ where: { id: data.id } }); return json({ success: true }); default: throw error(400, '无效的动作类型'); } } catch (err) { throw error(500, err.message); } }
2. 修改弹窗组件的提交逻辑
将submitAction改为调用API:
async function submitAction(action, payload) { try { const response = await fetch('/api/comments', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action, data: payload }) }); if (response.ok) { $modalAreaIdStore = 0; $modalCommentIdStore = 0; $showModalBackdrop = false; $refreshComments = !$refreshComments; Swal.fire('操作成功'); } else { const errData = await response.json(); Swal.fire(errData.message || '操作失败'); } } catch (err) { Swal.fire('网络错误,请稍后重试'); } } // 对应调整handleSave、handleEdit、handleDelete的参数 const handleSave = () => { if (!author || !comment) { Swal.fire('作者和评论不能为空'); return; } submitAction('create', { author, comment, areaId: $modalAreaIdStore }); }; const handleEdit = () => { if (!author || !comment) { Swal.fire('作者和评论不能为空'); return; } submitAction('edit', { id: $commentToEditStore.entry_id, author, comment }); }; async function handleDelete(id) { submitAction('delete', { id }); }
额外注意事项
- Prisma实例必须在服务端安全初始化,绝对不能在客户端代码中暴露数据库连接信息
- 文件上传逻辑需在服务端动作中处理,前端仅负责传递文件数据
- 可通过
refreshCommentsstore触发页面数据重新加载,确保操作后界面同步最新状态
内容的提问来源于stack exchange,提问作者Avi4nFLu
相关产品推荐
相关产品推荐

