gqutils
Utilities for GraphQL
String
, Int
, Float
, Boolean
, ID
JSON
StringOrInt
Email
URL
DateTime
UUID
StringOriginal
IntID
: this can be used where input is either an integer or a numeric string. value is casted as an integer.
String
is automatically trimmed of whitespaces. If you want an untrimmed string use StringOriginal
.
IntID
Functions
makeSchemasFromModules(modules, opts)
Create a graphQL schema from various modules. If the module is a folder, it'll automatically require it.
const modules = [
'employee/Employee',
'Category',
];
const {schemas} = makeSchemasFromModules(modules, {
baseFolder: `${__dirname}/lib`,
schema: ['admin', 'public'],
allowUndefinedInResolve: false,
resolverValidationOptions: {},
});
This function returns {schemas, pubsub}
, you can ignore pubsub if you're not using graphql subscriptions.
Each module can either export {schema, resolvers}
or the {schema}
can contain resolvers in itself.
Concept of Schemas
makeSchemasFromModules
, returns multiple graphql schemas. You have to list all possible schema names in the schema
option. Each graphql schema will only contain the types/queries/mutations etc, that have listed that schema name in their schema
option.
To be included in a particular schema, the following must be true:
- The schema name is defined in the
schema
option - The schema name is also defined in the parent's
schema
option
eg. if a query returns a particular type, then it'll not be included in a schema the that type doesn't have the schema name in its schema
option. In short, it works like intersection of schema
options of parent and child.
In case of fields, args & values, if you haven't defined schema
option, it'll be included in all schemas. So, generally speaking in case of args & values, only define schema
when you want them to exclude from a particular schema and that schema is listed in its parent's schema
.
Regardless of the schema option, a default schema named default
contains all the types/fields.
Example Schema
const Employee = {
graphql: 'type',
fields: {
id: 'ID!',
smartprixId: 'ID',
name: 'String',
email: 'String',
phone: 'String',
createdAt: 'DateTime',
updatedAt: 'DateTime',
},
schema: ['admin', 'public'],
relayConnection: true,
};
const getEmployee = {
graphql: 'query',
name: 'employee',
type: 'Employee',
args: {
$default: ['id', 'email'],
},
schema: ['admin', 'public'],
};
const getEmployees = {
graphql: 'query',
name: 'employess',
type: 'EmployeeConnection',
args: {
$default: ['name', '$paging'],
},
schema: ['admin', 'public'],
};
const saveEmployee = {
graphql: 'mutation',
args: {
$default: ['id', 'name', 'email', 'phone'],
smartprixId: {
type: 'ID',
default: 0,
schema: ['admin'],
},
},
schema: ['admin'],
};
const deleteEmployee = {
graphql: 'mutation',
args: {
id: 'ID!',
},
schema: ['admin'],
};
const employeeAdded = {
graphql: 'subscription',
type: 'Employee',
};
const employeeChanged = {
graphql: 'subscription',
type: 'Employee',
args: {
'id': 'ID!',
},
};
const resolvers = {
Query: {
employee: getEmployee,
employees: getEmployees,
},
Mutation: {
saveEmployee,
deleteEmployee,
},
Subscription: {
employeeAdded: {
subscribe() {
return pubsub.asyncIterator('employeeAdded');
},
resolve(employee) {
if (employee.password) employee.password = '******';
return employee;
},
},
employeeChanged: {
subscribe() {
return pubsub.asyncIterator('employeeChanged');
},
filter(employee, args) {
return employee.id === args.id;
},
resolve(employee) {
if (employee.password) employee.password = '******';
return employee;
},
},
},
};
export {
schema: {
Employee,
getEmployee,
getEmployeees,
saveEmployee,
deleteEmployee,
employeeAdded,
employeeChanged,
},
resolvers,
};
Language Reference
graphql option reference
type
: for object typeinput
: for input object typeunion
: for unioninterface
: for interfaceenum
: for enumscalar
: for scalarsquery
: for root querymutation
: for root mutationsubscription
: for root subscription
Types
Defined with graphql: type
const Employee = {
graphql: 'type',
name: 'Employee',
description: 'An employee',
interfaces: ['Person'],
relayConnection: true,
schema: ['admin', 'public'],
fields: {
id: 'ID!',
name: 'String',
},
}
Input Types
Defined with graphql: input
Its denition is mostly same as type.
const EmployeeInput = {
graphql: 'input',
name: 'EmployeeInput',
description: 'An employee input',
schema: ['admin', 'public'],
fields: {
id: 'ID!',
name: 'String',
},
}
Unions
Defined with graphql: union
const User = {
graphql: 'union',
name: 'User',
description: 'An employee or a guest',
schema: ['admin', 'public'],
types: ['Employee', 'Guest'],
resolveType: (value, info) => 'Type',
}
Interface
Defined with graphql: interface
const Vehicle = {
graphql: 'interface',
name: 'Vehicle',
description: 'A vehicle (can be a car or bike or bus etc)',
schema: ['admin', 'public'],
fields: {
id: 'ID!',
name: 'String',
},
resolveType: (value, info) => 'Type',
}
Enum
Defined with graphql: enum
const Color = {
graphql: 'enum',
name: 'Color',
description: 'color you know C-O-L-O-R',
schema: ['admin', 'public'],
values: {
RED: 'RED',
WHITE: 'white',
BLACK: 0,
BLUE: {
value: 'blue',
description: 'the best color obviously',
deprecationReason: 'too much blue is happening',
schema: ['admin'],
},
},
resolveType: (value, info) => 'Type',
}
Scalar
Defined with graphql: scalar
You need to give either resolve
or serialize, parseValue, parseLiteral
const URL = {
graphql: 'scalar',
name: 'URL',
description: 'A url',
schema: ['admin', 'public'],
resolve: GraphQLURL
serialize: (value) => serializedValue,
parseValue: (value) => parsedValue,
parseLiteral: (ast) => parsedValue,
}
Query / Mutation / Subscription
- Defined as
graphql: query
=> for Query - Defined as
graphql: mutation
=> for Mutation - Defined as
graphql: subscription
=> for Subscription
const getEmployees = {
graphql: 'query',
name: 'employees',
description: 'Get employees',
type: 'EmployeeConnection',
schema: ['admin', 'public'],
resolve: (root, args, ctx, info) => {}
args: {
$default: ['id', '$paging'],
name: 'String',
email: 'String',
},
}
Fields / Args
const Employee = {
graphql: 'type',
name: 'Employee',
fields: {
id: 'ID!',
email: 'String',
emails: '[String!]',
teams: {
type: 'TeamConnection',
description: 'teams that the employee belongs to',
default: 'yo',
schema: ['admin'],
deprecationReason: 'teams are so old fashioned',
resolve: (root, args, ctx, info) => {}
args: {
$default: ['id', 'phone!', '$paging', '$order'],
search: 'String',
status: {
type: 'String',
default: 'active',
schema: ['admin'],
},
},
},
}
}
getConnectionResolver(query, args)
Given a query (xorm query) and its arguments, it'll automatically generate a resolver for a relay connection.
async function getEmployees(root, args) {
const query = Employee.query();
if (args.name) {
query.where('name', 'like', `%${args.name}%`);
}
return getConnectionResolver(query, args);
}
formatError
Use this function to format the errors sent to the client, so that you can display them in a user friendly way.
It'll add fields
to each error, which you can use to display errors on front end.
import {formatError} from 'gqutils';
route.post('/api', apolloKoa({
schema: graphqlSchema,
formatError: formatError,
}));