graphql-compose

Toolkit for generating complex GraphQL Schemas on Node.js

README


graphql-compose

undefined codecov coverage Travis npm Commitizen friendly
TypeScript compatible Backers on Open Collective Sponsors on Open Collective

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 Resolver's – the named graphql fieldConfigs, which can be used for finding, updating, removing records
provides an easy way for creating relations between types via Resolver's
provides converter from OutputType to InputType
provides projection parser from AST
provides GraphQL schema language for defining simple types
adds additional types Date, Json

And a little bit more


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.

Type generators built on top graphql-compose


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

Utility plugins:


graphql-compose-relay - reassemble GraphQL types withRelay specific things, like Node type and interface, globalId, clientMutationId.
graphql-compose-connection - generatesconnection Resolver from findMany and count Resolvers.
graphql-compose-dataloader - adds DataLoader to graphql-composer resolvers.

Documentation



Live Demos


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)

Examples


Please follow Quick Start Guide for the complete example.

Here is just a demo of ambiguity ways of types definitions:

  1. ``` js
  2. import { schemaComposer} from 'graphql-compose';

  3. // You may use SDL format for type definition
  4. const CityTC = schemaComposer.createObjectTC(`
  5.   type City {
  6.     code: String!
  7.     name: String!
  8.     population: Number
  9.     countryCode: String
  10.     tz: String
  11.   }
  12. `);

  13. // Define type via Config object
  14. const CountryTC = schemaComposer.createObjectTC({
  15.   name: 'Country',
  16.   fields: {
  17.     title: 'String',
  18.     geo: `type LonLat { lon: Float, lat: Float }`,
  19.     hoisting: {
  20.       type: () => AnotherTC,
  21.       description: `
  22.         You may wrap type in thunk for solving
  23.         hoisting problems when two types cross reference
  24.         each other.
  25.       `,
  26.     }
  27.   }
  28. });

  29. // Or via declarative methods define some additional fields
  30. CityTC.addFields({
  31.   country: CountryTC, // some another Type
  32.   ucName: { // standard GraphQL like field definition
  33.     type: GraphQLString,
  34.     resolve: (source) => source.name.toUpperCase(),
  35.   },
  36.   currentLocalTime: { // extended GraphQL Compose field definition
  37.     type: 'Date',
  38.     resolve: (source) => moment().tz(source.tz).format(),
  39.     projection: { tz: true }, // load `tz` from database, when requested only `localTime` field
  40.   },
  41.   counter: 'Int', // shortening for only type definition for field
  42.   complex: `type ComplexType {
  43.     subField1: String
  44.     subField2: Float
  45.     subField3: Boolean
  46.     subField4: ID
  47.     subField5: JSON
  48.     subField6: Date
  49.   }`,
  50.   list0: {
  51.     type: '[String]',
  52.     description: 'Array of strings',
  53.   },
  54.   list1: '[String]',
  55.   list2: ['String'],
  56.   list3: [new GraphQLOutputType(...)],
  57.   list4: [`type Complex2Type { f1: Float, f2: Int }`],
  58. });

  59. // Add resolver method
  60. CityTC.addResolver({
  61.   kind: 'query',
  62.   name: 'findMany',
  63.   args: {
  64.     filter: `input CityFilterInput {
  65.       code: String!
  66.     }`,
  67.     limit: {
  68.       type: 'Int',
  69.       defaultValue: 20,
  70.     },
  71.     skip: 'Int',
  72.     // ... other args if needed
  73.   },
  74.   type: [CityTC], // array of cities
  75.   resolve: async ({ args, context }) => {
  76.     return context.someCityDB
  77.       .findMany(args.filter)
  78.       .limit(args.limit)
  79.       .skip(args.skip);
  80.   },
  81. });

  82. // Remove `tz` field from schema
  83. CityTC.removeField('tz');

  84. // Add description to field
  85. CityTC.extendField('name', {
  86.   description: 'City name',
  87. });

  88. schemaComposer.Query.addFields({
  89.   cities: CityTC.getResolver('findMany'),
  90.   currentTime: {
  91.     type: 'Date',
  92.     resolve: () => Date.now(),
  93.   },
  94. });

  95. schemaComposer.Mutation.addFields({
  96.   createCity: CityTC.getResolver('createOne'),
  97.   updateCity: CityTC.getResolver('updateById'),
  98.   ...adminAccess({
  99.     removeCity: CityTC.getResolver('removeById'),
  100.   }),
  101. });

  102. function adminAccess(resolvers) {
  103.   Object.keys(resolvers).forEach(k => {
  104.     resolvers[k] = resolvers[k].wrapResolve(next => rp => {
  105.       // rp = resolveParams = { source, args, context, info }
  106.       if (!rp.context.isAdmin) {
  107.         throw new Error('You should be admin, to have access to this action.');
  108.       }
  109.       return next(rp);
  110.     });
  111.   });
  112.   return resolvers;
  113. }

  114. // construct schema which can be passed to express-graphql, apollo-server or graphql-yoga
  115. export const schema = schemaComposer.buildSchema();
  116. ```

Contributors


This project exists thanks to all the people who contribute.

Backers


Thank you to all our backers! 🙏 [Become a backer]


Sponsors


Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [Become a sponsor]


License