Go Skeleton
面向实际项目开发的 Go Web API 脚手架。它将 HTTP、WebSocket、认证、数据库迁移、缓存、接口文档和可观测性组织成可继续扩展的基础工程。
当前模块名为
skeleton。创建新项目后,请先使用初始化脚本替换成自己的 Go module path。
特性
- Gin HTTP 路由及公开、JWT 私有路由分组
- PostgreSQL、MySQL、SQLite 三种 GORM 数据库驱动
- 按 SQL 方言隔离的 Atlas 数据库迁移
- 可选 Redis,以及 JWT 撤销记录存储
- Access Token、Refresh Token、刷新轮换和注销示例
- WebSocket 房间、广播、连接管理和心跳示例
- Swaggo OpenAPI 文档和 Swagger UI
- Zap 结构化日志、请求 ID、Prometheus 指标
- OpenTelemetry OTLP HTTP 链路追踪
- Dockerfile、Docker Compose、GitHub Actions
- Linux、macOS、Windows amd64 交叉编译
目录
技术栈
| 能力 | 实现 |
|---|---|
| HTTP | Gin |
| WebSocket | Gorilla WebSocket |
| ORM | GORM |
| 数据库 | PostgreSQL / MySQL / SQLite |
| 数据迁移 | Atlas + Atlas GORM Provider |
| 缓存 | Redis,可选 |
| 认证 | JWT HS256 + bcrypt |
| API 文档 | Swaggo / Swagger UI |
| 日志 | Zap |
| 指标 | Prometheus |
| Trace | OpenTelemetry OTLP HTTP |
快速开始
环境要求
- Go 1.25+
- Atlas CLI,仅执行数据库迁移时需要
- Swag CLI,仅重新生成 OpenAPI 文档时需要
- Docker,可选
- C 编译器,使用 SQLite 时需要
1. 创建自己的项目
git clone https://gitea.xchoumc.online/xchou/go-skeleton.git
cd go-skeleton
./scripts/init.sh github.com/yourname/your-project
2. 使用 SQLite 快速运行
修改 config/app.yaml:
database:
driver: sqlite
sqlite_path: data/skeleton.db
redis:
enabled: false
应用迁移:
export DATABASE_URL='sqlite://data/skeleton.db'
export DATABASE_DEV_URL='sqlite://dev?mode=memory&_fk=1'
go run cmd/migrate/main.go -action apply
启动服务:
go run .
默认服务地址:
| 服务 | 地址 |
|---|---|
| API | http://localhost:8080/api |
| Swagger UI | http://localhost:8080/docs/index.html |
| Prometheus Metrics | http://localhost:8080/metrics |
| WebSocket | ws://localhost:8080/api/ws |
项目结构
.
├── main.go # 应用启动与优雅关闭
├── config/ # YAML、环境变量及配置校验
├── database/ # GORM、Redis、Token 撤销和迁移封装
├── middlewares/ # JWT、日志、CORS、指标、Request ID
├── models/ # GORM 数据模型
├── modules/
│ ├── auth/ # 注册、登录、刷新和注销示例
│ ├── example/ # REST 业务模块示例
│ └── ws/ # WebSocket Hub、Client 和消息协议
├── observability/ # OpenTelemetry 初始化
├── routes/
│ ├── rest/ # REST 路由注册器
│ └── ws/ # WebSocket 路由挂载
├── docs/ # Swag 自动生成文件
├── migrations/
│ ├── postgres/
│ ├── mysql/
│ └── sqlite/
├── tools/atlas-loader/ # 独立 Atlas GORM Schema 工具模块
├── scripts/
│ ├── init.sh # 修改 Go module path
│ └── build.sh # 多平台 amd64 构建
├── Dockerfile
├── docker-compose.yml
└── Makefile
配置
应用默认读取 config/app.yaml。使用其他配置文件:
export SKELETON_CONFIG=config/app.yaml
环境变量优先级高于 YAML。生产环境应使用部署平台的 Secret 管理能力注入凭据。
应用和日志
| 环境变量 | 说明 |
|---|---|
APP_HOST、APP_PORT |
HTTP 监听地址 |
APP_VERSION |
应用版本 |
APP_ENVIRONMENT |
development 或 production |
APP_DEBUG |
Gin 调试模式 |
APP_TIMEZONE |
IANA 时区名称 |
LOG_LEVEL |
debug、info、warn、error |
LOG_FORMAT |
console 或 json |
LOG_OUTPUT |
stdout 或 file |
数据库
| 环境变量 | 说明 |
|---|---|
DATABASE_DRIVER |
postgres、mysql 或 sqlite |
DATABASE_DSN |
完整连接串,优先于其他连接参数 |
DATABASE_HOST、DATABASE_PORT |
服务端数据库地址 |
DATABASE_USERNAME、DATABASE_PASSWORD |
数据库凭据 |
DATABASE_NAME |
数据库名称 |
DATABASE_SSLMODE |
PostgreSQL SSL 模式 |
DATABASE_SQLITE_PATH |
SQLite 文件路径 |
DATABASE_MAX_IDLE_CONNS |
最大空闲连接数 |
DATABASE_MAX_OPEN_CONNS |
最大连接数 |
DATABASE_CONN_MAX_LIFETIME |
连接最大存活分钟数 |
未设置 database.driver 时默认使用 PostgreSQL。SQLite 会创建数据文件父目录,并默认限制为单连接。
Redis、JWT 和可观测性
| 环境变量 | 说明 |
|---|---|
REDIS_ENABLED |
是否启用 Redis |
REDIS_HOST、REDIS_PORT、REDIS_PASSWORD |
Redis 连接信息 |
JWT_SECRET |
HS256 签名密钥 |
JWT_ACCESS_EXPIRE_MINUTES |
Access Token 有效分钟数 |
JWT_REFRESH_EXPIRE_HOURS |
Refresh Token 有效小时数 |
METRICS_ENABLED、METRICS_PATH |
Prometheus 指标开关和路径 |
TRACING_ENABLED |
是否启用 OpenTelemetry |
OTEL_SERVICE_NAME |
Trace 服务名 |
OTEL_EXPORTER_OTLP_ENDPOINT |
OTLP HTTP 地址,如 localhost:4318 |
OTEL_EXPORTER_OTLP_INSECURE |
是否使用明文 OTLP |
生产模式会拒绝默认或少于 32 个字符的 JWT 密钥。
HTTP API
| 方法 | 路径 | 认证 | 说明 |
|---|---|---|---|
GET |
/api/ping |
否 | 进程存活检查 |
GET |
/api/health |
否 | 数据库和可选 Redis readiness 检查 |
GET |
/api/example/hello |
否 | REST 模块示例 |
POST |
/api/auth/register |
否 | 注册示例用户 |
POST |
/api/auth/login |
否 | 获取 Token Pair |
POST |
/api/auth/refresh |
否 | 轮换 Refresh Token |
POST |
/api/private/auth/logout |
Bearer | 撤销 Access/Refresh Token |
GET |
/api/ws |
否 | WebSocket 握手入口 |
完整请求和响应模型请查看 Swagger UI。
响应约定
业务接口统一使用 HTTP 200 返回结果,业务状态通过响应体中的 code 字段表达,不使用 HTTP 状态码传递业务成功或失败状态。
{
"code": 401,
"message": "用户名或密码错误",
"data": null
}
统一响应结构:
| 字段 | 说明 |
|---|---|
code |
业务状态码,例如 200、400、401、409、500 |
message |
面向调用方的结果说明 |
data |
业务数据;失败时通常为 null |
新增业务接口时必须遵守以下约定:
- HTTP 响应状态固定为
200 OK。 - 成功使用响应体
code: 200。 - 参数、认证、权限和业务冲突等错误写入响应体
code。 - 调用方应判断响应体中的
code,不能依赖 HTTP 状态码判断业务结果。 - WebSocket 握手、协议升级失败以及基础设施健康检查不属于普通业务响应,可以使用对应的 HTTP 状态码。
认证示例
curl -X POST http://localhost:8080/api/auth/register \
-H 'Content-Type: application/json' \
-d '{"username":"demo","password":"change-me-123"}'
curl -X POST http://localhost:8080/api/auth/login \
-H 'Content-Type: application/json' \
-d '{"username":"demo","password":"change-me-123"}'
启用 Redis 时,Token 撤销记录存储在 Redis,适合多实例部署;关闭 Redis 时使用进程内存,仅适合开发或单实例部署。
WebSocket
连接端点:
ws://localhost:8080/api/ws?room=lobby&client_id=demo
查询参数:
| 参数 | 必填 | 说明 |
|---|---|---|
room |
否 | 房间名,默认 lobby,最多 64 个字符 |
client_id |
否 | 客户端标识;未提供时服务端生成 |
使用 websocat:
websocat 'ws://localhost:8080/api/ws?room=lobby&client_id=terminal-1'
发送消息:
{"type":"message","data":"hello websocket"}
服务端事件:
{
"type": "message",
"room": "lobby",
"client_id": "terminal-1",
"data": "hello websocket",
"timestamp": "2026-08-09T00:00:00Z"
}
事件类型:
| 类型 | 说明 |
|---|---|
welcome |
当前客户端连接成功 |
message |
房间广播消息 |
presence |
客户端加入或离开 |
error |
消息格式或类型错误 |
浏览器示例:
const socket = new WebSocket(
"ws://localhost:8080/api/ws?room=lobby&client_id=browser-1"
);
socket.onmessage = (event) => console.log(JSON.parse(event.data));
socket.onopen = () => {
socket.send(JSON.stringify({ type: "message", data: "hello" }));
};
WebSocket 实现包含:
- 按房间隔离广播
- 在线连接数量
- 单连接一个 reader 和一个 writer
- Ping/Pong 心跳与读取超时
- 8 KiB 单消息限制
- 有界发送队列和慢客户端清理
- 安全的默认同源 Origin 校验
当前 Hub 是单进程内存实现。多实例部署时,应在 modules/ws/hub.go 接入 Redis Pub/Sub、NATS 或其他消息系统。
数据库迁移
安装 Atlas:
brew install ariga/tap/atlas
# 或
go install ariga.io/atlas/cmd/atlas@latest
设置当前数据库和开发数据库 URL:
# PostgreSQL
export DATABASE_URL='postgres://user:pass@localhost:5432/app?sslmode=disable'
export DATABASE_DEV_URL='docker://postgres/17/dev'
# MySQL
export DATABASE_URL='mysql://user:pass@localhost:3306/app'
export DATABASE_DEV_URL='docker://mysql/8/dev'
# SQLite
export DATABASE_URL='sqlite://data/app.db'
export DATABASE_DEV_URL='sqlite://dev?mode=memory&_fk=1'
常用命令:
# 状态
go run cmd/migrate/main.go -action status
# 根据 GORM 模型生成迁移
go run cmd/migrate/main.go -action diff -name add_orders
# 校验
go run cmd/migrate/main.go -action validate
# 预览
go run cmd/migrate/main.go -action apply -dry-run
# 应用
go run cmd/migrate/main.go -action apply
# 显式选择 Atlas 环境
go run cmd/migrate/main.go -action status -env local_mysql
迁移工具会根据 app.environment 和 database.driver 自动选择对应的 Atlas 环境。三种数据库的迁移文件不可混用。
接口文档
安装 Swag 并重新生成:
go install github.com/swaggo/swag/cmd/swag@v1.8.12
swag init
生成文件位于 docs/,应用运行时不依赖 Swag CLI。
可观测性
GET /api/ping:liveness,不访问外部服务GET /api/health:readiness,依赖异常时返回 HTTP 503GET /metrics:Prometheus Counter 和 HistogramX-Request-ID:接收上游 ID 或自动生成,并写入响应和日志- OpenTelemetry:启用后通过 OTLP HTTP 导出 Gin 请求 Span
Docker Compose 默认提供 Jaeger UI:http://localhost:16686。
测试
单元测试和静态检查:
go test ./...
go test -race ./config ./database ./middlewares ./modules/ws
go vet ./...
PostgreSQL 和 MySQL 集成测试:
export TEST_POSTGRES_DSN='postgres://skeleton:skeleton@127.0.0.1:5432/skeleton?sslmode=disable'
export TEST_MYSQL_DSN='skeleton:skeleton@tcp(127.0.0.1:3306)/skeleton?charset=utf8mb4&parseTime=True&loc=Local'
go test -tags=integration ./database
Makefile 快捷命令:
make test
make vet
make integration
编译
当前平台无 CGO 构建:
make build
Linux、macOS、Windows x64/amd64 交叉编译:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o dist/skeleton-linux-amd64 .
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -o dist/skeleton-darwin-amd64 .
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o dist/skeleton-windows-amd64.exe .
项目封装命令:
VERSION=0.1.0 make build-x64
SQLite 官方驱动依赖 CGO。跨平台 CGO_ENABLED=0 产物支持 PostgreSQL 和 MySQL;SQLite 应在目标平台原生构建:
make build-native
Docker
默认启动应用、PostgreSQL、Redis、Atlas 迁移和 Jaeger:
docker compose up --build
额外启动 MySQL:
docker compose --profile mysql up -d mysql
停止:
docker compose down
删除本地 Compose 数据卷:
docker compose down -v
开发新模块
推荐结构:
modules/order/
├── controller.go
├── service.go
├── types.go
└── repository.go
开发流程:
- 在
modules/添加业务代码。 - 在
routes/rest/注册公开或 JWT 私有路由。 - 在
models/添加或修改 GORM 模型。 - 为目标数据库分别生成和检查 Atlas 迁移。
- 添加单元测试和必要的集成测试。
- 更新 Swag 注解并执行
swag init。
安全说明
该仓库是脚手架,不应在未经审查的情况下直接用于生产:
- 必须修改默认 JWT 密钥和示例数据库密码。
- 根据实际前端域名收紧 CORS 和 WebSocket Origin 策略。
- 示例注册接口应增加邀请码、管理员权限或在生产环境关闭。
- 多实例 Token 撤销必须启用 Redis。
- 多实例 WebSocket 广播必须接入外部消息系统。
- TLS 应由网关、Ingress 或应用部署环境终止。
请通过私有渠道报告安全问题,不要在公开 Issue 中提交密钥或可利用细节。
贡献
欢迎提交 Issue 和 Pull Request。提交前请至少执行:
go fmt ./...
go test ./...
go vet ./...
建议每个 Pull Request 聚焦一个主题,并同步更新测试、迁移和文档。
License
当前仓库尚未包含许可证文件。正式公开分发前,请由项目维护者选择并添加合适的开源许可证。