Skip to content

docs\en\how_to_schema.md

maoxiaoyue edited this page May 14, 2026 · 1 revision

pkg/schema — Schema-first Route Registration (Multi-Protocol)

Attach metadata (input/output types, descriptions, tags) directly to routes so AI understands API behavior without reading handler source code. Supports six protocols: REST, gRPC, Bot, MCP, WebSocket, and CLI.

Design Philosophy

Traditional routing tells the framework only "this URL maps to this handler" — AI must read the handler source to understand the API. Schema-first routing embeds metadata at registration time:

Traditional: r.POST("/api/users", createUserHandler)    ← AI must read handler source
Schema:      r.Schema(Route{...}).Handle(handler)        ← AI reads metadata directly

Quick Start

import "github.com/maoxiaoyue/hypgo/pkg/schema"

// Define request/response types
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"`
}

// Register route with schema
r := router.New()

r.Schema(schema.Route{
    Method:  "POST",
    Path:    "/api/users",
    Summary: "Create user",
    Tags:    []string{"users"},
    Input:   CreateUserRequest{},
    Output:  UserResponse{},
    Responses: map[int]schema.ResponseSchema{
        201: {Description: "User created"},
        400: {Description: "Invalid input"},
    },
}).Handle(createUserHandler)

With Route Groups

api := r.NewGroup("/api/v1")

api.Schema(schema.Route{
    Method:  "GET",
    Path:    "/products",
    Summary: "List products",
    Tags:    []string{"products"},
    Output:  []ProductResponse{},
}).Handle(listProductsHandler)

// Route is automatically registered as GET /api/v1/products

Mixed Usage

Schema routes and traditional routes can coexist — fully backward compatible:

r.GET("/health", healthHandler)                   // Traditional route — works as usual
r.Schema(schema.Route{...}).Handle(createHandler)  // Schema route — carries metadata

Multi-Protocol Schema Registration

v0.8.5+ Multi-protocol support (gRPC / Bot / MCP / WebSocket / CLI) is new in v0.8.5. v0.8.1 supports REST only.

Beyond REST, Schema supports registering command contracts for other protocols directly:

// gRPC
schema.RegisterGRPC("UserService/CreateUser", "Create user", CreateUserReq{}, UserResp{})

// Bot (Telegram, Line, Discord, WhatsApp, etc.)
schema.RegisterBot("/start", "Start the bot", nil, WelcomeMsg{})
schema.RegisterBotPlatform("telegram", "/game", "Start game", GameOpts{}, GameState{})
schema.RegisterBotPlatform("discord", "!play", "Play music", PlayReq{}, PlayResp{})

// MCP (Model Context Protocol)
schema.RegisterMCP("search_repos", "Search repos", SearchInput{}, SearchOutput{})

// WebSocket message types
schema.RegisterWebSocket("join_room", "Join room", JoinReq{}, JoinResp{})

// CLI subcommands
schema.RegisterCLI("process", "Process data", ProcessFlags{}, ProcessResult{})

All protocols share a single Schema Registry and Manifest — AI reads one manifest to understand all APIs.

Core Types

Route (Multi-Protocol)

type Route struct {
    // Common fields (shared across all protocols)
    Protocol    string                 // "rest"(default), "grpc", "bot", "mcp", "websocket", "cli"
    Command     string                 // Non-REST command identifier (gRPC method, bot command, MCP tool...)
    Platform    string                 // Bot-specific platform: "telegram", "line", "discord", "whatsapp"
    Summary     string                 // One-line description (for AI and documentation)
    Description string                 // Detailed description
    Tags        []string               // Category tags
    Input       interface{}            // Request Go struct (used for validation)
    Output      interface{}            // Response Go struct (used for validation)
    InputName   string                 // Auto-populated, no need to set manually
    OutputName  string                 // Auto-populated, no need to set manually

    // REST-specific (backward compatible)
    Method      string                 // HTTP method (GET, POST, PUT, DELETE...)
    Path        string                 // Route path (supports :param and *catchall)
    Params      []ParamSchema          // Path/query parameter descriptions
    Headers     []HeaderSchema         // Required request header descriptions
    Responses   map[int]ResponseSchema // Response descriptions per status code
}

// RouteKey() returns the Registry lookup key:
// REST:  "rest|POST /api/users"
// gRPC:  "grpc|UserService/CreateUser"
// Bot:   "bot|/start"
// MCP:   "mcp|search_repos"

// IsREST() reports whether this is a REST route

Supported Protocols

Protocol Command Format Example
rest Method + Path (auto) POST /api/users
grpc Service/Method UserService/CreateUser
bot /command or event name /start, follow, !play
mcp tool_name search_repos, create_issue
websocket message_type join_room, send_message
cli command_name process, export

ParamSchema

type ParamSchema struct {
    Name     string // Parameter name
    In       string // "path", "query", "header"
    Required bool   // Whether required
    Type     string // "string", "int", "bool"
    Desc     string // Description
}

ResponseSchema

type ResponseSchema struct {
    Description string      // Response description
    Type        interface{} // Go struct type (used for Contract Testing validation)
    TypeName    string      // Auto-populated
}

Registry (Global Registry)

All routes registered via Schema() or Register*() are stored in the global Registry:

// Query REST route (backward compatible)
route, ok := schema.Global().Get("POST", "/api/users")

// Query by full key (any protocol)
route, ok := schema.Global().GetByKey("grpc|UserService/CreateUser")
route, ok := schema.Global().GetByKey("bot|/start")

// Filter by protocol
grpcRoutes := schema.Global().GetByProtocol("grpc")
botRoutes  := schema.Global().GetByProtocol("bot")

// Get all registered schemas (all protocols)
all := schema.Global().All()

// Check registered count
count := schema.Global().Len()

// Clear (for testing)
schema.Global().Reset()

The Registry is thread-safe, protected by sync.RWMutex.

Reflection Utilities

Type introspection tools used by Manifest and Contract Testing:

// Get type name
schema.TypeName(UserResponse{})  // → "UserResponse"

// Get struct field info
fields := schema.FieldsOf(UserResponse{})
// → [{Name:"id", Type:"integer", Required:true}, ...]

// Validate JSON against struct (including required field checks)
err := schema.ValidateJSON([]byte(`{"name":"alice"}`), CreateUserRequest{})

// Generate zero-value JSON (used for automatic testing)
data := schema.GenerateZeroJSON(CreateUserRequest{})
// → {"name":"","email":""}

Required Field Detection Rules

Condition Required
Name string \json:"name"`` ✅ Yes
Email string \json:"email,omitempty"`` ❌ No (omitempty)
Bio *string \json:"bio"`` ❌ No (pointer)
json:"-" Skipped (not present)

Architecture

pkg/schema/
├── schema.go       Route (multi-protocol), SchemaRoute, ParamSchema type definitions
├── registry.go     Global thread-safe Registry (supports RouteKey multi-protocol queries)
├── helpers.go      RegisterGRPC/Bot/MCP/WebSocket/CLI convenience functions
├── reflect.go      TypeName, FieldsOf, ValidateJSON reflection utilities
└── schema_test.go  Unit tests

Dependency Graph

pkg/schema ← depends only on pkg/context (HandlerFunc type)
           ← does NOT depend on pkg/router (avoids circular dependency)

The SchemaRegistrar interface is defined in schema, implemented by Router and Group, avoiding circular dependencies.

HypGo

繁體中文 | English


中文文件

設計文件

套件

AI 協作工具鏈

CLI 命令


English Docs

Design Docs

Packages

AI Collaboration Toolchain

CLI Commands

Clone this wiki locally