Generating Schema
SchemaComposer
is a builder of GraphQLSchema
object. Obtained Schema via buildSchema()
method may be used in express-graphql
, apollo-server
and other libs which uses GraphQL.js
under the hood for query execution at runtime.
Create Schema
SchemaComposer
provides basic root types Query
, Mutation
, Subscription
. You must add fields at least to one of these types, otherwise Schema will not have sense and cannot be build.
import { schemaComposer } from 'graphql-compose';
import { AuthorTC } from './author';
schemaComposer.Query.addFields({
// add field with regular FieldConfig
currentTime: {
type: 'Date',
resolve: () => Date.now(),
},
// Assume that `AuthorTC` build with `graphql-compose-mongoose` which has CRUD resolvers
// in such case we can use pre-generated Resolvers as a FieldConfig
authorById: AuthorTC.getResolver('findById'),
authorMany: AuthorTC.getResolver('findMany'),
// ...
});
schemaComposer.Mutation.addNestedFields({
// also it may be very useful define nested fields
// Mutation will have `author` field, `author` will have `create` and `update` fields inside
'author.create': AuthorTC.getResolver('createOne'),
'author.update': AuthorTC.getResolver('updateById'),
// ...
});
export default schemaComposer.buildSchema(); // exports GraphQLSchema
Restrict access
GraphQL.js does not provide any access rights checks. You should it do manually in resolve
methods. With graphql-compose
you may do it via wrapping Resolvers:
// rootMutation.js
import { schemaComposer } from 'graphql-compose';
import { CommentTC } from './comment';
import { UserTC } from './user';
schemaComposer.Mutation.addNestedFields({
commentCreate: CommentTC.getResolver('createOne'), // may anybody
...adminAccess({
// only for admins
'user.create': UserTC.getResolver('createOne'),
'user.update': UserTC.getResolver('updateById'),
'user.remove': UserTC.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;
}
For getting isAdmin
property from context
you must define it in express-graphql
or apollo-server
:
import express from 'express';
import graphqlHTTP from 'express-graphql';
import { schema } from './schema';
const PORT = 4000;
const app = express();
app.use(
'/graphql',
graphqlHTTP(async (request, response, graphQLParams) => {
return {
schema,
graphiql: true,
context: {
req: request,
isAdmin: someMethodForCheckingCookiesOrHeaders(request),
},
};
})
);
app.listen(PORT, () => {
console.log(`The server is running at http://localhost:${PORT}/graphql`);
});
Multiple Schemas
In some complex scenarios you may need to have several GraphQL Schemas in one app. Graphql-compose
by default exports following classes/instances for single schema mode:
import { schemaComposer, TypeComposer, Resolver, InputTypeComposer, EnumTypeComposer } from 'graphql-compose';
But for multi-schema mode you need to import class SchemaComposer
(starts from upper-case letter S
) and create instances from it:
import { SchemaComposer } from 'graphql-compose';
const schemaComposer1 = new SchemaComposer();
const TypeComposer1 = schemaComposer1.TypeComposer;
const Resolver1 = schemaComposer1.Resolver;
const InputTypeComposer1 = schemaComposer1.InputTypeComposer;
const EnumTypeComposer1 = schemaComposer1.EnumTypeComposer;
const schemaComposer2 = new SchemaComposer();
const TypeComposer2 = schemaComposer2.TypeComposer;
const Resolver2 = schemaComposer2.Resolver;
const InputTypeComposer2 = schemaComposer2.InputTypeComposer;
const EnumTypeComposer2 = schemaComposer2.EnumTypeComposer;
Types created via TypeComposer1
and TypeComposer2
will not be visible to each other. So may have different definitions for types with the same name.