Mongql
A package to convert your mongoose schema to graphql schema
TOC
- Mongql
- TOC
- Features
- Motivation
- Usage
- Basic Usage (Without initial typedef and resolvers)
- Intermediate Usage (With initial typedef and resolvers)
- Intermediate Usage (Output SDL and AST)
- Intermediate Usage (Fine grain Mutation configuration)
- Intermediate Usage (Fine grain Query configuration)
- Intermediate Usage (Extra Powerful Fine grain Query configuration)
- Advanced Usage (generating Schema and Models)
- Advanced Usage (Using local folders)
- Configs
- Concept
- API
- FAQ
- TODO
Features
- Create graphql schema (typedef and resolvers) from mongoose schema
- Stitch already created typedef and resolvers
- Easily configurable (any of the typedef and resolvers can be turned off)
- View the generated SDL
- Auto addition of graphql validators with mongoose
Motivation
- Creating a graphql SDL is not a difficult task by any means, but things get really cumbersome after a while, especially since a lot of the typedefs and resolvers are being repeated.
- Automating the schema generation helps to avoid errors regarding forgetting to define something in the schema thats been added to the resolver or vice versa.
- Creating resolvers for subtypes in a PITA, especially if all of them just refers to the same named key in parent
Usage
Basic Usage (Without initial typedef and resolvers)
// User.schema.jsconst mongoose = ;const UserSchema = mongoose;UserSchemamongql =resource: 'user'; // schema level configmoduleexports = UserSchema;
// index.jsconstmakeExecutableSchema} = ;constApolloServer} = ;const Mongql = ;const UserSchema = ;{const mongql =Schemas: UserSchema;// Calling the generate method generates the typedefs and resolversconstTransformedResolversTransformedTypedefs // Contains both arr and obj representation} = await mongql;const GRAPHQL_SERVER =schema:user: requser;};
Intermediate Usage (With initial typedef and resolvers)
// user.typedef.jsmoduleexports = gql `type BasicUserType{name:String!}`;
// user.resolver.jsmoduleexports =Mutation:updateUserSettings: ...
const UserAST = ;const UserResolver = ;const PreTransformedTypeDefsASTs =user: UserAST // This has to match with the resource name added in the mongoose schemaconst PreTransformedResolvers =user: UserResolverconst mongql =Schemas: UserSchema SettingsSchemaTypedefs:init: PreTransformedTypeDefsASTsResolvers:init: PreTransformedResolvers;
Intermediate Usage (Output SDL and AST)
const mongql =Schemas:output:SDL: pathAST: path;await mongql
Intermediate Usage (Fine grain Mutation configuration)
const mongql =Schemas: UserSchema SettingsSchemagenerate:mutation: false // will not generate any mutation typedef and resolver,mutation:create: false // Will not generate any create mutation typedef and resolver,update:multi: false // Will not generate any update multi mutation typedef and resolversingle: false // Will not generate any single mutation typedef and resolver;
Intermediate Usage (Fine grain Query configuration)
const mongql =Schemas: UserSchema SettingsSchemagenerate:query: falsequery:all: falsequery:paginated:self: falsequery:filtered:others:whole: false;
Intermediate Usage (Extra Powerful Fine grain Query configuration)
const mongql =Schemas: UserSchema SettingsSchemagenerate:query:self: false // remove all self related typedefs and resolvers,self:whole: false // remove all selfwhole related typedefs and resolvers,count: false // remove all count related typedefs and resolvers,;
Advanced Usage (generating Schema and Models)
const Mongql = ;constApolloServer} = ;{const mongql =Schemas:/* Your schema array here */;const server =schema: await mongql // there is a known issue with this use makeExecutableSchemacontext: mongql;await server;};
Advanced Usage (Using local folders)
const Mongql = ;constApolloServer} = ;{const mongql =Schemas: pathoutput:dir: __dirname + '\\SDL'Typedefs: pathResolvers: path;const server =schema: await mongql // there is a known issue with this use makeExecutableSchemacontext: mongql;await server;};
Mongql contains 3 level of configs
- Constructor/global level config: passed to the ctor during Mongql instantiation
- Schema level config: Attached to the schema via mongql key
- Field level config: Attached to the field via mongql key
Precedence of same config option is global < Schema < field. That is for the same option the one with the highest precedence will be used.
Configs
& refers to the whole key declared just right above.
Global Configs
Name | Description | Type | Default Value | Usage | Available in |
---|---|---|---|---|---|
output | output related configuration | boolean \| Object |
false | {output: false} {output: { dir: process.cwd()}} |
Schema |
&.(dir|SDL) | SDL Output directory | string |
undefined |
{output: { dir: process.cwd()}} |
Schema |
&. AST | AST Output directory | string |
undefined |
{output: { AST: process.cwd()}} |
Schema |
generate | Controls generation of type, query and mutations typedefs and resolvers | Object | boolean |
true |
generate: true |
Schema |
&.query | Controls generation of query typedefs and resolvers | Object | boolean |
Object |
generate :{query: true} |
Schema |
&.(range) | Controls generation of query range typedefs and resolvers | Object | boolean |
Object |
generate :{query:{ all: false}} Take a look at concepts to see all ranges |
Schema |
&.(auth) | Controls generation of query range auth typedefs and resolvers | Object | boolean |
Object |
generate :{query:{ all: {self: false}}} Take a look at concepts to see all auth |
Schema |
&.(part) | Controls generation of query range auth part typedefs and resolvers | Object | boolean |
Object |
generate :{query:{ all: {self: {whole: false}}}} Take a look at concepts to see all part |
Schema |
&.mutation | Controls generation of mutations typedefs and resolvers | Object | boolean |
true |
generate :{mutation: true} |
Schema |
&.(action) | Controls generation of mutations typedefs and resolvers action | Object | boolean |
true |
generate :{mutation: {create: {multi:true}, update: {single: false}}} Take a look at concepts to see all mutation action |
Schema |
&.(target) | Controls generation of mutations typedefs and resolvers target | Object | boolean |
true |
generate :{mutation: {create: {multi:true}, update: {single: false}}} Take a look at concepts to see all mutation targets |
Schema |
Schemas | Array of schemas generate by mongoose or path to schema folder | Schema[] | String |
[] |
Schemas: [UserSchema, ...] |
|
Typedefs | Typedefs related configuration or path to typedefs folder | Object | String |
{init: undefined} |
Typedefs: {init: {User: InitialUserTypedef}} |
|
&.init | Initial typedefs to be attached to resultant typedef | Object |
undefined |
init: {User: InitialUserTypedef} |
|
Resolvers | Resolvers related configuration or path to resolvers folders | Object | String |
{init: undefined} |
Resolvers: {init: {User: InitialUserResolvers}} |
|
&.init | Initial resolvers to be attached to resultant resolver | Object |
undefined |
init: {User: InitialUserResolver} |
|
appendRTypeToEmbedTypesKey | Controls whether or not to append the resource type to sub/embed/extra types | boolean |
true |
appendRTypeToEmbedTypesKey: true |
Schema |
Schema configs
Name | Description | Type | Default Value | Usage | Available in |
---|---|---|---|---|---|
resource | name of the resource | string |
Required | resource: User |
|
global_excludePartitions | Controls which auth partition will be excluded in the generated schemas | Object |
{base: [], extra: ['Others', 'Mixed']} |
global_excludePartitions: {base: [ 'Others', 'Mixed' ]} |
|
&.(base|extra) | Controls which auth partition will be excluded in the types of generated schemas | [] \| boolean |
{base: [], extra: ['Others', 'Mixed']} |
global_excludePartitions: {base: [ 'Others', 'Mixed' ], extra: ['Self']} |
|
generateInterface | Controls whether or not to generate interface from base resource | boolean |
true |
generateInterface: true |
|
skip | Skip mongql all together | boolean |
false |
skip: true |
Field configs
Name | Description | Type | Default Value | Usage | Available in |
---|---|---|---|---|---|
writable | Controls whether or not this field is present in generated input | boolean |
true |
writable: true |
|
scalar | Custom graphql scalar to be used (atm all graphql-scalars scalars are included) | string |
parsed type from mongoose | scalar: 'NonNegativeInt' |
Concept
During the generation of schema, a few concepts are followed
-
Each Resource query object type contains four parts
-
Range(Input):
- All: Gets all the resource
- Paginated : Used to get resource through pagination inpu
- Filtered : Used to get resource through filter input
- ID: Used to get a resource by id
-
Auth:
- Self: Used to indicate logged in users resource
- Others: Used to indicate other users resource (when current user is authenticated)
- Mixed: Used to incicate others users resource (when user is unauthenticated)
-
Resource: Name of the resource (capitalized & pluralized form)
-
Part(Output):
- Whole: Get the whole data along with sub/extra types
- NameAndId: get the name and id of the resource
- Count: get the count of resource
-
-
Each resource mutation object type contains 2 parts
- Action: One of create|update|delete
- Target: resource for targeting single resource, resources for targeting multiple resources
-
Each resource types contains the following parts
- Based on the permitted auths types will be generated with the following syntax auth resource type and type, eg SelfUserType
- All the embedded mongoose schema will be converted into its own type
- mongoose Enums fields will be converted into enums
- Based on the
generateInterface
option interface will be generated - Inputs will be created based on the generated type
Generated Query Examples: getSelfSettingsWhole, getOthersSettingsNameAndId
Generated Mutation Examples: createSetting, updateSettings
NOTE: Count part is not generate in paginated and id range as for paginated query the user already knows the count and id returns just one
API
These methods are available in the created Mongql instance
Name | Description | Params | Returned |
---|---|---|---|
generate() |
Generate the Typedefs and resolvers from the schemas | {TransformedTypedefs, TransformedResolvers} |
|
getResources() |
Gets all the resources collected from all schemas | (Schema.mongql.resource)[] |
|
generateModels() |
Generates models from the schema provided | Object:{[Schema.mongql.resource]: MongooseModel} |
|
generateSchema() |
Generates a schema by calling makeExecutableSchema internally |
options passed to makeExecutableSchema expect for typedefs and resolvers |
GraphQLSchema |
static outputSDL() |
Outputs the SDL from the given typedef | path: String // SDL output dir typedefs: GraphqlAST | String // String or AST to convert resource: String // name of file or resource |
FAQ
- Why do I need to use resource key?
Answer.
-
Resource key is used to merge the initial query, mutation and types in the correct place
-
Its used as the Model name, in the generated resolvers
-
Its used to derive relation between resources, (not yet supported), for eg in the mutation resolver, dependant resources can be created and deleted
TODO
- Add more well rounded tests
- Migrate the whole project to TS
- Using babel to transpile to transform modern featues
- Standard liniting configuration
- Provide ES modules to make the library tree-shakable
- More enriched API
- Better documentation
PRS are more than welcome and highly appreciated!!!!