Skip to content
Back to skills

Huma Endpoint

ASecurity

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

  • 666 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added February 9, 2026
developmentgogitapidocumentation

Works with

  • cursor
  • api

Security analysis

A100/100

Scanned February 10, 2026

npx -y skills add majiayu000/claude-skill-registry --skill huma-endpoint --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Huma Endpoint?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Huma Endpoint
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/majiayu000-huma-endpoint/badge)](https://www.skillsdirectory.com/skills/majiayu000-huma-endpoint)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
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`:

```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:

```go
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:

```go
// 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`:

```go
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:

```go
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

```go
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:

```go
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:

```go
// 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:

```go
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

| Method | Success Status | Use Case |
|--------|----------------|----------|
| GET    | 200 OK         | Retrieve resource(s) |
| POST   | 201 Created    | Create resource (include Location header) |
| PUT    | 200 OK         | Replace resource |
| PATCH  | 200 OK         | Partial update |
| DELETE | 204 No Content | Remove resource |

## Error Status Codes

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

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.