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

Jim*_*Jim 6 javascript ecmascript-6 graphql

我有以下REST端点:

/ orders / {id}

returns {
    orderId,
    orderItem,
    customerId
}
Run Code Online (Sandbox Code Playgroud)

/ customers / {id}

returns {
   customerId,
   firstName,
   lastName
}
Run Code Online (Sandbox Code Playgroud)

我受到这两个端点的限制,这两个端点将包装在我的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
}
Run Code Online (Sandbox Code Playgroud)

我想知道是否可以让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
      }
    }
    })
  })
Run Code Online (Sandbox Code Playgroud)

getCustomer 解析器

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

在我的解决方案中,Customer无论是否在getOrder查询中查询类型,始终获取类型都会产生额外的开销。是否可以以Customer仅在查询时GraphQL才能解析类型的方式重写GraphQL模式?

我给定的ORDERSREST API 的局限性只能返回,CustomerId 因此很难解决getOrder,因为该CustomerAPI需要一个customerId

Dan*_*den 5

这里有两件事要记住:

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

所以你的getOrder解析器只能担心返回一个订单对象:

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

请注意,如果 REST 端点以与您的字段类型相同的“形状”返回响应,则无需在此处将响应数据映射到您的实际字段 - 只需 return 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
    },
  },
  ...
}
Run Code Online (Sandbox Code Playgroud)

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