结合多个休息调用以在 apollo-server 中填充 1 种 graphQL 类型的最佳实践
Best practise to combine multiple rest calls to populate 1 graphQL type in apollo-server
我有 graphql 用户类型,需要来自多个 REST api 和不同服务器的信息。
基本示例:从 rest 域 1 获取用户名,从 rest 域 2 获取姓氏。两个 rest 域都有一个共同的 "userID" 属性。
我的解析器代码 atm 的一个简单示例:
user: async (_source, args, { dataSources }) => {
try {
const datasource1 = await dataSources.RESTAPI1.getUser(args.id);
const datasource2 = await dataSources.RESTAPI2.getUser(args.id);
return { ...datasource1, ...datasource2 };
} catch (error) {
console.log("An error occurred.", error);
}
return [];
}
这对于这个简化版本来说效果很好,但是这个解决方案有 2 个问题:
首先,IRL 合并 2 json 结果有很多逻辑。由于某些字段是共享的但具有不同的数据(或为空)。所以这就像挑选两个结果来创建组合结果一样。
我的第二个问题是,这仍然是一个瀑布方法。首先从restapi1获取数据,完成后调用restapi2。基本上 apollo-server 正在重新引入 rest-waterfall-fetch graphql 试图解决。
牢记这两个问题。我可以优化这段代码或重写它以获得更好的性能或可读性吗?或者是否有任何包可以帮助解决此问题?
非常感谢!
假设您的 user
解析器 returns type User
放弃...
type User {
id: ID!
datasource1: RandomType
datasource1: RandomType
}
您可以为 type User
中的每个字段创建单独的解析器,这可以将 user
查询的复杂性降低到仅请求的字段。
query {
user {
id
datasource1 {
...
}
}
}
const resolvers = {
Query: {
user: () => {
return { id: "..." };
}
},
User: {
datasource1: () => { ... },
datasource2: () => { ... } // i wont execute
}
};
datasource1
& datasource2
解析器只会在 Query.user
执行后并行执行。
关于性能,如果两个调用相互独立,可以利用Promise.all
并行执行:
const [dataSource1,dataSource2] = await Promise.all([
dataSources.RESTAPI1.getUser(args.id),
dataSources.RESTAPI2.getUser(args.id),
])
我们通常让 GraphQL 的默认解析器逻辑完成繁重的工作,但如果您发现需要 "cherry pick" 来自两个调用的数据,您可以 return 在您的根解析器:
return { dataSource1, dataSource2 }
然后为每个字段编写解析器:
const resolvers = {
User: {
someField: ({ dataSource1, dataSource2 }) => {
return dataSource1.a || dataSource2.b
},
someOtherField: ({ dataSource1, dataSource2 }) => {
return someCondition ? dataSource1.foo : dataSource2.bar
},
}
}
并行调用。
const users = async (_source, args, { dataSources }) => {
try {
const promises = [
dataSources.RESTAPI1,
dataSources.RESTAPI2
].map(({ getUser }) => getUser(args.id));
const data = await Promise.all(promises);
return Object.assign({}, ...data);
} catch (error) {
console.log("An error occurred.", error);
}
return [];
};
我有 graphql 用户类型,需要来自多个 REST api 和不同服务器的信息。 基本示例:从 rest 域 1 获取用户名,从 rest 域 2 获取姓氏。两个 rest 域都有一个共同的 "userID" 属性。
我的解析器代码 atm 的一个简单示例:
user: async (_source, args, { dataSources }) => {
try {
const datasource1 = await dataSources.RESTAPI1.getUser(args.id);
const datasource2 = await dataSources.RESTAPI2.getUser(args.id);
return { ...datasource1, ...datasource2 };
} catch (error) {
console.log("An error occurred.", error);
}
return [];
}
这对于这个简化版本来说效果很好,但是这个解决方案有 2 个问题: 首先,IRL 合并 2 json 结果有很多逻辑。由于某些字段是共享的但具有不同的数据(或为空)。所以这就像挑选两个结果来创建组合结果一样。
我的第二个问题是,这仍然是一个瀑布方法。首先从restapi1获取数据,完成后调用restapi2。基本上 apollo-server 正在重新引入 rest-waterfall-fetch graphql 试图解决。
牢记这两个问题。我可以优化这段代码或重写它以获得更好的性能或可读性吗?或者是否有任何包可以帮助解决此问题?
非常感谢!
假设您的 user
解析器 returns type User
放弃...
type User {
id: ID!
datasource1: RandomType
datasource1: RandomType
}
您可以为 type User
中的每个字段创建单独的解析器,这可以将 user
查询的复杂性降低到仅请求的字段。
query {
user {
id
datasource1 {
...
}
}
}
const resolvers = {
Query: {
user: () => {
return { id: "..." };
}
},
User: {
datasource1: () => { ... },
datasource2: () => { ... } // i wont execute
}
};
datasource1
& datasource2
解析器只会在 Query.user
执行后并行执行。
关于性能,如果两个调用相互独立,可以利用Promise.all
并行执行:
const [dataSource1,dataSource2] = await Promise.all([
dataSources.RESTAPI1.getUser(args.id),
dataSources.RESTAPI2.getUser(args.id),
])
我们通常让 GraphQL 的默认解析器逻辑完成繁重的工作,但如果您发现需要 "cherry pick" 来自两个调用的数据,您可以 return 在您的根解析器:
return { dataSource1, dataSource2 }
然后为每个字段编写解析器:
const resolvers = {
User: {
someField: ({ dataSource1, dataSource2 }) => {
return dataSource1.a || dataSource2.b
},
someOtherField: ({ dataSource1, dataSource2 }) => {
return someCondition ? dataSource1.foo : dataSource2.bar
},
}
}
并行调用。
const users = async (_source, args, { dataSources }) => {
try {
const promises = [
dataSources.RESTAPI1,
dataSources.RESTAPI2
].map(({ getUser }) => getUser(args.id));
const data = await Promise.all(promises);
return Object.assign({}, ...data);
} catch (error) {
console.log("An error occurred.", error);
}
return [];
};