使用 Go 和 gqlgen 實現 GraphQL API:實戰指南
在本文中,我將分享如何使用 Go 語言和 gqlgen 框架實現一個完整的 GraphQL API。我們將構建一個包含用戶、文章和評論功能的博客系統 API。
技術棧
- Go
- gqlgen (GraphQL 框架)
- MySQL (數據存儲)
- Redis (緩存,可選)
項目結構
go_graphql/
├── config/
│ └── database.go # 數據庫配置
├── graph/
│ ├── model/ # 數據模型
│ ├── schema.graphqls # GraphQL schema
│ └── schema.resolvers.go # Resolver 實現
├── server.go # 主程序入口
└── gqlgen.yml # gqlgen 配置文件
GraphQL Schema 設計
首先,我們需要定義 GraphQL schema,這是整個 API 的基礎:
type User {id: ID!username: String!email: String!avatar: StringcreatedAt: String!posts: [Post!]comments: [Comment!]
}type Post {id: ID!title: String!content: String!author: User!category: Category!createdAt: String!updatedAt: Stringcomments: [Comment!]images: [Image!]
}type Comment {id: ID!content: String!author: User!post: Post!createdAt: String!
}type Query {users: [User!]!user(id: ID!): Userposts(categoryId: ID): [Post!]!post(id: ID!): Post
}type Mutation {createUser(input: CreateUserInput!): User!createPost(input: CreatePostInput!): Post!createComment(input: CreateCommentInput!): Comment!
}
Resolver 實現
下面是一個完整的用戶查詢 resolver 實現示例:
// Users resolver 實現
func (r *queryResolver) Users(ctx context.Context) ([]*model.User, error) {rows, err := config.DB.Query(`SELECT id, username, email, avatar, created_at FROM users`)if err != nil {return nil, fmt.Errorf("failed to query users: %v", err)}defer rows.Close()var users []*model.Userfor rows.Next() {var user model.Uservar createdAt time.Timeerr := rows.Scan(&user.ID, &user.Username, &user.Email, &user.Avatar, &createdAt)if err != nil {return nil, fmt.Errorf("failed to scan user: %v", err)}user.CreatedAt = createdAt.Format(time.RFC3339)users = append(users, &user)}return users, nil
}// User 類型的 posts 字段 resolver
func (r *userResolver) Posts(ctx context.Context, obj *model.User) ([]*model.Post, error) {rows, err := config.DB.Query(`SELECT p.id, p.title, p.content, p.created_at, p.updated_at, p.category_id, p.author_id FROM posts p WHERE p.author_id = ?`, obj.ID)if err != nil {return nil, fmt.Errorf("failed to query posts: %v", err)}defer rows.Close()var posts []*model.Postfor rows.Next() {var post model.Postvar createdAt, updatedAt time.Timevar categoryID, authorID stringerr := rows.Scan(&post.ID, &post.Title, &post.Content, &createdAt, &updatedAt, &categoryID, &authorID)if err != nil {return nil, fmt.Errorf("failed to scan post: %v", err)}post.CreatedAt = createdAt.Format(time.RFC3339)updatedAtStr := updatedAt.Format(time.RFC3339)post.UpdatedAt = &updatedAtStrposts = append(posts, &post)}return posts, nil
}
代碼生成
gqlgen 是一個強大的 GraphQL 代碼生成工具,它可以:
- 根據 schema 自動生成 Go 類型
- 生成所有必要的接口和類型定義
- 保持自定義實現代碼不變
使用以下命令生成代碼:
go run github.com/99designs/gqlgen generate
生成的代碼包括:
graph/generated/generated.go
: 包含所有生成的接口和類型graph/model/models_gen.go
: 包含根據 schema 生成的 Go 結構體graph/schema.resolvers.go
: 包含 resolver 實現的框架代碼
最佳實踐
-
類型安全:利用 Go 的類型系統和 gqlgen 的代碼生成確保類型安全
-
錯誤處理:
if err != nil {return nil, fmt.Errorf("failed to query users: %v", err) }
-
資源清理:使用 defer 確保資源正確釋放
defer rows.Close()
-
時間處理:統一使用 RFC3339 格式處理時間
createdAt.Format(time.RFC3339)
-
空值處理:對可選字段使用指針類型
updatedAtStr := updatedAt.Format(time.RFC3339) post.UpdatedAt = &updatedAtStr