graphmon-express

1.0.3 • Public • Published

GraphMon-Express

Purpose of the project

GraphQL has a lot of advantages but at the same time it has some chanllenges associated with it.

This project aims to resolve some of the key problems.

Problems with GraphQL when used with mongoose

  1. Schema duplication
  2. Superfluous database calls

Background

Lets discuss a sample express app with mongoose and see how GraphMon-Express helps to solve the above problems.

File structure of the project is like:

Project Structure

Sample DB structure based on mongoose

Product Model FileName: product.js

const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const productSchema = new Schema({
    name: String,
    category: { // <<---------------- Object reference
        type: Schema.Types.ObjectId,
        required: true,
        ref: 'category'
    },
    subcategory: { // <<---------------- Object reference
        type: Schema.Types.ObjectId,
        ref: 'subcategory',
        required: true
    }
    offers: [{ // <<-------------- Array of references
        type: Schema.Types.ObjectId,
        ref: 'offer'
    }],
    description: String,
    // total views of the product
    promote: Boolean,
    // total views of the product
    views: Number,
    // total sales of the product
    sales: Number,
    // unit in which product is measured 
    unit: {
        type: String,
        enum: ['Kg', 'Pcs', 'Ltr']
    },
    // max retail price
    mrp: Number,
    // selling price
    // sp: Number,
    image: String,
    // discountDescription: String,
    dateCreated: {
        type: Date,
        required: true,
        default: Date.now
    }
});
module.exports = mongoose.model('product', productSchema);

Category Model FileName: category.js

const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const categorySchema = new Schema({
    name: String,
    image: String,
    priority: Number,
    subcategories: [{
        type: Schema.Types.ObjectId,
        required: true,
        ref: 'subcategory'
    }],
    offers: [{
        type: Schema.Types.ObjectId,
        ref: 'offer'
    }],
    dateDeleted: {
        type: Date,
        default: null
    }
});


const Category = mongoose.model('category', categorySchema);
module.exports = Category;

Subcategory Model FileName: subcategory.js

const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const subcategorySchema = new Schema({
    name: String,
    image: String,
    priority: Number,
    filters: [{
        type: Schema.Types.ObjectId,
        required: false,
        ref: 'filter'
    }],
    offers: [{
        type: Schema.Types.ObjectId,
        ref: 'offer'
    }],
    dateDeleted: Date
});
module.exports = mongoose.model('subcategory', subcategorySchema);

Offer Model FileName: offer.js

const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const offerSchema = new Schema({
    name: {
        type: String,
        required: true
    },
    description: {
        type: String,
        required: true
    },
    unit: {
        type: String,
        enum: ['percentage', 'fixed'],
        default: 'percentage',
        required: true
    },
    value: {
        type: Number,
        required: true,
        min: 1
    },
    offerStartDate: {
        type: Date,
        required: true
    },
    offerEndDate: {
        type: Date,
        required: true
    },
    dateCreated: {
        type: Date,
        required: true,
        default: Date.now
    },
    dateDeleted: Date
});
module.exports = mongoose.model('offer', offerSchema);

GraphQL Schemas

Generating GRaphQL Schemas based on your existing Mongoose models is very simple. All what is required is using the GraphMon-Express exported function which takes mongoose model and returns a GraphQL model.

The only catch is with reference types, Reference types need to be `extended`using the extend option so that they can later be populated. If we don't do so, we can't select them in future GraphQL queries

Product Schema FileName: products-schema.js

const { GraphQLList } = require('graphql/type');
// `GraphMon-Express` generator
const generateSchema = require('graphmon-express');

// Mongo Models
const ProductMongo = require('../../models/product');

// **GraphQL Schemas**
const categoryGraphQLModel = require('./categories-schema').model;
const subcategoryGraphQLModel = require('./subcategories-schema').model; 
const offerGraphQLModel = require('./offers-schema').model;
const filterGraphQLModel = require('./filters-schema').model;

const { schema, model } = generateSchema('products', ProductMongo, {
    extend: {
        category: {
            type: categoryGraphQLModel
        },
        subcategory: {
            type: subcategoryGraphQLModel
        },
        offers: {
            type: new GraphQLList(offerGraphQLModel)
        },
        filters: {
            type: new GraphQLList(filterGraphQLModel)
        }
    }
});
module.exports = {
    model: model,
    schema: schema
};

Category Schema FileName: categories-schema.js

const { GraphQLList } = require('graphql/type');
// `GraphMon-Express` generator
const generateSchema = require('graphmon-express');

// Mongo Models
const CategoryMongo = require('../../models/category');

// **GraphQL Schemas**
const subcategoryGraphQLModel = require('./subcategories-schema').model;
const offerGraphQLModel = require('./offers-schema').model;

const { schema, model } = generateSchema('categories', CategoryMongo, {
    extend: {
        subcategories: {
            type: new GraphQLList(subcategoryGraphQLModel)
        },
        offers: {
            type: new GraphQLList(offerGraphQLModel)
        }
    }
});

module.exports = {
    model: model,
    schema: schema
};

Subcategory Schema FileName: subcategories-schema.js

const { GraphQLList } = require('graphql/type');
// `GraphMon-Express` generator
const generateSchema = require('graphmon-express');

// Mongo Models
const SubcategoryMongo = require('../../models/subcategory');

// **GraphQL Schemas**
const offerGraphQLModel = require('./offers-schema').model;

const { schema, model } = generateSchema('subcategories', SubcategoryMongo, {
    extend: {
        offers: {
            type: new GraphQLList(offerGraphQLModel)
        }
    }
});

module.exports = {
    model: model,
    schema: schema
};

Offers Schema FileName: offers-schema.js

// `GraphMon-Express` generator
const generateSchema = require('graphmon-express');
// Mongo Models
const OfferMongo = require('../../models/offer');


const { schema, model } = generateSchema('offers', OfferMongo);
module.exports = {
    model: model,
    schema: schema
};

The above schema files show how simple it is to convert a mongoose model into a GraphQL model. This convertion is done using mongoose-schema-to-graphql

Now these schemas which have just been created need to be wiredup with the express application.

To do this I have used express-graphql and graphql middlewares.

The following Index.js file initialises GraphQL on the basic express app.

App startup file FileName: index.js

const express = require('express');
const bodyParser = require('body-parser');
const router = require('./controllers');
const mongoose = require('mongoose');
const initGraphql = require('./middlewares/init-graphql');

const port = 3000;
const app = express();


app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
    extended: true
}));

app.use('/api', router);
// initialize graphql for all schemas
initGraphql(app); // <<------ Initialize graphQL

mongoose.connect('mongodb://localhost:27017/test', {
    useNewUrlParser: true
}, (err) => {
    app.listen(port, () => {
        console.log(`Server Started on port ${port}`);
    })
})

GraphQL initialisation FileName: init-graphql.js

const fs = require('fs');
const graphqlHTTP = require('express-graphql');

module.exports = function (app) {
    const fileNames = fs.readdirSync(`${__dirname}/../graphQl/schemas`, {
        encoding: 'utf8'
    });
    fileNames.forEach(file => {
        const schema = require(`${__dirname}/../graphQl/schemas/${file.substr(0,file.indexOf('.'))}`).schema
        app.use(`/graphql/${file.substr(0,file.indexOf('-'))}`, graphqlHTTP({
            schema: schema,
            graphiql: true
        }))
    });
}

Once this setup is done, We can run the app and issue GraphQL queries

Superfluous database calls

Superfluous database calls can be reduced by adding an arguement to the query, which accepts list of paths which need to be populated

Package Sidebar

Install

npm i graphmon-express

Weekly Downloads

0

Version

1.0.3

License

ISC

Unpacked Size

58.1 kB

Total Files

4

Last publish

Collaborators

  • crohit92