GraphQL是否可以根据解析器中的查询结果选择性地解析字段?

时间:2019-05-16 14:38:52

标签: javascript ecmascript-6 graphql

我有以下REST端点:

  

/ orders / {id}

returns {
    orderId,
    orderItem,
    customerId
}
  

/ customers / {id}

returns {
   customerId,
   firstName,
   lastName
}

我受到这两个端点的限制,这两个端点将包裹在我的graphql模式中。

我想要以下架构:

type Order {
  orderId: ID!,
  orderItem: String,
  customer: Customer
}

type Customer{
  customerId: ID!
  firstName: String!
  lastName: String!
}

type Query {
  getOrder(id: String!): Order,
  getCustomer(id: String!): Customer
}

我想知道是否可以让GraphQL解析Order类型中的Customer对象?我了解您无法将查询结果传递给另一个参数。

我认为getOrder的解析器为:

const getOrderResolver = axios.get(`/orders/${id}`)
  .then((ordersRes) => {
    let customerId;
    if(ordersRes.data.customerId !== null) {
      customerId = ordersRes.data.customerId 
      axios.get(`/customers/${customerId}`)
      .then((customerRes) => ({
        return {
          orderId: ordersRes.data.orderId
          orderItem: ordersRes.data.orderItem
          customer: {
            customerId: customerRes.data.customerId
            firstName: customerRes.data.firstName
            lastName: customerRes.data.lastName 
          }
        }
      })
    } else {
      return {
          orderId: ordersRes.data.orderId
          orderItem: ordersRes.data.orderItem
          customer: null
      }
    }
    })
  })

getCustomer解析器

const getCustomerResolver = axios.get(`/customers/${customerId}`)
          .then((customerRes) => ({
            return {
                customerId: customerRes.data.customerId
                firstName: customerRes.data.firstName
                lastName: customerRes.data.lastName 
            }
          })

对于我的解决方案来说,无论是否在Customer查询中查询getOrder类型,都将产生额外的开销。

是否可以通过GraphQL仅在查询时才能解析Customer类型的方式来重写GraphQL模式?

我给我的ORDERS REST API的限制仅返回了CustomerId,这使得在getOrder中很难解决,因为Customer API需要一个{{1} }

1 个答案:

答案 0 :(得分:1)

这里有两件事要记住:

  • GraphQL除非请求字段,否则不会解析该字段(即,除非该字段实际包含在请求中,否则不会调用您的解析器)。
  • 每个解析程序都可以访问其“父级”字段解析为的值。

因此,您的getOrder解析器可以只担心返回订单对象:

const resolvers = {
  Query: {
    getOrder: (parent, args, context, info) => {
      const response = await axios.get(`/orders/${args.id}`)
      return response.data
    },
    ...
  },
  ...
}

请注意,如果REST端点以与字段类型相同的“形状”返回响应,则无需在此处将响应数据映射到实际字段-只需返回response.data

现在,我们可以在订单类型的customer字段中添加解析器:

const resolvers = {
  Query: { ... },
  Order: {
    customer: (parent, args, context, info) => {
      if (!parent.customerId) {
        return null
      }
      const response = await axios.get('/customers/${parent.customerId}')
      return response.data
    },
  },
  ...
}

我们的父字段将为getOrder(或任何其他类型为Order的字段)。 parent的值将是您在该字段的解析程序中返回的任何值(或者,如果您返回了Promise,则是Promise解析为的任何值)。因此,我们可以使用parent.customerId来调用/customers端点,并从响应中返回数据。同样,只有实际请求customer字段时,才会调用此解析器。