UNPKG

@graphql-tools/federation

Version:

Useful tools to create and manipulate GraphQL schemas.

85 lines (79 loc) 3.19 kB
import { visit } from 'graphql'; import { ValueOrPromise } from 'value-or-promise'; import { mergeResolvers, mergeTypeDefs } from '@graphql-tools/merge'; import { makeExecutableSchema } from '@graphql-tools/schema'; import { printSchemaWithDirectives } from '@graphql-tools/utils'; export const SubgraphBaseSDL = /* GraphQL */ ` scalar _Any scalar _FieldSet scalar link__Import enum link__Purpose { SECURITY EXECUTION } type _Service { sdl: String! } type Query { _entities(representations: [_Any!]!): [_Entity]! _service: _Service! } directive @external on FIELD_DEFINITION | OBJECT directive @requires(fields: _FieldSet!) on FIELD_DEFINITION directive @provides(fields: _FieldSet!) on FIELD_DEFINITION directive @key(fields: _FieldSet!, resolvable: Boolean = true) repeatable on OBJECT | INTERFACE directive @link( url: String! as: String for: link__Purpose import: [link__Import] ) repeatable on SCHEMA directive @shareable repeatable on OBJECT | FIELD_DEFINITION directive @inaccessible on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION directive @tag( name: String! ) repeatable on FIELD_DEFINITION | INTERFACE | OBJECT | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION directive @override(from: String!) on FIELD_DEFINITION directive @composeDirective(name: String!) repeatable on SCHEMA directive @extends on OBJECT | INTERFACE `; export function buildSubgraphSchema(opts) { const typeDefs = mergeTypeDefs([SubgraphBaseSDL, opts.typeDefs]); const entityTypeNames = []; visit(typeDefs, { ObjectTypeDefinition: node => { if (node.directives?.some(directive => directive.name.value === 'key')) { entityTypeNames.push(node.name.value); } }, }); const entityTypeDefinition = `union _Entity = ${entityTypeNames.join(' | ')}`; const givenResolvers = mergeResolvers(opts.resolvers); const subgraphResolvers = { _Entity: { __resolveType: (obj) => obj.__typename, }, Query: { _entities: (_root, args, context, info) => ValueOrPromise.all(args.representations.map(representation => new ValueOrPromise(() => givenResolvers[representation.__typename]?.__resolveReference?.(representation, context, info)).then(resolvedEntity => { if (!resolvedEntity) { return representation; } if (!resolvedEntity.__typename) { resolvedEntity.__typename = representation.__typename; } return resolvedEntity; }))).resolve(), _service: () => ({}), }, _Service: { sdl: (_root, _args, _context, info) => printSchemaWithDirectives(info.schema), }, }; return makeExecutableSchema({ assumeValid: true, assumeValidSDL: true, ...opts, typeDefs: [entityTypeDefinition, typeDefs], resolvers: [subgraphResolvers, givenResolvers], }); }