Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
GraphQL在查询级别获取数据会导致冗余/无用请求_Graphql_Graphql Js_Apollo Server_Express Graphql - Fatal编程技术网

GraphQL在查询级别获取数据会导致冗余/无用请求

GraphQL在查询级别获取数据会导致冗余/无用请求,graphql,graphql-js,apollo-server,express-graphql,Graphql,Graphql Js,Apollo Server,Express Graphql,我们正在实现GraphQL服务,它位于几个后端微服务的前面 例如,我们有一个产品,每个产品都有一个历史订单列表。我们的后端服务器提供两个RESTAPI,一个用于产品详细信息数据,另一个用于返回产品的历史订单列表 我们的客户端应用程序有两个页面:一个是产品详细信息页面,另一个是产品的历史订单列表 因此,在产品详细信息页面中,我们只能检索产品的详细信息数据,而在订单列表页面中,我们只需要列表数据 GraphQL模式如下所示: type ProductOrder { createAt: Dat

我们正在实现GraphQL服务,它位于几个后端微服务的前面

例如,我们有一个
产品
,每个产品都有一个历史订单列表。我们的后端服务器提供两个RESTAPI,一个用于产品详细信息数据,另一个用于返回产品的历史订单列表

我们的客户端应用程序有两个页面:一个是产品详细信息页面,另一个是产品的历史订单列表

因此,在产品详细信息页面中,我们只能检索产品的详细信息数据,而在订单列表页面中,我们只需要列表数据

GraphQL模式如下所示:

type ProductOrder {
    createAt: Date!
    userName: String!
    count: Int
}
type Product {
    productId: ID
    name: String
    orders: [ProductOrder!]!
}
Query {
    product(productId: ID): Product
}
解析程序是这样的

const resolvers = {
    Query: {
        product(_, { productId}){
            // fetch detail data from backend API
            return await someService.getProductDetail(productId);
        }
    },
    Product: {
        orders(product){
            // fetch order list from another API
            return await someService.getProductOrders(product.productId);
        }
    }
};
但是我们使用上面的代码发现了一个潜在的过度请求

当我们从订单列表页面请求订单列表数据时,我们必须首先请求产品详细信息API,然后才能请求订单列表API。但是我们只需要订单列表数据,根本不需要产品数据。在这种情况下,我们认为产品详细信息请求是无用的,我们如何消除该请求

如果我们只发送一个请求来检索订单列表数据,可能会更好。

A)以不同的方式构建您的模式: 第1版:不要将ProductOrder设置为产品上的字段

type Query {
  product(productId: ID): Product
  productOrders(productId: ID): [ProductOrder!]
}

type Product {
  productId: ID
  name: String
}
type Product {
    productId: ID
    details: ProductDetails!
    orders: [ProductOrder!]!
}

type ProductDetails {
  name: String
}
第2版:将详细信息作为产品中的子字段

type Query {
  product(productId: ID): Product
  productOrders(productId: ID): [ProductOrder!]
}

type Product {
  productId: ID
  name: String
}
type Product {
    productId: ID
    details: ProductDetails!
    orders: [ProductOrder!]!
}

type ProductDetails {
  name: String
}
使用解析程序:

const解析器={
查询:{
产品:({productId})=>productId,
},
产品:{
id:productId=>productId,
详细信息:productId=>someService.getProductDetail(productId),
orders:productId=>someService.getProductOrders(productId),
},
};
B) 如果未请求任何详细信息,则跳过提取 您可以使用解析器的第四个参数来检查查询的子字段。理想情况下,您可以使用一个库来实现这一点。我记得我们这样做的时候,前端只会请求对象的
id
字段。如果是这样,我们可以简单地用
{id}
解决

从“graphql字段列表”导入{fieldList};
常量解析程序={
查询:{
产品(u,{productId},ctx,resolveInfo){
常量字段=字段列表(resolveInfo);
if(fields.filter(f=>f!='orders'| | f!='id')。长度===0){
返回{productId};
}
返回someService.getProductDetail(productId);
},
},
};
C) 延迟提取,直到查询子字段 如果您已经在使用Dataloader,那么这相对容易做到。您不再立即在查询解析器中获取细节,而是再次传递id,让每个细节字段自己获取细节。这似乎是反直觉的,但Dataloader将确保您的服务只被查询一次:

const解析器={
查询:{
产品:({productId})=>productId,
},
产品:{
id:productId=>productId,
//所有其他详细信息字段也是如此
名称:(productId,args,ctx)=>ctx.ProductDetailsByIdLoader.load(productId)
.然后(product=>product.name),
orders:productId=>someService.getProductOrders(productId),
},
};
如果没有dataloader,可以构建一个简单的代理:

类proxy{
建造师(id){
this.id=id;
设cached=null;
this.getDetails=()=>{
如果(缓存===null){
cached=someService.getProductDetails(productId)
}
返回缓存;
}
}
//不需要参数,但您可以查看graphql js的工作方式
productId(参数、ctx、resolveInfo){
返回此.id;
}
名称(args、ctx、resolveInfo){
返回此.getDetails().then(details=>details.name);
}
订单(args、ctx、resolveInfo){
返回someService.getProductOrders(this.id);
}
}
常量解析程序={
查询:{
product:({productId})=>newproductproxy(productId),
},
//这里不需要产品解析程序
};

Thx。方法C看起来就像PayPal在文章中使用的方法。但是为每个字段显式地编写解析器在某种程度上是重复的,特别是当它涉及到一个包含太多字段的复杂
类型时。方法C也是我理解用Hack编写的Facebooks GraphQL代码的方式。我认为现在他们生成了很多这样的样板,但我认为项目越大,显式代码的问题就越少?看来我也得走这条路。顺便问一下,Facebook是否公开了一些GraphQL代码的源代码?我没有找到。我不知道他们在做什么,但丹·谢弗展示了类似的东西(特别是22:15)。FB人员在会议上也发表了更多的讲话。我不会想太多,只要选择一些东西,一旦你更喜欢另一种方式,就改变它。FB会谈讨论了他们是如何一次又一次地改变做事方式的。