应直接使用gqlgen而非graphql-go手写方案:前者通过schema-first强制编译期校验resolver签名、字段映射与ctx传递,后者易漏指令支持与错误传播,调试成本高;gqlgen init卡住主因是module未初始化或GOPROXY失效,需执行go mod init、配置有效代理、避免GOPATH混用;schema修改后必须运行gqlgen generate并清理旧generated.go,否则resolver不生效或编译报错;resolver函数必须接收context.Context、透传至DB调用,并用gqlerror.Errorf返回错误;HTTP路由须满足POST+JSON要求,用graphql/handler.NewDefaultServer包装,Playground路径须与query路径分离。

别用 graphql-go 手写 schema,直接上 gqlgen——它生成强类型代码、编译期报错、resolver 签名不匹配当场 panic,省掉 80% 的 runtime 调试时间。
为什么 gqlgen 是 Gin 集成 GraphQL 的唯一合理起点
graphql-go 需手动拼 graphql.NewObject、硬编码字段、自己处理 Args 类型断言和错误传播,稍一漏写 ctx 或返回类型不对,请求就 500 或静默丢数据。而 gqlgen 强制你先写 schema.graphqls,再生成 Go 接口和模型,resolver 必须实现该接口,类型、参数顺序、ctx 位置全由编译器把关。
- schema 变更后必须跑
go run github.com/99designs/gqlgen generate,否则 resolver 不生效 - 生成的
graph/generated/generated.go是只读文件,别手动改;业务逻辑只写在resolver.go里 - resolver 方法签名形如
func (r *queryResolver) Users(ctx context.Context, args *UsersArgs) ([]*User, error),少ctx或返回类型与 schema 不一致(比如 schema 写[User!]!,Go 里却返回[]User)会直接编译失败
Gin 路由注册时最容易 404 的三个点
Gin 不像 Express 自带 GraphQL 中间件,gqlgen 生成的 handler 默认是 http.Handler,必须显式注册到 Gin 的 POST 路径,且路径不能和 Playground 混用。
- HTTP 方法必须是
POST:GraphQL 查询走 JSON body,router.POST("/graphql", handler.GraphQLHandler()) - Playground 要单独挂载:
router.GET("/playground", handler.PlaygroundHandler("GraphQL Playground", "/graphql")),否则访问/graphql会返回 HTML 而不是 GraphQL 错误 - 别用
gin.Default()直接套 —— 它默认加了Recovery()和Logger(),但gqlgen的 handler 已自带日志和 panic 捕获,重复中间件可能导致响应头冲突或 body 读取两次
resolver 里传 ctx 和返回 error 的硬性规则
所有数据库操作、RPC 调用、缓存读写,都必须透传 ctx,否则超时、取消、链路追踪全失效;错误也不能用 fmt.Errorf 直返,前端收不到标准 GraphQL error 字段。
- DB 查询必须用
db.QueryRowContext(ctx, ...),事务用tx.ExecContext(ctx, ...) - 错误必须用
gqlerror.Errorf("xxx")或gqlerror.ErrorPosf(pos, "xxx"),否则errors数组为空,前端只看到{"data": null} - 如果 schema 字段定义为非空(如
String!),resolver 返回nil会 panic;应返回空字符串""或提前校验并报错
真正卡住人的从来不是“怎么写第一个 query”,而是 schema 修改后忘了 generate、resolver 函数名大小写错了、或者 ctx 没传进 DB 调用——这些地方没 warning,只有 runtime 崩溃或数据不返回。











