Skip to content

Send a CloudEvent over NATS

Publishing

nats.Header is defined as map[string][]string, the same type Marshal returns, so this is a type conversion rather than a copy:

headers, body, err := cloudevents.Marshal(e)
if err != nil {
    return err
}

err = conn.PublishMsg(&nats.Msg{
    Subject: "orders.created",
    Header:  nats.Header(headers),
    Data:    body,
})

Use go/nats's client rather than a bare nats.Conn. This module has no opinion about which you use, but that one is where the bounded subscriptions and the shed reporting live.

Subscribing

sub, err := client.Subscribe(ctx, "orders.created", func(msg *nats.Msg) {
    e, err := cloudevents.Unmarshal(msg.Header, msg.Data)
    if err != nil {
        refused.Add(1)
        logger.Error("refused a CloudEvent", "id", e.ID, "err", err)

        return
    }

    handle(e)
})

Route on the type, not on the subject

A NATS subject and a CloudEvent type say overlapping things, and the temptation is to encode the type into the subject and stop setting the attribute. Do not: the attribute is what makes the event readable by a consumer that is not on NATS, and the round trip through HTTP or an object store keeps it while the subject is lost at the first hop.

Publish to a subject that names the stream, and let type name the occurrence.

What this does not do

There is no Send(conn, subject, event) helper here, and there will not be one. It would have to import nats.go, which would put NATS in the dependency graph of every service that sends a CloudEvent over HTTP. Spec 0001 D4, and TestNoTransportIsImported enforces it.