graphmon-express
v1.0.3
Published
abstraction layer over graphql with mongoose as ODM
Downloads
1
Maintainers
Readme
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
- Schema duplication
- 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:
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