graphql-compose – provides a type registry with a bunch of methods for programmatic schema construction. It allows not only to extend types but also remove fields, interfaces, args. If you want to write your graphql schema generator – graphql-compose is a good instrument for you.
- provides methods for editing GraphQL output/input types (add/remove fields/args/interfaces)
- introduces
Resolvers – the named graphql fieldConfigs, which can be used for finding, updating, removing records - provides an easy way for creating relations between types via
Resolvers - provides converter from
OutputTypetoInputType - provides
projectionparser from AST - provides
GraphQL schema languagefor defining simple types - adds additional types
Date,Json
graphql-compose-[plugin] – are declarative generators/plugins built on top of graphql-compose, which take some ORMs, schema definitions and create GraphQL Models from them or modify existing GraphQL Types.
- graphql-compose-json - generates GraphQL type from JSON (a good helper for wrapping REST APIs)
- graphql-compose-mongoose - generates GraphQL types from mongoose (MongoDB models) with Resolvers.
- graphql-compose-elasticsearch - generates GraphQL types from elastic mappings; ElasticSearch REST API proxy via GraphQL.
- graphql-compose-aws - expose AWS Cloud API via GraphQL
- graphql-compose-relay - reassemble GraphQL types with
Relayspecific things, likeNodetype and interface,globalId,clientMutationId. - graphql-compose-connection - generates
connectionResolver fromfindManyandcountResolvers. - graphql-compose-dataloader - adds DataLoader to graphql-composer resolvers.
- graphql-compose.herokuapp.com - Live demo of GraphQL Server (9 models, 14 files, ~750 LOC)
- nodkz.github.io/relay-northwind - Live demo of Relay client working with the server above (8 crazy pages, 47 files, ~3000 LOC)
Please follow Quick Start Guide for the complete example.
Here is just a demo of ambiguity ways of types definitions:
import{schemaComposer}from'graphql-compose';// You may use SDL format for type definitionconstCityTC=schemaComposer.createObjectTC(` type City { code: String! name: String! population: Number countryCode: String tz: String }`);// Define type via Config objectconstCountryTC=schemaComposer.createObjectTC({name: 'Country',fields: {title: 'String',geo: `type LonLat { lon: Float, lat: Float }`,hoisting: {type: ()=>AnotherTC,description: ` You may wrap type in thunk for solving hoisting problems when two types cross reference each other. `,}}});// Or via declarative methods define some additional fieldsCityTC.addFields({country: CountryTC,// some another TypeucName: {// standard GraphQL like field definitiontype: GraphQLString,resolve: (source)=>source.name.toUpperCase(),},currentLocalTime: {// extended GraphQL Compose field definitiontype: 'Date',resolve: (source)=>moment().tz(source.tz).format(),projection: {tz: true},// load `tz` from database, when requested only `localTime` field},counter: 'Int',// shortening for only type definition for fieldcomplex: `type ComplexType { subField1: String subField2: Float subField3: Boolean subField4: ID subField5: JSON subField6: Date }`,list0: {type: '[String]',description: 'Array of strings',},list1: '[String]',list2: ['String'],list3: [newGraphQLOutputType(...)],list4: [`type Complex2Type { f1: Float, f2: Int }`],});// Add resolver methodCityTC.addResolver({kind: 'query',name: 'findMany',args: {filter: `input CityFilterInput { code: String! }`,limit: {type: 'Int',defaultValue: 20,},skip: 'Int',// ... other args if needed},type: [CityTC],// array of citiesresolve: async({ args, context })=>{returncontext.someCityDB.findMany(args.filter).limit(args.limit).skip(args.skip);},});// Remove `tz` field from schemaCityTC.removeField('tz');// Add description to fieldCityTC.extendField('name',{description: 'City name',});schemaComposer.Query.addFields({cities: CityTC.getResolver('findMany'),currentTime: {type: 'Date',resolve: ()=>Date.now(),},});schemaComposer.Mutation.addFields({createCity: CityTC.getResolver('createOne'),updateCity: CityTC.getResolver('updateById'),
...adminAccess({removeCity: CityTC.getResolver('removeById'),}),});functionadminAccess(resolvers){Object.keys(resolvers).forEach(k=>{resolvers[k]=resolvers[k].wrapResolve(next=>rp=>{// rp = resolveParams = { source, args, context, info }if(!rp.context.isAdmin){thrownewError('You should be admin, to have access to this action.');}returnnext(rp);});});returnresolvers;}// construct schema which can be passed to express-graphql, apollo-server or graphql-yogaexportconstschema=schemaComposer.buildSchema();This project exists thanks to all the people who contribute.
Thank you to all our backers! 🙏 [Become a backer]
Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [Become a sponsor]
