How can a nested field in graphQL query get its own arguments - node.js

I'm working on a cursor based pagination that requires the a nested field to access the args. So far, i've been unable to get the args to its desired field.
type Game {
players(first: Int, after: String, last: Int, before: String): GameToPlayerConnection
}
game(id: GameID!): Game
I am able to only access the args passed to the game and not the args passed to the players.
game: async (parent: any, args: { id: string; first: number; last: number; after:
string; before: string; }, _ctx: any, info: any) =>
{
const { id, first, last, after, before } = args;
console.log("args", args);
}
game(id: 'fortnite', first: 3){
players(first: 2){
....
}
}
I am trying to access the args passed to the players

That's correct. Every resolver can only access the arguments for its own property. Each object also has its own set of resolvers, so you need one object for Query, and another for Game:
const resolvers = {
Query: {
game: async (parent, { id }, context, info) {
return games.load(id);
},
},
Game: {
players: async ({ id }, { first, last, before, after }, context, info) {
return playersByGame({
gameId: id,
first,
last,
before,
after,
});
},
},
};

Related

Expected Iterable, but did not find one for field \"Query.customers\ [duplicate]

I'm currently trying GraphQL with NodeJS and I don't know, why this error occurs with the following query:
{
library{
name,
user {
name
email
}
}
}
I am not sure if the type of my resolveLibrary is right, because at any example I had a look at they used new GraphQL.GraphQLList(), but in my case I really want to return a single user object, not an array of users.
My code:
const GraphQL = require('graphql');
const DB = require('../database/db');
const user = require('./user').type;
const library = new GraphQL.GraphQLObjectType({
name: 'library',
description: `This represents a user's library`,
fields: () => {
return {
name: {
type: GraphQL.GraphQLString,
resolve(library) {
return library.name;
}
},
user: {
type: user,
resolve(library) {
console.log(library.user);
return library.user
}
}
}
}
});
const resolveLibrary = {
type: library,
resolve(root) {
return {
name: 'My fancy library',
user: {
name: 'User name',
email: {
email: 'test#123.de'
}
}
}
}
}
module.exports = resolveLibrary;
Error:
Error: Expected Iterable, but did not find one for field library.user.
So my library schema provides a user field which returns the right data (the console.log is called).
I ran into this problem as well. It appears that what you're returning from your resolver doesn't match the return type in your schema.
Specifically for the error message Expected Iterable, but did not find one for field library.user., your schema expects an array(Iterable) but you aren't returning an array in your resolver
I had this in my schema.js:
login(email: String, password: String): [SuccessfulLogin]
I changed that to:
login(email: String, password: String): SuccessfulLogin
Notice the square brackets around "SuccessfulLogin". It's up to you whether you want to update the resolver return type or update the schema's expectations
I guess your user is an instance of GraphQLList that is why the field user is expecting to resolve to an iterable object.
I had the same problem. I was using find instead filter.
I ran into the same issue but i was using GraphQL with Go.
Solution:
I mentioned the return type to be a list( or you can say an array), but my resolver function was returning an interface and not a list of interfaces.
Before it was =>
Type: graphql.NewList(graphqll.UniversalType)
Later i changed it to =>
Type: graphqll.UniversalType
graphqll.UniversalType : 'graphqll' is the name of my user-defined package and 'UniversalType' is the GraphQL object i have created.
The previous structure of graphql object was :
var GetAllEmpDet = &graphql.Field{
Type: graphql.NewList(graphqll.UniversalType),
Resolve: func(params graphql.ResolveParams) (interface{}, error) {
...
...
// Your resolver code goes here, how you handle.
...
return models.Universal, nil // models.Universal is struct and not list of struct so it gave that error.
},
}
It worked when i changed this to:
var GetAllEmpDet = &graphql.Field{
Type: graphqll.UniversalType,
Resolve: func(params graphql.ResolveParams) (interface{}, error) {
...
...
// Your resolver code goes here, how you handle.
...
return models.Universal, nil // models.Universal is struct and not list of struct so it gave that error.
},
}
It's usually a simple mistake. Caused by declaring in the schema a List instead of a Field. The reverse will happen if you interchange. An example from Django-graphene. Switch from this:
my_query_name = graphene.List(MyModelType, id=graphene.Int())
to this:
my_query_name = graphene.Field(MyModelType, id=graphene.Int())
In my case it was related to django-graphene I didn't have a resolve method defined.
class SomeNode(DjangoObjectType):
things = graphene.List(ThingNode)
def resolve_things(self, info, **kwargs):
return self.things.all()
For me, it was a simple fix.
items: {
type: new GraphQLList(VideoType),<-- error
resolve(parentValue, args) {
const url = 'www'
return axios.get(url)
.then(res => res.data);
}
}
and change it to
items: {
type: VideoType,
resolve(parentValue, args) {
const url = 'www'
return axios.get(url)
.then(res => res.data);
}
}
I faced the same issue. For me, it was an issue with Mongo DB model.js file.
GraphQL kept throwing that error because my model was saving the field as an object whereas graphQL was returning it as an array.
The code that caused the error was this.
tableHeaders: {
text: {
type: String,
required: false,
},
align: {
type: String,
required: false,
},
sortable: {
type: Boolean,
required: false,
},
value: {
type: String,
required: false,
},
},
It was corrected to the following.
tableHeaders: [
{
text: {
type: String,
required: false,
},
align: {
type: String,
required: false,
},
sortable: {
type: Boolean,
required: false,
},
value: {
type: String,
required: false,
},
},
],
Changing type from object to array fixed it.
i had the same issue i was using findOne and that seems like the issue that didnt worked. i changed to find and it worked
#Query(()=> [Post])
async getSinglePost(
#Arg('post_id') id: string,
){
/*
const post = await getConnection().getRepository(Post).findOne({uuid:postuid})
console.log(post);
return post
*/
const post = Post.find({uuid:id})
return post
}
This simply results due to the import error
earlier code
const books =require('./data')
// Resolvers define the technique for fetching the types defined in the
// schema. This resolver retrieves books from the "books" array above.
const resolvers = {
Query: {
books(){
return books;
},
},
}
module.exports = { resolvers };
just replace the import statement with
const {books} =require('./data')
as you had ex

GraphQL error Cannot perform update query

I'm working on a ToDo list application in NodeJS, Koa, and GraphQL.
I wrote an update card mutation but when I run the query to update I get the following error:
Cannot perform update query because update values are not defined. Call "qb.set(...)" method to specify updated values.
The mutation:
import { getRepository } from 'typeorm';
import { Card } from '../../entities/card';
export const updateCardMutation = {
async updateCard(_, { id, patch }): Promise<Card> {
const repository = getRepository(Card);
const card = await repository.findOne({ id });
const result = await repository.update(id, patch);
return {
...card,
...patch,
};
},
};
I would like to know what I'm doing wrong and if something more is needed it please notify me so I will edit the question accordingly
card entity:
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from 'typeorm';
#Entity('cards')
export class Card {
#PrimaryGeneratedColumn('uuid')
id: string;
#CreateDateColumn()
created_at: Date;
#UpdateDateColumn()
updated_at: Date;
#Column('text')
title: string;
#Column('text', {
nullable: true,
})
description: string;
#Column('boolean', {
default: 'false',
})
done: boolean;
}
You need to spread the update Object.
export const updateCardMutation = {
async updateCard(_, { id, patch }): Promise<Card> {
const repository = getRepository(Card);
const card = await repository.findOne({ id });
const result = await repository.update(id, {...patch}); // here
return {
...card,
...patch,
};
},
};
The issue was when I was calling the updateMutation, it was creating the patch object of anonymous type. So it just needed to be clean before going to the DB engine
I resolved my issues by adding the following code:
{ ...patch }
Inside the next script:
export const updateCardMutation = {
async updateCard(_, { id, patch }): Promise<Card> {
const repository = getRepository(Card);
const card = await repository.findOne({ id });
const result = await repository.update(id, { ...patch }); // Added here
return {
...card,
...patch,
};
},
};
In this way, I was able to update my card.
https://github.com/typeorm/typeorm/blob/master/docs/update-query-builder.md
As an error Call qb.set() , typeorm query builder are different with other orm
await getRepository().createQueryBuilder().update(Card).set(patch)
.where("id = :id", { id })
.execute();
some how patch object may stringify [object], so you can spread it like set({...patch})
I have had this error before with my update query is nestjs & graqhql
Cannot perform update query because update values are not defined
I have fixed it by using the save() function from the repository on the same id, so I have changed from this
async update(
id: number,
updateEmployeeInput: UpdateEmployeeInput,
): Promise<Employee> {
await this.employeeRepository.update(id, updateEmployeeInput);
return this.employeeRepository.findOneOrFail(id);
}
to this
async update(
id: number,
updateEmployeeInput: UpdateEmployeeInput,
): Promise<Employee> {
await this.employeeRepository.save(updateEmployeeInput)
return this.employeeRepository.findOneOrFail(id);
}

How to implement isTypeOf method?

Given this schema:
interface INode {
id: ID
}
type Todo implements INode {
id: ID
title: String!
}
type Query {
node(id: ID!): INode
}
Given this class:
export default class Todo {
constructor (public id: string, public title: string) { }
isTypeOf(value: any): Boolean {
return value instanceof Todo;
}
}
Given this resolver:
type NodeArgs = {
id: string
}
export const resolver = {
node: ({ id }: NodeArgs) => {
return new Todo('1', 'Todo 1');
}
}
When I call the query:
query {
node(id: "1") {
id
... on Todo {
title
}
}
}
Then I get the return below:
{
"errors": [
{
"message": "Abstract type INode must resolve to an Object type at runtime for field Query.node with value { id: \"1\", title: \"Todo 1\" }, received \"undefined\". Either the INode type should provide a \"resolveType\" function or each possible type should provide an \"isTypeOf\" function.",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"node"
]
}
],
"data": {
"node": null
}
}
As you can see, I've implemented the isTypeOf function but I am still getting the error message.
What am I doing wrong?
Notes:
I am using Typescript, express and express-graphql;
isTypeOf is a function that is passed to the constructor of a GraphQLObjectType when you create your schema programatically. Ditto for resolveType functions and unions/interfaces. If you use SDL and create your schema using buildSchema, there is no way to inject those functions into your created schema, just like you don't have a way to provide resolvers for fields on types other than Query and Mutation.
You have a couple of options. One option is to utilize the default resolveType behavior. This checks for a __typename property on the object, and falls back to calling isTypeOf on every implementing type until it matches. That means if you're using a class, it should be sufficient to do something like this:
export default class Todo {
get __typename() {
return 'Todo'
}
}
The better option would be to drop buildSchema and use makeExecutableSchema from graphql-tools. Then you can define your resolveType and/or isTypeOf functions directly in your resolvers. For example:
const resolvers = {
Query: {
node: (obj, args, context, info) => {
return new Todo('1', 'Todo 1')
}
},
INode: {
__resolveType: (obj, context, info) => {
if (obj instanceof Todo) return 'Todo'
},
}
}
Not only can you easily define isTypeOf or resolveType this way, you can also easily add resolvers for fields of any type and add custom scalars without any hassle. You cannot do any of that (easily) if you're using just buildSchema.
Edit:
If you prefer to utilize isTypeOf instead of resolveType, the resolvers would look something like this:
const resolvers = {
Query: {
node: (obj, args, context, info) => {
return new Todo('1', 'Todo 1')
}
},
Todo: {
__isTypeOf: (obj, context, info) => {
return obj instanceof Todo
},
}
}
Only one or the other is necessary. Either write a resolveType function for every abstract type you use, or write a isTypeOf for every object type that could be an abstract type.

Graphql: How can get field arguments of a type in resolver?

I use graphql-tools library and makeExecutableSchema function to make my schema by passing schema and resolver to it
here is my schema:
type Trip {
code: String!
driver: User!
vehicle: Vehicle!
destination: Location!
passengers(page: Int, count: Int): [User!]!
}
type Query {
trip(id: String!): Trip
}
and here is my resolver:
// some imports...
export default {
Query: {
async trip(_, { id }, ctx, info) {
const trip = await Trip.findById(id);
// const page = ???, count = ???
// work on fetch data...
return result;
},
};
how can I get page and count which are defined as nested argument for passengers?
You should define a resolver for the type Trip, such as:
export default {
Query: {
async trip(_, { id }, ctx, info) {
const trip = await Trip.findById(id);
// const page = ???, count = ???
// work on fetch data...
return result;
},
Trip: {
async passengers(trip, { page, count }, ctx, info) {
...
},
}
};
In GraphQL, it's not the concept of "nested fields of a type", but just combinations of "the type of a field". The trip field of type Query has the Trip type, so when you want to work with the passengers field, it should be considered as a field under the Trip type, not a nested field of the Query type.

GraphQL Expected Iterable, but did not find one for field xxx.yyy

I'm currently trying GraphQL with NodeJS and I don't know, why this error occurs with the following query:
{
library{
name,
user {
name
email
}
}
}
I am not sure if the type of my resolveLibrary is right, because at any example I had a look at they used new GraphQL.GraphQLList(), but in my case I really want to return a single user object, not an array of users.
My code:
const GraphQL = require('graphql');
const DB = require('../database/db');
const user = require('./user').type;
const library = new GraphQL.GraphQLObjectType({
name: 'library',
description: `This represents a user's library`,
fields: () => {
return {
name: {
type: GraphQL.GraphQLString,
resolve(library) {
return library.name;
}
},
user: {
type: user,
resolve(library) {
console.log(library.user);
return library.user
}
}
}
}
});
const resolveLibrary = {
type: library,
resolve(root) {
return {
name: 'My fancy library',
user: {
name: 'User name',
email: {
email: 'test#123.de'
}
}
}
}
}
module.exports = resolveLibrary;
Error:
Error: Expected Iterable, but did not find one for field library.user.
So my library schema provides a user field which returns the right data (the console.log is called).
I ran into this problem as well. It appears that what you're returning from your resolver doesn't match the return type in your schema.
Specifically for the error message Expected Iterable, but did not find one for field library.user., your schema expects an array(Iterable) but you aren't returning an array in your resolver
I had this in my schema.js:
login(email: String, password: String): [SuccessfulLogin]
I changed that to:
login(email: String, password: String): SuccessfulLogin
Notice the square brackets around "SuccessfulLogin". It's up to you whether you want to update the resolver return type or update the schema's expectations
I guess your user is an instance of GraphQLList that is why the field user is expecting to resolve to an iterable object.
I had the same problem. I was using find instead filter.
I ran into the same issue but i was using GraphQL with Go.
Solution:
I mentioned the return type to be a list( or you can say an array), but my resolver function was returning an interface and not a list of interfaces.
Before it was =>
Type: graphql.NewList(graphqll.UniversalType)
Later i changed it to =>
Type: graphqll.UniversalType
graphqll.UniversalType : 'graphqll' is the name of my user-defined package and 'UniversalType' is the GraphQL object i have created.
The previous structure of graphql object was :
var GetAllEmpDet = &graphql.Field{
Type: graphql.NewList(graphqll.UniversalType),
Resolve: func(params graphql.ResolveParams) (interface{}, error) {
...
...
// Your resolver code goes here, how you handle.
...
return models.Universal, nil // models.Universal is struct and not list of struct so it gave that error.
},
}
It worked when i changed this to:
var GetAllEmpDet = &graphql.Field{
Type: graphqll.UniversalType,
Resolve: func(params graphql.ResolveParams) (interface{}, error) {
...
...
// Your resolver code goes here, how you handle.
...
return models.Universal, nil // models.Universal is struct and not list of struct so it gave that error.
},
}
It's usually a simple mistake. Caused by declaring in the schema a List instead of a Field. The reverse will happen if you interchange. An example from Django-graphene. Switch from this:
my_query_name = graphene.List(MyModelType, id=graphene.Int())
to this:
my_query_name = graphene.Field(MyModelType, id=graphene.Int())
In my case it was related to django-graphene I didn't have a resolve method defined.
class SomeNode(DjangoObjectType):
things = graphene.List(ThingNode)
def resolve_things(self, info, **kwargs):
return self.things.all()
For me, it was a simple fix.
items: {
type: new GraphQLList(VideoType),<-- error
resolve(parentValue, args) {
const url = 'www'
return axios.get(url)
.then(res => res.data);
}
}
and change it to
items: {
type: VideoType,
resolve(parentValue, args) {
const url = 'www'
return axios.get(url)
.then(res => res.data);
}
}
I faced the same issue. For me, it was an issue with Mongo DB model.js file.
GraphQL kept throwing that error because my model was saving the field as an object whereas graphQL was returning it as an array.
The code that caused the error was this.
tableHeaders: {
text: {
type: String,
required: false,
},
align: {
type: String,
required: false,
},
sortable: {
type: Boolean,
required: false,
},
value: {
type: String,
required: false,
},
},
It was corrected to the following.
tableHeaders: [
{
text: {
type: String,
required: false,
},
align: {
type: String,
required: false,
},
sortable: {
type: Boolean,
required: false,
},
value: {
type: String,
required: false,
},
},
],
Changing type from object to array fixed it.
i had the same issue i was using findOne and that seems like the issue that didnt worked. i changed to find and it worked
#Query(()=> [Post])
async getSinglePost(
#Arg('post_id') id: string,
){
/*
const post = await getConnection().getRepository(Post).findOne({uuid:postuid})
console.log(post);
return post
*/
const post = Post.find({uuid:id})
return post
}
This simply results due to the import error
earlier code
const books =require('./data')
// Resolvers define the technique for fetching the types defined in the
// schema. This resolver retrieves books from the "books" array above.
const resolvers = {
Query: {
books(){
return books;
},
},
}
module.exports = { resolvers };
just replace the import statement with
const {books} =require('./data')
as you had ex

Resources