$ npm install --save graphql-sequelize
graphql-sequelize assumes you have graphql and sequelize installed.
A helper for resolving graphql queries targeted at Sequelize models or associations. Please take a look at the tests to best get an idea of implementation. *** Resolver helpers is limit query 2000 row. If you want more. Recommend use query 2 time, Example (limit: 2000,offset: 0) and (limit: 2000,offset: 1)
- Automatically converts args to where if arg keys matches model attributes
- Automatically converts an arg named 'limit' to a sequelize limit
- Automatically converts an arg named 'order' to a sequelize order
- Only loads the attributes defined in the query (automatically adds primary key and foreign keys)
- Batching of nested associations (see dataloader-sequelize)
import {resolver} from 'graphql-sequelize';
let User = sequelize.define('user', {
name: Sequelize.STRING
});
let Task = sequelize.define('task', {
title: Sequelize.STRING
});
User.Tasks = User.hasMany(Task, {as: 'tasks'});
let taskType = new GraphQLObjectType({
name: 'Task',
description: 'A task',
fields: {
id: {
type: new GraphQLNonNull(GraphQLInt),
description: 'The id of the task.',
},
title: {
type: GraphQLString,
description: 'The title of the task.',
}
}
});
let userType = new GraphQLObjectType({
name: 'User',
description: 'A user',
fields: {
id: {
type: new GraphQLNonNull(GraphQLInt),
description: 'The id of the user.',
},
name: {
type: GraphQLString,
description: 'The name of the user.',
},
tasks: {
type: new GraphQLList(taskType),
resolve: resolver(User.Tasks)
}
}
});
let schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'RootQueryType',
fields: {
user: {
type: userType,
// args will automatically be mapped to `where`
args: {
id: {
description: 'id of the user',
type: new GraphQLNonNull(GraphQLInt)
}
},
resolve: resolver(User)
}
}
})
});
let schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'RootQueryType',
fields: {
users: {
// The resolver will use `findOne` or `findAll` depending on whether the field it's used in is a `GraphQLList` or not.
type: new GraphQLList(userType),
args: {
// An arg with the key limit will automatically be converted to a limit on the target
limit: {
type: GraphQLInt
},
// An arg with the key order will automatically be converted to a order on the target
order: {
type: new GraphQLList(new GraphQLList(GraphQLString))
}
},
resolve: resolver(User)
}
}
})
});
Function resloverQuery(sequelize, sql, options()) or resloverQuery(sequelize, sql, replacements)
import {resolverQuery} from 'graphql-sequelize';
let User = sequelize.define('user', {
name: Sequelize.STRING
});
let Task = sequelize.define('task', {
title: Sequelize.STRING
});
User.Tasks = User.hasMany(Task, {as: 'tasks'});
let taskType = new GraphQLObjectType({
name: 'Task',
description: 'A task',
fields: {
id: {
type: new GraphQLNonNull(GraphQLInt),
description: 'The id of the task.',
},
title: {
type: GraphQLString,
description: 'The title of the task.',
}
}
});
let userType = new GraphQLObjectType({
name: 'User',
description: 'A user',
fields: {
id: {
type: new GraphQLNonNull(GraphQLInt),
description: 'The id of the user.',
},
name: {
type: GraphQLString,
description: 'The name of the user.',
},
tasks: {
type: new GraphQLList(taskType),
resolve: resolverQuery(sequelize, 'SELECT * FROM task WHERE id = :id', { id: 20 })
}
}
});
let schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'RootQueryType',
fields: {
user: {
type: userType,
args: {
id: {
description: 'id of the user',
type: new GraphQLNonNull(GraphQLInt)
}
},
resolve: resolverQuery(sequelize, 'SELECT id, name, tasks FROM user WHERE id = :id', { id: 20 })
}
}
})
});
let schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'RootQueryType',
fields: {
users: {
// The resolver will use `findOne` or `findAll` depending on whether the field it's used in is a `GraphQLList` or not.
type: new GraphQLList(userType),
args: {
// An arg with the key limit will automatically be converted to a limit on the target
limit: {
type: GraphQLInt
},
// An arg with the key order will automatically be converted to a order on the target
order: {
type: new GraphQLList(new GraphQLList(GraphQLString))
}
},
resolve: resolverQuery(sequelize, 'SELECT id, name, tasks FROM user', {})
}
}
})
});
Example Input
query {
user(limit: 20, offset: 5, where: { name: 'John' }, order: [['name','DESC']]) {
id
name
}
}
Code
resolverQuery(sequelize, 'SELECT id, name, tasks FROM user', {}, sequelize.models.User);
Output
SELECT * FROM (SELECT id, name, tasks FROM user) subquery WHERE `name` = 'John' LIMIT 5, 20 ORDER BY `name` DESC
Example resloverQuery() use nested query Input
query {
todo {
id
name
userId
user(limit: 20, offset: 5, where: { name: 'John' }, order: [['name','DESC']]) {
id
name
}
}
}
resolverQuery(sequelize, 'SELECT id, name, tasks FROM user WHERE id = :id', (source, args, context, info) => {
source.replacements = { id: info.source.userId }
return source;
}, sequelize.models.User);
field helpers help you automatically define a models attributes as fields for a GraphQL object type.
var Model = sequelize.define('User', {
email: {
type: Sequelize.STRING,
allowNull: false
},
firstName: {
type: Sequelize.STRING
},
lastName: {
type: Sequelize.STRING
}
});
import {attributeFields} from 'graphql-sequelize';
attributeFields(Model, {
// ... options
exclude: Array, // array of model attributes to ignore - default: []
only: Array, // only generate definitions for these model attributes - default: null
globalId: Boolean, // return an relay global id field - default: false
map: Object, // rename fields - default: {}
allowNull: Boolean, // disable wrapping mandatory fields in `GraphQLNonNull` - default: false
commentToDescription: Boolean, // convert model comment to GraphQL description - default: false
cache: Object, // Cache enum types to prevent duplicate type name error - default: {}
});
/*
{
id: {
type: new GraphQLNonNull(GraphQLInt)
},
email: {
type: new GraphQLNonNull(GraphQLString)
},
firstName: {
type: GraphQLString
},
lastName: {
type: GraphQLString
}
}
*/
userType = new GraphQLObjectType({
name: 'User',
description: 'A user',
fields: _.assign(attributeFields(Model), {
// ... extra fields
})
});
attributeFields
uses the graphql-sequelize typeMapper
to map Sequelize types to GraphQL types. You can supply your own
mapping function to override this behavior using the mapType
export.
var Model = sequelize.define('User', {
email: {
type: Sequelize.STRING,
allowNull: false
},
isValid: {
type: Sequelize.BOOLEAN,
allowNull: false
}
});
import {attributeFields,typeMapper} from 'graphql-sequelize';
typeMapper.mapType((type) => {
//map bools as strings
if (type instanceof Sequelize.BOOLEAN) {
return GraphQLString
}
//use default for everything else
return false
});
//map fields
attributeFields(Model);
/*
{
id: {
type: new GraphQLNonNull(GraphQLInt)
},
email: {
type: new GraphQLNonNull(GraphQLString)
},
isValid: {
type: new GraphQLNonNull(GraphQLString)
},
}
*/
attributeFields accepts a map
option to customize the way the attribute fields are named. The map
option accepts
an object or a function that returns a string.
var Model = sequelize.define('User', {
email: {
type: Sequelize.STRING,
allowNull: false
},
firstName: {
type: Sequelize.STRING
},
lastName: {
type: Sequelize.STRING
}
});
attributeFields(Model, {
map:{
email:"Email",
firstName:"FirstName",
lastName:"LastName"
}
});
/*
{
id: {
type: new GraphQLNonNull(GraphQLInt)
},
Email: {
type: new GraphQLNonNull(GraphQLString)
},
FirstName: {
type: GraphQLString
},
LastName: {
type: GraphQLString
}
}
*/
attributeFields(Model, {
map:(k) => k.toLowerCase()
});
/*
{
id: {
type: new GraphQLNonNull(GraphQLInt)
},
email: {
type: new GraphQLNonNull(GraphQLString)
},
firstname: {
type: GraphQLString
},
lastname: {
type: GraphQLString
}
}
*/
GraphQL enum types only support ASCII alphanumeric characters and underscores.
If you have other characters, like a dash (-
) in your Sequelize enum types,
they will be converted to camelCase. For example: foo-bar
becomes fooBar
.
If you have Sequelize.VIRTUAL
attributes on your sequelize model, you need to explicitly set the return type and any field dependencies via new Sequelize.VIRTUAL(returnType, [dependencies ... ])
.
For example, fullName
here will not always return valid data when queried via GraphQL:
firstName: { type: Sequelize.STRING },
lastName: { type: Sequelize.STRING },
fullName: {
type: Sequelize.VIRTUAL,
get: function() { return `${this.firstName} ${this.lastName}`; },
},
To work properly fullName
needs to be more fully specified:
firstName: { type: Sequelize.STRING },
lastName: { type: Sequelize.STRING },
fullName: {
type: new Sequelize.VIRTUAL(Sequelize.STRING, ['firstName', 'lastName']),
get: function() { return `${this.firstName} ${this.lastName}`; },
},
defaultArgs(Model)
will return an object containing an arg with a key and type matching your models primary key and
the "where" argument for passing complex query operations described here
var Model = sequelize.define('User', {
});
defaultArgs(Model);
/*
{
id: {
type: new GraphQLNonNull(GraphQLInt)
}
}
*/
var Model = sequelize.define('Project', {
project_id: {
type: Sequelize.UUID
}
});
defaultArgs(Model);
/*
{
project_id: {
type: GraphQLString
},
where: {
type: JSONType
}
}
*/
defaultListArgs
will return an object like:
{
limit: {
type: GraphQLInt
},
offset: {
type: GraphQLInt
},
order: {
type: new GraphQLList(new GraphQLList(GraphQLString))
},
where: {
type: JSONType
}
}
Example Query
query {
users (limit:20, offset: 5, order:[["firstname","DESC"],"createdAt"], where: { firstname: "John" }) {
id
firstname
createAt
}
}
Which when added to args will let the resolver automatically support limit and ordering in args for graphql queries.
Should be used with fields of type GraphQLList
.
import {defaultListArgs} from 'graphql-sequelize'
args: _.assign(defaultListArgs(), {
// ... additional args
})