Issues with GraphQL Nested Mutation - graphql

I am trying to achieve nesting mutation by adding player name in Team (Parent) and struggling trying to fetch list of player name...
Inside GraphiQL tool (localhost:4000/graphiql), this is the Add Mutation variable that I have included...
mutation AddPlayerToTeam($name: String!, $teamId: ID!){
addPlayerToTeam(player: $name, teamId: $teamId){
id
players{
name
}
}
}
The query variables, adding teamID and name...
{
"teamId": "5aff545371fc930a4c43b2b9",
"name": "John Doe"
}
The result shown...
{
"data": {
"addPlayerToTeam": {
"id": "5b072774e385740c38483111",
"players": []
}
}
}
But I was expecting for player name to show up like this....
{
"data": {
"addPlayerToTeam": {
"id": "5b072774e385740c38483111",
"players": [
{
"name": "John Doe"
}
]
}
}
}
The mutation code...
AddPlayerToTeam: {
type: TeamType,
args: {
name: { type: new GraphQLNonNull(GraphQLString) },
teamId: { type: new GraphQLNonNull(GraphQLID) }
},
resolve(parent, { name, teamId }) {
let addPlayer = new Player({ name, teamId });
return addPlayer.save();
}
},
I've struggled to find reason why I am getting "players": [] instead of "players": [ {"name": "John Doe" } ].
Need I include .then(...) after .save() to get result? Any examples? Your help is appreciated.
BTW, I using mongoDB/mongoose method. Saving them in local mongoDB.

Found solution for this... Thank #andrewingram from graphql.slack for helping. Just include .then(...) to return result.
AddPlayerToTeam: {
type: TeamType,
args: {
name: { type: new GraphQLNonNull(GraphQLString) },
teamId: { type: new GraphQLNonNull(GraphQLID) }
},
async resolve(parent, { name, teamId }) {
let addPlayer = new Player({ name, teamId });
await addPlayer.save();
return Team.findById(teamId);
}
},
or in promise version
resolve(parent, { name, teamId }) {
let addPlayer = new Player({ name, teamId });
return addPlayer.save().then(() => Team.findById(teamId));
}
Hope that help.

Related

Cannot pass custom result from resolver to Graphql

I am trying to fetch data with sequelize with an attribute and pass it to graphql.
The result is fine in console but the graphql query is returning null for the attribute field.
my resolver
getUnpayedLessons: async (_, args, { models }) => {
const { Attendance, Student } = models;
return await Attendance.findAll({
include: {
model: Student,
},
where: {
fk_lessonsSerieId: { [Op.is]: null },
},
attributes: ["id", [sequelize.fn("count", sequelize.col("absenceFlag")), "unpayedLessons"]],
group: ["student.id"],
});
},
query
getUnpayedLessons {
id
unpayedLessons
student {
id
firstName
lastName
}
}
schema
type UnpayedLessons {
id: Int
unpayedLessons: Int
student: Student
}
extend type Query {
getUnpayedLessons: [UnpayedLessons]
}
and this is the console.log of the resolver when I run the query
[
attendance {
dataValues: { id: 2, unpayedLessons: 8, student: [student] },
_previousDataValues: { id: 2, unpayedLessons: 8, student: [student] },
_changed: Set {},
_options: {
isNewRecord: false,
_schema: null,
_schemaDelimiter: '',
include: [Array],
includeNames: [Array],
includeMap: [Object],
includeValidated: true,
attributes: [Array],
raw: true
},
]
and from graphql
{
"data": {
"getUnpayedLessons": [
{
"id": 2,
"unpayedLessons": null,
"student": {
"id": 2,
"__typename": "Student"
},
"__typename": "UnpayedLessons"
},
]
}
}
Any idea how I can have unpayedLessons passed to graphql?
To debug this you need to check what is returned from DB, the shape:
const values = await Attendance.findAll({...
console.log( values );
// adapt structure to match query requirements
// finally return
return values;

Using multiple mutations in one call

I have written my first script that utilises GraphQL (Still a learning curve)
Currently i am making 3 calls using GraphQL,
First is a product lookup,
Second is a Price Update,
Third is a Inventory Update.
To reduce the number of calls to the end point i wanted to merge both Price update and Inventory, But i am having 0 luck, i dont know if its bad formatting.
Here is my GraphQL Code (I am using Postman to help ensure the schema is correct before taking it to PHP)
mutation productVariantUpdate($input: ProductVariantInput!) {
productVariantUpdate(input: $input) {
product {
id
}
productVariant {
id
price
}
userErrors {
field
message
}}
second: inventoryActivate($inventoryItemId: ID!, $locationId: ID!, $available: Int) {
inventoryActivate(inventoryItemId: $inventoryItemId, locationId: $locationId, available: $available) {
inventoryLevel {
id
available
}
userErrors {
field
message
}
}
}
}
Variables:
{
"inventoryItemId": "gid://shopify/InventoryItem/XXXXXXXXXXX",
"locationId": "gid://shopify/Location/XXXXXXXXXX",
"available": 11 ,
"input": {
"id": "gid://shopify/ProductVariant/XXXXXXXXX",
"price": 55
}
}
Error i keep getting:
{
"errors": [
{
"message": "Parse error on \"$\" (VAR_SIGN) at [29, 29]",
"locations": [
{
"line": 29,
"column": 29
}
]
}
]
}
The way that you'd go about this is by specifying all your arguments at the root of your mutation, just like you did for ProductVariantInput:
mutation batchProductUpdates(
$input: ProductVariantInput!
$inventoryItemId: ID!
$locationId: ID!
$available: Int
) {
productVariantUpdate(input: $input) {
product { id }
productVariant { id price }
...
}
inventoryActivate(
inventoryItemId: $inventoryItemId
locationId: $locationId
available: $available
) {
inventoryLevel { id available }
...
}
}
Here's an example how this would work if you were to use fetch in JavaScript:
fetch("https://example.com/graphql", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
query: `
mutation MyMutation($firstId: Int, $secondId: Int) {
m1: ToggleLike(id: $firstId) {
id
}
m2: ToggleLike(id: $secondId) {
id
}
}
`,
variables: {
firstId: 1,
secondId: 2
}
})
})
Hope this helps.

How to create a new key and add or append an array of objects on AWS DynamoDB

I have this object in my dynamodb table which looks like
{
"id": "b31de483"
}
I want to add another object that looks like
{
"id": "b31de483",
"players": [{"playerId": "1234"}]
}
This is my code
const addPlayerToGame = async (gameId, playerId) => {
const params = {
TableName: process.env.DYNAMODB_GAMES_TABLE,
Key: {
id: gameId
},
UpdateExpression: 'set players = list_append(if_not_exists(players, :players)',
ExpressionAttributeValues: {
':players': {
"L": [
{ "S": playerId }
]
}
}
};
return await documentClient.update(params);
}
This throws an error but I cannot understand how to fix it. I am looking at documentation here
Figured it out
const addPlayerToGame = async (gameId, playerId) => {
const params = {
TableName: process.env.DYNAMODB_GAMES_TABLE,
Key: {
id: gameId
},
UpdateExpression: 'set players = list_append(players, :players)',
ExpressionAttributeValues: {
':players': [{
"playerId": playerId
}]
}
};
return await documentClient.update(params).promise();
}

How to udpate an entry in graphql using variables

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.

How can GraphQL enable an ID based query at sub fields level?

If an existing service supporting the following GraphQL queries respectively:
query to a person's bank account:
query {
balance(id: "1") {
checking
saving
}
}
result
{
"data": {
"balance": {
"checking": "800",
"saving": "3000"
}
}
}
query to a person's pending order:
query {
pending_order(id: "1") {
books
tickets
}
}
result
{
"data": {
"pending_order": {
"books": "5",
"tickets": "2"
}
}
}
The source code achieving the above functionality is something like this:
module.exports = new GraphQLObjectType({
name: 'Query',
description: 'Queries individual fields by ID',
fields: () => ({
balance: {
type: BalanceType,
description: 'Get balance',
args: {
id: {
description: 'id of the person',
type: GraphQLString
}
},
resolve: (root, { id }) => getBalance(id)
},
pending_order: {
type: OrderType,
description: 'Get the pending orders',
args: {
id: {
description: 'id of the person',
type: GraphQLString
}
},
resolve: (root, { id }) => getPendingOrders(id)
}
})
});
Now, I want to make my GraphQL service schema support person level schema, i.e.,
query {
person (id: "1") {
balance
pending_order
}
}
and get the following results:
{
"data": {
"balance": {
"checking": "800",
"saving": "3000"
}
"pending_order": {
"books": "5",
"tickets": "2"
}
}
}
How can I re-structure the schema, and how can I reuse the existing query service?
EDIT (after reading Daniel Rearden's answer):
Can we optimize the GraphQL service so that we make service call based upon the query? i.e., if the incoming query is
query {
person (id: "1") {
pending_order
}
}
my actually query becomes
person: {
...
resolve: (root, { id }) => Promise.all([
getBalance(id)
]) => ({ balance})
}
You're going to have to define a separate Person type to wrap the balance and pending_order fields.
module.exports = new GraphQLObjectType({
name: 'Person',
fields: () => ({
balance: {
type: BalanceType,
resolve: ({ id }) => getBalance(id)
},
pending_order: {
type: OrderType,
resolve: ({ id }) => getPendingOrders(id)
}
})
});
And you're going to need to add a new field to your Query type:
person: {
type: PersonType,
args: {
id: {
type: GraphQLString
}
},
// We just need to return an object with the id, the resolvers for
// our Person type fields will do the result
resolve: (root, { id }) => ({ id })
}
There's not much you can do to keep things more DRY and reuse your existing code. If you're looking for a way to reduce boilerplate, I would suggest using graphql-tools.

Resources