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 – is a query language for APIs. graphql-js is the reference implementation of GraphQL for nodejs which introduce GraphQL type system for describing schema (definition over configuration) and executes queries on the server side. express-graphql is a HTTP server which gets request data, passes it to graphql-js
and returned result passes to response.
graphql-compose
– the imperative tool which worked on top of graphql-js
. It provides some methods for creating types and GraphQL Models (so I call types with a list of common resolvers) for further building of complex relations in your schema.
- 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
graphql-compose-[plugin]
– is a declarative generators/plugins that build on top of graphql-compose
, which take some ORMs, schema definitions and creates GraphQL Models from them or modify existed GraphQL Types.
Type generator plugins:
Utility plugins:
Documentation
graphql-compose.github.io
Live Demos
Example
city.js
import { TypeComposer} from 'graphql-compose';
import { CountryTC } from './country';
export const CityTC = TypeComposer.create(`
type City {
code: String!
name: String!
population: Number
countryCode: String
tz: String
}
`);
CityTC.addFields({
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.addRelation(
'country',
{
resolver: () => CountryTC.getResolver('findOne'),
prepareArgs: {
filter: source => ({ code: `${source.countryCode}` }),
},
projection: { countryCode: true },
}
);
CityTC.removeField('tz');
CityTC.extendField('name', {
description: 'City name',
});
schema.js
import { schemaComposer } from 'graphql-compose';
import { CityTC } from './city';
import { CountryTC } from './country';
schemaComposer.Query.addFields({
cities: CityTC.getResolver('findMany'),
country: CountryTC.getResolver('findOne'),
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 default 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