I'm trying to implement a very basic GraphQL interface in Node.js, but no matter what I do I can't seem to get the resolve method of my foo type to trigger. When I run the following code in a unit test it runs successfully, but I can see from the (lack of) console output that resolve wasn't called, and as a result I get an empty object back when I call graphql(FooSchema, query).
Can anyone more experienced with GraphQL suggest what I might be doing wrong? I'm completely baffled as to how the whole operation can even complete successfully if GraphQL can't find and call the method that is supposed to return the results ...
const fooType = new GraphQLInterfaceType({
name: `Foo`,
description: `A foo`,
fields: () => ({
id: {
description: `The foo's id`,
type: new GraphQLNonNull(GraphQLInt),
},
title: {
description: `The foo's title`,
type: new GraphQLNonNull(GraphQLString),
}
})
});
const queryType = new GraphQLObjectType({
fields: {
foo: {
args: {
id: {
description: 'ID of the foo',
type: new GraphQLNonNull(GraphQLString)
}
},
resolve: (root, { id }) => {
console.log(12345);
return getFoo(id)
},
type: fooType,
}
},
name: 'Query',
});
export default new GraphQLSchema({
query: queryType,
types: [fooType],
});
// In test:
const query = `
foo {
title
}
`;
const result = graphql(FooSchema, query); // == {}
const fooType = new GraphQLInterfaceType({
name: `Foo`,
description: `A foo`,
fields: () => ({
id: {
description: `The foo's id`,
type: new GraphQLNonNull(GraphQLInt),
},
title: {
description: `The foo's title`,
type: new GraphQLNonNull(GraphQLString),
}
})
});
This is an interface type, however your consumer queryType never implements it. A quick solution should be to change it to this:
const fooType = new GraphQLObjectType({
name: `Foo`,
description: `A foo`,
fields: () => ({
id: {
description: `The foo's id`,
type: new GraphQLNonNull(GraphQLInt),
},
title: {
description: `The foo's title`,
type: new GraphQLNonNull(GraphQLString),
}
})
});
Here's an example that works for me:
const {
GraphQLNonNull,
GraphQLInt,
GraphQLString,
GraphQLObjectType,
GraphQLSchema,
graphql,
} = require('graphql');
const fooType = new GraphQLObjectType({
name: `Foo`,
description: `A foo`,
fields: () => ({
id: {
description: `The foo's id`,
type: new GraphQLNonNull(GraphQLInt),
},
title: {
description: `The foo's title`,
type: new GraphQLNonNull(GraphQLString),
},
}),
});
const queryType = new GraphQLObjectType({
fields: {
foo: {
args: {
id: {
description: 'ID of the foo',
type: new GraphQLNonNull(GraphQLString),
},
},
resolve: (root, { id }) => {
return { id, title: 'some-title' };
},
type: fooType,
},
},
name: 'Query',
});
const schema = new GraphQLSchema({
query: queryType,
types: [fooType],
});
graphql(schema, `{ foo (id:"123") { id, title } }`).then(console.log.bind(console));
This should print:
$ node test.js
{ data: { foo: { id: 123, title: 'some-title' } } }
Here's the docs on the InterfaceType: http://graphql.org/learn/schema/#interfaces
Related
I want to achieve the fields of one object type within another object type
Here is my schema file.
const Films = new GraphQLInterfaceType({
name: 'films',
fields: () => ({
id:{
type: GraphQLID
},
name: {
type: GraphQLString,
},
})
})
const MovieStream = new GraphQLObjectType({
name: 'MovieStream',
interfaces: () => [Films],
fields: () => ({
id: {
type: GraphQLID,
},
movie_id: {
type: GraphQLString,
},
})
})
Here I am trying to use the interface. But It shows error:
{
"errors": [
{
"message": "Query root type must be Object type, it cannot be { __validationErrors: undefined, __allowedLegacyNames: [], _queryType: undefined, _mutationType: undefined, _subscriptionType: undefined, _directives: [#include, #skip, #deprecated], astNode: undefined, extensionASTNodes: undefined, _typeMap: { __Schema: __Schema, __Type: __Type, __TypeKind: __TypeKind, String: String, Boolean: Boolean, __Field: __Field, __InputValue: __InputValue, __EnumValue: __EnumValue, __Directive: __Directive, __DirectiveLocation: __DirectiveLocation, films: films, ID: ID, Date: Date, JSON: JSON, MovieStream: MovieStream }, _possibleTypeMap: {}, _implementations: { films: [] } }."
},
{
"message": "Expected GraphQL named type but got: { __validationErrors: undefined, __allowedLegacyNames: [], _queryType: undefined, _mutationType: undefined, _subscriptionType: undefined, _directives: [#include, #skip, #deprecated], astNode: undefined, extensionASTNodes: undefined, _typeMap: { __Schema: __Schema, __Type: __Type, __TypeKind: __TypeKind, String: String, Boolean: Boolean, __Field: __Field, __InputValue: __InputValue, __EnumValue: __EnumValue, __Directive: __Directive, __DirectiveLocation: __DirectiveLocation, films: films, ID: ID, Date: Date, JSON: JSON, MovieStream: MovieStream }, _possibleTypeMap: {}, _implementations: { films: [] } }."
}
]
}
Here is Query type:
const QueryRoot = new GraphQLObjectType({
name: 'Query',
fields: () => ({
getContentList:{
type: new GraphQLList(contentCategory),
args: {
id: {
type: GraphQLInt
},
permalink: {
type: GraphQLString
},
language: {
type: GraphQLString
},
content_types_id: {
type: GraphQLString
},
oauth_token:{
type: GraphQLString
}
},
resolve: (parent, args, context, resolveInfo) => {
var category_flag = 0;
var menuItemInfo = '';
user_id = args.user_id ? args.user_id : 0;
// console.log("context"+context['oauth_token']);
return AuthDb.models.oauth_registration.findAll({attributes: ['oauth_token', 'studio_id'],where:{
// oauth_token:context['oauth_token'],
$or: [
{
oauth_token:
{
$eq: context['oauth_token']
}
},
{
oauth_token:
{
$eq: args.oauth_token
}
},
]
},limit:1}).then(oauth_registration => {
var oauthRegistration = oauth_registration[0]
// for(var i = 0;i<=oauth_registration.ength;i++){
if(oauth_registration && oauthRegistration && oauthRegistration.oauth_token == context['oauth_token'] || oauthRegistration.oauth_token == args.oauth_token){
studio_id = oauthRegistration.studio_id;
return joinMonster.default(resolveInfo,{}, sql => {
return contentCategoryDb.query(sql).then(function(result) {
return result[0];
});
} ,{dialect: 'mysql'});
}else{
throw new Error('Invalid OAuth Token');
}
})
},
where: (filmTable, args, context) => {
return getLanguage_id(args.language).then(language_id=>{
return ` ${filmTable}.permalink = "${args.permalink}" and ${filmTable}.studio_id = "${studio_id}" and (${filmTable}.language_id = "${language_id}" OR ${filmTable}.parent_id = 0 AND ${filmTable}.id NOT IN (SELECT ${filmTable}.parent_id FROM content_category WHERE ${filmTable}.permalink = "${args.permalink}" and ${filmTable}.language_id = "${language_id}" and ${filmTable}.studio_id = "${studio_id}"))`
})
},
}
})
})
module.exports = new GraphQLSchema({
query: QueryRoot
})
Please help me out. have i done something wrong in the use of interface?
I have found the answer through this post
Is it possible to fetch data from multiple tables using GraphQLList
Anyone please tell me the exact way to use the interface in my code.
Although the error you have printed does not really relate to interfaces implementations, in order for you to use interfaces, you have to implement the methods/types the interface references. So in your situation your object MovieStream is missing the type name that you refer in the object Films.
Your code should look something like:
const Films = new GraphQLInterfaceType({
name: 'films',
fields: () => ({
id:{
type: GraphQLID
},
name: {
type: GraphQLString,
},
})
})
const MovieStream = new GraphQLObjectType({
name: 'MovieStream',
interfaces: () => [Films],
fields: () => ({
id: {
type: GraphQLID,
},
name: {
type: GraphQLString // You're missing this!
},
movie_id: {
type: GraphQLString,
},
})
})
Now back to the error you have printed "message": "Query root type must be Object type, it cannot be...
This seems to be related to your QueryRoot object, it seems that GraphQLSchema is not recognizing the root object. If this issue is still there once you fix the interface, have a look at this answer here
I am trying to create a Node.js graphql server in Typescript. I am using Express and express-graphql. I have some issues with how to structure my mutation when I want to create a new User.
My goal is to be able to use a mutation like this:
mutation {
user {
create(
data: {
name: "Foo Bar"
}
) {
id,
name
}
}
}
Here is my User types:
import {
GraphQLObjectType,
GraphQLNonNull,
GraphQLBoolean,
GraphQLString,
GraphQLInputObjectType
} from 'graphql';
export const UserType = new GraphQLObjectType({
name: 'User',
description: 'A user of the application',
fields: () => ({
id: {
type: new GraphQLNonNull(GraphQLString),
description: 'The id of the user',
},
name: {
type: new GraphQLNonNull(GraphQLString),
description: 'The name of the user',
}
})
});
export const UserInputType = new GraphQLInputObjectType({
name: 'UserInputType',
description: 'User payload definition',
fields: () => ({
name: { type: new GraphQLNonNull(GraphQLString) }
})
});
Here is my attempt at defining the mutation on the server:
// ../user/user-mutations.ts
export const userMutations = {
user: {
type: new GraphQLObjectType({
name: 'CreateUser',
fields: {
create: {
type: UserType,
args: {
data: {
type: new GraphQLNonNull(UserInputType),
}
},
resolve: async (rootValue, { data }) => {
return Object.assign(data, {
id: '123'
});
}
}
}
})
}
};
My errors/output:
{
"errors": [
{
"message": "Cannot convert undefined or null to object",
"locations": [
{
"line": 36,
"column": 3
}
],
"path": [
"user"
]
}
],
"data": {
"user": null
}
}
Question 1: Is this way of structuring a mutation not optimal? Should I rather do something like:
mutation {
createUser(
name: "Foo Bar"
) {
id,
name
}
}
Question 2: If my first structure is fine, how can I fix the structure of my mutation on the server to create my user and return the values requested?
Edit: Here is my top level schema:
import { userQueries } from '../user/user-queries';
export const queries = {
...userQueries
};
import { userMutations } from '../user/user-mutations';
export const mutations = {
...userMutations
};
const rootQuery = new GraphQLObjectType({
name: 'RootQuery',
fields: queries
});
const rootMutation = new GraphQLObjectType({
name: 'RootMutation',
fields: mutations
});
export const schema = new GraphQLSchema({
query: rootQuery,
mutation: rootMutation
});
I am new to graphql, I was creating following schema with graphql
// promotion type
const PromoType = new GraphQLObjectType({
name: 'Promo',
description: 'Promo object',
fields: () => ({
id: {
type: GraphQLID,
description: 'id of the promo'
},
title: {
type: GraphQLString,
description: 'this is just a test'
},
departments: {
type: new GraphQLList(DepartmentType),
description: 'departments associated with the promo'
}
})
})
and department type
// department type
const DepartmentType = new GraphQLObjectType({
name: 'Department',
description: 'Department object',
fields: () => ({
id: {
type: GraphQLID,
description: 'id of the department'
},
name: {
type: GraphQLString,
description: 'name of the department'
},
createdAt: {
type: GraphQLDate,
description: 'date the promo is created'
},
updatedAt: {
type: GraphQLDate,
description: 'date the promo is last updated'
}
})
});
and the following are the resolvers
// Promos resolver
const promos = {
type: new GraphQLList(PromoType),
resolve: (_, args, context) => {
let promos = getPromos()
let departments = getDepartmentsById(promos.promoId)
return merge(promos, departments)
}
};
//Departments resolver
const departments = {
type: new GraphQLList(DepartmentType),
args: {
promoId: {
type: GraphQLID
}
},
resolve: (_, args, context) => {
return getDepartmentsById(args.promoId)
}
};
the problem is I want to use the resolver of the departments into the resolver of the promos to get the departments.
I might be missing something obvious but is there any way to do this?
This is the way to do it. You want to think of it as graphs, rather than just a single rest endpoint.
To get data for Promo, you need to do it similarly to how I did it here, but for the parent node, if that makes sense. So, in e.g. viewer's resolve you add the query for Promo.
const PromoType = new GraphQLObjectType({
name: 'Promo',
description: 'Promo object',
fields: () => ({
id: {
type: GraphQLID,
description: 'id of the promo',
},
title: {
type: GraphQLString,
description: 'this is just a test',
},
departments: {
type: new GraphQLList(DepartmentType),
description: 'departments associated with the promo',
resolve: (rootValue) => {
return getDepartmentsById(rootValue.promoId);
}
}
})
});
All the examples I find have a query top level object, then a list of queries, which then return types to go deeper.
Since I have a large number of queries, I would like to group them up, this is what I tried:
const AppType = new GraphQLObjectType({
name: 'App',
description: 'Generic App Details',
fields: () => ({
name: { type: GraphQLString },
appId: { type: GraphQLInt },
}),
});
const MyFirstQuery = {
type: new GraphQLList(AppType),
args: {
appId: { type: GraphQLInt },
},
resolve: (root, args) => fetchApp(args.appId),
};
/* snip MySecondQuery, MyThirdQuery, MyFourthQuery */
const MyFirstGroupQuery = new GraphQLObjectType({
name: 'myFirstGroup',
description: 'the first group of queries',
fields: () => ({
myFirstQuery: MyFirstQuery,
mySecondQuery: MySecondQuery,
myThirdQuery: MyThirdQuery,
myFourthQuery: MyFourthQuery,
}),
});
/* snip MySecondGroupQuery, MyThirdGroupQuery and their types */
const QueryType = new GraphQLObjectType({
name: 'query',
description: 'read-only query',
fields: () => ({
myFirstGroup: MyFirstGroupQuery,
mySecondGroup: MySecondGroupQuery,
myThirdGroup: MyThirdGroupQuery,
}),
});
const Schema = new GraphQLSchema({
query: QueryType,
});
Why can't I make MyFirstGroupQuery like I did QueryType to make more nesting levels? The code works fine if I put all queries in QueryType, but my MyFirstGroupQuery produces errors:
Error: query.myFirstGroup field type must be Output Type but got: undefined.
How do I accomplish what I want? I really don't want to just prefix all my queries.
Error query.myFirstGroup field type must be Output Type but got: undefined. means that you haven't provided the type for myFirstGroup
you have to provide the type using type field
myFirstGroup: {
type: MyFirstGroupQuery,
resolve: () => MyFirstGroupQuery,
},
and if the type MyFirstGroupQuery each field must have the type defined such as GraphQLInt, GraphQLString, GraphQLID even if it's tht customtype like MyFirstGroupQuery
In GraphQLSchema constructor function you provide your RootQuery which is QueryType, It's a GraphQLSchema it only accepts the rootQuery with the GraphQLObjectType whose fields must have the type defined
GraphQL is strictly type based, every field you declared must have the type defined
https://github.com/graphql/graphql-js
https://github.com/graphql/graphql-js/blob/master/src/type/schema.js#L32
const {
GraphQLID,
GraphQLInt,
GraphQLString,
GraphQLObjectType,
GraphQLSchema,
GraphQLList,
} = require('graphql');
const AppType = new GraphQLObjectType({
name: 'App',
description: 'Generic App Details',
fields: () => ({
name: { type: GraphQLString },
appId: { type: GraphQLInt },
}),
});
// const MyFirstQuery = {
// type: new GraphQLList(AppType),
// args: {
// appId: { type: GraphQLInt },
// },
// resolve: (root, args) => fetchApp(args.appId),
// };
const myFirstQuery = new GraphQLObjectType({
name: 'First',
fields: () => ({
app: {
type: new GraphQLList(AppType),
args: {
appId: { type: GraphQLInt },
},
resolve: (root, args) => fetchApp(args.appId),
},
}),
});
/* snip MySecondQuery, MyThirdQuery, MyFourthQuery */
const MyFirstGroupQuery = new GraphQLObjectType({
name: 'myFirstGroup',
description: 'the first group of queries',
fields: () => ({
myFirstQuery: {
type: myFirstQuery,
resolve: () => [], // promise
},
// mySecondQuery: {
// type: MySecondQuery,
// resolve: () => //data
// }
// myThirdQuery: {
// type: MyThirdQuery,
// resolve: () => // data
// }
// myFourthQuery: {
// type: MyFourthQuery,
// resolve: () => //data
// }
}),
});
/* snip MySecondGroupQuery, MyThirdGroupQuery and their types */
const QueryType = new GraphQLObjectType({
name: 'query',
description: 'read-only query',
fields: () => ({
myFirstGroup: {
type: MyFirstGroupQuery,
resolve: () => MyFirstGroupQuery,
},
// mySecondGroup: {
// type: MySecondGroupQuery,
// resolve: MySecondGroupQuery
// }
// myThirdGroup: {
// type: MyThirdGroupQuery,
// resolve: MyThirdGroupQuery
// }
}),
});
const Schema = new GraphQLSchema({
query: QueryType,
});
module.exports = Schema;
GraphiQL
I am using GraphQL to query an object that will be composed from about 15 different REST calls. This is my root query in which I pass in in the ID from the query. This works fine for the main student object that resolves correctly. However, I need to figure out how to pass the ID down to the address resolver. I tried adding args to the address object but I get an error that indicates that the args are not passed down from the Student object. So my question is: How do I pass arguments from the client query to sub objects in a GraphQL server?
let rootQuery = new GraphQLObjectType({
name: 'Query',
description: `The root query`,
fields: () => ({
Student : {
type: Student ,
args: {
id: {
name: 'id',
type: new GraphQLNonNull(GraphQLString)
}
},
resolve: (obj, args, ast) => {
return Resolver(args.id).Student();
}
}
})
});
export default rootQuery;
This is my primary student object that I link the other objects. In this case I have attached the ADDRESS object.
import {
GraphQLInt,
GraphQLObjectType,
GraphQLString,
GraphQLNonNull,
GraphQLList
} from 'graphql';
import Resolver from '../../resolver.js'
import iAddressType from './address.js'
let Student = new GraphQLObjectType({
name: 'STUDENT',
fields: () => ({
SCHOOLCODE: { type: GraphQLString },
LASTNAME: { type: GraphQLString },
ACCOUNTID: { type: GraphQLInt },
ALIENIDNUMBER: { type: GraphQLInt },
MIDDLEINITIAL: { type: GraphQLString },
DATELASTCHANGED: { type: GraphQLString },
ENROLLDATE: { type: GraphQLString },
FIRSTNAME: { type: GraphQLString },
DRIVERSLICENSESTATE: { type: GraphQLString },
ENROLLMENTSOURCE: { type: GraphQLString },
ADDRESSES: {
type: new GraphQLList(Address),
resolve(obj, args, ast){
return Resolver(args.id).Address();
}}
})
});
Here is my address object that is resolved by a second REST call:
let Address = new GraphQLObjectType({
name: 'ADDRESS',
fields: () => ({
ACTIVE: { type: GraphQLString },
ADDRESS1: { type: GraphQLString },
ADDRESS2: { type: GraphQLString },
ADDRESS3: { type: GraphQLString },
CAMPAIGN: { type: GraphQLString },
CITY: { type: GraphQLString },
STATE: { type: GraphQLString },
STATUS: { type: GraphQLString },
TIMECREATED: { type: GraphQLString },
TYPE: { type: GraphQLString },
ZIP: { type: GraphQLString },
})
});
export default Address;
These are my resolver
var Resolver = (id) => {
var options = {
hostname: "myhostname",
port: 4000
};
var GetPromise = (options, id, path) => {
return new Promise((resolve, reject) => {
http.get(options, (response) => {
var completeResponse = '';
response.on('data', (chunk) => {
completeResponse += chunk;
});
response.on('end', () => {
parser.parseString(completeResponse, (err, result) => {
let pathElements = path.split('.');
resolve(result[pathElements[0]][pathElements[1]]);
});
});
}).on('error', (e) => { });
});
};
let Student= () => {
options.path = '/Student/' + id;
return GetPromise(options, id, 'GetStudentResult.StudentINFO');
}
let Address= () => {
options.path = '/Address/' + id + '/All';
return GetPromise(options, id, 'getAddressResult.ADDRESS');
};
return {
Student,
Address
};
}
export default Resolver;
ADDRESSES: {
type: new GraphQLList(Address),
resolve(obj, args, ast){
return Resolver(args.id).Address();
}
}
args passed to ADDRESSES are arguments passed to ADDRESSES field at query time. In the resolve method, obj should be the student object and if you have an id property on it, all you need to do is: return Resolver(obj.id).Address();.