How to execute a mutation in GraphQL? - node.js

In GraphQL we have basically two types of operations: queries and mutations. While queries are well described in the documentation and there are many examples of them, I'm having a hard time to understand how to execute a mutation. Mutations obviously are update methods.
I've created very simple Node.js server:
var express = require("express");
var graphqlHTTP = require("express-graphql");
var graphql = require("graphql");
var inMemoryDatabase = require("./inMemoryDatabase").inMemoryDatabase;
var _ = require("lodash-node");
var userType = new graphql.GraphQLObjectType({
name: "User",
fields: {
id: { type: graphql.GraphQLString },
name: { type: graphql.GraphQLString }
}
});
var queryType = new graphql.GraphQLObjectType({
name: "Query",
fields: {
user: {
type: userType,
args: {
id: { type: graphql.GraphQLString }
},
resolve: function(parent, { id }) {
return _.find(inMemoryDatabase, { id: id });
}
}
}
});
var mutationType = new graphql.GraphQLObjectType({
name: "Mutation",
fields: {
user: {
type: userType,
args: {
id: { type: graphql.GraphQLString },
name: { type: graphql.GraphQLString }
},
resolve: function(parent, { id, name }) {
var index = _.findIndex(inMemoryDatabase, { id: id });
inMemoryDatabase.splice(index, 1, { id: id, name: name });
return _.find(inMemoryDatabase, { id: id });
}
}
}
});
var schema = new graphql.GraphQLSchema({
query: queryType,
mutation: mutationType
});
var app = express();
app.use(
"/graphql",
graphqlHTTP({
schema: schema,
graphiql: true
})
);
var port = 9000;
if (process.env.PORT) {
port = process.env.PORT;
}
app.listen(port);
console.log("Running a GraphQL API server at localhost:" + port + "/graphql");
In memory database is just in an array of User objects {id, name}:
var inMemoryDatabase = [
{
id: "31ce0260-2c23-4be5-ab78-4a5d1603cbc8",
name: "Mark"
},
{
id: "2fb6fd09-2697-43e2-9404-68c2f1ffbf1b",
name: "Bill"
}
];
module.exports = {
inMemoryDatabase
};
Executing query to get user by id looks as follows:
{
user(id: "31ce0260-2c23-4be5-ab78-4a5d1603cbc8"){
name
}
}
How would the mutation changing user name look like?

Hey may completely be missing what you are saying, but the way that I look at a mutation is like this
I get some arguments and a field, that is the same thing as params and a path in rest, with those i do something (in your case lookup the user and update the attribute based on the arguments passed in
After That, i return something from the resolve function that will fulfill the type you specify in the type of the mutation
var mutationType = new graphql.GraphQLObjectType({
name: "Mutation",
fields: {
user: {
// You must return something from your resolve function
// that will fulfill userType requirements
type: userType,
// with these arguments, find the user and update them
args: {
id: { type: graphql.GraphQLString },
name: { type: graphql.GraphQLString }
},
// this does the lookup and change of the data
// the last step of your result is to return something
// that will fulfill the userType interface
resolve: function(parent, { id, name }) {
// Find the user, Update it
// return something that will respond to id and name, probably a user object
}
}
}
});
Then with that as a context, you pass some arguments and request back a user
mutation updateUser {
user(id: "1", name: "NewName") {
id
name
}
}
In a normal production schema you would also normally have something like errors that could be returned to convey the different states of the update for failed/not found

#Austio's answer was pretty close, but the proper way is:
mutation updateUser {
user(id: "31ce0260-2c23-4be5-ab78-4a5d1603cbc8", name: "Markus") {
id
name
}
}

if we connect directly with MongoDB below will help you.
mutation {
taskTrackerCreateOne
(
record:
{
id:"63980ae0f019789eeea0cd33",
name:"63980c86f019789eeea0cda0"
}
)
{
recordId
}
}

Related

graphql mutation giving error while creating record

here is my scheme, I wasnt to create one record so when i am passing field in graphql it is showing error
const { BookTC } = require("../model/book");
const { BookSchema } = require("../model/book");
BookTC.addResolver({
name: "create",
kind: "mutation",
type: BookTC.getResolver("createOne").getType(),
args: BookTC.getResolver("createOne").getArgs(),
resolve: async ({ source, args, context, info }) => {
const book = await BookSchema.create(args.record);
return {
record: book,
recordId: BookTC.getRecordIdFn()(book),
};
},
});
const BookMutation = {
bookWithFile: BookTC.getResolver("create"),
bookCreateOne: BookTC.getResolver("createOne"),
bookCreateMany: BookTC.getResolver("createMany")
};
module.exports = { BookQuery: BookQuery, BookMutation: BookMutation };

POST array of objects using req.body parser

I am trying to post a simple request which includes array of objects. I have created a model and passing the data as per the model.
I am having trouble accessing body parameters as it contains array of data.
I am able to store line item data by req.body.tasks[0]
which is not a standrad way of storing details in mongodb.
I am looking for a standrad way of storing array of data in mongodb
Controller:
let createBug = (req, res) => {
console.log(req.body.tasks[0].subtask[0].description)
for (var key in req.body) {
if (req.body.hasOwnProperty(key)) {
item = req.body[key];
console.log(item);
}
}
const createBug = new listModel({
title: req.body.title,
tasks: [{
title: req.body.tasks[0].title,
description: req.body.tasks[0].description,
subtask: [{
description: req.body.tasks[0].subtask[0].description
}]
}]
}).save((error, data) => {
if (data) {
let apiResponse = response.generate(false, null, 201, data);
res.status(201).send(apiResponse);
} else {
let apiResponse = response.generate(true, error, 404, null);
res.status(404).send(apiResponse);
}
});
};
body:
{
"title":"sample title",
"tasks":[{
"title": "task 1",
"description":"task1 description",
"subtask":[{
"description":"task3 description"
}]
}]
}
Model:
const mongoose = require("mongoose");
const mySchema = mongoose.Schema;
let subtask = new mySchema({
description: String
})
let taskdata = new mySchema({
title: String,
description: String,
subtask: [subtask]
});
let listSchema = new mySchema({
title: {
type: String,
require: true,
},
tasks: [taskdata],
owner: {
type: mongoose.Schema.Types.ObjectId,
ref: "users",
}
});
module.exports = mongoose.model("list", listSchema);
I think you're overcomplicating things here a little bit. The request body exactly matches the model definitions, so you can simply pass the req.body object to your mongoose model:
const createBug = new listModel(req.body).save((error, data) => { ... }

Rename a field to resolve data in GraphQL [duplicate]

When defining the userType in the following GraphQL schema on the server, how can I rename the "name" field to "firstname" while still referring to the "name" field in fakeDatabase?
The following code snippet has been copied from the official GraphQL docs
var express = require('express');
var graphqlHTTP = require('express-graphql');
var graphql = require('graphql');
// Maps id to User object
var fakeDatabase = {
'a': {
id: 'a',
name: 'alice',
},
'b': {
id: 'b',
name: 'bob',
},
};
// Define the User type
var userType = new graphql.GraphQLObjectType({
name: 'User',
fields: {
id: { type: graphql.GraphQLString },
// How can I change the name of this field to "firstname" while still referencing "name" in our database?
name: { type: graphql.GraphQLString },
}
});
// Define the Query type
var queryType = new graphql.GraphQLObjectType({
name: 'Query',
fields: {
user: {
type: userType,
// `args` describes the arguments that the `user` query accepts
args: {
id: { type: graphql.GraphQLString }
},
resolve: function (_, {id}) {
return fakeDatabase[id];
}
}
}
});
var schema = new graphql.GraphQLSchema({query: queryType});
var app = express();
app.use('/graphql', graphqlHTTP({
schema: schema,
graphiql: true,
}));
app.listen(4000);
console.log('Running a GraphQL API server at localhost:4000/graphql');
Resolvers can be used for any type, not just Query and Mutation. That means you can easily do something like this:
const userType = new graphql.GraphQLObjectType({
name: 'User',
fields: {
id: {
type: graphql.GraphQLString,
},
firstName: {
type: graphql.GraphQLString,
resolve: (user, args, ctx) => user.name
},
}
})
The resolver function specifies, given the parent value, arguments for that field and the context, what a field for any instance of a type will resolve to. It could even always return the same static value each time.
There's also a library graphql-tools that let you transform your schema, we use that on our schema stitching service.
const { RenameTypes, transformSchema } = require("graphql-tools");
/*
* Schema transformations:
* Types:
* <> Task -> GetTask
*/
const transformMySchema = schema => {
return transformSchema(schema, [
new RenameTypes(function(name) {
return name == "Task" ? "GetTask" : name;
}),
]);
};
Read more : https://github.com/apollographql/graphql-tools/blob/513108b1a6928730e347191527cba07d68aadb74/docs/source/schema-transforms.md#modifying-types
Does this answer the question ?

GraphQL Resolver for Interface on same Mongoose collection

I'm creating a GraphQL server that uses Mongoose and GraphQLInterfaceType. I have a GraphQLInterfaceType of Books and sub types of SchoolBooksType and ColoringBookType. in my Mongoose Schema I specified that both SchoolBooks and ColoringBooks are to be stored in the same books collection
const coloringSchema = new Schema({
title: String,//Interface
pages: String
});
module.exports = mongoose.model("ColoringBook", coloringSchema , "books");
const schoolSchema = new Schema({
title: String, //Interface
subject: String
});
module.exports = mongoose.model("SchoolBook", schoolSchema , "books");
Here is one of my types
const SchoolBookType = new GraphQLObjectType({
name: "SchoolBook",
interfaces: [BooksInterface],
isTypeOf: obj => obj instanceof SchoolBook,
fields: () => ({
title: { type: GraphQLString },
subject: { type: GraphQLString }
})
});
Here is my query: But I don't know what to return, if I need to combine the two collections into the same array?
books: {
type: new GraphQLList(BooksInterface),
resolve() {
return SchoolBook.find({}) //<---- What to return?
}
}
Here is my query:
{
books{
title
... on ColoringBook{
pages
}
... on SchoolBook{
subject
}
}
}
Any help would be great, Thank you.
I guess you can use an async resolver, and concat both queries.
resolve: async () => {
const schoolBooks = SchoolBook.find({}).exec()
const coloringBooks = ColoringBook.find({}).exec()
const [sbooks, cbooks] = await Promise.all([schoolBooks, coloringBooks])
return [...sbooks, ...cbooks]
}

GraphQL - passing an ObjectType a parameter

I'm using GraphQL and it's working great, however, I can't seem to figure out how to pass a parameter into the fields section of my Event GraphQLObjectType.
I would like to be able to pass in the currentUserId (which is given to me through a token) into the Event GraphQLObjectType so I can add in an isAttending attribute.
I've attached code with comments of what I'm basically trying to do:
const Event = new GraphQLObjectType({
name: 'Event',
description: 'This represents an Event',
fields: (currentUserId) => { // currentUserId is the parameter I would like to pass in
return {
id: {
type: GraphQLInt,
resolve (event) {
return event.id;
}
},
title: {
type: GraphQLString,
resolve (event) {
return event.title;
}
},
attendees: {
type: new GraphQLList(User),
resolve (event) {
return event.getAttendees()
}
},
// this is what I would like to do
isAttending: {
type: GraphQLBool,
resolve (event) {
return event.getAttendees({
where: {
id: currentUserId // that's the parameter I would like pass in
}
}).then(attendee => {
return (attendee.length > 0 ? true : false);
)};
}
}
// end of what I'm trying to do //
};
}
});
const Query = new GraphQLObjectType({
name: 'Query',
description: 'Root query object',
fields: () => {
return {
events: {
type: new GraphQLList(Event),
args: {
id: {
type: GraphQLInt
}
},
resolve (root, args) {
// here is the parameter I would like to pass to the event object
let currentUserId = root.userId;
////////
return Db.models.event.findAll({ where: args });
}
},
...
Update
The reason I can't just do data.currentUserId = root.userId, is because it's not visible when I'm returned a collection of event objects, since what is passed into my Event GraphQLOBjectType is only the {event} object.
What it looks like when I do data.currentUserId and there is an array of objects inside data is this:
[{objects}, currentUserId: 1]
As opposed to what we want which is this:
[{object, currentUserId: 1}, {anotherObject, currentUserId: 1}]
If I wanted to have access to the currentUserId in the Event GraphQLObject, the only thing I can think of is to loop through every object and add the currentUserId onto it like this:
return events.map(event => {
event.currentUserId = currentUserId;
return event;
});`
Is this the best solution?
I'm afraid you can't do that. fields doesn't recieve any parameters, so you won't send any either.
Fortunately, you can achieve that in more convenient way.
Everything your parent type (Query) returns in resolve function is visible in child resolve's root parameter.
const Query = new GraphQLObjectType({
name: 'Query',
description: 'Root query object',
fields: () => ({
events: {
type: new GraphQLList(Event),
args: {
id: {
type: GraphQLInt
}
},
resolve (root, args) {
return Db.models.event.findAll({ where: args })
.then(data => {
// pass the parameter here
data.currentUserId = root.userId;
return data;
});
}
},
...
Then your Event object would look like this:
const Event = new GraphQLObjectType({
name: 'Event',
description: 'This represents an Event',
fields: () => ({
...
isAttending: {
type: GraphQLBool,
resolve: (event) => {
return event.getAttendees({
where: {
id: event.currentUserId // that's the parameter you've passed through parent resolve
}
}).then(attendee => {
return (attendee.length > 0 ? true : false);
});
}
}
})
});

Resources