trivago 为旅行者提供了广泛的酒店选择,使他们能够比较价格并发现最佳的度假优惠。由于有这么多可用的特殊选项,我们引入了一项名为“收藏夹”的新功能来简化导航过程。此功能使用户能够轻松保存他们喜欢的住宿并稍后访问,确保易用性。
幕后花絮
我们已经开始实施强大的后端解决方案来支持该功能,并付出必要的努力来确保其成功。我们最初的计划是使用 GraphQL 服务器(GraphQL 整体)作为 API 和传入请求之间的中间层。这种方法意味着请求在到达 API 之前必须经过另一个服务。下图说明了我们最初的想法以及它的运作方式。
值得注意的是,我们的 GraphQL 设置是联合的,这意味着请求通过 GraphQL 网关分布在子图之间。我们的 GraphQL 整体架构就是这样的一个子图。然而,通过整体与网关通信会增加延迟和依赖性,因此我们试图避免这种情况。由于我们已经有一个 GraphQL 网关来减少资源消耗并提高响应时间,因此我们选择通过在网关后面实现专用 GraphQL 服务器来利用它,从而绕过整体。这一变化为我们带来了以下好处:
- 客户端始终使用网关的架构,幕后更改不会影响任何客户端。
- 由于我们通过绕过整体跳过了一个步骤,因此客户端可能应该直接从最喜欢的 GraphQL 服务器本身接收更快的响应。
- 维护和支持更改更容易,因为我们只需更改最喜欢的 GraphQL 服务器上的代码和架构,而不必调整整体架构。
- Go 支持在同一服务器中使用不同的路由运行 GraphQL 服务器和 REST API,这意味着在需要时迁移现有的 REST API 总是很容易。
- 在 Go 中实现 GraphQL 服务器非常容易,唯一需要替换的是接口,其余实现保持不变,这意味着不需要额外的复杂实现。
- 最喜欢的 GraphQL Server 可以完全依赖 GraphQL Gateway 来进行所有凭证身份验证。
下图显示了最终设计。
生成 GraphQL 服务器
通过使用名为的库,在 Go 中实现 GraphQL 服务器非常容易吉吉根。可以通过以下命令启动 GraphQL 服务器:
go run github.com/99designs/gqlgen init
然后将按照官方说明创建以下文件夹结构文档:
├── go.mod
├── go.sum
├── gqlgen.yml - The gqlgen config file, knobs for controlling the generated code.
├── graph
│ ├── generated - A package that only contains the generated runtime
│ │ └── generated.go
│ ├── model - A package for all your graph models, generated or otherwise
│ │ └── models_gen.go
│ ├── resolver.go - The root graph resolver type. This file wont get regenerated
│ ├── schema.graphqls - Some schema. You can split the schema into as many graphql files as you like
│ └── schema.resolvers.go - the resolver implementation for schema.graphql
└── server.go - The entry point to your app. Customize it however you see fit
生成文件后,必须定义 GraphQL 服务器的模式,该模式将在 GraphQL 网关的超级图中使用。定义架构后,可以通过运行以下命令来生成模型和解析器:
//go:generate go run github.com/99designs/gqlgen generate
但也可以执行一些make
命令来生成所需的文件:
GRAPHQL = bin/gqlgen
$(GRAPHQL): export GOBIN := $(PWD)/bin
$(GRAPHQL): go.mod
$(GO) install github.com/99designs/gqlgen
$(GO) mod tidy
$(addprefix generate-graphql-,$(COMMANDS)):
generate-graphql-%: export PATH = $(GOROOT)/bin:$(PWD)/bin
generate-graphql-%: $(GRAPHQL)
$(GO) generate ./cmd/$*/...
其余的代码非常类似于在 Go 中实现传统的 REST API。GraphQL 服务器的处理程序必须启动,并且可以将依赖项注入到resolver.go
:
qlSrv := handler.NewDefaultServer(
generated.NewExecutableSchema(generated.Config{Resolvers: &graph.Resolver{
Logger: logger,
Service: service
}}))
之后,依赖项将可在 schema.resolvers.go 中使用,因为它resolver.go
默认嵌入为属性。
导航 GraphQL 服务器
一旦定义了模式并生成了服务器,查询和突变解析器将在schema.resolvers.go
. 这将是 GraphQL 服务器的处理程序文件。以下示例显示了架构定义和用于获取收藏夹的查询:
type Query {
"""
Returns user's saved favorite accommodations.
"""
getMyFavoriteAccommodations: MyFavoriteAccommodationsResponse!
}
代码如下所示:
type queryResolver struct{ *Resolver }
func (r *queryResolver) GetMyFavoriteAccommodations(ctx context.Context) (*model.MyFavoriteAccommodationsResponse, error) {...}
该函数是返回用户收藏夹的处理程序。从这一点来看,它与实现传统 REST API 的流程相同,您可以在这里简单地实现您的业务流程。
结论
转换现有 API 可能有些挑战性,但由于 Go 服务器可以支持 REST API 和 GraphQL,因此从一种 API 切换到另一种 API 非常容易。在我们的例子中,客户端仍处于实现阶段,因此我们不需要同时支持 REST API 和 GraphQL 服务器,我们只需切换到 GraphQL 服务器即可。
另一个重要的一点是为您的模式定义编写注释。当超级图收集您构建的模式时,它将呈现给客户,您写的注释越多,客户就越容易导航。