Response envelope

Envelope<T> is Conta’s standardized API response envelope: a response payload plus optional pagination, tracing, and HATEOAS metadata.

Envelope<T> is the contract; the concrete shapes are SingleEnvelope<T> (a single result) and SliceEnvelope<E> (a paginated list), sharing the EnvelopeAdapter<T> base. They are plain Lombok (@Data) classes in the no.conta.http package — java.util + Lombok only, with no Jackson or Micronaut dependency and no serialization annotations. cmdUuid is a UUID; meta and _related are Map<String, Object>; _links is Map<String, URI>.

Shape

Every field except content is optional. Absent fields are omitted from the serialized JSON.

The envelope keys come from default, as-declared property naming. A global Jackson naming strategy (e.g. SNAKE_CASE) would rename them and emit a non-conforming envelope — keep default naming for envelope serialization.
{
  "content": { },
  "tenantId": "_org123",
  "cmdUuid": "a1b2c3d4-e5f6-7890-1234-567890abcdef",
  "pageNumber": 0,
  "pageSize": 10,
  "meta": { },
  "_related": { },
  "_links": { "self": "https://..." }
}

content is the payload; everything else is optional metadata. cmdUuid appears only on command-triggered responses, pageNumber/pageSize only on paginated ones (pageNumber is zero-based). meta and _related are free-form per-endpoint maps; _links maps rel to a URI. Use the Meta and Links builders (Meta.of(…​), Links.self(…​) / Links.of(…​)) to construct these maps.

Building an envelope

A single result wraps its payload with SingleEnvelope.of:

return SingleEnvelope.of(widget);

The of(…​) factories cover the common cases; optional metadata is set on the created envelope:

var envelope = SingleEnvelope.of(widget, tenantId);
envelope.setMeta(Meta.of("calculatedAt", Instant.now()));
envelope.set_links(Links.self("https://api.conta.no/widgets/" + widget.id()));

return envelope;

For paginated responses, Envelopes.slice(…​) maps a Micronaut Page or Slice into a SliceEnvelope, pre-filling content, pageNumber, and pageSize — see Paging.

The meta drawer

meta is the per-endpoint extension point for metadata that doesn’t belong in content and isn’t a standard envelope field.

The rule that keeps it from sprawling: data that is part of the answer goes in content; data about the answer that the client must act on goes in meta; anything that applies to every response should become a new top-level envelope field.