Domino-rest generates REST clients from JAX-RS interfaces. The same generated client works in the browser (GWT2 or GWT3/J2CL) and on the JVM. JSON serialization uses domino-jackson, and clients are generated at compile time using annotation processing (APT).
- Generate fluent REST clients from JAX-RS interfaces.
- Use the same client in browser and JVM environments.
- Built-in JSON mapping with domino-jackson and optional custom readers/writers.
- Flexible configuration (service roots, interceptors, retries, and more).
domino-rest-client: browser/GWT/J2CL runtime implementation.domino-rest-jvm: JVM runtime implementation.domino-rest-shared: shared request model, annotations, and utilities.domino-rest-processor: annotation processor that generates request factories.domino-rest-jaxrs: minimal JAX-RS API shim for environments without full JAX-RS.domino-rest-test,domino-rest-test-java17,domino-rest-client-test: test modules.
- Java 11+
- Maven 3.6+
Add dependencies:
<dependency>
<groupId>org.dominokit</groupId>
<artifactId>domino-rest-client</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>org.dominokit</groupId>
<artifactId>domino-rest-processor</artifactId>
<version>2.0.0</version>
<scope>provided</scope>
</dependency>If you configure annotation processors explicitly, include the processor:
<annotationProcessorPaths>
<path>
<groupId>org.dominokit</groupId>
<artifactId>domino-rest-processor</artifactId>
<version>2.0.0</version>
</path>
</annotationProcessorPaths>For GWT/J2CL add the inherit directive:
<inherits name="org.dominokit.rest.Rest"/>Initialize Domino REST at startup:
DominoRestConfig.initDefaults();Annotate a JAX-RS interface with @RequestFactory and use JAX-RS annotations:
@RequestFactory
public interface MoviesService {
@Path("library/movies/:movieName")
@GET
Movie getMovieByName(@PathParam("movieName") String movieName);
@Path("library/movies")
@GET
List<Movie> listMovies();
@Path("library/movies/:name")
@PUT
void updateMovie(@BeanParam @RequestBody Movie movie);
}The generated client name is the interface name plus Factory:
MoviesServiceFactory.INSTANCE
.getMovieByName("hulk")
.onSuccess(movie -> { })
.onFailed(failedResponse -> { })
.send();For POJOs used in requests or responses, add @JSONMapper to reuse generated mappers:
@JSONMapper
public class Movie {
@PathParam("name")
private String name;
private int rating;
private String bio;
private String releaseDate;
}By default, requests target the host where the app is served with a service/ prefix. You can override this globally:
DominoRestConfig.getInstance()
.setDefaultServiceRoot("http://127.0.0.1:9090/");Or per service:
@RequestFactory(serviceRoot = "http://localhost:7070/library/")
public interface MoviesService {
@Path("movies/:movieName")
@GET
Movie getMovieByName(@PathParam("movieName") String movieName);
}Dynamic routing is also supported:
DominoRestConfig.getInstance()
.addDynamicServiceRoot(DynamicServiceRoot
.pathMatcher(path -> path.startsWith("movies"))
.serviceRoot(() -> "http://localhost:7070/library/")
);When using the default service root, the resource root defaults to service/. You can change it:
DominoRestConfig.getInstance()
.setDefaultResourceRootPath("endpoint");@Pathdefines endpoints. Use:nameor{name}placeholders.@PathParamfills path placeholders.@QueryParamadds query parameters.@HeaderParamadds request headers.@MatrixParamappends matrix params to the path segment.
Example path + path param:
@RequestFactory
public interface MoviesService {
@Path("library/movies/{name}")
@GET
Movie getMovieByName(@PathParam("name") String movieName);
}For POST, PUT, or PATCH, the request body is resolved by:
- A parameter annotated with
@RequestBody. - A parameter type annotated with
@RequestBody. - The last parameter that is not
@QueryParam,@HeaderParam, or@PathParam.
Domino REST defaults to JSON. To use custom formats, define a RequestWriter/ResponseReader and bind it via
@Writer/@Reader or CustomMapper.
public class XmlMovieWriter implements RequestWriter<Movie> {
@Override
public String write(Movie request) {
String movieXml = /* convert to xml */;
return movieXml;
}
}@PUT
@Consumes(MediaType.APPLICATION_XML)
@Writer(MovieXmlWriter.class)
void updateMovie(@BeanParam @RequestBody Movie movie);Each request exposes a RequestMeta instance for inspection in callbacks or interceptors (method, URL, params,
consumes/produces, and more).
Global interceptors can adjust requests or responses:
DominoRestConfig.getInstance()
.addResponseInterceptor(new ResponseInterceptor() {
@Override
public void onBeforeFailedCallback(ServerRequest serverRequest, FailedResponseBean failedResponse) {
if (failedResponse.getStatusCode() == 401) {
serverRequest.skipFailHandler();
}
}
});Override the default fail handler:
DominoRestConfig.getInstance()
.setDefaultFailHandler(failedResponse -> { });Timeouts and retries are configured per request:
@Retries(timeout = 3000, maxRetries = 5)
void updateMovie(@BeanParam @RequestBody Movie movie);Use @WithCredentials or per-request setWithCredentials(true) to send cookies/credentials on cross-site requests.
For HATEOAS or dynamic URLs, use setUrl on the request to override all mapping:
MoviesServiceFactory.INSTANCE
.updateMovie(movie)
.setUrl("http://localhost:6060/movies")
.send();Send multipart/form-data by using @FormParam or grouping with @Multipart:
@POST
@Path("upload")
@Consumes(MediaType.MULTIPART_FORM_DATA)
void textMultipart(@FormParam("id") String id, @FormParam("file") byte[] fileContent);When you do not want a typed interface, use RestRequestBuilder, or return jakarta.ws.rs.core.Response to inspect
status, headers, and body.
Resource locators allow splitting sub-resources while keeping full path composition:
@RequestFactory
@Path("library")
public interface LibraryResource {
@Path("movies")
MoviesResource movies();
}A request factory interface may extend other interfaces (even external ones) to generate a single client.
For Date parameters on @QueryParam, @PathParam, or @HeaderParam, use @DateFormat to control formatting.
mvn -DskipTests install