使用 golang 构建 graphql 服务的核心步骤包括:1. 定义数据模型和 resolver,2. 使用 gqlgen 框架生成 schema 和 resolver 接口,3. 实现 resolver 业务逻辑,4. 创建并注册 graphql server。首先安装 gqlgen 工具并初始化项目,生成 schema.graphqls 文件定义类型、查询和 mutation,运行 gqlgen generate 生成代码,接着实现 resolver 函数获取数据,最后通过 net/http 包创建 http server 并注册 graphql handler,完成服务搭建。

使用 Golang 构建 GraphQL 服务,核心在于定义你的数据模型和解析器,然后用像
gqlgen

gqlgen 框架简化了 GraphQL 服务的开发,它通过读取你的 Go 代码来生成 schema 和 resolver 接口,你只需要专注于编写业务逻辑。

gqlgen 框架的使用方法
立即学习“go语言免费学习笔记(深入)”;
首先,确保你已经安装了 Go 语言环境。然后,使用
go get
gqlgen

go install github.com/99designs/gqlgen
接下来,在你的项目目录下,运行
gqlgen init
gqlgen.yml
schema.graphqls
gqlgen init
现在,你需要定义你的 GraphQL schema。在
schema.graphqls
type Query {
todo(id: ID!): Todo
todos: [Todo!]!
}
type Mutation {
createTodo(text: String!, userId: String!): Todo!
updateTodo(id: ID!, text: String, done: Boolean): Todo!
deleteTodo(id: ID!): Boolean!
}
type Todo {
id: ID!
text: String!
done: Boolean!
user: User!
}
type User {
id: ID!
name: String!
}定义好 schema 后,运行
gqlgen generate
gqlgen generate
接下来,你需要实现 resolver 接口。resolver 负责从你的数据源中获取数据,并将数据转换为 GraphQL schema 中定义的类型。例如,你可以实现
Query.todos
func (r *queryResolver) Todos(ctx context.Context) ([]*Todo, error) {
// 从数据库或缓存中获取所有 todos
todos := []*Todo{
{ID: "1", Text: "Learn GraphQL", Done: false, User: &User{ID: "1", Name: "Alice"}},
{ID: "2", Text: "Build a GraphQL server", Done: true, User: &User{ID: "2", Name: "Bob"}},
}
return todos, nil
}最后,你需要创建一个 GraphQL server,并将你的 schema 和 resolver 注册到 server 中。你可以使用
net/http
gqlgen
handler.GraphQL
package main
import (
"context"
"log"
"net/http"
"os"
"github.com/99designs/gqlgen/graphql/handler"
"github.com/99designs/gqlgen/graphql/playground"
"your-project/graph" // 替换为你的项目路径
"your-project/graph/generated" // 替换为你的项目路径
)
const defaultPort = "8080"
func main() {
port := os.Getenv("PORT")
if port == "" {
port = defaultPort
}
srv := handler.NewDefaultServer(generated.NewExecutableSchema(generated.Config{Resolvers: &graph.Resolver{}}))
http.Handle("/", playground.Handler("GraphQL playground", "/query"))
http.Handle("/query", srv)
log.Printf("connect to http://localhost:%s/ for GraphQL playground", port)
log.Fatal(http.ListenAndServe(":"+port, nil))
}
这样,你就成功地使用 Golang 和
gqlgen
如何处理 GraphQL 服务的身份验证和授权?
身份验证和授权在 GraphQL 服务中至关重要。一种常见的做法是使用 JWT (JSON Web Tokens)。当用户登录时,服务器会生成一个 JWT 并返回给客户端。客户端在后续的请求中将 JWT 放在 HTTP Header 中 (通常是
Authorization: Bearer <token>
在 GraphQL resolver 中,你可以从 context 中获取 JWT,并验证用户的身份。如果用户身份验证失败,你可以返回一个错误。
对于授权,你可以使用基于角色的访问控制 (RBAC) 或基于属性的访问控制 (ABAC)。RBAC 允许你为用户分配角色,并根据角色来控制用户的访问权限。ABAC 允许你根据用户的属性、资源属性和环境属性来控制用户的访问权限。
例如,你可以创建一个 middleware 来验证 JWT,并将用户信息添加到 context 中:
func AuthMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
tokenString := r.Header.Get("Authorization")
if tokenString == "" {
next.ServeHTTP(w, r) // Allow anonymous access for some endpoints
return
}
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
// Validate signing method
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
return []byte("your-secret-key"), nil // Replace with your actual secret key
})
if err != nil {
http.Error(w, "Invalid token", http.StatusUnauthorized)
return
}
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
ctx := context.WithValue(r.Context(), "user", claims)
r = r.WithContext(ctx)
next.ServeHTTP(w, r)
} else {
http.Error(w, "Invalid token", http.StatusUnauthorized)
}
})
}
// 在你的 main 函数中,使用 AuthMiddleware 包裹你的 GraphQL handler
func main() {
// ...
srv := handler.NewDefaultServer(generated.NewExecutableSchema(generated.Config{Resolvers: &graph.Resolver{}}))
http.Handle("/query", AuthMiddleware(srv))
// ...
}
如何在 Golang GraphQL 服务中处理错误?
GraphQL 错误处理需要仔细考虑,因为你需要将错误信息返回给客户端,同时保持 API 的清晰和一致性。gqlgen 提供了处理错误的机制。
在 resolver 中,如果发生错误,你可以直接返回
error
errors
你可以使用自定义的错误类型来提供更详细的错误信息。例如:
type ValidationError struct {
Field string
Message string
}
func (e *ValidationError) Error() string {
return fmt.Sprintf("Validation error on field %s: %s", e.Field, e.Message)
}
func (r *mutationResolver) CreateTodo(ctx context.Context, text string, userID string) (*Todo, error) {
if len(text) < 3 {
return nil, &ValidationError{Field: "text", Message: "Text must be at least 3 characters long"}
}
// ...
}你也可以使用
graphql.Error
import "github.com/99designs/gqlgen/graphql"
func (r *queryResolver) Todo(ctx context.Context, id string) (*Todo, error) {
todo, err := r.DB.GetTodo(id)
if err != nil {
return nil, &graphql.Error{
Message: "Todo not found",
Extensions: map[string]interface{}{
"code": "TODO_NOT_FOUND",
"id": id,
},
}
}
return todo, nil
}在客户端,你可以解析 GraphQL 响应的
errors
如何在 Golang GraphQL 服务中进行性能优化?
GraphQL 性能优化是一个复杂的主题,涉及到多个方面。
例如,使用 DataLoader 优化 N+1 问题:
package graph
import (
"context"
"fmt"
"net/http"
"time"
"github.com/99designs/gqlgen/graphql"
"github.com/99designs/gqlgen/graphql/handler"
"github.com/vektah/dataloaden"
"your-project/graph/model" // 替换为你的项目路径
)
type contextKey string
const (
loadersKey = contextKey("dataloaders")
)
// TodoLoader 批量加载 Todo
func TodoLoader(db *DB) *dataloaden.Loader {
return dataloaden.NewLoader(func(keys []string) ([]*model.Todo, []error) {
todos := make([]*model.Todo, len(keys))
errors := make([]error, len(keys))
// 假设 DB.GetTodos(keys) 可以批量获取 Todos
results, err := db.GetTodos(keys)
if err != nil {
fmt.Println("Error fetching todos:", err)
for i := range keys {
errors[i] = err
}
return todos, errors
}
todoMap := make(map[string]*model.Todo)
for _, todo := range results {
todoMap[todo.ID] = todo
}
for i, key := range keys {
todos[i] = todoMap[key]
if todos[i] == nil {
errors[i] = fmt.Errorf("todo not found for id %s", key)
}
}
return todos, errors
})
}
// DataloaderMiddleware 将 DataLoader 注入到 Context 中
func DataloaderMiddleware(db *DB, next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
loaders := &Loaders{
Todo: TodoLoader(db),
}
ctx = context.WithValue(ctx, loadersKey, loaders)
r = r.WithContext(ctx)
next.ServeHTTP(w, r)
})
}
// GetLoaders 从 Context 中获取 DataLoader
func GetLoaders(ctx context.Context) *Loaders {
return ctx.Value(loadersKey).(*Loaders)
}
type Loaders struct {
Todo *dataloaden.Loader
}
// 在你的 main 函数中,使用 DataloaderMiddleware 包裹你的 GraphQL handler
func main() {
// ...
db := &DB{} // 替换为你的数据库连接
srv := handler.NewDefaultServer(generated.NewExecutableSchema(generated.Config{Resolvers: &graph.Resolver{DB: db}}))
http.Handle("/query", DataloaderMiddleware(db, srv))
// ...
}
// 在你的 resolver 中使用 DataLoader
func (r *todoResolver) User(ctx context.Context, obj *model.Todo) (*model.User, error) {
loaders := GetLoaders(ctx)
user, err := loaders.User.Load(obj.UserID)
if err != nil {
return nil, err
}
return user, nil
}如何测试 Golang GraphQL 服务?
测试 GraphQL 服务需要测试 schema 的正确性、resolver 的正确性以及服务的性能。
testify
gomock
net/http/httptest
Selenium
Cypress
例如,使用
net/http/httptest
package main
import (
"bytes"
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"github.com/99designs/gqlgen/graphql/handler"
"github.com/stretchr/testify/assert"
"your-project/graph" // 替换为你的项目路径
"your-project/graph/generated" // 替换为你的项目路径
)
func TestGraphQLQuery(t *testing.T) {
// 创建一个测试服务器
srv := handler.NewDefaultServer(generated.NewExecutableSchema(generated.Config{Resolvers: &graph.Resolver{}}))
ts := httptest.NewServer(srv)
defer ts.Close()
// 构造 GraphQL 查询
query := `
query {
todos {
id
text
done
}
}
`
// 构造 HTTP 请求
var b bytes.Buffer
json.NewEncoder(&b).Encode(map[string]interface{}{
"query": query,
})
req, err := http.NewRequest("POST", ts.URL, &b)
assert.NoError(t, err)
req.Header.Set("Content-Type", "application/json")
// 发送 HTTP 请求
client := &http.Client{}
resp, err := client.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()
// 解析 HTTP 响应
var result map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&result)
assert.NoError(t, err)
// 断言结果
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.NotNil(t, result["data"])
todos := result["data"].(map[string]interface{})["todos"].([]interface{})
assert.NotEmpty(t, todos)
}
以上就是如何用Golang构建GraphQL服务 介绍gqlgen框架的使用方法的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号