This is my schema QWS GraphQL schema
type Mutation {
createPost(input: postInput): Post
}
type Post {
id: ID!
title: String!
}
type Query {
singlePost(id: ID!): Post
getPost: [Post]
}
input postInput {
id: ID
title: String
}
schema {
query: Query
mutation: Mutation
}
When I query(add new data) the mutatin with following code
mutation addPost {
createPost(input: {
title: "Test title"
}) {
title
}
}
It's not saving to db and the result showing is
{
"data": {
"createPost": null
}
}
What is the reason and how to solve this
Same happening for list all data query in other API
Related
tldr: How do I write a mutation in GraphQL playground to add an array of strings when creating a new record in the database
I'm using a MongoDB database for an application. I always have trouble finding documentation for how to write queries and mutations in GraphQL apollo playground. In this example, let's take an example of an athlete. An athlete can win many awards. The awards are just an array of strings. I will use a simple model as an example...
const playerSchema = new Schema({
playerName: {
type: String,
required: true,
},
awards: [{
type: String,
}]
})
const Player = model('Player', playerSchema)
module.exports = Player;
Here is my typeDef for this model with Queries and Mutations
const typeDefs = gql`
type Player{
playerName: String!
awards: [String]
}
type Query {
players: [Player!]
}
type typeDefs {
addPlayer:(playerName: String!, awards:[String]
}
}`;
module.exports = typeDefs;
Here is my resolvers for this model
const resolvers = {
Query: {
players: async () => {
return await Player.find(populate('player');
}
Mutation: {
addPlayer: async( parent, {playerName, awards}) => {
return await Player.create({playerName, awards})
}
}
module.exports = resolvers;
Now, I start the server and go to GraphQL playground in the browser. I cannot add an array of strings for the awards. How do I write this query in GraphQL playground? Here is an example of my best attempt:
mutation addPlayer($playerName: String!, $awards:[String]){
addPlayer(playerName:$playerName, awards:$awards){
playerName
awards
}
}
and finally my query variables for this mutation
{
"playerName": "Michael Jordan",
"awards": ["Most Valuable Player", "Rookie of the Year", "Scoring Champion"]
}
If I run a query in GraphQL to see what players exist in the database:
query players{
players{
playerName
awards
}
}
This is the results. Why is the 'awards' array empty??
{
"data": {
"players": [
{
"playerName": "Michael Jordan",
"awards": [], //EMPTY ARRAY HERE, WHY?
}
]
}
}
I have the following resolver for GraphQL:
const Post = require("../../models/Post");
module.exports = {
getAllActivePosts: async (userId) => {
try {
const posts = await Post.find({
userId: userId
})
.select(["name", "createdAt"])
.populate("posts", ["name", "createdAt"]);
return posts;
} catch (err) {
console.log(err);
throw err;
}
},
};
which tries to get all active posts by the ID of the user from the Post model:
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const PostSchema = new mongoose.Schema({
userId: {
type: Schema.Types.ObjectId,
ref: "User",
required: true,
},
content: {
type: String,
required: true,
},
createdAt: {
type: Date,
required: true,
}
});
module.exports = Post = mongoose.model("Post", PostSchema);
Here's the GraphQL Schema:
const { buildSchema } = require('graphql');
module.exports = buildSchema(`
type User {
_id: MongoId!
email: String!
password: String
}
type Post {
_id: MongoId!
userId: MongoId!
content: String!
createdAt: String!
}
scalar MongoId
input LoginInput {
email: String!
password: String!
}
type RootQuery {
login(email: String!, password: String!): AuthData!
getAllActivePosts(userId: MongoId!): [Post]
}
type RootMutation {
createUser(loginInput: LoginInput): AuthData!
}
schema {
query: RootQuery
mutation: RootMutation
}
`);
... and the GraphQL query I'm running in GraphiQL:
{
getAllActivePosts(userId: "5fbfc92312b90071179a160f") {
name
createdAt
}
}
For this, the result of the query is:
{
"errors": [
{
"message": "Cast to ObjectId failed for value \"{ userId: '5fbfc92312b90071179a160f' }\" at path \"userId\" for model \"Post\"",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"getAllActivePosts"
]
}
],
"data": {
"getAllActivePosts": null
}
}
Searched here for similar issues, tried wrapping userId in ObjectId, but nothing helped. What am I missing here?
I was go through this problem once a year ago with no solution till i get main concept of graphql.
Here you are passing string
{
getAllActivePosts(userId: "5fbfc92312b90071179a160f") {
name
createdAt
}
}
and graphql expecting to have mongoose.Types.ObjectId
getAllActivePosts(userId: MongoId!): [Post]
You need to do sync like
getAllActivePosts(userId: mongoose.Types.ObjectId("5fbfc92312b90071179a160f")) {
But using above way you are not eligible for run query in graphiQL becuse there is no mongoose defined.
type RootQuery {
login(email: String!, password: String!): AuthData!
getAllActivePosts(userId: String!): [Post]
}
Better solution is use userId input as string and then validate on your resolver function like
getAllActivePosts: async ({ userId }) => {
try {
if(mongoose.Types.ObjectId.isValid(userId)) {
const posts = await Post.find({
userId: userId
})
.select(["name", "createdAt"])
.populate("posts", ["name", "createdAt"]);
// you can;t return null you need to return array
return posts ? posts : []
} else {
// if mongoose id is wrong
return []
}
} catch(error) {
// it is better to throw error return blank array to complete flow
throw error
}
}
Turned out, I was using userId directly, whereas I should've used args.userId. The proper resolver below:
module.exports = {
getAllActivePosts: async (args) => {
try {
const posts = await Post.find({
userId: args.userId
})
.select(["name", "createdAt"])
.populate("posts", ["name", "createdAt"]);
return posts;
} catch (err) {
console.log(err);
throw err;
}
},
};
and for the schema:
getAllActivePosts(userId: String!): [Post]
I have a datamodel type for example
type XYZ {
id: ID!
relatedTable: ABC
someValue: String
}
Graphql part:
const createXYZ = gql`
mutation createXYZ(
relatedTableId: ID
someValue: String
) {
createXYZ(
data: {
relatedTableId: $relatedTableId
someValue: $someValue
}
) { ... }
`
const updateXYZ = gql`
mutation updateXYZ(
id: ID!
relatedTableId: ID
someValue: String
) {
updateXYZ(
data: {
relatedTableId: $relatedTableId
someValue: $someValue
},
where: { id: $id }
) { ... }
`
From the forntend I create XYZ record, with a mutation more or less like this:
createXYZ(variables: {
relatedTableId: 1,
someValue: 'cde'
})
server side:
createXYZ: async (obj, args, context, info) => {
return prisma.createXYZ({
data: {
relatedTable: { connect: { id: args.data.relatedTableId } },
someValue: args.data.someValue
}
})
}
Frontend side update looks similar to create
updateXYZ(variables: {
id: 2,
relatedTableId: 1,
someValue: 'cde'
})
On the server side, in order to prevent from updating fields with the same value I need to fetch first XYZ record and compare before updating:
updateXYZ: async (obj, args, context, info) => {
const currentXYZ = await prisma.XYZ({ where: args.where })
return prisma.updateXYZ({
data: {
relatedTable: currentXYZ.relatedTable.id !== args.data.relatedTableId
? { connect: { id: args.data.relatedTableId } }
: undefined,
someValue: currentXYZ.someValue !== args.data.someValue
? args.data.someValue
: undefined
},
where: args.where
})
}
Does Prisma have any automatic way of detecting whether the received field value is the same as the current field value or is fetching and comparing values the only solution to this issue?
I am trying my hand at GraphQL and I seem to have run into a strange error.
Here is my mutation
const createNewTask = {
name: "AddATask",
description: "A mutation using which you can add a task to the todo list",
type: taskType,
args: {
taskName: {
type: new gql.GraphQLNonNull(gql.GraphQLString)
},
authorId: {
type: new gql.GraphQLNonNull(gql.GraphQLString)
}
},
async resolve(_, params) {
try {
const task = newTask(params.taskName);
return await task.save();
} catch (err) {
throw new Error(err);
}
}
};
Task type is as defined as follows
const taskType = new gql.GraphQLObjectType({
name: "task",
description: "GraphQL type for the Task object",
fields: () => {
return {
id: {
type: gql.GraphQLNonNull(gql.GraphQLID)
},
taskName: {
type: gql.GraphQLNonNull(gql.GraphQLString)
},
taskDone: {
type: gql.GraphQLNonNull(gql.GraphQLBoolean)
},
authorId: {
type: gql.GraphQLNonNull(gql.GraphQLString)
}
}
}
});
I am trying to add a task using the graphiql playground.
mutation {
addTask(taskName: "Get something", authorId: "5cb8c2371ada735a84ec8403") {
id
taskName
taskDone
authorId
}
}
When I make this query I get the following error
"ValidationError: authorId: Path `authorId` is required."
But when I remove the authorId field from the mutation code and send over a mutation without the authorId in it, I get this error
"Unknown argument \"authorId\" on field \"addTask\" of type \"Mutation\"."
So this proves that the authorId is available is in the request. I debugged the same on vscode and can see the value. I can't seem to figure out what is wrong.
I figured out what the error was. The erro was actually caused by my mongoose schema and not by graphql schema.
const taskSchema = new Schema(
{
taskName: {
type: String,
required: true
},
taskDone: {
type: Boolean,
required: true
},
authorId: {
type: mongoose.Types.ObjectId,
required: true
}
},
{
collection: "tasks"
}
);
But what is wierd is that the final error message has no indication that it was the mongoose schema validation failure. And the error states that it is a graphql error hence the confusion. Hope it helps someone.
I'm working with mongoDB, mongoose and graphQL. I'm trying to make an update in my DB.
I'm doing an update in an array called phones, the changes work perfectly, the only problem is that when the update ends, the value of the objectId changes.
// Models -> Schema Organization
const organizationSchema = new mongoose.Schema({
name: String,
address: String,
phones: [
{
number: Number,
prefix: Number
}
],
email: String
})
// Types -> Organization
type Response {
success: Boolean!
token: String
errors: [Error]
}
type Error {
path: String!
message: String!
}
input iOrganization {
_id: ID
arrID: ID
address: String
email: String
number: Int
prefix: Int
name: String
}
type Mutation {
updateOrgGeneric(iOrg: iOrganization): Response!
}
// Resolvers -> Organization (1st way)
Mutation: {
updateOrgGeneric: (parent, args, {models}) => {
return models.Organization.findOneAndUpdate(
{ "_id": args.iOrg._id, "phones._id": args.iOrg.arrID },
{ $set: { "phones.$": { number: args.iOrg.number, prefix: args.iOrg.prefix }} },
{new: true}
)
.then((resp) => {
console.log(resp);
return {
success: true,
errors: []
}
})
.catch((error) => {
return {
success: false,
errors: error
};
})
},
}
// Resolvers -> Organization (2nd way)
Mutation: {
updateOrgGeneric: (parent, args, {models}) => {
return models.Organization.findOneAndUpdate(
{ "_id": args.iOrg._id },
{ $set: { "phones.$[arr]": { number: args.iOrg.number, prefix: args.iOrg.prefix }} },
{new: true}
{ arrayFilters:[{ "arr._id": mongoose.Types.ObjectId(args.iOrg.arrID) }], new: true}
)
.then((resp) => {
console.log(resp);
return {
success: true,
errors: []
}
})
.catch((error) => {
return {
success: false,
errors: error
};
})
}
}
// Playground (http://localhost:5000/graphql)
mutation {
updateOrgGeneric(
iOrg: {
_id: "5bdbee1b794b972bc8562aeb"
arrID: "5bdcea7cae88be098c020b19"
number: 85239,
prefix: 862
}
){
success
errors {
path
message
}
}
}
Both _id, as arrID, exist in the BD.
In the playground example the initial arrID was: _id:ObjectId("5bdcea7cae88be098c020b19"), but after the update is another, example: _id:ObjectId("5bdcec0a2ab78533b4bd1d98"). What am I doing wrong?
Thank you!
Mongodb is a nosql database which means that every object in the database should consist of an Id and revision values. Once an update occurs the revision value changes as part of the update process to implement the changes made to the data object. Since your data object don't have the revision value then the id value changes. Because it is unique. Now I'm no expert with mongo but you should check the docs on how to persist data objects and change accordingly
In case anyone lands here (despite this being old post), the problem probably lies in trying to update the entire phones object, of which the overwritten _id is a part. Since there's a model defined for phonesin mongoose, it will try to create a new _id any time an entire new phones object is created.
Someone who wanted to keep the same id would need to $set only the fields they want to change, rather than the entire object. So
{ $set: { "phones.$[arr]": { number: args.iOrg.number, prefix: args.iOrg.prefix }} }
could be changed to
{ $set: { "phones.$[arr].number": args.iOrg.number, "phones.$[arr].prefix": args.iOrg.prefix } }