GraphQL - 如果父返回类型是对象列表,则模式拼接上的模式委派不起作用

Posted

技术标签:

【中文标题】GraphQL - 如果父返回类型是对象列表,则模式拼接上的模式委派不起作用【英文标题】:GraphQL - Schema Delegation on Schema Stitching is not working if the Parent return type is a list of Objects 【发布时间】:2018-12-24 04:34:00 【问题描述】:

我有一个包含 imageId 的 Publication 对象,我想用它来创建架构委托并加载整个对象。

发布类型定义为:

const PublicationType = new GraphQLObjectType(
name: 'Publication',
description: 'Publication Type',
fields: () => (
id: 
type: GraphQLID
,
callToAction: 
type: CallToActionType
,
userId: 
type: GraphQLID
,
groupId: 
type: GraphQLID
,
instruction: 
type: InstructionType
,
customizedTitle: 
type: GraphQLString
,
content: 
type: GraphQLString
,
activityType: 
type: ActivityType
,
creationTool: 
type: CreationTool
,
gift: 
type: GiftType
,
imageId: 
type: GraphQLInt
,
thumbnailId: 
type: GraphQLInt
,
createdAt: 
type: GraphQLInt
,
updatedAt: 
type: GraphQLInt

)
);

媒体类型定义为:

const MediaType = new GraphQLObjectType(
name: 'Media',
description: 'Media Object',
fields: () => (
id: 
type: GraphQLInt
,
resource: 
type: GraphQLString
,
metadata: 
type: MetadataType
,
discr: 
type: GraphQLString

)
);

其中 MetadataType 是代码中定义的另一种类型。

这 2 个对象通过这些查询公开:

this._schema = new GraphQLObjectType(
name: 'Query',
description: 'Wall Query',
fields: () => (
getGroupPublications: 
type: new GraphQLList(PublicationType),
args: 
groupId: type: new GraphQLNonNull(GraphQLInt),
from: type: new GraphQLNonNull(GraphQLInt),
to: type: new GraphQLNonNull(GraphQLInt),
,
resolve: (root, args, context) => PublicationResolver.groupPublications(context)

),
);

 this._schema = new GraphQLObjectType(
              name: 'Query',
              description: 'Media Query',
              fields: () => (
                  getMedia: 
                      type: MediaType,
                      args: 
                          mediaId: type: new GraphQLNonNull(GraphQLInt)
                      ,
                      resolve: (root, args, context) => MediaResolver.mediaLoader(context).load(args.mediaId)
                  
              ),
          );

现在我尝试将定义的所有模式合并到一个模式中,并添加一个委托来加载类型 Media 而不是 ImageID,因此下一个类代表我将公开的主要模式:

import mergeSchemas from 'graphql-tools';
// other imports ...

class Services 

    private static _instance: Services;
    private _wall: PublicationService;
    private _media: MediaService;

    private constructor() 
        this._media = new MediaService();
        this._wall = new PublicationService();
    

    public static Instance() 
        return this._instance || (this._instance = new this())
    

    public MainSchema = async () => Promise.all([this._publication.schema, this._media.schema])
            .then(args => 
                const publicationSchema = args[0];
                const mediaSchema = args[1];

                const linkSchemaDefs = `
                                extend type Publication 
                                    image: Media
                                    thumbnail: Media
                                `;

                return mergeSchemas(
                    schemas: [publicationSchema, mediaSchema, linkSchemaDefs],
                    resolvers: 
                        
                            Publication: 
                                image: 
                                    fragment: `... on Publication  imageId `,
                                    resolve(parent, args, context, info) 
                                        let mediaId = (parent.imageId === parseInt(parent.imageId, 10)) ? parent.imageId : 0;
                                        return info.mergeInfo.delegateToSchema(
                                            schema: mediaSchema,
                                            operation: 'query',
                                            fieldName: 'getMedia',
                                            args: 
                                                mediaId: mediaId,
                                            ,
                                            context,
                                            info
                                        )
                                    
                                ,
                                thumbnail: 
                                    fragment: 'fragment PublicationThumbnailFragment on Publication  thumbnailId ',
                                    resolve(parent, args, context, info) 
                                        let mediaId = (parent.thumbnailId === parseInt(parent.thumbnailId, 10)) ? parent.thumbnailId : 0;
                                        return info.mergeInfo.delegateToSchema(
                                            schema: mediaSchema,
                                            operation: 'query',
                                            fieldName: 'getMedia',
                                            args: 
                                                mediaId: mediaId,
                                            ,
                                            context,
                                            info
                                        )
                                    
                                ,
                            ,
                        ,
                );
            )
            .catch(error => Logger.Instance().error(
                    errorName: 'SchemaStitchingError',
                    logName: 'GraphQL Schema Stitching Error',
                    cause: error,
                    message: [`Services GraphQl Schema stitching rejected`],
                )
            );


export Services

主架构将使用 graphqlExpress 服务器向最终用户公开 但是,我可以在架构定义中看到扩展类型。

但是当我尝试使用定义为的查询查询媒体类型的字段图像时

query 
getGroupPublications(groupId:2, from: 1531180800,to: 1532276998) 
id
imageId
image 
resource

createdAt


查询将直接命中 getGroupPublications 出版物,而不考虑 mergeSchema,我收到此错误:

 errors:
[  Cannot query field "image" on type "Publication". Did you mean "imageId"?

GraphQL request (5:3)
4: imageId
5: image 
^
6: resource

at Object.Field (/home/docker/workspace/api-gateway/node_modules/graphql/validation/rules/FieldsOnCorrectType.js:65:31)
at Object.enter (/home/docker/workspace/api-gateway/node_modules/graphql/language/visitor.js:324:29)
at Object.enter (/home/docker/workspace/api-gateway/node_modules/graphql/language/visitor.js:366:25)
at visit (/home/docker/workspace/api-gateway/node_modules/graphql/language/visitor.js:254:26)
at visitUsingRules (/home/docker/workspace/api-gateway/node_modules/graphql/validation/validate.js:74:22)
at validate (/home/docker/workspace/api-gateway/node_modules/graphql/validation/validate.js:59:10)
at graphqlImpl (/home/docker/workspace/api-gateway/node_modules/graphql/graphql.js:106:50)
at /home/docker/workspace/api-gateway/node_modules/graphql/graphql.js:66:223
at new Promise ()
at Object.graphql (/home/docker/workspace/api-gateway/node_modules/graphql/graphql.js:63:10)
message: 'Cannot query field "image" on type "Publication". Did you mean "imageId"?',
locations: [Array],
path: undefined  ] 

但是如果我在没有字段图像的情况下运行我的查询,它可以正常工作

query 
getGroupPublications(groupId:1, from: 1531180800,to: 1532276998) 
id
imageId


【问题讨论】:

【参考方案1】:

我遇到的问题是由于将完整的 Query 传递给远程架构,MergeSchema Resolver 只有在远程架构返回结果后才会执行。

所以在 getGroupPublications 解析器PublicationResolver.groupPublications(context) 中,我替换了通过扩展类型添加的字段。

            let queryString = context.req.body.query;
            queryString = queryString.replace(/image[\s\r\n]*?[^]*/gm, "");
            queryString = queryString.replace(/thumbnail[\s\r\n]*?[^]*/gm, "");
            return graphql(schema, queryString,
                null,
                context,
                context.req.body.variables)

【讨论】:

以上是关于GraphQL - 如果父返回类型是对象列表,则模式拼接上的模式委派不起作用的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Graphql 中返回 JSON 对象并定义 JSON 类型

如何为嵌套对象或同一父对象中的对象列表定义 GraphQLObjectType

GraphQL 为列表和详细查询共享相同的对象类型

GraphQL SDL(aws appsync)中的“不能为不可为空的类型返回 null:父“消息”(/getMessages/sendBy)中的“人”

如何在我的 GraphQL 中为对象中的对象列表定义类型

如何在我的GraphQL中为对象中的对象列表定义类型