使用 type-graphql 和 RESTDataSource 为第二个 api 调用添加字段解析器

Posted

技术标签:

【中文标题】使用 type-graphql 和 RESTDataSource 为第二个 api 调用添加字段解析器【英文标题】:Adding a field resolver for a second api call with type-graphql and the RESTDataSource 【发布时间】:2021-03-09 08:58:20 【问题描述】:

我们希望下面的查询根据返回的truckId 解析truck 字段:

实体

// RosterEntity.ts
import  Truck  from '@sap-truck-roster/entity/Truck'
import  Field, ID, ObjectType  from 'type-graphql'

@ObjectType()
export class Roster 
  @Field(() => Truck,  nullable: true )
  truck: Truck
  
  @Field(() => ID)
  readonly truckId: string

// TruckEntity.ts
import  Field, ID, ObjectType  from 'type-graphql'

@ObjectType()
export class Truck 
  @Field(() => ID)
  readonly id: string

  @Field()
  readonly description: string

解析器

// TruckResolver.ts
import  Resolver, Arg, Query, Ctx, Field, ObjectType, createUnionType, from 'type-graphql'
import  Truck  from '@sap-truck-roster/entity/Truck'
import  plainToClass  from 'class-transformer'
import  Context  from '@shared/typings'
import  ApiError  from '@shared/graphql'

@ObjectType()
class TruckArray 
  @Field(() => [Truck],  nullable: true )
  data?: [Truck]


const TruckQueryResultUnion = createUnionType(
  name: 'TruckQueryResult',
  // types: () => [[Truck], ApiError] as const,
  // an array is not supported by the Graphql spec in unions
  types: () => [TruckArray, ApiError] as const,
)

@Resolver(() => Truck)
export class TruckResolver 
  @Query(() => TruckQueryResultUnion)
  async truck(
    @Ctx() ctx: Context,
    @Arg('id',  nullable: true ) id?: string,
    @Arg('country',  nullable: true ) country?: string
  ): Promise<typeof TruckQueryResultUnion> 
      const response = await ctx.dataSources.sapTruckRosterAPI.getTruck( id, country )

      return plainToClass(TruckArray,  data: response.data, )
  

// RosterResolver.ts
import  Resolver, Arg, Query, Ctx, Field, ObjectType, createUnionType, from 'type-graphql'
import  Roster  from '@sap-truck-roster/entity/Roster'
import  plainToClass  from 'class-transformer'
import  Context  from '@shared/typings'
import  ApiError  from '@shared/graphql'
import  Truck  from '@sap-truck-roster/entity/Truck'

@ObjectType()
class RosterArray 
  @Field(() => [Roster],  nullable: true )
  data?: [Roster]


const RosterQueryResultUnion = createUnionType(
  name: 'RosterQueryResult',
  // types: () => [[Roster], ApiError] as const,
  // an array is not supported by the Graphql spec in unions
  types: () => [RosterArray, ApiError] as const,
)

@Resolver(() => Roster)
export class RosterResolver 
  @Query(() => RosterQueryResultUnion)
  async roster(
    @Ctx() ctx: Context,
    @Arg('date', () => String) date: string,
    @Arg('truckId',  nullable: true ) truckId?: string,
    @Arg('driverId',  nullable: true ) driverId?: string
  ): Promise<typeof RosterQueryResultUnion> 
      const response = await ctx.dataSources.sapTruckRosterAPI.getRoster( date, driverId, truckId )

      // @FieldResolver(() => Truck)
      // truck(@Root() truck: Truck) 
      //   return await ctx.dataSources.sapTruckRosterAPI.getTruck(
      //     id: response.truckId
      //   )
      // 
      
     return plainToClass(RosterArray,  data: response.data )
  

正如您在RosterResolver.ts 中看到的,我们尝试使用response.truckId 来查询API 上的另一个端点,但失败了。用数据填充 truck 字段的正确方法是什么?

这会在控制台中返回正确的数据,但与 roster 不匹配,因为我们获得了所有 truck 属性的 null 值:

  // RosterResolver.ts

  @FieldResolver(() => Truck,  nullable: true )
  async truck(@Root() roster: Roster, @Ctx() ctx: Context) 
    console.log('roster.truckId: ', roster.truckId)

    const response = await ctx.dataSources.sapTruckRosterAPI.getTruck(
      id: roster.truckId,
    )

    console.log('response: ', response);
    
    return response
  

【问题讨论】:

【参考方案1】:

您可以做的是解析器继承。 https://github.com/MichalLytek/type-graphql/blob/master/docs/resolvers.md#field-resolvers

@Resolver(() => Roster)
export class RosterResolver 

  ....

  @FieldResolver()
  truck(@Root() roster: Roster) 
    return ctx.dataSources.sapTruckRosterAPI.getTruck(
      id: roster.truckId
    );
  

【讨论】:

谢谢你的建议,但我试过了,但它失败了,“不能为不可为空的字段 Truck.id 返回 null。”。即使这样做@FieldResolver(() =&gt; Truck, nullable: true ) 当我尝试 @FieldResolver(() =&gt; Roster, nullable: true ) 时,我收到错误“无法在类型 \"Roster\" 上查询字段 \"id\"

以上是关于使用 type-graphql 和 RESTDataSource 为第二个 api 调用添加字段解析器的主要内容,如果未能解决你的问题,请参考以下文章

使用 type-graphql typeorm 和 dataloader 处理一对多的最佳方法

使用 type-graphql 和 RESTDataSource 为第二个 api 调用添加字段解析器

Type-GraphQL 上 JSON 字段的类型

对 Type-graphql 和 Typeorm 实体中的外键字段使用 ID 标量类型在语义上是不是正确?

如何使用 type-graphql 解析器函数获取 graphql 后端中的选定字段?

在 type-graphql 突变中传递多个参数以及 `GraphQLUpload`