sequelize-graphql-cruder
v1.0.2
Published
Generate GraphQL Schema and CRUD operations from Sequelize models
Downloads
9
Maintainers
Readme
Sequelize GraphQL Cruder
Automatically generate GraphQL schema and CRUD operations from Sequelize models
Install
Install using NPM.
npm i sequelize-graphql-cruder
Or clone the repository
git clone https://github.com/SalahBelila/sequelize-graphql-cruder.git
Features
- [x] Simple and easy to use
- [x] Does not depend on any package
- [x] Generates the schema and resolvers for you in the form of JS modules
- [x] Handles Sequelize associations automatically
- [x] Generates custom scalars when needed
Usage
The package exposes the function codeGen
which allows you to generate the schema and resolvers from the seqeulize models.
The codeGen
function takes two parameters (required), sequelize models and the schema directory respectively.
const sequelizeModels = require('./models');
const { codeGen } = require('sequelize-graphql-cruder');
const SCHEMA_DIR = './schema';
codeGen(sequelizeModels, SCHEMA_DIR);
After the schema is generated you can import it into your project in the following manner:
const {schema, resolvers, customScalars} = require('./schema');
A Simple Server Example
const { plugCustomScalars } = require('sequelize-graphql-cruder');
const { Sequelize } = require('sequelize');
const express = require('express');
const { graphqlHTTP } = require('express-graphql');
const { buildSchema } = require('graphql');
const sequelizeModels = require('./models');
const sequelizeConfig = require('./sequelize_config');
const { schema, resolvers, customScalars } = require('./schema');
// Build the schema.
const executableSchema = buildSchema(schema);
// Add the generated custom scalars to schema type mapper.
plugCustomScalars(executableSchema, customScalars);
const sequelize = new Sequelize(sequelizeConfig);
const app = express();
// the models and the sequelize instance must provided to the resolvers through the context;
app.use('/graphql', graphqlHTTP({
schema: executableSchema,
context: {
models: sequelizeModels,
sequelize
},
rootValue: resolvers,
graphiql: true
}));
app.listen(5000, () => console.log("The Server is Running..."));
Generated Files
The generated schema is split into several JS modules, each module corresponds to a data model providing the schema Typedefs and resolvers of that model.
Schema
The Schema generated by the package is totally infered from the given sequelize models. As mentioned before, each model has its corresponding JS module that exposes two objects, one of which is the schema object. A schema object may look like this:
const schema = {
typeDefs: `
type Author {
id: Int
name: String
bio: String
books: [Book!]
}
input AuthorInput {
id: Int
name: String
bio: String
}
`,
Query: `
authorGet(id: Int, name: String, bio: String): [Author!]
`,
Mutation: `
authorAdd(id: Int, name: String, bio: String): Author
authorUpdate(searchInput: AuthorInput, updateInput: AuthorInput): Int
authorDelete(id: Int, name: String, bio: String): Int
`
}
The above schema object has three properties typeDefs
, Query
and Mutation
. You may have noticed that this follows the same logic of GraphQL
, meaning that you can add a third proprety Subscription
.
All properties are optional.
All properties must follow GraphQL schema definition language syntax
This is what the above schema object would look like in GraphQL SDL
:
type Author {
id: Int
name: String
bio: String
books: [Book!]
}
input AuthorInput {
id: Int
name: String
bio: String
}
type Query {
authorGet(id: Int, name: String, bio: String): [Author!]
}
type Mutation {
authorAdd(id: Int, name: String, bio: String): Author
authorUpdate(searchInput: AuthorInput, updateInput: AuthorInput): Int
authorDelete(id: Int, name: String, bio: String): Int
}
Resolvers
The resolvers are exposed in each JS module as mentioned before. The exposed Resolvers are merely an object with four methods, each method represents a CRUD operation. This is how a resolvers object looks:
const resolvers = {
authorAdd: async (args, { models, sequelize }) => {
const transaction = await sequelize.transaction();
try {
const result = await models.Author.create(args, {transaction});
await transaction.commit();
return result;
} catch { await transaction.rollback(); }
},
authorGet: async (args, { models, sequelize }) => {
const transaction = await sequelize.transaction();
try {
const result = await models.Author.findAll({ where: args, transaction });
await transaction.commit();
return result;
} catch { await transaction.rollback(); }
},
authorUpdate: async (args, { models, sequelize }) => {
const transaction = await sequelize.transaction();
try {
const result = await models.Author.update
(
args.updateInput,
{ where: args.searchInput, transaction }
);
await transaction.commit();
return result[0];
} catch { await transaction.rollback(); }
},
authorDelete: async (args, { models, sequelize }) => {
const transaction = await sequelize.transaction();
try {
const result = await models.Author.destroy({where: args, transaction});
await transaction.commit();
return result;
} catch { await transaction.rollback(); }
}
}
Notice that the context parameter must contain the models and the sequelize instance
Custom Scalars
Requiring the generated schema yields an object with three properties schema
, resolvers
and customScalars
. We discussed above the schema
and resolvers
and now it is time to talk about customScalars
.customScalars
is just a list containing the names of all custom scalars used in the schema. This list can be passed to the plugCustomScalars
function which is responsible for adding the custom scalars to the built schema type map.
const { plugCustomScalars } = require('sequelize-graphql-cruder');
const { schema, resolvers, customScalars } = require('./schema');
// Build the schema.
const executableSchema = buildSchema(schema);
// Add the generated custom scalars to schema type mapper.
plugCustomScalars(executableSchema, customScalars);
Alternatively you can add the mapping manually using your own Custom Scalar objects.
If the schema does not use any custom scalars customScalars
will be an empty array
The table below shows all provided custom scalars and the corresponding Seqeulize data types:
|Custom Scalar | Sequelize data type|
|:-------------:|:--------:|
|Date | DATE
DATEONLY
|
|Time | TIME
|
|Numeric | NUMERIC
DECIMAL
DOUBLE
DOUBLE PRECISION
|
|BigInt | BIGINT
|
Note: if a mapping could not be found for a Sequelize data type, the string scalar will be used.
Sequelize Associations
Sequelize implements the three types of associations (one-to-one, one-to-many, many-to-many) through four methods of the Model
class: belongsTo
, hasOne
, hasMany
and belongsToMany
. Sequelize GraphQL Cruder
takes these associations into consideration when generating the schema. The table below describes how Sequelize GraphQL Cruder
behaves when it finds any of the aforementionned associations defined on a model:
|Association| Cruder Behaviour |
|:---------:|:----------------:|
|BelongsTo
| Cruder will generate a field of the same type of the referenced Object (replaces the FK reference).|
|HasMany
| Cruder will generate a field of type list, each element of the list is of the same type of the referenced Object.|
|HasOne
| Not supported yet, Cruder will generate the fields as they are in the model.|
|BelongsToMany
| Not supported yet, Cruder will generate the fields as they are in the model.|