graphql-compose
Toolkit for generating complex GraphQL Schemas on Node.js
README

graphql-compose
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-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:
- ``` js
- import { schemaComposer} from 'graphql-compose';
- // You may use SDL format for type definition
- const CityTC = schemaComposer.createObjectTC(`
- type City {
- code: String!
- name: String!
- population: Number
- countryCode: String
- tz: String
- }
- `);
- // Define type via Config object
- const CountryTC = 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 fields
- CityTC.addFields({
- country: CountryTC, // some another Type
- ucName: { // standard GraphQL like field definition
- type: GraphQLString,
- resolve: (source) => source.name.toUpperCase(),
- },
- currentLocalTime: { // extended GraphQL Compose field definition
- type: '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 field
- complex: `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: [new GraphQLOutputType(...)],
- list4: [`type Complex2Type { f1: Float, f2: Int }`],
- });
- // Add resolver method
- CityTC.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 cities
- resolve: async ({ args, context }) => {
- return context.someCityDB
- .findMany(args.filter)
- .limit(args.limit)
- .skip(args.skip);
- },
- });
- // Remove `tz` field from schema
- CityTC.removeField('tz');
- // Add description to field
- CityTC.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'),
- }),
- });
- function adminAccess(resolvers) {
- Object.keys(resolvers).forEach(k => {
- resolvers[k] = resolvers[k].wrapResolve(next => rp => {
- // rp = resolveParams = { source, args, context, info }
- if (!rp.context.isAdmin) {
- throw new Error('You should be admin, to have access to this action.');
- }
- return next(rp);
- });
- });
- return resolvers;
- }
- // construct schema which can be passed to express-graphql, apollo-server or graphql-yoga
- export const schema = schemaComposer.buildSchema();
- ```
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]
探客时代
