What is graphql-compose?
graphql-compose is a toolkit for generating and working with GraphQL schemas in a more flexible and composable way. It provides utilities to create GraphQL types, resolvers, and schemas with ease, allowing for a more modular and maintainable approach to building GraphQL APIs.
What are graphql-compose's main functionalities?
Schema Creation
This feature allows you to create a GraphQL schema by adding fields to the Query type. The example demonstrates adding a simple 'hello' field that returns a string.
const { schemaComposer } = require('graphql-compose');
schemaComposer.Query.addFields({
hello: {
type: 'String',
resolve: () => 'Hello, world!',
},
});
const schema = schemaComposer.buildSchema();
Type Composers
Type Composers allow you to define GraphQL object types and their fields in a modular way. The example shows how to create a 'User' type and add a custom resolver to it.
const { schemaComposer } = require('graphql-compose');
const UserTC = schemaComposer.createObjectTC({
name: 'User',
fields: {
id: 'ID!',
name: 'String',
},
});
UserTC.addResolver({
name: 'findById',
type: 'User',
args: { id: 'ID!' },
resolve: ({ source, args, context, info }) => {
// resolver logic here
},
});
Resolver Composition
Resolver Composition allows you to add multiple resolvers to a type, making it easier to manage complex query logic. The example shows how to add 'findById' and 'findByName' resolvers to the 'User' type.
const { schemaComposer } = require('graphql-compose');
const UserTC = schemaComposer.getOrCreateOTC('User', {
fields: {
id: 'ID!',
name: 'String',
},
});
UserTC.addResolver({
name: 'findById',
type: UserTC,
args: { id: 'ID!' },
resolve: ({ source, args, context, info }) => {
// resolver logic here
},
});
UserTC.addResolver({
name: 'findByName',
type: UserTC,
args: { name: 'String' },
resolve: ({ source, args, context, info }) => {
// resolver logic here
},
});
Other packages similar to graphql-compose
graphql-tools
graphql-tools is a set of utilities from Apollo for creating and manipulating GraphQL schemas. It provides schema stitching, schema delegation, and other advanced features. Compared to graphql-compose, graphql-tools is more focused on schema stitching and merging, while graphql-compose offers a more composable and modular approach to schema and resolver creation.
type-graphql
type-graphql is a library that allows you to define your GraphQL schema using TypeScript classes and decorators. It provides a more type-safe approach to schema creation compared to graphql-compose, which is more flexible and JavaScript-centric. type-graphql is ideal for TypeScript users who want to leverage decorators and strong typing.
nexus
nexus is a code-first GraphQL schema construction library for TypeScript and JavaScript. It allows you to define your schema using a fluent API and integrates well with the Prisma ORM. Compared to graphql-compose, nexus offers a more opinionated and integrated approach, especially when used with Prisma, while graphql-compose provides more flexibility and composability.
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
Utility plugins:
Documentation
graphql-compose.github.io
Live Demos
Examples
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';
const CityTC = schemaComposer.createObjectTC(`
type City {
code: String!
name: String!
population: Number
countryCode: String
tz: String
}
`);
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.
`,
}
}
});
CityTC.addFields({
country: CountryTC,
ucName: {
type: GraphQLString,
resolve: (source) => source.name.toUpperCase(),
},
currentLocalTime: {
type: 'Date',
resolve: (source) => moment().tz(source.tz).format(),
projection: { tz: true },
},
counter: 'Int',
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 }`],
});
CityTC.addResolver({
kind: 'query',
name: 'findMany',
args: {
filter: `input CityFilterInput {
code: String!
}`,
limit: {
type: 'Int',
defaultValue: 20,
},
skip: 'Int',
},
type: [CityTC],
resolve: async ({ args, context }) => {
return context.someCityDB
.findMany(args.filter)
.limit(args.limit)
.skip(args.skip);
},
});
CityTC.removeField('tz');
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 => {
if (!rp.context.isAdmin) {
throw new Error('You should be admin, to have access to this action.');
}
return next(rp);
});
});
return resolvers;
}
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]
Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [Become a sponsor]
License
MIT