Getting started

Dependency

conta-http is published to the conta-open group Maven registry.

repositories {
    mavenCentral()
    maven {
        name 'contaOpen'
        url 'https://gitlab.com/api/v4/groups/12992043/-/packages/maven'
    }
}

dependencies {
    implementation 'no.conta.http:conta-http-kit:0.+'
}

Micronaut wiring

conta-http-kit carries the Micronaut annotation processor metadata, so Paging binds with @RequestBean and QueryParamGuard can introspect your query beans without further configuration.

The library throws no.conta.problem.ThrowableProblem for rejected requests but does not ship an exception handler. To render those as application/problem+json, the consuming application must depend on conta-problem-json-mn, which provides the handler.

dependencies {
    implementation 'no.conta.problem:conta-problem-json-mn:1.+'
}
conta-problem-json-mn extracts an OpenTelemetry trace into the problem response, so the application must also have io.opentelemetry:opentelemetry-api on its runtime classpath (Micronaut tracing already provides it).

Serialization

The envelope types carry no serialization annotations, so they serialize by reflection under micronaut-jackson-databind:

dependencies {
    implementation 'io.micronaut:micronaut-jackson-databind'
}

Configure the ObjectMapper to omit nulls so optional fields are absent rather than null:

jackson:
  serialization-inclusion: NON_NULL

An application on micronaut-serde instead of databind must register the envelopes it returns with @SerdeImport, e.g. @SerdeImport(SingleEnvelope.class) / @SerdeImport(SliceEnvelope.class), since the types are deliberately annotation-free.

A complete controller

@Controller("/widgets")
public class WidgetController {

    private final QueryParamGuard queryParamGuard = QueryParamGuard.fromBeans(Paging.class, WidgetQuery.class);
    private final OrderFieldsGuard orderFieldsGuard = OrderFieldsGuard.fromFields(WidgetOrderField.allowedNames());

    private final WidgetService service;

    public WidgetController(WidgetService service) {
        this.service = service;
    }

    @Get("{?queryParams*}")
    public SliceEnvelope<Widget> list(
        @RequestBean Paging paging,
        @RequestBean WidgetQuery query,
        @Nullable @QueryValue Map<String, String> queryParams
    ) {
        queryParamGuard.rejectUnknown(queryParams);
        orderFieldsGuard.requireAllowed(paging.getOrderByField());

        var page = service.find(query, paging.toPageableWithDefaults("createdAt", Direction.DESC));

        return Envelopes.slice(page);
    }
}

Read on: