Skip to content

Huma ignores custom body marshaler #789

Open
@akhayyat

Description

@akhayyat

I can't get Huma to invoke response body custom marshalers.

Here is a minimal reproduction:

package main

import (
	"context"
	"encoding/json"
	"net/http"

	"github.com/danielgtaylor/huma/v2"
	"github.com/danielgtaylor/huma/v2/adapters/humago"
	"github.com/google/uuid"
	"github.com/mr-tron/base58"
)

func main() {
	router := http.NewServeMux()
	api := humago.New(router, huma.DefaultConfig("Marshal Test", "1"))

	huma.Get(api, "/auto", handler)
	huma.Get(api, "/manual", manualMarshal)
	http.ListenAndServe(":8081", router)
}

type Output struct {
	Body Body
}

type Body struct {
	ID      uuid.UUID `json:"id"`
	Message string    `json:"message"`
}

type Output2 struct {
	Body []byte
}

func handler(ctx context.Context, i *struct{}) (*Output, error) {
	output := Output{
		Body: Body{
			ID:      uuid.New(),
			Message: "Hello",
		},
	}
	return &output, nil
}

func manualMarshal(ctx context.Context, i *struct{}) (*Output2, error) {
	body := Body{
		ID:      uuid.New(),
		Message: "Hello",
	}
	bodyJSON, err := json.Marshal(body)
	return &Output2{Body: bodyJSON}, err
}

// Convert ID from UUID to base58 string
func (b Body) MarshalJSON() ([]byte, error) {
	type BodyAlias Body
	type BodyJSON struct {
		ID string `json:"id"`
		BodyAlias
	}
	idB58, err := uuidToBase58(b.ID)
	if err != nil {
		return nil, err
	}
	return json.Marshal(BodyJSON{
		ID:        idB58,
		BodyAlias: BodyAlias(b),
	})
}

func uuidToBase58(id uuid.UUID) (string, error) {
	binID, err := id.MarshalBinary()
	if err != nil {
		return "", err
	}
	return base58.Encode(binID), nil
}

Basically, I have a custom marshaler that converts the UUID ID field to a base58-encoded string, and there are two handlers: one relying on Huma to invoke the custom marshaler (/auto), and one invoking the custom marshaler manually and returning a json-encoded byte array (/manual).

Calling the handler whose output body has a custom marshaler ignores the marshaler and returns unencoded UUIDs:

» restish :8081/auto
HTTP/1.1 200 OK
Content-Length: 116
Content-Type: application/json
Date: Fri, 04 Apr 2025 14:14:49 GMT
Link: </schemas/Body.json>; rel="describedBy"

{
  $schema: "http://localhost:8081/schemas/Body.json"
  id: "abd2feee-569b-4a47-b360-9bfdfcf456de"
  message: "Hello"
}

Calling the handler that manually marshals its output does encode the ID to a base58 string, demonstrating that the custom marshaler works:

» restish :8081/manual
HTTP/1.1 200 OK
Content-Length: 49
Content-Type: text/plain; charset=utf-8
Date: Fri, 04 Apr 2025 14:14:53 GMT

{"id":"QS6Y2UJyGKk4PGpHn7d2Bf","message":"Hello"}

How can I get Huma to recognize and invoke custom marshalers on the output body or sub-structures thereof?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions