Supabase+PostGraphile环境下GraphQL Mutation无法工作求助
使用Supabase作为数据库、PostGraphile提供GraphQL服务、Nuxt.js构建UI的项目中,GraphQL查询功能正常,但表单对应的GraphQL Mutation无法运行,已确认拥有数据库完整权限。
模板代码
<template> <div> <form method="POST" @submit.prevent="createAgreement()"> <nav class="navbar navbar-dark bg-dark"> <div class="container-fluid"> <a class="navbar-brand"> <input type="reset" class="btn btn-warning" value="Reset"></a> <a class="navbar-brand"> <input type="submit" class="btn btn-warning" value="Save Agreement"></a> </div> </nav> <br> <div class="row"> <div class="col-3"> <!-- Tab navs --> <div id="v-tabs-tab" class="nav flex-column nav-tabs text-center" role="tablist" aria-orientation="vertical"> <a id="v-tabs-home-tab" class="nav-link active" data-mdb-toggle="tab" href="#v-tabs-home" role="tab" aria-controls="v-tabs-home" aria-selected="true">Create A New Agreement</a> </div> <!-- Tab navs --> </div> <div class="col-9"> <div id="v-tabs-tabContent" class="tab-scope"> <div id="v-tabs-home" class="tab-pane fade show active" role="tabpanel" aria-labelledby="v-tabs-home-tab"> <div class="table table-responsive"> <table class="table"> <tbody> <tr> <td style="text-align: right;">Agreement Name</td> <td> <input v-model="name" type="text" required /> </td> </tr> <tr> <td style="text-align: right;">Agreement Type</td> <td> <input v-model="type" type="text" name="Type" /> </td> </tr> </tbody> </table> </div> <br><br> <div id="accordionExample" class="accordion"> <div class="accordion-item"> <h2 id="headingOne" class="accordion-header"> <button class="accordion-button" type="button" data-mdb-toggle="collapse" data-mdb-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne"> Content </button> </h2> <div id="collapseOne" class="accordion-collapse collapse" aria-labelledby="headingOne" data-mdb-parent="#accordionExample"> <div class="accordion-body"> <div class="table table-responsive"> <table class="table"> <tbody> <tr> <td style="text-align: right;">Excerpt</td> <td> <textarea id="excerpt" v-model="excerpt" type="textarea" cols="50" rows="10" value="Add a short Description"></textarea> </td> </tr> <tr> <td style="text-align: right;">Description</td> <td> <textarea id="excerpt" v-model="content" type="textarea" cols="50" rows="10" value="Add a Description"></textarea> </td> </tr> </tbody> </table> </div> </div> </div> </div> <div class="accordion-item"> <h2 id="headingThree" class="accordion-header"> <button class="accordion-button" type="button" data-mdb-toggle="collapse" data-mdb-target="#collapseThree" aria-expanded="false" aria-controls="collapseThree"> Images and Videos </button> </h2> <div id="collapseThree" class="accordion-collapse collapse" aria-labelledby="headingThree" data-mdb-parent="#accordionExample"> <div class="accordion-body"> <td> <input v-model="image" type="image" name="headshot" value="Select an image to upload" help="Select a png, jpg or gif to upload." validation="mime:image/jpeg,image/png,image/gif" /> </td> </div> </div> </div> </div> </div> </div> </div> </div> </form> </div> </template>
脚本代码
<script> import gql from "graphql-tag"; import allAgreementsList from "~/apollo/queries/sales/agreements"; const ADD_AGREEMENTS = gql` mutation ($name:String!,$excerpt:String,$type:String,$content:String,$image:String){ createAgreement(objects: {name: $name, excerpt: $excerpt, type: $type, content: $content, image: $image}) { affected_rows returning { name excerpt type content image } } }`; export default { data() { return { type: [], name: " ", excerpt: " ", content: " ", image: " ", } }, head: { title: 'Add New Agreement' }, methods: { async addAgreement() { const name = this.name; const content = this.content; const excerpt = this.excerpt; const type = this.type; const image = this.image; await this.$apollo.mutate({ mutation: ADD_AGREEMENTS, variables: { name, excerpt, type, content, image, }, update: (cache, { data: { insertAgreements }}) => { // Read data from cache for this query try { const insertedAgreement = insertAgreements.returning; console.log(insertedAgreement) cache.writeQuery({ query: allAgreementsList }) } catch (err) { console.error(err) } } }).then(() => { this.$router.push({path: '../sales/agreements'}) }).catch(err => console.log(err)); this.name = ' '; this.excerpt = ' '; this.type = ' '; this.content = ' '; this.image = ' '; }, } } </script>
问题排查与修复方案
1. 方法名称不匹配(核心触发问题)
模板中表单提交绑定的是createAgreement(),但脚本里定义的处理方法是addAgreement,导致提交时无法触发Mutation逻辑。
修复:将模板中的@submit.prevent="createAgreement()"改为@submit.prevent="addAgreement()"
2. 变量类型不匹配
data中type初始值为数组[],但Mutation定义的$type是String类型,提交时会传递错误类型的参数。
修复:将data中的type: []改为type: ""
3. Mutation字段名称不匹配
Mutation中定义的是createAgreement,但update函数里引用的是insertAgreements,字段名不一致会导致缓存更新失败,同时可能暴露实际后端Mutation名称是否正确。
修复:将update函数中的data: { insertAgreements }改为data: { createAgreement },同步字段名。
4. 文件上传标签类型错误
图片上传使用了type="image",该类型是表单提交按钮,不是文件上传控件,无法获取文件内容。
修复:将图片上传标签改为type="file",并添加accept属性限制文件类型:
<input v-model="image" type="file" name="headshot" accept="image/jpeg,image/png,image/gif" />
5. 初始值空格问题
data中各字段初始值为空格" ",可能导致后端非空校验失败(空格可能被视为无效内容)。
修复:将所有字段初始值改为空字符串""
6. 缓存更新逻辑缺失
原代码中cache.writeQuery仅传入query,未更新数据,导致列表缓存无法同步新增内容。
修复:补充缓存更新逻辑:
update: (cache, { data: { createAgreement }}) => { try { const insertedAgreement = createAgreement.returning; // 读取现有缓存数据 const existingAgreements = cache.readQuery({ query: allAgreementsList }); // 写入新数据 cache.writeQuery({ query: allAgreementsList, data: { agreements: [...existingAgreements.agreements, ...insertedAgreement] } }) } catch (err) { console.error(err) } }
内容的提问来源于stack exchange,提问作者Sebastian

