让 API 开发像搭积木一样简单,文档自动生成,测试一键搞定!
📋 目录
- 什么是 Huma?
- Huma 的核心特性
- 快速开始:第一个 Huma API
- OpenAPI 集成:文档自动生成
- 集成 SurrealDB:现代数据库方案
- 完整实战:用户 CRUD API
- Huma vs 其他框架
- 小结
🤔 什么是 Huma?
Huma 是一个现代化的 Go Web 框架,专注于 OpenAPI 规范和开发者体验 [[1]]。
它的核心理念很简单:
- ✅ 基于 OpenAPI 3.1 规范构建
- ✅ 自动生成 API 文档(Swagger UI)
- ✅ 类型安全的请求/响应验证
- ✅ 零反射,高性能运行时
- ✅ 简洁的 API 设计
为什么选择 Huma?
想象一下传统 API 开发的痛点:
// 传统方式:手动解析、验证、文档...
func handler(w http.ResponseWriter, r *http.Request) {
// 1. 手动解析 JSON
// 2. 手动验证字段
// 3. 手动写文档
// 4. 手动测试...
}
// Huma 方式:一切自动化!
Huma 让你专注于业务逻辑,而不是样板代码!
🎯 Huma 的核心特性
1. OpenAPI 优先设计
Huma 从一开始就围绕 OpenAPI 规范构建,所有路由、参数、响应都会自动生成 OpenAPI 文档。
2. 类型安全的请求/响应
使用 Go 结构体定义 API,编译时就能捕获错误:
type GreetingInput struct {
Name string `json:"name" maxLength:"50" minLength:"1"`
}
type GreetingOutput struct {
Body struct {
Message string `json:"message"`
}
}
3. 自动验证
字段验证规则直接写在结构体标签中,Huma 自动处理:
type CreateUserInput struct {
Body struct {
Name string `json:"name" maxLength:"100"`
Email string `json:"email" format:"email"`
Age int `json:"age" minimum:"18" maximum:"120"`
}
}
4. 零反射运行时
Huma 在编译时生成所有代码,运行时零反射,性能接近原生 net/http。
🚀 快速开始:第一个 Huma API
安装 Huma
go get -u github.com/danielgtaylor/huma/v2
创建基础 API
package main
import (
"context"
"log"
"net/http"
"github.com/danielgtaylor/huma/v2"
"github.com/danielgtaylor/huma/v2/adapters/humago"
)
// 定义输入结构体
type GreetingInput struct {
Name string `json:"name" maxLength:"50" minLength:"1"`
}
// 定义输出结构体
type GreetingOutput struct {
Body struct {
Message string `json:"message"`
}
}
func main() {
// 创建 Go 标准库适配器
router := http.NewServeMux()
api := humago.New(router, huma.DefaultConfig("My API", "1.0.0"))
// 注册路由
huma.Register(api, huma.Operation{
OperationID: "get-greeting",
Method: http.MethodPost,
Path: "/greeting",
Summary: "获取问候语",
Description: "根据名字返回个性化的问候语",
}, func(ctx context.Context, input *GreetingInput) (*GreetingOutput, error) {
resp := &GreetingOutput{}
resp.Body.Message = "Hello, " + input.Name + "!"
return resp, nil
})
// 启动服务器
log.Println("服务器启动在 http://localhost:8888")
http.ListenAndServe(":8888", router)
}
测试 API
# 启动服务器
go run main.go
# 测试 API
curl -X POST http://localhost:8888/greeting \
-H "Content-Type: application/json" \
-d '{"name": "Alice"}'
# 响应
{"message":"Hello, Alice!"}
访问自动生成的文档
打开浏览器访问:
-
http://localhost:8888/docs- Swagger UI 交互式文档 -
http://localhost:8888/openapi.json- OpenAPI JSON 规范
📚 OpenAPI 集成:文档自动生成
自定义 OpenAPI 元数据
huma.Register(api, huma.Operation{
OperationID: "create-user",
Method: http.MethodPost,
Path: "/users",
Summary: "创建新用户",
Description: "创建一个新用户,返回用户详细信息",
Tags: []string{"用户管理"},
// 安全要求
Security: []map[string][]string{
{"BearerAuth": {}},
},
}, func(ctx context.Context, input *CreateUserInput) (*CreateUserOutput, error) {
// 处理逻辑
})
响应示例
type GetUserOutput struct {
Body struct {
ID string `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
CreatedAt time.Time `json:"created_at"`
}
}
// 添加响应示例
huma.Register(api, huma.Operation{
// ...
Responses: map[string]*huma.Response{
"200": {
Description: "成功",
Example: &GetUserOutput{
Body: struct {
ID string
Name string
Email string
CreatedAt time.Time
}{
ID: "123",
Name: "John Doe",
Email: "john@example.com",
CreatedAt: time.Now(),
},
},
},
},
}, /* handler */)
🗄️ 集成 SurrealDB:现代数据库方案
什么是 SurrealDB?
SurrealDB 是一个现代化的、分布式的、文档-关系混合数据库 :
- ✅ 文档 + 关系:支持 JSON 文档和 SQL 表
- ✅ 实时订阅:数据变化实时推送
- ✅ 类型安全:内置类型系统
- ✅ 多协议支持:HTTP、WebSocket、gRPC
安装 SurrealDB
# macOS
brew install surrealdb/tap/surreal
# Linux
curl -sSf https://install.surrealdb.com | sh
# Docker
docker run -p 8000:8000 surrealdb/surrealdb:latest start
启动 SurrealDB
# 开发模式
surreal start --user root --pass root file://data.db
# 访问控制台
surreal sql --conn http://localhost:8000 --user root --pass root
Go 中集成 SurrealDB
package main
import (
"context"
"log"
"net/http"
"time"
"github.com/danielgtaylor/huma/v2"
"github.com/danielgtaylor/huma/v2/adapters/humago"
"github.com/surrealdb/surrealdb.go"
)
// 数据库连接
var db *surrealdb.DB
// 初始化数据库
func initDB() error {
var err error
db, err = surrealdb.New("http://localhost:8000")
if err != nil {
return err
}
// 登录
_, err = db.Signin(map[string]interface{}{
"user": "root",
"pass": "root",
})
if err != nil {
return err
}
// 使用命名空间和数据库
_, err = db.Use("test", "test")
return err
}
// 用户模型
type User struct {
ID string `json:"id,omitempty"`
Name string `json:"name"`
Email string `json:"email"`
Age int `json:"age"`
CreatedAt time.Time `json:"created_at"`
}
// 创建用户输入
type CreateUserInput struct {
Body struct {
Name string `json:"name" maxLength:"100" minLength:"1"`
Email string `json:"email" format:"email"`
Age int `json:"age" minimum:"18" maximum:"120"`
}
}
// 创建用户输出
type CreateUserOutput struct {
Body struct {
ID string `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
Age int `json:"age"`
CreatedAt time.Time `json:"created_at"`
}
}
func main() {
// 初始化数据库
if err := initDB(); err != nil {
log.Fatal("数据库初始化失败:", err)
}
// 创建 API
router := http.NewServeMux()
api := humago.New(router, huma.DefaultConfig("User API", "1.0.0"))
// 注册创建用户路由
huma.Register(api, huma.Operation{
OperationID: "create-user",
Method: http.MethodPost,
Path: "/users",
Summary: "创建用户",
Description: "创建一个新用户",
}, func(ctx context.Context, input *CreateUserInput) (*CreateUserOutput, error) {
// 创建用户
user := User{
Name: input.Body.Name,
Email: input.Body.Email,
Age: input.Body.Age,
CreatedAt: time.Now(),
}
// 保存到 SurrealDB
created, err := db.Create("user", user)
if err != nil {
return nil, err
}
// 转换为输出格式
output := &CreateUserOutput{}
if err := surrealdb.Unmarshal(created, &output.Body); err != nil {
return nil, err
}
return output, nil
})
log.Println("服务器启动在 http://localhost:8888")
http.ListenAndServe(":8888", router)
}
SurrealDB 也提供了一个UI工具方便操作自己:Surreallist
🛠️ 完整实战:用户 CRUD API
完整代码示例
package main
import (
"context"
"log"
"net/http"
"time"
"github.com/danielgtaylor/huma/v2"
"github.com/danielgtaylor/huma/v2/adapters/humago"
"github.com/surrealdb/surrealdb.go"
)
var db *surrealdb.DB
func initDB() error {
var err error
db, err = surrealdb.New("http://localhost:8000")
if err != nil {
return err
}
_, err = db.Signin(map[string]interface{}{
"user": "root",
"pass": "root",
})
if err != nil {
return err
}
_, err = db.Use("test", "test")
return err
}
type User struct {
ID string `json:"id,omitempty"`
Name string `json:"name"`
Email string `json:"email"`
Age int `json:"age"`
CreatedAt time.Time `json:"created_at"`
}
// ====== 创建用户 ======
type CreateUserInput struct {
Body struct {
Name string `json:"name" maxLength:"100" minLength:"1"`
Email string `json:"email" format:"email"`
Age int `json:"age" minimum:"18" maximum:"120"`
}
}
type CreateUserOutput struct {
Body User
}
// ====== 获取用户 ======
type GetUserInput struct {
ID string `path:"id"`
}
type GetUserOutput struct {
Body User
}
// ====== 更新用户 ======
type UpdateUserInput struct {
ID string `path:"id"`
Body struct {
Name *string `json:"name,omitempty" maxLength:"100"`
Email *string `json:"email,omitempty" format:"email"`
Age *int `json:"age,omitempty" minimum:"18" maximum:"120"`
}
}
type UpdateUserOutput struct {
Body User
}
// ====== 删除用户 ======
type DeleteUserInput struct {
ID string `path:"id"`
}
type DeleteUserOutput struct {
Body struct{}
}
func main() {
if err := initDB(); err != nil {
log.Fatal("数据库初始化失败:", err)
}
router := http.NewServeMux()
api := humago.New(router, huma.DefaultConfig("User API", "1.0.0"))
// 创建用户
huma.Register(api, huma.Operation{
OperationID: "create-user",
Method: http.MethodPost,
Path: "/users",
Summary: "创建用户",
}, func(ctx context.Context, input *CreateUserInput) (*CreateUserOutput, error) {
user := User{
Name: input.Body.Name,
Email: input.Body.Email,
Age: input.Body.Age,
CreatedAt: time.Now(),
}
created, err := db.Create("user", user)
if err != nil {
return nil, err
}
output := &CreateUserOutput{}
if err := surrealdb.Unmarshal(created, &output.Body); err != nil {
return nil, err
}
return output, nil
})
// 获取用户
huma.Register(api, huma.Operation{
OperationID: "get-user",
Method: http.MethodGet,
Path: "/users/{id}",
Summary: "获取用户",
}, func(ctx context.Context, input *GetUserInput) (*GetUserOutput, error) {
result, err := db.Select(input.ID)
if err != nil {
return nil, err
}
output := &GetUserOutput{}
if err := surrealdb.Unmarshal(result, &output.Body); err != nil {
return nil, err
}
return output, nil
})
// 更新用户
huma.Register(api, huma.Operation{
OperationID: "update-user",
Method: http.MethodPatch,
Path: "/users/{id}",
Summary: "更新用户",
}, func(ctx context.Context, input *UpdateUserInput) (*UpdateUserOutput, error) {
// 构建更新数据
updateData := make(map[string]interface{})
if input.Body.Name != nil {
updateData["name"] = *input.Body.Name
}
if input.Body.Email != nil {
updateData["email"] = *input.Body.Email
}
if input.Body.Age != nil {
updateData["age"] = *input.Body.Age
}
result, err := db.Merge(input.ID, updateData)
if err != nil {
return nil, err
}
output := &UpdateUserOutput{}
if err := surrealdb.Unmarshal(result, &output.Body); err != nil {
return nil, err
}
return output, nil
})
// 删除用户
huma.Register(api, huma.Operation{
OperationID: "delete-user",
Method: http.MethodDelete,
Path: "/users/{id}",
Summary: "删除用户",
}, func(ctx context.Context, input *DeleteUserInput) (*DeleteUserOutput, error) {
_, err := db.Delete(input.ID)
if err != nil {
return nil, err
}
return &DeleteUserOutput{}, nil
})
log.Println("✅ 服务器启动在 http://localhost:8888")
log.Println("📚 访问文档: http://localhost:8888/docs")
http.ListenAndServe(":8888", router)
}
测试 API
# 1. 创建用户
curl -X POST http://localhost:8888/users \
-H "Content-Type: application/json" \
-d '{
"name": "Alice",
"email": "alice@example.com",
"age": 25
}'
# 响应
{
"id": "user:xxx",
"name": "Alice",
"email": "alice@example.com",
"age": 25,
"created_at": "2024-02-08T10:00:00Z"
}
# 2. 获取用户
curl http://localhost:8888/users/user:xxx
# 3. 更新用户
curl -X PATCH http://localhost:8888/users/user:xxx \
-H "Content-Type: application/json" \
-d '{
"age": 26
}'
# 4. 删除用户
curl -X DELETE http://localhost:8888/users/user:xxx
⚖️ Huma vs 其他框架
| 特性 | Huma | Gin | Echo | Fiber |
|---|---|---|---|---|
| OpenAPI 支持 | ✅ 原生 | ⚠️ 需要插件 | ⚠️ 需要插件 | ⚠️ 需要插件 |
| 自动文档 | ✅ 内置 Swagger UI | ❌ | ❌ | ❌ |
| 类型安全 | ✅ 编译时检查 | ⚠️ 运行时 | ⚠️ 运行时 | ⚠️ 运行时 |
| 性能 | ⚡ 零反射 | ⚡ 快 | ⚡ 快 | ⚡ 极快 |
| 学习曲线 | 📈 低 | 📈 低 | 📈 低 | 📈 中 |
何时选择 Huma?
✅ API 优先项目 - 需要高质量 API 文档
✅ 团队协作 - 前后端契约明确
✅ 类型安全 - 编译时捕获错误
✅ 快速原型 - 文档和代码同步生成
何时选择其他框架?
✅ 极致性能 - 选择 Fiber
✅ 简单内部 API - 选择 Gin/Echo
✅ 已有项目 - 保持技术栈一致
🎉 小结
Huma 的优势
- 开箱即用的 OpenAPI 支持 - 无需额外配置,文档自动生成
- 类型安全 - 编译时验证,减少运行时错误
- 简洁的 API - 代码清晰,易于维护
- 高性能 - 零反射设计,接近原生性能
完整项目结构
user-api/
├── main.go # 主程序
├── models/
│ └── user.go # 数据模型
├── handlers/
│ └── user_handlers.go # 处理器
├── db/
│ └── surreal.go # 数据库连接
└── go.mod # 依赖管理
💬 最后说两句
Huma + SurrealDB 的组合,就像给 Go Web 开发装上了"自动驾驶"——你只需要告诉它要去哪里(定义 API),剩下的路由、验证、文档、数据库操作,它都帮你搞定。
下次当你需要快速构建一个 RESTful API 时,不妨试试:
go get -u github.com/danielgtaylor/huma/v2
go get -u github.com/surrealdb/surrealdb.go
然后,享受类型安全、自动生成文档的快乐开发吧!🚀
小贴士:Huma 的设计理念是"约定优于配置",所以很多东西都是自动化的。刚开始可能会觉得"魔法太多",但一旦习惯了,你会发现开发效率提升了不少!