Custom codec (MessagePack)
Consumer and Producer are functions; adding a codec for a new
wire format is just writing two of them and registering them under
the right MIME type. This page uses
github.com/vmihailenco/msgpack/v5
as the worked example because it’s the most widely-used Go MessagePack
implementation; any third-party codec works the same way.
Pick a Content-Type
MessagePack has no IANA-registered MIME. Two conventions are common:
application/x-msgpack(olderx-style)application/msgpack(newer)
Pick one and stick to it across spec, server registration and client
expectation. The examples below use application/x-msgpack.
The Consumer + Producer pair
// Mime is the content-type the recipe registers under. MessagePack has
// no IANA-registered MIME; application/x-msgpack and application/msgpack
// are both common — pick one and stick to it.
const Mime = "application/x-msgpack"
// Consumer returns a runtime.Consumer that decodes a MessagePack body
// into the target value v.
func Consumer() runtime.Consumer {
return runtime.ConsumerFunc(func(r io.Reader, v any) error {
return msgpack.NewDecoder(r).Decode(v)
})
}
// Producer returns a runtime.Producer that serialises v as MessagePack
// onto w.
func Producer() runtime.Producer {
return runtime.ProducerFunc(func(w io.Writer, v any) error {
return msgpack.NewEncoder(w).Encode(v)
})
}Full source: docs/examples/contenttypes/customcodec/main.go
Two-line implementations are typical; the runtime never inspects codec internals. Anything more sophisticated (configurable encoder options, format-specific error wrapping) goes inside the closure.
Register on the server
Spec — declare the new MIME under consumes / produces:
Wire it up:
api := untyped.NewAPI(doc).WithJSONDefaults() // JSON codecs registered for free
api.RegisterConsumer(Mime, Consumer())
api.RegisterProducer(Mime, Producer())Full source: docs/examples/contenttypes/customcodec/main.go
The runtime now picks MessagePack whenever the inbound Content-Type
matches and the route lists application/x-msgpack under consumes,
or Accept: application/x-msgpack selects it from produces.
Register on the client
rt := client.New("api.example.com", "/v1", []string{"https"})
rt.Consumers[Mime] = Consumer()
rt.Producers[Mime] = Producer()Full source: docs/examples/contenttypes/customcodec/main.go
For an individual call, set the operation’s content-type lists:
op.ConsumesMediaTypes = []string{Mime}
op.ProducesMediaTypes = []string{Mime}Full source: docs/examples/contenttypes/customcodec/main.go
Exercise
A request with Content-Type outside the operation’s consumes
list yields 415 Unsupported Media Type; an Accept outside
produces yields 406 Not Acceptable. See
server / pipeline
for the full failure-mode mapping.
Variations
- Vendor MIME types (
application/vnd.acme.v1+msgpack) need separate registrations even when they delegate to the same codec — see vendor types. - Streaming bodies:
Consumer/Producerget anio.Reader/io.Writerdirectly, so streaming codecs work the same way. The streaming bodies page covers raw-byte payloads and theClosesStreamoption.