我如何根据他们在 graphql 中查询的字段来响应客户?

How can I response to client based on what fields they are querying in graphql?

我正在为 graphql 服务器使用 AWS appsync 并且具有如下模式:

type Order {
  id: ID!
  price: Int
  refundAmount: Int
  period: String!
}
  

query orders (userId: ID!) [Order]

支持根据用户id查询订单。它响应不同 time period 的一系列订单。响应可能是:

[{
  id: xxx
  price: 100
  refundAmount: 10
  period: '2021-01-01'
},{
  id: xxx
  price: 200
  refundAmount: 0
  period: '2021-01-03'
},
...
]

如果period中的price和refundAmount为0,我不会响应数组中的空元素。在上面的例子中,2021-01-02上有price和refundAmount,所以数组中没有这个元素。

我的问题是如何根据前端查询响应数据?如果客户在响应中只查询 refundAmount 字段,我不想响应 2021-01-03 期间。我怎么知道前端要在响应中显示哪些字段?

例如

如果客户发送此查询:

query {
   orders (userId: "someUserId") {
      refundAmount
   }
}

我会回复下面的数据,但我不希望第二个在那里,因为值为 0。

[{
  id: xxx
  refundAmount: 10
  period: '2021-01-01'
},{
  id: xxx
  refundAmount: 0
  period: '2021-01-03'
}
]

My problem is how can I response the data based on what frontend queries?

GraphQL 将立即为您执行此操作,前提是您拥有查询中字段的解析器。根据您的基础数据源查看 appropriate resolver

How do I know what fields frontend wants to show in the response?

这是前端决定的,它可以根据它感兴趣的字段发送不同的查询。下面是几个例子。

如果前端只对一个字段感兴趣,即 refundAmount,那么它会发送类似这样的查询。

query {
   orders (userId: "someUserId") {
      refundAmount
   }
}

如果它对超过 1 个字段感兴趣,比如 pricerefundAmount 那么查询将是这样的

query {
   orders (userId: "someUserId") {
      price,
      refundAmount
   }
}

更新:过滤器响应:

现在,根据更新后的问题,您需要增强您的解析器以执行此附加过滤。

  • 解析器始终可以执行此过滤(有点像 refundAmount > 0 的硬编码)
  • 在查询模型中支持过滤条件 query orders (userId: ID!, OrderFilterInput) [Order] 并定义要过滤的条件。然后在解析器中支持这些过滤条件来查询底层数据源。同样从客户端获取过滤条件。

this 示例中查看 ModelPostFilterInput 生成的模型。

编辑 2:为过滤器添加更改的架构

假设您将 Schema 更改为支持过滤并且没有额外的 VTL request/response 映射器并且您直接与 Lambda 对话。

这就是 Schema 的样子(当然你会有你的突变和订阅,这里省略了。)

input IntFilterInput { # This is all the kind of filtering you want to support for Int data types
    ne: Int
    eq: Int
    le: Int
    lt: Int
    ge: Int
    gt: Int
}

type Order {
    id: ID!
    price: Int
    refundAmount: Int
    period: String!
}

input OrderFilterInput { # This only supports filter by refundAmount. You can add more filters if you need them.
    refundAmount: IntFilterInput
}

type Query {
    orders(userId: ID!, filter: OrderFilterInput): [Order] # Here you add an optional filter input
}

schema {
    query: Query
}

假设您在查询 orders 中附加了 Lambda 解析器。 在这种情况下,Lambda 需要 return 一个 array/list 的订单。

如果您进一步将此查询发送给某些 table/api,您需要了解过滤器,并为下游系统创建适当的查询或 api 调用。

我展示了一个带有硬编码响应的简单 Lambda。如果我们引入过滤器,这就是变化。

const getFilterFunction = (operator, key, value) => {
    switch (operator) {
        case "ne":
            return x => x[key] != value
        case "eq":
            return x => x[key] == value
        case "le":
            return x => x[key] <= value
        case "lt":
            return x => x[key] < value
        case "ge":
            return x => x[key] >= value
        case "gt":
            return x => x[key] > value
        default:
            throw Error("Unsupported filter operation");
    }
}


exports.handler = async(event) => {

    let response = [{
        "id": "xxx1",
        "refundAmount": 10,
        "period": '2021-01-01'
    }, {
        "id": "xxx2",
        "refundAmount": 0,
        "period": '2021-01-03'
    }]
    const filter = event.arguments.filter; 
    if (filter) { // If possible send the filter to your downstream system rather handling in the Lambda
        if (filter.refundAmount) {
            const refundAmountFilters = Object.keys(filter.refundAmount)
                .map(operator => getFilterFunction(operator + "", "refundAmount", filter.refundAmount[operator]));
            refundAmountFilters.forEach(filterFunction => { response = response.filter(filterFunction) });
        }
    }

    return response; // You don't have to return individual fields the query asks for. It is taken care by AppSync. Just return a list of orders.
};

有了上面的内容,您可以发送各种查询,例如

query MyQuery {
  orders(userId: "1") { #without any filters
    id
    refundAmount
  }
}

query MyQuery {
  orders(userId: "1", filter: {refundAmount: {ne: 0}}) { # The filter you are interested
    id
    refundAmount
  }
}

query MyQuery {
  orders(userId: "1", filter: {refundAmount: {ne: 0, gt: 5}}) { # Mix and Match filters
    id
    refundAmount
  }
}

您不必支持所有运算符进行过滤,您可以只关注 ne!= 并进一步简化事情。请查看 this 博客以获取更简单的版本,其中假定了过滤操作。

最后,在不修改架构的情况下进行过滤的另一种可能性是仅更改您的 Lambda 以确保它 return 是一组经过过滤的结果,要么自己进行过滤,要么向 query/request 发送适当的 query/request进行过滤的底层系统。