📜 ⬆️ ⬇️

How to use the Mongoose schema to generate graphQL types

In this article I will tell you how, based on my experience, I wrote a small npm-module that helped me and, I think, can help you save a decent amount of time and reduce the code almost twice.

It all started with the fact that I decided to write an isomorphic CMS for one of my projects using the following technologies:


The simplified architecture is as follows:
')
__root 1 |__client 2 |__public 3 |__middleware 4 |__server 

  1. React components
  2. Client code bundle and other public files
  3. Mongoose: schemas, additional methods and graphQL: types, class with queries, class with mutations, scheme
  4. Server code bundle with express server

Accordingly, for each entity in the database, we must create a Mongoose schema in order to be able to manipulate data. It looks something like this:

 let couponSchema = mongoose.Schema({ couponCode: Array, description: String, discountAmount: String, minimumAmount: String, singleUseOnly: Boolean, createdAt: mongoose.Schema.Types.Date, updatedAt: mongoose.Schema.Types.Date, expirationDate: mongoose.Schema.Types.Date }); 

Now, if we use graphQL, we have to create a type for each entity in the database, and the type of each property should be identical to the one in the Mongoose schema. Without graphQL types we simply cannot generate graphQL schema. The type is as follows:

 let couponType = new GraphQLObjectType({ name: 'couponType', description: 'single use coupon', fields: { _id: {type: GraphQLString}, couponCode: {type: new GraphQLList(GraphQLString)}, description: {type: GraphQLString}, discountAmount: {type: GraphQLString}, minimumAmount: {type: GraphQLString}, singleUseOnly: {type: GraphQLBoolean}, createdAt: {type: GraphQLString}, updatedAt: {type: GraphQLString}, expirationDate: {type: GraphQLString} } }); 

I think the similarity is obvious? Now imagine that you have several dozen entities and for each of them you need to practically duplicate the same logic. But types can contain arrays of other types and many other nested elements.

Actually, to solve this problem, I wrote a small module (only 3kb), which will help avoid duplication of logically identical code and practically halve your code, in my case I have reduced the code by more than 2000 lines.

First, install the module:

 npm i mongoose-schema-to-graphql --save 

The module contains one single function “MTGQL”, which takes one object with configurations as an argument. The object has the following structure:

 let configs = { name: 'couponType', //  graphQL  description: 'Coupon base schema', //  graphQL  class: 'GraphQLObjectType', // graphQL       schema: couponSchema, // Mongoose  exclude: ['_id'], //     props: { price: {type: GraphQLFloat} } //        } 

A couple of examples of using the module below:

dbSchema.js

 import mongoose from 'mongoose'; let selectObj = { value: String, label: String }; let answerSchema = mongoose.Schema({ createdAt: mongoose.Schema.Types.Date, updatedAt: mongoose.Schema.Types.Date, title: String, answersImage: String, recommended: [selectObj], isPublished: Boolean }); export let questionSchema = mongoose.Schema({ question: String, defRecommended: [selectObj], createdAt: mongoose.Schema.Types.Date, updatedAt: mongoose.Schema.Types.Date, isPublished: Boolean, multipleChoice: Boolean, answers: [answerSchema] }); 

In the file with your graphQL types:

type.js

 import MTGQL from 'mongoose-schema-to-graphql'; import {questionSchema} from './dbSchemas'; let config = { name: 'questionType', description: 'Question collection\'s type', class: 'GraphQLObjectType', schema: questionSchema, exclude: ['_id'] }; export let questionType = MTGQL(config); 

Actually only 10 lines of code that are equivalent to the following:

 import { GraphQLObjectType, GraphQLString, GraphQLBoolean, GraphQLList, GraphQLInt } from 'graphql'; let selectType = new GraphQLObjectType({ name: 'selectType', fields: { value: {type: GraphQLString}, label: {type: GraphQLString} } }); let answerType = new GraphQLObjectType({ name: 'answerType', description: 'answer type for question', fields: { title: {type: GraphQLString}, answersImage: {type: GraphQLString}, recommended: {type: new GraphQLList(selectType)}, createdAt: {type: GraphQLString}, updatedAt: {type: GraphQLString}, isPublished: {type: GraphQLBoolean} } }); export let questionType = new GraphQLObjectType({ name: 'questionType', description: 'Question collection\'s type', fields: { question: {type: GraphQLString}, defRecommended: {type: new GraphQLList(selectType)}, createdAt: {type: GraphQLString}, updatedAt: {type: GraphQLString}, isPublished: {type: GraphQLBoolean}, multipleChoice: {type: GraphQLBoolean}, answers: {type: new GraphQLList(answerType)} } }); 

I think the difference is obvious. The module does not directly support “GraphQLFloat”, because in the Mongoose scheme you can only specify the type “Number”, which is equivalent to an integer. But this problem can be easily solved by passing the desired property through the configuration object.

For more information on how the module works, you can look at GitHub .

I hope the article was useful to you, if you have any suggestions for improvement, write, be sure to consider. Thanks for attention.

The original article is on medium.com .

Source: https://habr.com/ru/post/310820/


All Articles