问题描述
公司使用了Apollo Graphql Federation作为BFF的技术方案。这带来了一个开发流程的问题,需要先提交 Subgraph 代码到 Federation Gateway,Gateway 构建 supergraph
后,Client 端才能获取supergraph schema生成对应的TS类型,然后进行client端的开发。
由于Federation Gateway构建supergraph所需时间比较长,这使得client端的开发常常被阻塞,大大降低了开发效率。如果能在本地环境中模拟 Federation Gateway,构建 supergraph
,那么client端的开发就不会被阻塞了。
本地构建 Supergraph 的方案
第一步:下载所有的subgraph schema到本地
如下面的bash脚本所示,通过运行脚本rover subgraph list $GRAPH_REF
,我们可以获得完整的subgraph list表格。然后根据subgraph的名字和url,我们可以把所有的schema文件下载到subgraph-schemas/schemas
文件夹中
#!/bin/bash
# Define variables
GRAPH_REF="platform-graphql-federation@staging"
# Create a directory to store schemas
mkdir -p subgraph-schemas
# Run the rover subgraph list command and store the output
output=$(rover subgraph list $GRAPH_REF)
# Check if the command was successful
if [ $? -ne 0 ]; then
echo "Failed to list subgraphs"
exit 1
fi
# Parse the output to extract subgraph names and URLs
subgraph_names=$(echo "$output" | grep '│' | awk -F '│' '{print $2}' | sed 's/^[ \t]*//;s/[ \t]*$//' | grep -v '^Name$')
parse_names() {
echo "$1" | awk -F '│' '
/│/ {
gsub(/^[ \t]+|[ \t]+$/, "", $2); # Trim leading/trailing whitespace
if ($2 != "" && $2 != "Name" && $2 !~ /─+/) # Ignore header and separator lines
print $2
}
'
}
# Extract the subgraph names
subgraph_names=$(parse_names "$output")
# Fetch and save each subgraph schema
for name in $subgraph_names; do
sanitized_name=$(echo $name | tr -d ' ')
rover subgraph fetch $GRAPH_REF --name $name > "subgraph-schemas/schemas/$sanitized_name.graphql"
echo "Downloaded schema for subgraph: $name"
done
echo "All subgraph schemas have been downloaded."
第二步: 生成supergraph.yaml
rover可以根据supergraph.yaml和每个subgraph的schema.graphql文件生成对应的supergrah-schema.graphql文件。如下面这段代码展示了如何根据subgraph-schemas/schemas
文件夹的内容生成对应的supergrah-schema.graphql文件
/* eslint-disable @typescript-eslint/no-var-requires */
const fs = require('fs');
const yaml = require('js-yaml');
const schemaFolder = 'subgraph-schemas';
const files = fs.readdirSync(schemaFolder + '/schemas');
const subgraphNames = files.map((file) => file.replace('.graphql', ''));
const config = {
federation_version: 2,
subgraphs: {},
};
subgraphNames.forEach((subgraphName) => {
config.subgraphs[subgraphName] = {
routing_url: 'unspecified',
schema: { file: `./schemas/${subgraphName}.graphql` }, //relative path
};
});
const yamlString = yaml.dump(config);
fs.writeFileSync(`${schemaFolder}/supergraph-config.yaml`, yamlString, 'utf8');
// rover supergraph compose --config subgraph-schemas/supergraph-config.yaml > subgraph-schemas/supergraph-schema.graphql
第三步:下载本地项目的schema.graphql
rover graph introspect http://0.0.0.0:4001/graphql > ./subgraph-schemas/schemas/{your_subgraph_name}.graphql
第四步:生成supergraph-schema.graphql
rover supergraph compose --config subgraph-schemas/supergraph-config.yaml > subgraph-schemas/supergraph-schema.graphql
第五步:修改codegen.yaml文件
如下所示,使用生成的supergraph-schema.graphql作为schema,然后运行graphql-codegen --config codegen.yml
来生成对应的TS文件
overwrite: true
# schema: "http://0.0.0.0:4001/graphql"
schema: "subgraph-schemas/supergraph-schema.graphql"
documents: "src/**/!(*.d).{ts,tsx,gql}"
generates:
src/types/graphql.ts:
plugins:
- "typescript"
- "typescript-operations"
config:
enumsAsTypes: true
typesPrefix: I
结语
在本文中,我们深入探讨了 Apollo GraphQL Federation 开发流程中的一个关键问题——Client 端代码生成对 supergraph
的依赖,并提出了一种本地构建 supergraph
的解决方案。通过这一方案,开发者可以在本地环境中模拟 Federation Gateway,独立构建 supergraph
,从而摆脱对远程 Gateway 的依赖,实现 Subgraph 和 Client 端的并行开发。这不仅显著提升了开发效率,还为本地调试和测试提供了极大的便利。
展望未来,这一方案仍有进一步优化的空间。例如,如何通过脚本让所有步骤都全部自动化起来,使得开发者无需关心里面的实现细节。
最后,我们鼓励读者尝试这一方案,并在实际项目中验证其效果。如果你在实践过程中遇到问题或有更好的改进建议,欢迎提出反馈,共同推动 Apollo Federation 开发流程的优化与创新。通过不断探索和分享,我们可以让 GraphQL 生态更加成熟,为开发者带来更高效的开发体验。