スキル一覧に戻る
janisto

huma-endpoint

by janisto

Huma playground

0🍴 0📅 2026年1月25日
GitHubで見るManusで実行

SKILL.md


name: huma-endpoint description: Guide for creating Huma API endpoints following this project's conventions including routing, input/output structs, error handling, and OpenAPI documentation.

Huma Endpoint Creation

Use this skill when creating new API endpoints for this Huma REST API application.

For comprehensive coding guidelines, see AGENTS.md in the repository root.

Router Setup

Create route handlers in internal/http/v1/ and register them in routes.go:

// internal/http/v1/routes/routes.go
func Register(api huma.API) {
    hello.Register(api)
    items.Register(api)
    // Add new routes here
}

Note: The health endpoint is a plain HTTP handler registered at the root level in main.go, not via Huma.

Output Struct Pattern

Use plain structs with a Body field for the response payload:

import "github.com/janisto/huma-playground/internal/platform/timeutil"

// ResourceData models the response payload.
type ResourceData struct {
    ID        string      `json:"id"        doc:"Unique identifier"   example:"res-001"`
    Name      string      `json:"name"      doc:"Display name"        example:"My Resource"`
    CreatedAt timeutil.Time `json:"createdAt" doc:"Creation timestamp"  example:"2024-01-15T10:30:00.000Z"`
}

// ResourceOutput is the response wrapper.
type ResourceOutput struct {
    Body ResourceData
}

Input Struct Pattern

Always prefix input types with the resource name for consistency:

// Path parameters
type ResourceGetInput struct {
    ID string `path:"id" doc:"Resource identifier" example:"res-001"`
}

// Query parameters
type ResourceListInput struct {
    Status string `query:"status" doc:"Filter by status" example:"active" enum:"active,inactive"`
    Limit  int    `query:"limit"  doc:"Maximum items"    example:"10"     minimum:"1" maximum:"100"`
}

// Request body
type ResourceCreateInput struct {
    Body struct {
        Name string `json:"name" doc:"Resource name" example:"New Resource" minLength:"1" maxLength:"100"`
    }
}

GET Endpoint

Simple retrieval endpoints use huma.Get:

func registerResource(api huma.API) {
    huma.Get(api, "/resources/{id}", func(ctx context.Context, input *ResourceGetInput) (*ResourceOutput, error) {
        resource, err := getResource(input.ID)
        if err != nil {
            return nil, huma.Error404NotFound("resource not found")
        }
        return &ResourceOutput{Body: resource}, nil
    })
}

POST Endpoint with 201 Created

Use huma.Register for operations requiring custom configuration:

type CreateResourceOutput struct {
    Location string `header:"Location" doc:"URL of created resource"`
    Body     ResourceData
}

huma.Register(api, huma.Operation{
    OperationID:   "create-resource",
    Method:        http.MethodPost,
    Path:          "/resources",
    Summary:       "Create a new resource",
    Description:   "Creates a new resource and returns its data.",
    DefaultStatus: http.StatusCreated,
    Tags:          []string{"Resources"},
}, func(ctx context.Context, input *ResourceCreateInput) (*CreateResourceOutput, error) {
    resource := createResource(input.Body.Name)
    return &CreateResourceOutput{
        Location: fmt.Sprintf("/resources/%s", resource.ID),
        Body:     resource,
    }, nil
})

PUT/PATCH Endpoint

type UpdateResourceInput struct {
    ID   string `path:"id"`
    Body struct {
        Name string `json:"name" doc:"Updated name" minLength:"1" maxLength:"100"`
    }
}

huma.Register(api, huma.Operation{
    OperationID: "update-resource",
    Method:      http.MethodPut,
    Path:        "/resources/{id}",
    Summary:     "Update a resource",
    Tags:        []string{"Resources"},
}, func(ctx context.Context, input *UpdateResourceInput) (*ResourceOutput, error) {
    resource, err := updateResource(input.ID, input.Body.Name)
    if err != nil {
        return nil, huma.Error404NotFound("resource not found")
    }
    return &ResourceOutput{Body: resource}, nil
})

DELETE Endpoint

Return 204 No Content for successful deletions:

huma.Register(api, huma.Operation{
    OperationID:   "delete-resource",
    Method:        http.MethodDelete,
    Path:          "/resources/{id}",
    Summary:       "Delete a resource",
    DefaultStatus: http.StatusNoContent,
    Tags:          []string{"Resources"},
}, func(ctx context.Context, input *ResourceInput) (*struct{}, error) {
    if err := deleteResource(input.ID); err != nil {
        return nil, huma.Error404NotFound("resource not found")
    }
    return nil, nil
})

Error Handling

Use Huma's built-in error helpers for RFC 9457 Problem Details:

// Common error responses
huma.Error400BadRequest("invalid request")
huma.Error403Forbidden("access denied")
huma.Error404NotFound("resource not found")
huma.Error422UnprocessableEntity("validation failed", fieldErrors...)
huma.Error500InternalServerError("internal error")

// Custom status codes
huma.NewError(http.StatusTeapot, "custom message")
huma.NewError(http.StatusConflict, "resource already exists")

Logging

Use context-aware logging helpers:

import (
    "go.uber.org/zap"
    applog "github.com/janisto/huma-playground/internal/platform/logging"
)

func handler(ctx context.Context, input *Input) (*Output, error) {
    applog.LogInfo(ctx, "processing request", zap.String("id", input.ID))

    if err != nil {
        applog.LogError(ctx, "operation failed", err, zap.String("id", input.ID))
        return nil, huma.Error500InternalServerError("operation failed")
    }

    return &Output{Body: result}, nil
}

Field Documentation

Every field MUST have:

  • doc: tag for OpenAPI description
  • example: tag for OpenAPI examples

Validation tags:

  • minLength, maxLength for strings
  • minimum, maximum for numbers
  • enum: for allowed values
  • pattern: for regex validation

Status Code Reference

MethodSuccess StatusUse Case
GET200 OKRetrieve resource(s)
POST201 CreatedCreate resource (include Location header)
PUT200 OKReplace resource
PATCH200 OKPartial update
DELETE204 No ContentRemove resource

Error Status Codes

StatusUse Case
400Malformed syntax, invalid cursor
401Missing authentication
403Authenticated but not authorized
404Resource not found
409Conflict (duplicate resource)
422Validation failures
500Unexpected server error

スコア

総合スコア

60/100

リポジトリの品質指標に基づく評価

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

+10
説明文

100文字以上の説明がある

0/10
人気

GitHub Stars 100以上

0/15
最近の活動

3ヶ月以内に更新がある

0/10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

0/5

レビュー

💬

レビュー機能は近日公開予定です