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.