REST用路径版本(如/v1、/v2)隔离,GraphQL靠字段级演进(@deprecated+新增字段)维持兼容;两者共存时须物理隔离版本逻辑,Go Gin中必须用Group分路由,GraphQL只暴露单一endpoint,DTO与数据库层需各自兼容。

直接上结论:REST 用 /v1、/v2 路径分组,GraphQL 不做版本路径隔离,靠字段级演进(@deprecated + 新增字段)维持兼容。两者共存时,版本管理逻辑必须物理隔离,不能混用同一套路由或中间件。
Go Gin 中 REST 版本路由必须用 Group 分离
别试图用一个 handler 里 if-else 切版本,那会迅速变成维护噩梦。Gin 的 Group 是唯一干净的隔离方式:
-
v1 := r.Group("/api/v1")和v2 := r.Group("/api/v2")必须各自注册独立 handler,比如getUsersV1和getUsersV2 - handler 内部不要读
c.GetHeader("Api-Version")做分支——这会让 v1 接口悄悄调用 v2 逻辑,测试和监控全失效 - 如果用了 API 网关(如 Kong、Traefik),确保网关按路径前缀转发到不同服务实例,而不是在 Go 服务内二次解析
GraphQL Schema 不能加 /v1/graphql 这种路径版本
GraphQL 的本质是客户端驱动查询,加路径版本等于否定它的灵活性。正确做法是只暴露一个 endpoint(如 /graphql),所有兼容性靠 schema 演进保障:
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 旧字段不删,加
@deprecated(reason: "Use contactEmail instead") - 新字段命名要语义清晰,避免
email_v2这种临时命名,直接叫contactEmail - 用
apollo-server-plugin-operation-registry或自研日志埋点,监控email字段调用量,归零后再下线 - 禁止在 resolver 里写
if version == "v2" { ... }—— GraphQL 没有请求级版本概念,只有字段存在性
共存时最容易踩的坑:共享 DTO 或数据库 Model
REST v2 返回 CreatedAt time.Time,GraphQL 却仍用旧版 struct,结果时间格式错乱或 panic。关键约束如下:
立即学习“go语言免费学习笔记(深入)”;
- REST v1/v2 的响应 struct 必须各自定义,比如
userV1Response和userV2Response,哪怕只差一个字段 - GraphQL 的
Usertype 对应的是 schema 定义,不是某个 REST 版本的 struct;resolver 应该从 domain model 映射,而非复用 REST DTO - 数据库层字段变更(如
email拆成primary_email+backup_email)必须兼容旧字段读取逻辑,不能让 v1 REST 或旧 GraphQL 字段直接报错
真正难的不是怎么写代码,而是团队是否接受“REST 版本是契约,GraphQL 版本是演化”这个认知差异。一旦有人提议把 GraphQL 字段也按 v1/v2 分 schema 文件,就说明还没跳出 REST 思维定式。

















