返回 Skill 列表
extension
分类: 开发与工程无需 API Key

go-api-gateway

使用Fiber/Chi在Go语言中构建AgentStack API网关。用于创建REST API端点、HTTP处理器、中间件、请求验证和API路由。当触发“构建API”、“创建端点”、“HTTP处理器”、“REST API”、“API网关”、“Fiber处理器”、“Chi路由器”,或实现spec/004-api-design.md中的端点时激活。

person作者: jakexiaohubgithub

Go API Gateway

Overview

Build production-grade REST API endpoints for the AgentStack platform using Go with Fiber or Chi frameworks, following the specifications in /spec/004-api-design.md.

Project Structure

cmd/api/main.go              # Entry point
internal/api/
├── handlers/                # HTTP handlers by domain
│   ├── agents.go
│   ├── chat.go
│   ├── deployments.go
│   └── health.go
├── middleware/              # Cross-cutting concerns
│   ├── auth.go
│   ├── logging.go
│   ├── ratelimit.go
│   └── tracing.go
├── routes/                  # Route definitions
│   └── router.go
├── dto/                     # Request/Response DTOs
│   └── agents.go
└── server.go               # Server configuration

Core Workflow

Step 1: Define DTOs

Create request/response structures with validation tags:

// internal/api/dto/agents.go
package dto

import "time"

type CreateAgentRequest struct {
    Name       string            `json:"name" validate:"required,min=3,max=64"`
    Framework  string            `json:"framework" validate:"required,oneof=google-adk langchain crewai"`
    Config     AgentConfig       `json:"config" validate:"required"`
    AutoDeploy bool              `json:"auto_deploy"`
}

type AgentConfig struct {
    Model        string   `json:"model" validate:"required"`
    SystemPrompt string   `json:"system_prompt"`
    Tools        []string `json:"tools"`
}

type AgentResponse struct {
    ID        string    `json:"id"`
    Name      string    `json:"name"`
    Status    string    `json:"status"`
    URLs      AgentURLs `json:"urls,omitempty"`
    CreatedAt time.Time `json:"created_at"`
}

type AgentURLs struct {
    Chat string `json:"chat,omitempty"`
}

Step 2: Create Handler

// internal/api/handlers/agents.go
package handlers

import (
    "github.com/gofiber/fiber/v2"
    "github.com/raphaelmansuy/agentstack/internal/api/dto"
    "github.com/raphaelmansuy/agentstack/internal/domain/agent"
)

type AgentHandler struct {
    service *agent.Service
}

func NewAgentHandler(svc *agent.Service) *AgentHandler {
    return &AgentHandler{service: svc}
}

// CreateAgent godoc
// @Summary Create a new agent
// @Tags agents
// @Accept json
// @Produce json
// @Param request body dto.CreateAgentRequest true "Agent configuration"
// @Success 201 {object} dto.AgentResponse
// @Failure 400 {object} dto.ErrorResponse
// @Router /v1/agents [post]
func (h *AgentHandler) CreateAgent(c *fiber.Ctx) error {
    var req dto.CreateAgentRequest
    if err := c.BodyParser(&req); err != nil {
        return fiber.NewError(fiber.StatusBadRequest, "Invalid request body")
    }

    // Validate request
    if err := validate.Struct(req); err != nil {
        return c.Status(fiber.StatusBadRequest).JSON(dto.ValidationError(err))
    }

    // Get project ID from context (set by auth middleware)
    projectID := c.Locals("projectID").(string)

    // Create agent
    agt, err := h.service.Create(c.Context(), projectID, agent.CreateInput{
        Name:       req.Name,
        Framework:  req.Framework,
        Config:     req.Config,
        AutoDeploy: req.AutoDeploy,
    })
    if err != nil {
        return handleDomainError(c, err)
    }

    return c.Status(fiber.StatusCreated).JSON(toAgentResponse(agt))
}

// ListAgents godoc
// @Summary List agents in project
// @Tags agents
// @Produce json
// @Param cursor query string false "Pagination cursor"
// @Param limit query int false "Items per page" default(20)
// @Success 200 {object} dto.AgentListResponse
// @Router /v1/agents [get]
func (h *AgentHandler) ListAgents(c *fiber.Ctx) error {
    projectID := c.Locals("projectID").(string)
    cursor := c.Query("cursor")
    limit := c.QueryInt("limit", 20)

    agents, nextCursor, err := h.service.List(c.Context(), projectID, cursor, limit)
    if err != nil {
        return handleDomainError(c, err)
    }

    return c.JSON(dto.AgentListResponse{
        Data:       toAgentResponses(agents),
        NextCursor: nextCursor,
    })
}

Step 3: Register Routes

// internal/api/routes/router.go
package routes

import (
    "github.com/gofiber/fiber/v2"
    "github.com/raphaelmansuy/agentstack/internal/api/handlers"
    "github.com/raphaelmansuy/agentstack/internal/api/middleware"
)

func SetupRoutes(app *fiber.App, deps *Dependencies) {
    // Health endpoints (no auth)
    app.Get("/health", handlers.Health)
    app.Get("/ready", handlers.Ready)

    // API v1 group
    v1 := app.Group("/v1")
    
    // Apply middleware
    v1.Use(middleware.RequestID())
    v1.Use(middleware.Logger())
    v1.Use(middleware.Tracing())
    v1.Use(middleware.Auth(deps.AuthService))
    v1.Use(middleware.RateLimit(deps.RateLimiter))

    // Agent routes
    agents := v1.Group("/agents")
    agentHandler := handlers.NewAgentHandler(deps.AgentService)
    agents.Post("/", agentHandler.CreateAgent)
    agents.Get("/", agentHandler.ListAgents)
    agents.Get("/:agentId", agentHandler.GetAgent)
    agents.Patch("/:agentId", agentHandler.UpdateAgent)
    agents.Delete("/:agentId", agentHandler.DeleteAgent)

    // Nested routes
    agents.Post("/:agentId/chat", agentHandler.Chat)
    agents.Get("/:agentId/chat/stream", agentHandler.ChatStream)
    agents.Get("/:agentId/deployments", agentHandler.ListDeployments)
    agents.Post("/:agentId/deployments", agentHandler.CreateDeployment)
}

Step 4: Implement Middleware

See references/middleware-patterns.md for:

  • Authentication middleware
  • Rate limiting
  • Request tracing
  • Error handling

Step 5: Add SSE Streaming

// For streaming endpoints (chat, events)
func (h *AgentHandler) ChatStream(c *fiber.Ctx) error {
    c.Set("Content-Type", "text/event-stream")
    c.Set("Cache-Control", "no-cache")
    c.Set("Connection", "keep-alive")

    agentID := c.Params("agentId")
    
    c.Context().SetBodyStreamWriter(func(w *bufio.Writer) {
        for event := range h.service.StreamChat(c.Context(), agentID) {
            fmt.Fprintf(w, "event: %s\n", event.Type)
            fmt.Fprintf(w, "data: %s\n\n", event.Data)
            w.Flush()
        }
    })
    
    return nil
}

Error Handling

Follow RFC 7807 Problem Details:

// internal/api/dto/errors.go
type ErrorResponse struct {
    Type     string            `json:"type"`
    Title    string            `json:"title"`
    Status   int               `json:"status"`
    Detail   string            `json:"detail"`
    Instance string            `json:"instance,omitempty"`
    TraceID  string            `json:"trace_id,omitempty"`
    Errors   []ValidationError `json:"errors,omitempty"`
}

func NewError(c *fiber.Ctx, status int, title, detail string) error {
    return c.Status(status).JSON(ErrorResponse{
        Type:     errorTypeURL(status),
        Title:    title,
        Status:   status,
        Detail:   detail,
        Instance: c.Path(),
        TraceID:  c.Locals("traceID").(string),
    })
}

Validation

Use go-playground/validator:

import "github.com/go-playground/validator/v10"

var validate = validator.New()

func init() {
    // Custom validators
    validate.RegisterValidation("agent_name", validateAgentName)
}

Testing

// internal/api/handlers/agents_test.go
func TestCreateAgent(t *testing.T) {
    app := fiber.New()
    // Setup mock services
    svc := mocks.NewAgentService(t)
    handler := NewAgentHandler(svc)
    
    app.Post("/v1/agents", handler.CreateAgent)

    req := httptest.NewRequest("POST", "/v1/agents", strings.NewReader(`{
        "name": "test-agent",
        "framework": "google-adk",
        "config": {"model": "gpt-4"}
    }`))
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("X-Project-ID", "prj_test123")

    resp, _ := app.Test(req)
    assert.Equal(t, 201, resp.StatusCode)
}

Resources

  • references/middleware-patterns.md - Auth, logging, rate limiting patterns
  • references/sse-streaming.md - Server-Sent Events implementation
  • assets/handler-template.go - Handler boilerplate template