1.0.2 • Published 3 years ago

sequelize-graphql-cruder v1.0.2

Weekly downloads
-
License
MIT
Repository
github
Last release
3 years ago

Sequelize GraphQL Cruder

npm npm NPM

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

  • Simple and easy to use
  • Does not depend on any package
  • Generates the schema and resolvers for you in the form of JS modules
  • Handles Sequelize associations automatically
  • 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 ScalarSequelize data type
DateDATE DATEONLY
TimeTIME
NumericNUMERIC DECIMAL DOUBLE DOUBLE PRECISION
BigIntBIGINT

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.|