npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

graphmon-express

v1.0.3

Published

abstraction layer over graphql with mongoose as ODM

Downloads

1

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

  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