【问题标题】:How can I response to client based on what fields they are querying in graphql?如何根据他们在 graphql 中查询的字段来响应客户?
【发布时间】:2021-07-25 20:05:03
【问题描述】:

我正在为 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'
}
]

【问题讨论】:

  • 不是字段 [existence] 角色来过滤记录 - 添加一些 where: { refundAmount: { ne: 0 }
  • 在哪里添加where: { refundAmount: { ne: 0 }

标签: graphql aws-appsync


【解决方案1】:

我的问题是如何根据前端响应数据 查询?

只要您拥有查询中字段的解析器,GraphQL 就会为您开箱即用地执行此操作。根据你的底层数据源查看appropriate resolver

我如何知道前端想要在响应中显示哪些字段?

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

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

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

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

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

更新:过滤响应:

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

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

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

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

假设您更改了架构以支持过滤,并且没有额外的 VTL 请求/响应映射器并且您直接与 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 需要返回一个订单数组/列表。

如果您进一步将此查询发送到某个表/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,以确保它返回一组过滤后的结果,要么自己进行过滤,要么向底层系统发送适当的查询/请求以进行过滤。

【讨论】:

  • 我已根据您的第一个查询示例更新了我的问题。我不希望 0 数量出现在响应数组数据中。
  • 更新了答案。如果您需要准确的答案,请为您的查询添加解析器详细信息。例如,它是 Dynamo 支持的数据库、Lambda 调用或 Http 调用。没有细节,我只能说它需要在解析器上发生。如果您使用来自 AppSync 的 model,它将创建由 DynamoDB 自动支持的那些,如果您添加 searchable,它也会流式传输到 ElasticSearch。虽然这需要额外付费。
  • 我使用 lambda 作为解析器。添加过滤器将更改架构。如何根据前端查询应用过滤器逻辑?如果前端只需要 refundAmount 在响应中,那么我可以在我的 lambda 中过滤它,否则我过滤 refundAmountprice
  • 终极数据源是什么?您是否还有用于请求响应映射或直接 lambda 调用的 VTL?
  • @JoeyYiZhao 为退款金额添加了最低限度的过滤器。
猜你喜欢
  • 2020-06-14
  • 2020-12-03
  • 2021-10-13
  • 2019-07-26
  • 2019-06-20
  • 2019-07-26
  • 2020-03-15
  • 2018-03-31
  • 2021-07-02
相关资源
最近更新 更多