引言:为什么 GraphQL 和 Go 是理想组合
\n在现代 Web 开发中,GraphQL 已成为创建灵活高效 API 的标准。结合以其高性能和简洁性著称的 Go 语言 (Golang),您将获得一个强大的后端开发工具。在本逐步教程中,我们将使用流行的 gqlgen 库,从零开始使用 Go 构建一个完整的 GraphQL API。
您将学习如何配置项目、定义模式、实现解析器以及添加用于身份验证的中间件。到文章结束时,您将拥有一个可运行的 API,随时可与任何前端集成。
\n\n1. 项目设置与依赖安装
\n首先,创建一个新的 Go 模块并安装必要的包。请确保您已安装 Go 1.18 或更高版本。
\nmkdir graphql-go-tutorial\ncd graphql-go-tutorial\ngo mod init graphql-go-tutorial\ngo get github.com/99designs/gqlgen@latest\ngo get github.com/99designs/gqlgen/graphql/introspection@latest\n现在,在项目中初始化 gqlgen。此命令将创建基本的文件结构:
go run github.com/99designs/gqlgen init\n执行后,您将看到以下结构:
\n- \n
graph/— 包含模式文件和解析器的目录 \ngraph/schema.graphqls— GraphQL 模式定义 \ngraph/resolver.go— 解析器的占位符 \nserver.go— 应用程序入口点 \ngqlgen.yml— 生成器配置 \n
2. 定义 GraphQL 模式
\n打开 graph/schema.graphqls 文件并定义我们的数据模型。作为示例,我们将创建一个简单的任务管理系统 (Task Manager):
type Task {\n id: ID!\n title: String!\n description: String\n status: TaskStatus!\n createdAt: Time!\n}\n\nenum TaskStatus {\n TODO\n IN_PROGRESS\n DONE\n}\n\ntype Query {\n tasks(status: TaskStatus): [Task!]!\n task(id: ID!): Task\n}\n\ntype Mutation {\n createTask(title: String!, description: String): Task!\n updateTask(id: ID!, title: String, description: String, status: TaskStatus): Task!\n deleteTask(id: ID!): Boolean!\n}\n\nscalar Time\n保存模式后,运行代码生成:
\ngo run github.com/99designs/gqlgen generate\n此命令将创建包含 Go 结构体的 graph/model/models_gen.go 文件,并更新解析器。
3. 实现解析器和业务逻辑
\n现在,在 graph/schema.resolvers.go 文件中实现逻辑。首先,创建一个简单的内存存储 (in-memory store):
// 在 graph/resolver.go 中添加存储\ntype Resolver struct {\n tasks []*model.Task\n mu sync.Mutex\n}\n\nfunc NewResolver() *Resolver {\n return &Resolver{\n tasks: make([]*model.Task, 0),\n }\n}\n现在,实现 Query 和 Mutation 的解析器:
\n// Query.Task\nfunc (r *queryResolver) Task(ctx context.Context, id string) (*model.Task, error) {\n r.mu.Lock()\n defer r.mu.Unlock()\n \n for _, task := range r.tasks {\n if task.ID == id {\n return task, nil\n }\n }\n return nil, fmt.Errorf("task not found")\n}\n\n// Mutation.CreateTask\nfunc (r *mutationResolver) CreateTask(ctx context.Context, title string, description *string) (*model.Task, error) {\n r.mu.Lock()\n defer r.mu.Unlock()\n \n task := &model.Task{\n ID: uuid.New().String(),\n Title: title,\n Description: description,\n Status: model.TaskStatusTodo,\n CreatedAt: time.Now(),\n }\n \n r.tasks = append(r.tasks, task)\n return task, nil\n}\n\n// Mutation.UpdateTask\nfunc (r *mutationResolver) UpdateTask(ctx context.Context, id string, title *string, description *string, status *model.TaskStatus) (*model.Task, error) {\n r.mu.Lock()\n defer r.mu.Unlock()\n \n for _, task