Adding a field resolver for a second api call with type-graphql and the RESTDataSource

1.2k views Asked by At

We want the query below to resolve the truck fields based on the truckId returned:

enter image description here

Entities

// 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
}

Resolvers

// 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 })
  }
}

As you can see in the RosterResolver.ts we tried to use the response.truckId to query the other endpoint on the API but this fails. What is the correct way to populate the truck field with data?

This returns in the console the correct data but does not match it with the roster as we get null values for all truck properties:

  // 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
  }

enter image description here

enter image description here

1

There are 1 answers

2
Vinicius Estevam On

Something you can do is Resolver inheritance. 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
    });
  }