在Go中GraphQL vs REST:2025年如何选择

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

Go中的GraphQL vs REST:双雄对决,如何避免选错

\n\n

如果您使用Go语言至少半年,您很可能遇到过这个永恒的争论:REST还是GraphQL?一个是久经考验的“老黄牛”,另一个是功能丰富的时尚“跑车”。但在Go的世界里,性能和简洁是信条,选择并不总是那么显而易见。

\n\n

在这篇文章中,我们不仅仅列举优缺点。我们将编写真实的Go代码,测量执行时间,并分析何时GraphQL是搬起石头砸自己的脚,何时又是通往高效API世界的捷径。开始吧!

\n\n

Go中的REST:稳定与可预测

\n\n

REST就像一把久经考验的旧锤子。它不耀眼,但钉钉子非常完美。在Go中,REST API使用net/http或像gin和chi这样的轻量级框架编写。让我们看一个简单的例子:

\n\n
package main\n\nimport (\n    "encoding/json"\n    "net/http"\n)\n\ntype User struct {\n    ID   int    `json:"id"`\n    Name string `json:"name"`\n}\n\nfunc getUserHandler(w http.ResponseWriter, r *http.Request) {\n    user := User{ID: 1, Name: "Alexey"}\n    w.Header().Set("Content-Type", "application/json")\n    json.NewEncoder(w).Encode(user)\n}\n\nfunc main() {\n    http.HandleFunc("/api/user", getUserHandler)\n    http.ListenAndServe(":8080", nil)\n}
\n\n

漂亮吗?是的。简单吗?非常。但是,如果前端只需要用户名,而我们却拉取了整个对象呢?在REST中,这被称为过度获取——数据冗余。在Go中,我们习惯于控制每一个字节,这可能会让人抓狂。

\n\n

REST中的N+1问题

\n\n

假设我们有一个订单列表,并且需要每个订单的用户名。在REST中,我们要么发出N个请求(慢),要么创建一个单独的端点/api/orders-with-users(端点过多)。Go通过在数据库中使用join查询来处理这个问题,但在API层面,这看起来仍然很笨拙。

\n\n

Go中的GraphQL:强大与灵活

\n\n

GraphQL就像一把瑞士军刀:一个端点,客户端自己决定需要哪些数据。在Go中,有gqlgen框架(来自99designs)——它根据模式生成代码,这非常符合Go的风格(更少的样板代码,更多的工作)。

\n\n
// schema.graphql\ntype Query {\n    user(id: Int!): User\n}\n\ntype User {\n    id: Int!\n    name: String!\n    orders: [Order!]!\n}\n\ntype Order {\n    id: Int!\n    total: Float!\n}
\n\n

现在是在Go中的实现:

\n\n
package graph\n\nimport (\n    "context"\n    "fmt"\n)\n\ntype Resolver struct{}\n\nfunc (r *Resolver) User(ctx context.Context, args struct{ ID int }) (*User, error) {\n    // 实际应用中——查询数据库\n    return &User{\n        ID:   args.ID,\n        Name: "Maria",\n        Orders: []*Order{\n            {ID: 1, Total: 250.50},\n        },\n    }, nil\n}
\n\n

客户端只请求name——就只得到name。没有过度获取。但有一个问题:如果客户端请求10个用户的orders,并且每个用户有50个订单——数据库可能会崩溃。这在GraphQL中被称为N+1问题,在Go中通过DataLoaders来解决。

\n\n

DataLoader——N+1问题的救星

\n\n
import "github.com/graph-gophers/dataloader"\n\ntype UserLoader struct {\n    *dataloader.Loader\n}\n\nfunc NewUserLoader() *UserLoader {\n    return &UserLoader{\n        Loader: dataloader.NewBatchedLoader(func(ctx context.Context, keys dataloader.Keys) []*dataloader.Result {\n            // 批量查询数据库\n            userIDs := make([]int, len(keys))\n            for i, key := range keys {\n                userIDs[i] = key.Int()\n            }\n            users := batchFetchUsers(userIDs) // 一个带有WHERE IN的SQL查询\n            results := make([]*dataloader.Result, len(ke                

博客

书籍推荐