我如何根据他们在 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 个字段感兴趣,比如 price
和 refundAmount
那么查询将是这样的
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进行过滤的底层系统。
我正在为 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 个字段感兴趣,比如 price
和 refundAmount
那么查询将是这样的
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进行过滤的底层系统。