Skip to content

schema_en.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) to routes so that AI can understand API behavior without tracing handler implementations. Supports REST, gRPC, Bot, MCP, WebSocket, and CLI protocols.

Design Philosophy

Traditional routes only tell the framework "which handler this URL maps to," requiring AI to read handler source code to understand the API. Schema-first routes attach metadata directly to routes:

Traditional: r.POST("/api/users", createUserHandler)     <- AI needs to read handler source
Schema:      r.Schema(Route{...}).Handle(handler)        <- AI understands directly from metadata

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 a 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: "Get product list",
    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 with full backward compatibility:

r.GET("/health", healthHandler)                    // Traditional route -- works as usual
r.Schema(schema.Route{...}).Handle(createHandler)   // Schema route -- with 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:

// 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 the same Schema Registry and Manifest — AI reads one manifest to understand all APIs.

Core Types

Route (Multi-Protocol)

type Route struct {
    // Universal fields (all protocols)
    Protocol    string                 // "rest"(default), "grpc", "bot", "mcp", "websocket", "cli"
    Command     string                 // Non-REST command identifier
    Platform    string                 // Bot-specific platform: "telegram", "line", "discord", "whatsapp"
    Summary     string                 // One-line description (for AI and documentation)
    Description string                 // Detailed description
    Tags        []string               // Classification tags
    Input       interface{}            // Request Go struct (for validation)
    Output      interface{}            // Response Go struct (for validation)
    InputName   string                 // Auto-populated, no manual setup needed
    OutputName  string                 // Auto-populated, no manual setup needed

    // 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 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() returns true if 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 (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()

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

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

Reflection Utilities

Provides type introspection tools for use by Manifest and Contract Testing:

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

// Get struct field information
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 (for automated testing)
data := schema.GenerateZeroJSON(CreateUserRequest{})
// -> {"name":"","email":""}

Required Field 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 lookup)
├── helpers.go      RegisterGRPC/Bot/MCP/WebSocket/CLI convenience functions
├── reflect.go      TypeName, FieldsOf, ValidateJSON reflection utilities
└── schema_test.go  Unit tests

Dependencies

pkg/schema <- Only depends on pkg/context (HandlerFunc type)
           <- Does not depend on pkg/router (avoids circular dependency)

The SchemaRegistrar interface is defined by 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