I'm trying to setup the updateNode mutation in graphql with Prisma running on GraphQL-yoga server. Here's the error I'm receiving when I try to run the mutation:
"Variable \"$_v0_data\" got invalid value { data: { name: \"Test\" }, where: { id: \"cjqulnr0yftuh0a71sdkek697\" } }; Field \"data\" is not defined by type CocktailUpdateInput.\nVariable \"$_v0_data\" got invalid value { data: { name: \"Test\" }, where: { id: \"cjqulnr0yftuh0a71sdkek697\" } }; Field \"where\" is not defined by type CocktailUpdateInput."
Here's my mutation resolver:
const Mutation = {
async updateCocktail(parent, args, ctx, info) {
const data = { ...args };
delete data.id;
const where = {id: args.id};
return await ctx.db.mutation.updateCocktail({ data, where }, info);
},
}
datamodel.prisma:
type Cocktail {
id: ID! #unique
name: String!
info: String
glass: Glass
ingredients: [Ingredient]
steps: [Step]
}
schema.graphql
type Mutation {
updateCocktail(data: CocktailUpdateInput!, where: CocktailWhereUniqueInput!): Cocktail
}
and finally here's what I'm trying to execute in playground:
mutation{
updateCocktail(
where: {id: "cjqulnr0y0tuh0a71sdkek697"},
data: {
name: "Test"
}
){
id
name
}
}
If I read your resolver correctly, you resolvers does the following:
Take the args and put them in data (without the id)
Take the id in the args and put it in where
But, in the playground, you give the following args:
args = {
where: {id: "cjqulnr0y0tuh0a71sdkek697"},
data: {
name: "Test"
}
}
They are already well formed! Which means your resolvers is gonna do the step as follow and build the following variables:
data = {
where: {id: "cjqulnr0y0tuh0a71sdkek697"},
data: {
name: "Test"
}
}
where = { id: null }
You can fix this two ways:
1/ Don't rebuild data and where in the resolvers and just pass the args down to prisma
2/ When calling your mutations, give it the args as follow:
updateCocktail(id: "abc", name: "Test") {...}
According to your error, the problem should lie in your playground execution. It is taking your "where" and "data" as data types.
You could try doing something more like this:
mutation UpdateCocktailMutation($data: CocktailUpdateInput!, $where: CocktailWhereUniqueInput!) {
updateCocktail(data: $data, where: $where) {
id
name
}
}
and in your bottom of the playground they have a query variable field.
Fill it will your variable data. Do correct my case sensitivity and naming conventions as I may have missed out on parts of it.
Related
All I want to do is add an item to the items array in my Cart object.
What I am trying to do is simply execute my backend addItem mutation. After that I want to manually update the cache, but for now I am just re-fetching the query because I am unable to even successfully get the query to run.
In this code I am using the pothos withinput plugin: link to docs
I have tried:
Just putting the hardcoded input object into the addItem hook
Listing each Variable out one by one into the addItem hook
Describing the types of each prop in the original gql MUTATION
And passing the hardcoded input into the addItem hook via variables object
Passing hardcoded values into the actual addItem mutation
I have tried inputting the proper typing via a gql tag example below:
const THE_TYPE = gql`input addItemInput {
cartId: String!
id: String!
name: String!
price: Float!
}
`
const MUTATION = gql`
mutation AddItem($input: ${THE_TYPE}!) {
addItem(input: $input){carts{
id
items{
name
}}}
`;
*When I run the following mutation in my graphiql interface it works:
mutation MyMutation{
addItem(input:{
cartId: "2",
id: "12",
name: "New Item!",
price: 1900,
}){
items{
name
}
}}
However when I run the mutation below I get a 400 error:
Error: Response not successful: Received status code 400
import { useQuery, gql, useMutation } from '#apollo/client';
export default function DisplayCarts() {
interface Cart {
id: string;
items: string[];
}
interface Items {
}
const GET_CARTS = gql`
query {
carts{
id
items{
name
}}} `;
const MUTATION = gql`
mutation AddItem($input: Any) {
addItem(input: $input){
carts{
id
items{
name
}}
}}`;
const { loading, error, data } = useQuery(GET_CARTS)
const [addItem] = useMutation(MUTATION, {
refetchQueries: [{ query: GET_CARTS }]
// update(cache, { data: { addItem } }) {
// addItem is the response of the query of add item function
// console.log(data);
// #ts-ignore
// const { carts } = cache.readQuery({ query: GET_CARTS });
// cache.writeQuery({
// query: GET_CARTS,
// data: { carts: [...carts, addItem] }
// })
// }
})
function AddTodo() {
let theInput = {
cartId: "2",
id: "12",
name: "New Item!",
price: 1900,
quantity: 2
}
// #ts-ignore
addItem({ variables: { input: theInput } });
};
Here is my backend resolver function using pothos
Keep in mind my query does work in my graphiql interface so the issue is probably not on the backend
builder.mutationType({
fields: (t) => ({
addItem: t.fieldWithInput({
input: {
cartId: t.input.string({ required: true }),
id: t.input.string({ required: true }),
name: t.input.string({ required: true }),
price: t.input.int({ required: true }),
quantity: t.input.int({ required: true, defaultValue: 1 }),
},
type: Cart,
resolve: (_, { input: { cartId, ...input } }) => {
const cart = CARTS.find((cart) => cart.id === cartId);
if (!cart) {
throw new Error(`Cart with id ${cartId} not found`)
}
return {
id: cartId,
items: [...cart?.items, input]
}
}
}),
}),
})
The problem lies with:
mutation AddItem($input: Any) {
addItem(input: $input){…}
There is no Any in GraphQL. The 400 is a result of an invalid query/mutation. Note that you're not actually running the same mutation that you are in GraphiQL.
Try using an input type for example in your typeDefs (on the server), add:
input addItemInput {
cartId: String!
id: String!
name: String!
price: Float!
}
Then in your client code:
const MUTATION = gql`
mutation AddItem($input: addItemInput) {
addItem(input: $input){…}
}
`
Firstly some necessary information:
When using pothos with input plugin it formulates the query type for you following the following rule: ${ParentType.name}${Field.name}Input. I hoghly recomend you follow the link and look at the docs yourself so you can understand exactly how your query should look.
Here is the link to the corresponding docs
The correct query:
const MUTATION = gql`
mutation AddItem($input:MutationAddItemInput!) {
addItem(input: $input){
items{
name
}
}
}
`;
If you get a 400 error it is probably your query is just wrong
If you get a weird error with in it check your brackets you might be missing one or two
I'm creating a simple CRUD app to learn GraphQL and am using a Supabase postgres instance. All queries and mutations work fine except for one thing, I can't get the id field from my schemas because they are of type int8 on Supabase, and GraphQL only supports Int.
I'm getting this error when I try to get a row's id using the gql Int type in my type defs: GraphQLError: Int cannot represent non-integer value: 1
I know the solution involves creating a custom scalar type as in this example, but I'm not sure how to implement this type. Also, I cannot change this on Supabase's side, so I must find a way to handle this in gql. How can I handle this type in GraphQL?
TypeDefs:
export const typeDefs = `#graphql
type User {
id: Int!
name: String!
email: String!
age: Int!
verified: Boolean!
}
type Todo {
id: Int!
title: String!
description: String!
}
type Query {
# users queries
getAllUsers: [User]
getUser(email: String!): User
# todo queries
getAllTodos: [Todo]
getTodo(id: String!): Todo
}
type Mutation {
createUser(name: String!, email: String!, age: Int!): User
createTodo(title: String!, description: String!): Todo
}
`;
Resolvers:
import { GraphQLScalarType } from 'graphql';
import { prisma } from '../lib/db.js';
const BigInt = new GraphQLScalarType({
// how do I implement this type?
});
export const resolvers = {
BigInt,
Query: {
getAllUsers() {
return prisma.user.findMany();
},
getUser(parent, args) {
return prisma.user.findUnique({
where: {
email: args.email,
},
});
},
getAllTodos() {
return prisma.todo.findMany();
},
getTodo(parent, args) {
return prisma.todo.findUnique({
where: {
id: args.id,
},
});
},
},
// parent, arge are other arguments that get passes to resolvers automatically
Mutation: {
createUser(parent, args) {
return prisma.user.create({
data: args,
});
},
createTodo(parent, args) {
return prisma.todo.create({
data: args,
});
},
},
};
Solved this by using the graphql-type-ints package. You can just install it and then add the type you need to your schemas and resolvers. However, I don't quite understand why we need to do this. If someone could explain why Supabase uses int8 and that doesn't conform to graphql's Int I would appreciate it.
I made my first apollo server to try to understand it and so far I'm testing it with an array I created and I test my mutation with graphQL playground.
My data is a big array Like this:
const lists = [
{
id: 'list-0',
name: 'Example 1',
tasks: [{name:"task1", id:"1", completed: false},{name:"task2", id:"2", completed: true}, ]
},
{
id: 'list-1',
name: 'Example 2',
tasks: [{name:"task1", id:"1", completed: false},{name:"task2", id:"2", completed: true}, ]
},
];
So far I'm able to add a list name, delete a list and get a list by id. What I would like to do is, add a new task object to a list.
My mutation and type are like this:
type Task {
id: String!
name: String!
completed: Boolean!
}
type List {
id: String!
name: String!
tasks: [Task!]!
}
addTask(listId: String!, name: String!): Task!
The function addTask I made for my resolvers is this:
addTask(parents, {listId, name}) {
const newTask = lists.map((list) => {
if (listId === list.id) {
return {
...list,
tasks: [...list.tasks, { name, completed: false, id:"eaz"}],
};
}
console.log("test1", list)
return list
})
console.log("test2", newTask)
return newTask
},
When I use the graphQL playground I target one of my list.id like this one below but the console log tell me tasks: [ [Object], [Object] ] and the graphQL playground bring tell me tasks is null.
mutation Mutation {
addTask(listId: "task-0", name: "eze") {
name
}
}
Am I missing something about graphQL?Should I seperate the array tasks and lists?
Thanks for reading.
In your console logs, newTask looks like this:
[ [Object], [Object] ]
It's an array, not an object, and [].name is undefined, which gets converted to null.
Based on your type definitions, you must return an object that has this structure:
type Task {
id: String!
name: String!
completed: Boolean!
}
If instead, your code did something like this, I think it'll give you want you're saying it's returning:
addTask(parents, { listId, name }) {
let newTask
lists.forEach((list) => {
if (listId === list.id) {
newTask = { name, completed: false, id: 'eaz' }
list.tasks.push(newTask)
}
console.log('test1', list)
return list
})
console.log('test2', newTask)
return newTask
}
newTask here is the object { name, completed: false, id: 'eaz' }
Extra credit:
Alternative to .map or .forEach, which loops through EVERY ITEM NO MATTER WHAT, you can do something a bit more efficient like
addTask(parents, { listId, name }) {
const newTask = { name, completed: false, id: 'eaz' }
const list = lists.find((list) => {
return listId === list.id
}
if (!list) throw new Error('listId not found')
list.tasks.push(newTask)
console.log('test1', list)
console.log('test2', newTask)
return newTask
}
I'm using GraphQL plugin with strapi cms if it matters.
I cannot figure out how to update an existing query using a dynamic variable. My original mutation without using variables:
mutation {
updateExam(input: {
where: {
id: "1234"
},
data: {
questions: "hello"
}
}) {
exam {
questions
}
}
}
I learned that if I would like to create a new entry using variables I should write it like so (answer by David Maze here: How to pass JSON object in grpahql and strapi):
const response = await strap.request('POST', '/graphql', {
data: {
query: `mutation CreateExam($input: CreateExamInput!) {
createExam(input: $input) {
exam { name, desription, time, questions }
}
}`,
variables: {
input: {
name: examInfo.newExamName,
desription: examInfo.newExamDescription,
time: Number(examInfo.newExamTime),
questions: [{ gf: "hello" }],
subjects: [this.state.modalSubjeexisting
}
}
}
});
But how can I update an exising query? Where should I put the
where: {id: "1234"}
How can I provide the existing id of the entry?
I don't know about this strapi cms, but by the way it looks the mutation you have already working, I'd try something like this for the update one:
const response = await strap.request('POST', '/graphql', {
data: {
query: `mutation UpdateExam($input: UpdateExamInput!) {
updateExam(input: $input) {
exam {
questions
}
}
}`,
variables: {
input: {
where: {
id: examInfo.id
},
data: {
questions: [{ gf: "hello" }]
}
}
}
}
});
Give it a try and see if it works.
i am pretty new to GraphQL, getting to become a huge fan :)
But, something is not clear to me. I am using Prisma with and GraphQL-Yoga with Prisma bindings.
I do not know how to pass params from my graphQL server to sub properties. Don't know if this is clear, but i will show it with code, thats hopefully easier :)
These are my types
type User {
id: ID! #unique
name: String!
posts: [Post!]!
}
type Post {
id: ID! #unique
title: String!
content: String!
published: Boolean! #default(value: "false")
author: User!
}
My schema.graphql
type Query {
hello: String
posts(searchString: String): [Post]
users(searchString: String, searchPostsTitle: String): [User]
me(id: ID): User
}
and my users resolver:
import { Context } from "../../utils";
export const user = {
hello: () => "world",
users: (parent, args, ctx: Context, info) => {
return ctx.db.query.users(
{
where: {
OR: [
{
name_contains: args.searchString
},
{
posts_some: { title_contains: args.searchPostsTitle }
}
]
}
},
info
);
},
me: (parent, args, ctx: Context, info) => {
console.log("parent", parent);
console.log("args", args);
console.log("info", info);
console.log("end_________________");
return ctx.db.query.user({ where: { id: args.id } }, info);
}
};
and my posts resolver
import { Context } from "../../utils";
export const post = {
posts: (parent, args, ctx: Context, info) => {
return ctx.db.query.posts(
{
where: {
OR: [
{
title_contains: args.searchString
},
{
content_contains: args.searchString
}
]
}
},
info
);
}
};
so, now :)
I am able to do the following when i am in the GraphQL playground on my prisma service:
{
user(where: {id: "cjhrx5kaplbu50b751a3at99d"}) {
id
name
posts(first: 1, after: "cjhweuosv5nsq0b75yc18wb2v") {
id
title
content
}
}
}
but i cant do it on the server, if i do something like that.. i am getting the error:
"error": "Response not successful: Received status code 400"
this is what i am trying:
{
me(id: "cjhrx5kaplbu50b751a3at99d") {
id
name
posts(first:1) {
id
title
content
}
}
}
does somebody know how i could do that?
since i have a custom type of user, posts does not have params like the generated one. Either i am using the the generated one, or modifying it to look like this:
type User {
id: ID!
name: String!
posts(where: PostWhereInput, orderBy: PostOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [Post!]
}
EDIT 2018 June 4th
# import Post from './generated/prisma.graphql'
type Query {
hello: String
posts(searchString: String): [Post]
users(searchString: String, where: UserWhereInput, orderBy: UserOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [User]
me(id: ID): User
}
type Mutation {
createUser(name: String!): User
createPost(
title: String!
content: String!
published: Boolean!
userId: ID!
): Post
}
I copied the params over from prisma.graphql manually.