Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

schema.md

maoxiaoyue edited this page Mar 29, 2026 · 1 revision

pkg/schema — Schema-first 路由註冊

讓路由攜帶 metadata(輸入/輸出型別、描述、標籤),AI 無需追蹤 handler 實作即可理解 API 行為。

設計理念

傳統路由只告訴框架「這個 URL 對應哪個 handler」,AI 必須閱讀 handler 原始碼才能理解 API。Schema-first 路由讓 metadata 直接附著在路由上:

傳統:r.POST("/api/users", createUserHandler) ← AI 需讀 handler 原始碼
Schema:r.Schema(Route{...}).Handle(handler) ← AI 直接從 metadata 理解

快速上手

import "github.com/maoxiaoyue/hypgo/pkg/schema"
// 定義請求/回應型別
type CreateUserRequest struct {
 Name string `json:"name"`
 Email string `json:"email"`
}
type UserResponse struct {
 ID int `json:"id"`
 Name string `json:"name"`
 Email string `json:"email"`
}
// 註冊帶 schema 的路由
r := router.New()
r.Schema(schema.Route{
 Method: "POST",
 Path: "/api/users",
 Summary: "建立使用者",
 Tags: []string{"users"},
 Input: CreateUserRequest{},
 Output: UserResponse{},
 Responses: map[int]schema.ResponseSchema{
 201: {Description: "User created"},
 400: {Description: "Invalid input"},
 },
}).Handle(createUserHandler)

搭配路由群組

api := r.NewGroup("/api/v1")
api.Schema(schema.Route{
 Method: "GET",
 Path: "/products",
 Summary: "取得商品列表",
 Tags: []string{"products"},
 Output: []ProductResponse{},
}).Handle(listProductsHandler)
// 路由自動註冊為 GET /api/v1/products

混合使用

Schema 路由與傳統路由可以並存,完全向後相容:

r.GET("/health", healthHandler) // 傳統路由 — 照常使用
r.Schema(schema.Route{...}).Handle(createHandler) // Schema 路由 — 附帶 metadata

核心型別

Route

type Route struct {
 Method string // HTTP 方法(GET, POST, PUT, DELETE...)
 Path string // 路由路徑(支援 :param 和 *catchall)
 Summary string // 一行描述(給 AI 和文件用)
 Description string // 詳細描述
 Tags []string // 分類標籤
 Input interface{} // 請求 body 的 Go struct(用於驗證)
 Output interface{} // 回應 body 的 Go struct(用於驗證)
 InputName string // 自動填入,無需手動設定
 OutputName string // 自動填入,無需手動設定
 Params []ParamSchema // 路徑/查詢參數描述
 Headers []HeaderSchema // 必要標頭描述
 Responses map[int]ResponseSchema // 各狀態碼的回應描述
}

ParamSchema

type ParamSchema struct {
 Name string // 參數名稱
 In string // "path", "query", "header"
 Required bool // 是否必填
 Type string // "string", "int", "bool"
 Desc string // 描述
}

ResponseSchema

type ResponseSchema struct {
 Description string // 回應描述
 Type interface{} // Go struct type(用於 Contract Testing 驗證)
 TypeName string // 自動填入
}

Registry(全域註冊表)

所有透過 Schema() 註冊的路由都會自動存入全域 Registry:

// 查詢特定路由的 schema
route, ok := schema.Global().Get("POST", "/api/users")
// 取得所有已註冊的 schema
all := schema.Global().All()
// 查看已註冊數量
count := schema.Global().Len()
// 清空(測試用)
schema.Global().Reset()

Registry 為 thread-safe,使用 sync.RWMutex 保護。

反射工具

提供型別內省工具,供 Manifest 和 Contract Testing 使用:

// 取得型別名稱
schema.TypeName(UserResponse{}) // → "UserResponse"
// 取得 struct 欄位資訊
fields := schema.FieldsOf(UserResponse{})
// → [{Name:"id", Type:"integer", Required:true}, ...]
// 驗證 JSON 是否符合 struct(含必填欄位檢查)
err := schema.ValidateJSON([]byte(`{"name":"alice"}`), CreateUserRequest{})
// 生成零值 JSON(用於自動測試)
data := schema.GenerateZeroJSON(CreateUserRequest{})
// → {"name":"","email":""}

Required 判定規則

條件 Required
Name string \json:"name"`` ✅ 是
Email string \json:"email,omitempty"`` ❌ 否(omitempty)
Bio *string \json:"bio"`` ❌ 否(pointer)
json:"-" 跳過(不出現)

架構

pkg/schema/
├── schema.go Route、SchemaRoute、ParamSchema 等型別定義
├── registry.go 全域 thread-safe Registry
├── reflect.go TypeName、FieldsOf、ValidateJSON 反射工具
└── schema_test.go 18 個單元測試

依賴關係

pkg/schema ← 僅依賴 pkg/context(HandlerFunc 型別)
 ← 不依賴 pkg/router(避免迴圈依賴)

SchemaRegistrar 介面由 schema 定義,Router 和 Group 實作,避免迴圈依賴。

測試

go test ./pkg/schema/... -v

涵蓋:TypeName、FieldsOf、ValidateJSON、GenerateZeroJSON、Registry CRUD、SchemaRoute builder、goTypeToSchemaType。

HypGo

繁體中文 | English


中文文件

設計文件

套件

AI 協作工具鏈

CLI 命令


English Docs

Design Docs

Packages

AI Collaboration Toolchain

CLI Commands

Clone this wiki locally

AltStyle によって変換されたページ (->オリジナル) /