Skip to content

Instantly share code, notes, and snippets.

@moret
Last active May 18, 2017 01:17
Show Gist options
  • Save moret/fca0ccaf9fd16908131f to your computer and use it in GitHub Desktop.
Save moret/fca0ccaf9fd16908131f to your computer and use it in GitHub Desktop.
N to M GraphQL Sequelize Question
import _ from 'underscore';
import Promise from 'bluebird';
import {
GraphQLInt,
GraphQLList,
GraphQLObjectType,
GraphQLSchema,
GraphQLString
} from 'graphql';
import {
resolver,
attributeFields,
defaultArgs,
defaultListArgs
} from 'graphql-sequelize';
import Sequelize from 'sequelize';
import express from 'express';
import graphqlHTTP from 'express-graphql';
import cors from 'cors';
const peopleData = [
{id: 1, name: 'John'},
{id: 2, name: 'Jane'},
{id: 3, name: 'Jill'}
];
const destinationsData = [
{id: 1, name: 'Paris'},
{id: 2, name: 'Berlin'},
{id: 3, name: 'London'}
];
const peopleDestinationData = [
{personId: 1, destinationId: 3, sequence: 1},
{personId: 1, destinationId: 1, sequence: 2},
{personId: 2, destinationId: 3, sequence: 1},
{personId: 2, destinationId: 2, sequence: 2},
{personId: 2, destinationId: 1, sequence: 3},
{personId: 3, destinationId: 1, sequence: 1}
];
const sequelize = new Sequelize(process.env.DB);
const PersonModel = sequelize.define('Person', {
name: {type: Sequelize.STRING}
});
const DestinationModel = sequelize.define('Destination', {
name: {type: Sequelize.STRING}
});
const PersonDestinationModel = sequelize.define('PersonDestination', {
sequence: {type: Sequelize.INTEGER}
});
// the n-to-m which I couldn't figure out how to use directly
PersonModel.Destinations = PersonModel.belongsToMany(DestinationModel, {
through: PersonDestinationModel,
foreignKey: 'personId',
as: 'Destinations'
});
DestinationModel.Visitors = DestinationModel.belongsToMany(PersonModel, {
through: PersonDestinationModel,
foreignKey: 'destinationId',
as: 'Visitors'
});
PersonModel.PersonDestinations = PersonModel.hasMany(PersonDestinationModel, {
foreignKey: 'personId',
as: 'PersonDestinations'
});
PersonDestinationModel.Destination = PersonDestinationModel.belongsTo(DestinationModel, {
foreignKey: 'destinationId',
as: 'Destination'
});
DestinationModel.DestinationVisitors = DestinationModel.hasMany(PersonDestinationModel, {
foreignKey: 'destinationId',
as: 'DestinationVisitors'
});
PersonDestinationModel.Visitor = PersonDestinationModel.belongsTo(PersonModel, {
foreignKey: 'personId',
as: 'Visitor'
});
const PersonType = new GraphQLObjectType({
name: 'Person',
fields: () => (
_.assign(attributeFields(PersonModel), {
personDestinations: {
type: new GraphQLList(PersonDestinationType),
resolve: resolver(PersonModel.PersonDestinations),
args: _.assign(defaultListArgs())
},
destinations: {
type: new GraphQLList(DestinationType),
resolve: resolver(PersonModel.Destinations, {
before: (options, args, root) => {
if (args.order && args.order == 'sequence') {
delete args.order;
options.order = options.order || [];
options.order.splice(options.order.findIndex(
(orderOption) => (_.isEqual(orderOption, ['sequence']))
), 1);
options.order.push([PersonDestinationModel, 'sequence']);
}
return options;
}
}),
args: _.assign(defaultListArgs(), {
name: {type: GraphQLString}
})
}
})
)
});
const DestinationType = new GraphQLObjectType({
name: 'Destination',
fields: () => (
_.assign(attributeFields(DestinationModel), {
destinationVisitors: {
type: new GraphQLList(PersonDestinationType),
resolve: resolver(DestinationModel.DestinationVisitors),
args: _.assign(defaultListArgs(), {
sequence: {type: GraphQLString}
})
},
visitors: {
type: new GraphQLList(PersonType),
resolve: resolver(DestinationModel.Visitors),
args: _.assign(defaultListArgs(), {
sequence: {type: GraphQLString}
})
}
})
)
});
const PersonDestinationType = new GraphQLObjectType({
name: 'PersonDestination',
fields: () => (
_.assign(attributeFields(PersonDestinationModel), {
destination: {
type: DestinationType,
resolve: resolver(PersonDestinationModel.Destination)
}
})
)
});
const travelType = new GraphQLObjectType({
name: 'Travels',
fields: {
people: {
type: new GraphQLList(PersonType),
resolve: resolver(PersonModel),
args: _.assign(defaultListArgs(), {
name: {type: GraphQLString}
})
},
destinations: {
type: new GraphQLList(DestinationType),
resolve: resolver(DestinationModel),
args: _.assign(defaultListArgs(), {
name: {type: GraphQLString}
})
}
}
});
const rootSchema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'RootQueryType',
fields: {
travel: {
type: travelType,
resolve: () => (true)
}
}
})
});
const app = express();
app.use(cors());
app.use('/graphql', graphqlHTTP({
schema: rootSchema,
pretty: true,
graphiql: true
}));
//exploded models drop and sync, and data insert for clarity
Promise.each(
[
PersonDestinationModel,
DestinationModel,
PersonModel
],
(Model) => Model.drop()
).then(() => (
Promise.each(
[
{Model: PersonModel, data: peopleData},
{Model: DestinationModel, data: destinationsData},
{Model: PersonDestinationModel, data: peopleDestinationData}
],
(pair) => {
const {Model, data} = pair;
return Model.sync().then(() => (
Promise.each(data, (entry) => (
Model.create(entry)
))
));
}
)
)).then(() => {
const server = app.listen(process.env.PORT || 3000, () => {
console.log(
'Example app listening at http://%s:%s',
server.address().address,
server.address().port
);
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment