Define some root queries and mutations
In order to define your root queries and mutations, you need to create a function that maps a cohere type to a series of queries and mutations.
queries.js
import { GraphQLNonNull, GraphQLID, GraphQLList } from 'graphql';
// convert user -> User
const titleize = (str) => `${str[0].toUpperCase()}${str.slice(1)}`;
export default (type) => ({
// findOneUser and findOneBlog
[`findOne${titleize(type.name)}`]: {
description: `Retrieves a single ${type.name}`,
args: { id: { type: new GraphQLNonNull(GraphQLID) } },
actions: {
resolve: (args, ctx) => type.model.findOne(args, ctx).then(data => ctx.data = data),
finalize: (args, ctx) => ctx.data,
error: (err) => console.log(err),
},
},
// findAllUsers and findAllBlogs
[`findAll${titleize(type.name)}s`]: {
description: `Finds all ${type.name} types`,
actions: {
resolve: (args, ctx) => type.model.findAll(args, ctx).then(data => ctx.data = data),
finalize: (args, ctx) => ctx.data,
error: (err) => console.log(err),
},
},
});
Every type defined in schema.js is mapped to the following queries: fetchUser, fetchBlog, findUsers, and findBlogs. Micrograph expects the exported function to return an object whose keys are the root query names and whose values are an object with optional description, args, and output keys. The fourth key actions has a required resolve key and optional finalize and error keys.
descriptionis the GraphQL descriptionargsis a valid GraphQL args objectoutputis a valid GraphQL wrapper (like GraphQLList) or a function that takes in a GraphQL object type and returns an output type, i.e.output: (type) => new GraphQLList(type)actionsis an object of methods used for resolving, preparing responses, and handling errorsresolveis the method (located in the middle of the middleware chain) that handles data fetchingfinalizeis the method that transformsctxinto an object that matches the output typeerroris error handler
mutations.js
import { GraphQLInputObjectType, GraphQLID, GraphQLNonNull } from 'graphql';
let inputObjectCache = {};
// convert user -> User
const titleize = (str) => `${str[0].toUpperCase()}${str.slice(1)}`;
// map a type to a GraphQLInputObjectType
const createInputObject = (type) => {
if (inputObjectCache.hasOwnProperty(type.name)) return inputObjectCache[type.name];
const inputObject = new GraphQLInputObjectType({
// BlogInput and UserInput
name: `${titleize(type.name)}Input`,
fields: () => type.attributes.reduce((accumulator, { field, type }) => ({
...accumulator,
[field]: { type },
}), {}),
});
inputObjectCache[type.name] = inputObject;
return inputObject;
};
export default (type) => ({
// createUser and createBlog
[`create${titleize(type.name)}`]: {
description: `Creates a ${type.name}`,
args: {
input: { type: createInputObject(type) },
},
actions: {
resolve: (args, ctx) => type.model.create(args, ctx).then(data => ctx.data = data),
finalize: (args, ctx) => ctx.data
},
},
// updateUser and updateBlog
[`update${titleize(type.name)}`]: {
description: `Updates a ${type.name}`,
args: {
input: { type: createInputObject(type) },
id: { type: new GraphQLNonNull(GraphQLID) },
},
actions: {
resolve: (args, ctx) => type.model.update(args, ctx).then(data => ctx.data = data),
finalize: (args, ctx) => ctx.data,
},
},
});
Every type defined in schema.js is mapped to the following mutations: createUser, updateUser, createBlog, and updateBlog. The createInputObject function in the example maps a type's attributes into a GraphQL input object type, which is passed into args.input. inputObjectCache in the example is required so that multiple input object types of the same name are not created, otherwise GraphQL will throw an error.
The queries.js and mutations.js files are probably the most complex files in an entire Micrograph application, but they only need to be written once. They will rarely change, and your application can continue to grow by creating more types in schema.js.