介绍
GraphQL 是一种用于 API 的现代查询语言,由 Facebook 开发。与 REST 不同,它允许客户端仅请求所需的数据,从而减少网络负载并加速开发。在本教程中,我们将使用流行的 gqlgen 库,在 Go (Golang) 中创建一个完整的 GraphQL API。您将学习如何配置服务器、定义模式、编写解析器以及执行查询。本文面向熟悉 Go 基础的开发者,但即使是新手,也能通过详细的注释完成这些步骤。
1. 项目设置与依赖安装
首先,我们创建一个新的 Go 项目并安装必要的包。
步骤 1: 初始化模块
mkdir graphql-go-tutorialcd graphql-go-tutorialgo mod init graphql-go-tutorial步骤 2: 安装 gqlgen
gqlgen 是 Go 的 GraphQL 代码生成器。它会根据模式自动创建解析器。
go get github.com/99designs/gqlgen@latest步骤 3: 生成项目骨架
运行初始化命令:
go run github.com/99designs/gqlgen init执行后,您将看到以下结构:
graph/— 包含模式和解析器的文件夹server.go— 入口点gqlgen.yml— 生成器配置
2. 定义 GraphQL 模式
模式(schema)描述了 API 的数据类型和操作。打开文件 graph/schema.graphqls 并将其内容替换为以下内容:
type User { id: ID! name: String! email: String! age: Int!}
type Query { users: [User!]! user(id: ID!): User}
type Mutation { createUser(name: String!, email: String!, age: Int!): User! updateUser(id: ID!, name: String, email: String, age: Int): User deleteUser(id: ID!): Boolean}这里我们定义了:
- User — 包含 id、name、email、age 字段的对象。
- Query — 读取操作(获取所有用户或按 ID 获取单个用户)。
- Mutation — 写入操作(创建、更新、删除)。
保存后,运行代码生成:
go run github.com/99designs/gqlgen generate这将创建文件 models_gen.go(Go 类型)和 generated.go(解析器接口)。
3. 实现解析器和数据存储
现在需要编写处理查询的逻辑。为简单起见,我们将数据存储在内存中(结构体切片)。
步骤 1: 创建存储
创建文件 graph/store.go:
package graph
import "sync"
type Store struct { mu sync.RWMutex users []User nextID int}
func NewStore() *Store { return &Store{ users: []User{ {ID: "1", Name: "Alice", Email: "alice@example.com", Age: 30}, {ID: "2", Name: "Bob", Email: "bob@example.com", Age: 25}, }, nextID: 3, }}
func (s *Store) GetUsers() []User { s.mu.RLock() defer s.mu.RUnlock() return s.users}
func (s *Store) GetUser(id string) *User { s.mu.RLock() defer s.mu.RUnlock() for _, u := range s.users { if u.ID == id { return &u } } return nil}
func (s *Store) CreateUser(name, email string, age int) User { s.mu.Lock() defer s.mu.Unlock() user := User{ ID: fmt.Sprintf("%d", s.nextID), Name: name, Email: email, Age: age, } s.nextID++ s.users = append(s.users, user) return user}
func (s *Store) UpdateUser(id string, name, email *string, age *int) *User { s.mu.Lock() defer s.mu.Unlock() for i, u := range s.users { if u.ID == id { if name != nil { s.users[i].Name = *name } if email