Call GraphQL mutation from another mutation - javascript

I have a GraphQL mutation that adds a book. I want to check if the author given exists yet and if not, add that author with a different mutation. Is this possible?
Mutation: {
addAuthor: (root, args) => {
const author = { ...args, id: uuid() }
authors = authors.concat(author)
return author
},
addBook: (root, args) => {
const existingAuthor = authors.filter(author => author.name === args.author).length > 0
if (!existingAuthor) {
addAuthor({ name: args.author }) /// This is how I want to call a mutation within my mutation
}
const book = { ...args, id: uuid() }
books = books.concat(book)
return book
}
}
Right now, this approach throws an error from the Apollo Studio Explorer:
"path": [
"addBook"
],
"extensions": {
"code": "INTERNAL_SERVER_ERROR",
"exception": {
"stacktrace": [
"ReferenceError: addAuthor is not defined"

Factor out your addAuthor function and use it in both places:
Mutation: {
addAuthor: _addAuthor,
addBook: (_, args) => {
const existingAuthor = authors.findIndex((a) => a.name === args.author) > -1;
if (!existingAuthor) _addAuthor(null,{ name: args.author });
const book = { ...args, id: uuid() }
books = books.concat(book)
return book
}
}
const _addAuthor = (_, args) => {
const author = { ...args, id: uuid() }
authors = authors.concat(author)
return author
}

Related

Graphql doesn't return the data

I'm trying to find a specific data based on the id in graphql.
But it is returning null .
I have also tried the mutation. Here is also it is returning null.
What's wrong with this below code.
const { ApolloServer } = require("#apollo/server");
const { startStandaloneServer } = require("#apollo/server/standalone");
const students = [
{
name: "langesh",
roll: 131,
},
{
name: "ram",
roll: 134,
},
];
const typeDefs = `#graphql
type Student {
name: String,
roll: Int,
}
type Query {
students: [Student]
student(roll: Int) : Student
}
`;
const resolvers = {
Query: {
students: () => students,
student: (parent, roll) => {
return students.find((s) => s.roll === roll);
},
},
};
const server = new ApolloServer({ typeDefs, resolvers });
async function startServer() {
const { url } = await startStandaloneServer(server, {
listen: {
port: 8000,
},
});
console.log(`url : ${url}`);
}
startServer();
You need to destructure the args in your resolver.
Instead of:
student: (parent, roll) => {
return students.find((s) => s.roll === roll);
}
do:
student: (parent, { roll }) => {
return students.find((s) => s.roll === roll);
}

[Error: Query.Mutation defined in resolvers, but not in schema]

const { ApolloServer, gql } = require('apollo-server-express');
const express = require('express');
const port = process.env.PORT || 4000;
const notes = [
{ id: '1', content: 'This is a note', author: 'Adam Scott' },
{ id: '2', content: 'This is another note', author: 'Harlow Everly' },
{ id: '3', content: 'Oh hey look, another note!', author: 'Riley Harrison' }
];
const typeDefs = gql `
type Note {
id: ID
content: String
author: String
}
type Query {
hello: String
notes: [Note]
note(id: ID!): Note
}
type Mutation {
newNote(content: String!): Note
}
`;
const resolvers = {
Query:{
hello: () => 'Hello World',
notes: () => notes,
note: (parent, args) => {
return notes.find(note => note.id == args.id);
},
Mutation: {
newNote: (parent, args) => {
let noteValue = {
id : String(notes.length + 1),
content : args.content,
author: 'Adam Scott',
};
notes.push(noteValue);
return noteValue;
}
}
},
}
Some people had naming issues but seems that I'm using the same in resolver as well as in schema.
Please bare with me, this is my second day in GraphQL and Express. I removed intentionally imports and assignment of express object, middleware since it does not let me post.
I think you are simply missing a curly bracket.
const resolvers = {
Query:{
hello: () => 'Hello World',
notes: () => notes,
note: (parent, args) => {
return notes.find(note => note.id == args.id);
}
}, <==== THIS IS MISSING =====>
Mutation: {
newNote: (parent, args) => {
let noteValue = {
id : String(notes.length + 1),
content : args.content,
author: 'Adam Scott',
};
notes.push(noteValue);
return noteValue;
}
}

How to map over results in Realm custom resolver function find() response?

I am trying to create a function for my custom resolver that gets all documents in a collection and returns an amended payload with new data. Below is the code that im using to get one client and amend its data:
exports = (input) => {
const clientId = input._id;
const openStatusId = new BSON.ObjectId("898999");
const mongodb = context.services.get("mongodb-atlas");
const clientRecords = mongodb.db("db-name").collection("clients");
const jobRecords = mongodb.db("db-name").collection("jobs");
let client = clientRecords.findOne({"_id": clientId});
const query = { "client_id": clientId};
let jobsForClient = jobRecords.count(query)
.then(items => {
console.log(`Successfully found ${items} documents.`)
// items.forEach(console.log)
return items
})
.catch(err => console.error(`Failed to find documents: ${err}`));
let openJobs = jobRecords.count({"client_id": clientId,"status": openStatusId})
.then(numOfDocs => {
console.log(`Found ${numOfDocs} open jobs.`)
// items.forEach(console.log)
return numOfDocs
})
.catch(err => console.error(`Failed to find documents: ${err}`));
return Promise.all([client, jobsForClient, openJobs]).then(values => {
return {...values[0], "jobs": values[1], "openJobs": values[2]}
})
};
How can i fix this function to get all clients and loop over them to add data to each client?
I understand that changing this:
let client = clientRecords.findOne({"_id": clientId});
to this
let clients = clientRecords.find();
will get all the documents from the clients collection. How would i loop over each client after that?
UPDATE:
I have updated the function to the below and it works when running it in the realm environment but gives me an error when running it as a GraphQL query.
Updated code:
exports = (input) => {
const openStatusId = new BSON.ObjectId("999999");
const mongodb = context.services.get("mongodb-atlas");
const clientRecords = mongodb.db("db-name").collection("clients");
const jobRecords = mongodb.db("db-name").collection("jobs");
const clients = clientRecords.find();
const formatted = clients.toArray().then(cs => {
return cs.map((c,i) => {
const clientId = c._id;
const query = { "client_id": clientId};
let jobsForClient = jobRecords.count(query)
.then(items => {
console.log(`Successfully found ${items} documents.`)
// items.forEach(console.log)
return items
})
.catch(err => console.error(`Failed to find documents: ${err}`));
let openJobs = jobRecords.count({"client_id": clientId,"status": openStatusId})
.then(numOfDocs => {
console.log(`Found ${numOfDocs} open jobs.`)
// items.forEach(console.log)
return numOfDocs
})
.catch(err => console.error(`Failed to find documents: ${err}`));
return Promise.all([jobsForClient, openJobs]).then(values => {
return {...c, "jobs": values[0], "openJobs": values[1]}
});
})
}).catch(err => console.error(`Failed: ${err}`));
return Promise.all([clients, formatted]).then(values => {
return values[1]
}).catch(err => console.error(`Failed to find documents: ${err}`));
};
Error in GraphQL:
"message": "pending promise returned that will never resolve/reject",
It looks like you need wait for the last promise in your function to resolve before the function returns. I would do something like this:
exports = async (input) => {
...
let values = await Promise.all([jobsForClient, openJobs]);
return {...c, "jobs": values[0], "openJobs": values[1]};
}
Managed to solve by using mongodb aggregate. Solution below:
exports = async function(input) {
const openStatusId = new BSON.ObjectId("xxxxxx");
const mongodb = context.services.get("mongodb-atlas");
const clientRecords = mongodb.db("xxxxx").collection("xxxx");
const jobRecords = mongodb.db("xxxxx").collection("xxxx");
return clientRecords.aggregate([
{
$lookup: {
from: "jobs",
localField: "_id",
foreignField: "client_id",
as: "totalJobs"
}
},
{
$addFields: {
jobs: { $size: "$totalJobs" },
openJobs: {
$size: {
$filter: {
input: "$totalJobs",
as: "job",
cond: { "$eq": ["$$job.status", openStatusId]},
}
}
},
}
}
]);
};

Custom resolve function returned undefined in grandstack

I have type definitions for A and B defined in the schema.graphql file. Resolvers for these are auto-generated and work well (in other places).
To create a scoring mechanism that ranks nodes with label B in relation to the node with label A, I am writing a CustomResolver query that executes a cypher query and returns a collection of bs and a computed score as defined in the ScoredBs type.
The schema.graphql file looks like this.
type Query {
CustomResolver(idA: ID!, idsB: [ID!]!): [ScoredBs]
}
type A {
id: ID! #id
# and some more stuff of course
}
type B {
id: ID! #id
# more fields that use the #relation or #cypher decorator
}
type ScoredBs {
bs: [B]
score: Float
}
This is where the custom resolver is defined:
const resolvers = {
Query: {
CustomResolver: async (
parent,
{ idA, idsB },
context,
info
) => {
const cypher = `
MATCH (a:A {id: $idA})
MATCH (a)<--(b:B) WHERE b.id IN $idsB
WITH
b
RETURN DISTINCT collect(b) AS bs, rand() AS score
ORDER BY score DESC
`
const session = context.driver.session()
const results = await session
.run(cypher, { idA, idsB })
.then((result) => {
return result.records.map((record) => {
return {
bs: record.get('bs'),
score: record.get('score')?.low || null,
}
})
})
.catch(console.log)
.then((results) => {
session.close()
return results
})
return results
},
},
}
When I run the query in the apollo-server graphql playground i am receiving an error:
"message": "Resolve function for "B.id" returned undefined",
query {
CustomResolver(
idA:"2560886f-654b-4047-8d7a-386cd7d9f670",
idsB: ["01ec8367-a8ae-4600-9f88-ec141b2cae2c", "032f9a88-98c3-4968-8388-659ae26d63b3"]
) {
bs {
id
}
score
}
}
I solved this by rewriting part of the code:
const results = await session
.run(cypher, { idA, idsB })
.then((result) => {
return result.records.map((record) => {
// diff start
const obj = record.toObject()
const bs = obj.bs.map((b) => b.properties)
return {
bs, // diff end
score: record.get('score')?.low || null,
}
})
})
.catch(console.log)
.then((results) => {
session.close()
console.log(results)
return results
})
return results

Relay setVariables function fail

First of all here is my fragment:
initialVariables: {
limit: 3,
},
fragments: {
app: () => Relay.QL`
fragment on App {
id
personnels(first: $limit) {
pageInfo {
hasNextPage
hasPreviousPage
}
edges {
cursor
node {
name
}
}
}
}
`
}
}
initial read from server works fine, but when I am calling
this.props.relay.setVariables, and trying to set limit variable I always get:
Server request for query App_AppRelayQL failed for the following reasons:
globalId is not defined
node(id:$id_0) {
^^^
in the browser console. I think it might have something to do with the schema. But not sure what, so here is my schema:
import {
GraphQLSchema,
GraphQLObjectType,
GraphQLString,
GraphQLInt,
GraphQLList,
GraphQLID,
GraphQLNonNull
} from 'graphql';
import {
nodeDefinitions,
fromGlobalId,
globalIdField,
connectionDefinitions,
connectionFromArray,
connectionArgs,
mutationWithClientMutationId
} from 'graphql-relay';
class App {};
class Personnel {};
let app = new App();
let Personnels = [];
(() => {
let Jason = new Personnel();
let John = new Personnel();
Jason.name = 'Jason';
Jason.id = 1;
John.name = 'John';
John.id = 2;
personnels.push(YangGuoRong);
personnels.push(DengLiFang);
})();
let {nodeInterface, nodeField} = nodeDefinitions(
(gloablId) => {
const {type} = fromGlobalId(globalId);
switch(type) {
case 'App':
return app;
default:
return null;
}
},
(obj) => {
if (obj instanceof App) {
return appType;
} else if (obj instanceof Personnel) {
return personnelType;
} else {
return null;
}
}
);
let getPersonnel = (id) => personnels[id];
let getPersonnels = () => personnels;
let appType = new GraphQLObjectType({
name: 'App',
fields: () => ({
id: globalIdField('App'),
personnels: {
type: personnelConnection.connectionType,
args: connectionArgs,
resolve: (_, args) => connectionFromArray(personnels, args)
}
}),
interfaces: [nodeInterface]
});
let personnelType = new GraphQLObjectType({
name: 'Personnel',
fields: () => ({
id: {
type: new GraphQLNonNull(GraphQLID),
resolve: (obj) => obj.id
},
name: {type: GraphQLString},
}),
});
let personnelConnection = connectionDefinitions({
name: 'Personnel',
nodeType: personnelType
});
new GraphQLObjectType({
name: 'Query',
fields: {
node: nodeField,
app: {
type: appType,
resolve: () => app
},
}
}),
});
export default schema;
You made a spelling mistake in your node definitions (you wrote gloablId instead of globalId in the second line). That's why globalId is not defined.
let {nodeInterface, nodeField} = nodeDefinitions(
(gloablId) => {
const {type} = fromGlobalId(globalId);
switch(type) {
case 'App':
return app;
default:
return null;
}
},
(obj) => {
if (obj instanceof App) {
return appType;
} else if (obj instanceof Personnel) {
return personnelType;
} else {
return null;
}
}
);
When these errors appear I always try to pin down the bug by searching my code for the variable named in the error. That mostly helps

Categories