4
votes

I've been working with GraphQL for a while and was able to successfully launch a simple GraphQL Server. Now I'm trying to make it support Relay, my final goal, and I'm having trouble adding support for the root node query and the viewer query.

I do understand the concept of both node and viewer, but I'm not making it work in the way I've structured my code. Here is my code, organized into separate files:

./graphql/User/types.js

import { 
    GraphQLObjectType, 
    GraphQLInputObjectType,
    GraphQLNonNull,
    GraphQLID,
    GraphQLList,
    GraphQLString, 
    GraphQLInt, 
    GraphQLBoolean 
} from 'graphql';

 import { GraphQLLong } from '../scalars';

 import { NodeInterface } from '../interfaces';

const fields = {
        _id: {
            type: new GraphQLNonNull(GraphQLID)
        },
        email: {
            type: GraphQLString
        },
        firstName: {
            type: GraphQLString
        },
        lastName: {
            type: GraphQLString
        },
        jobTitle: {
            type: GraphQLString
        },
        phone: {
            type: GraphQLString
        }
    };


export const UserType = new GraphQLObjectType({
    name: 'User',
    description: 'User',
    interface: NodeInterface,
    fields: fields
})

./graphql/User/queries.js

import { GraphQLNonNull, GraphQLID, GraphQLList } from 'graphql';

import { UserType, UserInputType } from './types';
import UserModel from '../../models/User';

const User = {
    type: UserType,
    description: 'Get single user',
    args: {
        id: {
            name: 'id',
            type: new GraphQLNonNull(GraphQLID)
        }
    },
    resolve(root, params) {

        params.deleted = false;

        return UserModel.find(params).exec();
    }
}


const Users = {
    type: new GraphQLList(UserType),
    description: 'Get all users',
    resolve(root) {
        const companies = UserModel.find({ deleted: false }).exec();
        if (!companies) {
            throw new Error('Error getting users.')
        }
        return companies;   
    }
}

export default {
    User,
    Users
}

./graphql/Company/types.js

import { 
    GraphQLObjectType, 
    GraphQLInputObjectType,
    GraphQLNonNull,
    GraphQLID,
    GraphQLList,
    GraphQLString, 
    GraphQLInt, 
    GraphQLBoolean 
} from 'graphql';

 import { GraphQLLong } from '../scalars';

 import { UserType } from '../User/types';

 import { NodeInterface } from '../interfaces';

 import UserModel from '../../models/User';

 const fields = {
    _id: {
        type: new GraphQLNonNull(GraphQLID)
    },
    name: {
        type: GraphQLString
    },
    ein: {
        type: GraphQLString
    },
    users: {
        type: new GraphQLList(UserType),
        resolve(company) {
            const { _id } = company;
            return UserModel.find({ companyId: _id }).exec();
        }
    }
 };

 export const CompanyType = new GraphQLObjectType({
    name: 'Company',
    description: 'Company',
    interface: NodeInterface,
    fields: fields
 })
 

./graphql/Company/queries.js

import { GraphQLNonNull, GraphQLID, GraphQLList } from 'graphql';

import { CompanyType, CompanyInputType } from './types';
import CompanyModel from '../../models/Company';

const Company = {
    type: CompanyType,
    description: 'Get single company',
    args: {
        id: {
            name: 'id',
            type: new GraphQLNonNull(GraphQLID)
        }
    },
    resolve(root, params) {

        params.deleted = false;

        return CompanyModel.find(params).exec();
    }
}


const Companies = {
    type: new GraphQLList(CompanyType),
    description: 'Get all companies',
    resolve(root) {
        const companies = CompanyModel.find({ deleted: false }).exec();
        if (!companies) {
            throw new Error('Error getting companies.')
        }
        return companies;   
    }
}

export default {
    Company,
    Companies
}

./graphql/interfaces.js

import { 
    GraphQLInterfaceType,
    GraphQLNonNull, 
    GraphQLID 
} from 'graphql';


const NodeInterface = new GraphQLInterfaceType({
    name: 'Node',
    fields: {
        id: {
            type: new GraphQLNonNull(GraphQLID)
        }
    },
    resolveType: (source) => {

        return source.__type;
    }
});

export default NodeInterface;

./graphql/schema.js

import { GraphQLObjectType, GraphQLSchema } from 'graphql';
import queries from './queries';
import mutations from './mutations';


export default new GraphQLSchema({
    query: new GraphQLObjectType({
        name: 'Query',
        fields: queries
    }),
    mutation: new GraphQLObjectType({
        name: 'Mutation',
        fields: mutations
    })
});

./graphql/queries.js

import {
    GraphQLObjectType,
    GraphQLNonNull,
    GraphQLID
} from 'graphql';

import { NodeInterface } from './interfaces';
import CompanyQueries from './Company/queries';
import UserQueries from './User/queries';

const RootQuery = new GraphQLObjectType({

    name: 'RootQuery',
    description: 'The root query',
    fields: {
        viewer: {
            type: NodeInterface,
            resolve(source, args, context) {
                return { result: "VIEWER!" };
            }
        },
        node: {
            type: NodeInterface,
            args: {
                id: {
                    type: new GraphQLNonNull(GraphQLID)
                }
            },
            resolve(source, args, context, info) {
                return { result: "NODE QUERY" };
            }
        }
    }
});

export default {
    RootQuery,
    ...CompanyQueries,
    ...UserQueries
}

./server.js

import express from 'express';
import bodyParser from 'body-parser';
import mongoose from 'mongoose';
import morgan from 'morgan';
import graphqlHTTP from 'express-graphql';

import schema from './graphql/schema';

// set up example server
const app = express();
app.set('port', (process.env.API_PORT || 3001));


// logger
app.use(morgan('dev')); 

// parse body
app.use(bodyParser.json());

app.use('/graphql', graphqlHTTP({
    schema: schema,
    graphiql: true,
    pretty: true
}));

const mongoUri = process.env.MONGO_URI || 'mongodb://localhost/testdb';

mongoose.set('debug', true);

mongoose.connect(mongoUri, {
    useMongoClient: true,
});

export default app;

The RootQuery is not being added to Schema. In fact I'm getting this error:

Error: Query.RootQuery field type must be Output Type but got: undefined.

Conceptually I don't know how to fix this code:

a) I don't know how to add that RootQuery to my query, and I don't know if I need to keep my other queries to my server (as Relay basically relies on node queries).

b) I cannot also see how my RootQuery will discover the object type and return it at the return source.__type. I've added that piece of code but I don't know how or where the given type will fill up this field.

Although I'm understanding a little of GraphQL, it seems that I don't have yet the fundamentals on how to build the Relay required root query (not to talk about the paginators, but this is my next step once I solve that).

1
Can you show scalars.js ?RoutesMaps.com

1 Answers

0
votes

The problem appears to be

import queries from './queries';

new GraphQLObjectType({
    name: 'Query',
    fields: queries
}),
const RootQuery = new GraphQLObjectType({
    …
});

export default {
    RootQuery,
    …
};

The queries that you pass as the fields for the Query type is an object of types, not an object of field configurations. They are missing type and resolve properties, as the error message complains.