RestAhead - compile time generated REST client

Overview

RestAhead - compile time generated REST client

Quality Gate Status Coverage

This project draws inspiration from projects such as Retrofit and Feign, but with a twist: your services are generated at compile time, preventing any issues from being found at runtime.

The aim of this project is to have as much validation done at compile time, rather than runtime. Additionally, since the code is generated at compile time there is no need for reflection. The generated code can be inspected for a no-magic approach.

Features

Introduction

Much like other clients, the service is declared as following:

performGet(); }">
public interface HttpBinService {
    @Get("/get")
    Future<Response> performGet();
}

An instance of this class can be generated by using RestAhead class.

var service = RestAhead.builder("https://httpbin.org")
    .build(HttpBinService.class);

Calls can then be performed simply by calling the instance of the interface:

var response = service.performGet();

Samples of services can be found in demo project here , examples of obtaining their instances are in this directory.

Options

There are multiple options you have when generating requests, all of which will be done automatically when building your project.

Response types

Out of the box the following types are supported:

  • void
  • Response
  • Future
  • CompletableFuture

Other types require you to specify an instance of Converter (rest-ahead-jackson-converter contains an implementation for Jackson library). This will allow you to use virtually any type that the converter can construct.

Example of using a return type:

interface Service {
    @Get
    void requestIgnoringResponse();

    @Get
    Response requestFullResponse();

    @Get
    Map<String, Object> performGet();

    @Get
    CustomResponseType performGetWithSpecificTarget();
}

If you require response headers and the status code as well, two more types can be used along with custom response types:

interface Service {
    // BodyResponse.body() is an optional that contains the deserialized type in case of success.
    // If non 2xx code is returned InputStream errorBody will be present, that contains untouched response body.
    @Get
    BodyResponse<CustomResponseType> get();

    // BodyAndErrorResponse.body() is an optional that contains the deserialized body in case of success.
    // If non 2xx code is returned the errorBody will contain the deserialized body
    @Get
    BodyAndErrorResponse<CustomResponseType, CustomErrorType> getErrors();
}

Body

You can specify a request body by annotating it with @Body. Doing so will make the service require a converter to serialize the body.

performPost(@Body CustomRequest request); }">
public interface HttpBinService {
    @Post("/post")
    Future<Response> performPost(@Body CustomRequest request);
}

Form encoding

Sending a form-url-encoded body can be done by adding @FormUrlEncoded annotation to the body:

performPost(@FormUrlEncoded @Body CustomRequest request); }">
public interface HttpBinService {
    @Post("/post")
    Future<Response> performPost(@FormUrlEncoded @Body CustomRequest request);
}

Changing the name of parameters in the form line can be done by using @FormName annotation on desired fields:

&second= " record SampleFormBody(String first, @FormName("second") String b) { } // This will cause the body to send "customName=hello" class SampleFormClass { @FormName("customName") String getSomething() { return "hello"; } }">
// This will cause the body to send "first=
    
     &second=
     
      "
     
    
record SampleFormBody(String first, @FormName("second") String b) {
}

// This will cause the body to send "customName=hello"
class SampleFormClass {
    @FormName("customName")
    String getSomething() {
        return "hello";
    }
}

Such bodies do not require a converter, one will be generated for the given type.

Supported types:

  • Map and inherited classes
  • Records composed of primitives, boxed values, String or UUID
  • Classes with public, non-static getters returning only primitives, boxed values, String or UUID

Multipart encoding

Multipart requests can be executed as following:

public interface MultipartService {
    @Post("/post")
    HttpBinResponse postMultiPart(
        @Part String part,
        @Body @Part("two") String part2,
        @Part File file,
        @Part Path path,
        @Part FilePart part
    );
}

Parts can be added manually by using FilePart for example, it allows usage of InputStreams, byte[] etc.

Note that files and paths will be read when the request reads the body - meaning their evaluation is lazy.

Headers

Adding headers is possible by using the @Header annotation. Valid parameters for headers are either primitive types, their boxed counterparts, Strings, instances of UUID or collections/arrays of them.

Using multiple annotations with the same value will add extra headers. The following declarations will generate requests that behave the same:

headers); }">
interface Service {
    @Get
    void performGet(@Header("Some-Header") String first, @Header("Some-Header") String second);

    @Get
    void performGetVarargs(@Header("Some-Header") String... headers);

    @Get
    void performGetArray(@Header("Some-Header") String[] headers);

    // Can also use List, Set etc.
    @Get
    void performGetCollection(@Header("Some-Header") Collection<String> headers);
}

Queries

Queries can be added to a request in two ways, seen below. Collections, arrays and varargs types are allowed.

interface Service {
    @Get("/query?q=value")
    void getWithQuery(); // will use the preset value from path

    @Get("/query")
    void getWithParam(@Query("q") String query); // will use the parameter
}

Adapters

The default type for all services is Future . While the value can be mapped to Future using a converter directly, sometimes interop with other libraries is required, or maybe you need a blocking call and don't want to type .get() all the time, as well as catch the exceptions. For these cases a default adapter is included, to allow for blocking calls as evident here:

import java.util.concurrent.CompletableFuture;

interface SampleBlocking {
    @Get
    Future<Response> getFuture();

    @Get
    CompletableFuture<Response> getCompletableFuture();

    @Get
    Response getBlocking();
}

All three examples above will perform the same request, but Future and CompletableFuture will attempt to do this in non-blocking manner (this depends on the client, default JavaHttpClient supports this), but the last, Response will execute a blocking call.

If you wish to declare your own adapters simply create a class with a method annotated with @Adapter:

public class CustomAdapter {
    @Adapter
    public <T> Supplier<T> adapt(Future<T> future) {
        return () -> {
            try {
                return future.get();
            } catch (InterruptedException | ExecutionException e) {
                throw new RestException(e);
            }
        };
    }
}

Adapter will also need to be added to RestAhead builder, via the addAdapter(Object object) method. Exceptions can be thrown by declared adapters and can be propagated via the service (see Call exceptions).

Interceptors

Interceptors can be added to the client to perform common logic before, after or around a request. Interceptor should implement the Interceptor interface and be added to the client like so:

var client = new JavaHttpClient();
    client.addInterceptor(new PreRequestInterceptor());

    var service = RestAhead.builder("https://httpbin.org/")
        .client(client)
        .converter(new JacksonConverter())
        .build(InterceptedService.class);

Paths

Path parameters can also be provided through requests by using the @Path annotation on a parameter:

interface PathExample {
    @Get("/{path1}/{path2}")
    Response performGet(@Path("path1") String path, @Path String path2); // value can be omitted in favor or parameter name
}

Call exceptions

By default, no exceptions need to be declared to execute calls, but beware! An unchecked exception (RestException) will be thrown in case there was an exception thrown during execution. You can also add throws declaration for either or both exceptions that are likely to occur: ExecutionException, InterruptedException, to make sure they are handled. If either of these is not specified in the signature, RestException will still be thrown, wrapping the other one, for example:

import java.util.concurrent.ExecutionException;

public interface HttpBinService {
    // Will throw a RestException if any errors occur
    @Get("/get")
    Response performGet();

    // Allows you to handle IOException, RestException wrapping InterruptedException may still occur
    @Get("/get")
    Response performGet2() throws ExecutionException;

    // Allows you to handle both exception, no RestException will be thrown
    @Get("/get")
    Response performGet3() throws ExecutionException, InterruptedException;
}

A failed request, with custom responses will throw RequestFailedException, that contains a code and the input stream from the request.

Custom client

The RestAhead builder declares an interface Client that allows you to implement custom clients. By default, if no client is specified, Java HTTP client is used.

Spring Boot

For compatibility with spring boot you can add the following to your pom.xml:

<dependency>
    <groupId>io.github.zskamljicgroupId>
    <artifactId>rest-ahead-springartifactId>
    <version>${rest.ahead.version}version>
dependency>

To enable automatic creation of Spring beans add the @EnableRestAhead annotation to your application class as following:

@EnableRestAhead
@SpringBootApplication
public class SpringApplicationDemo {
    public static void main(String[] args) {
        SpringApplication.run(SpringApplicationDemo.class, args);
    }
}

Finally, to have services available as injectable beans add the @RestAheadService annotation to the service:

performGet(); }">
// Instead of placeholder you can also use a hardcoded URL
@RestAheadService(url = "${placeholder.url}", converter = JacksonConverter.class)
public interface DemoService {
    @Get("/get")
    Map<String, Object> performGet();
}

DemoService will then be injectable wherever you use it as a bean - either constructor injection or @Autowired injection. URL property needs to be provided to have a baseUrl configured, converter property is optional and is required only if the service requires one, see response types.

Adding to project

Add the dependencies as following:

<dependencies>
    
    <dependency>
        <groupId>io.github.zskamljicgroupId>
        <artifactId>rest-ahead-clientartifactId>
        <version>${rest.ahead.version}version>
    dependency>
    <dependency>
        <groupId>io.github.zskamljicgroupId>
        <artifactId>rest-ahead-processorartifactId>
        <version>${rest.ahead.version}version>
        <scope>providedscope>
    dependency>
    
    <dependency>
        <groupId>io.github.zskamljicgroupId>
        <artifactId>rest-ahead-jackson-converterartifactId>
        <version>${rest.ahead.version}version>
    dependency>
dependencies>

Also add the maven-compiler-plugin if not present:

<plugin>
    <groupId>org.apache.maven.pluginsgroupId>
    <artifactId>maven-compiler-pluginartifactId>
    <version>${compiler.plugin.version}version>
plugin>

Snapshots

Snapshots can be accessed by adding the snapshot repository:

<repositories>
    <repository>
        <id>oss.sonatype.org-snapshotid>
        <url>https://s01.oss.sonatype.org/content/repositories/snapshotsurl>
    repository>
repositories>

License

Project uses Apache 2.0 license. More info in license file

You might also like...

Library which allows the use and rendering of Blockbench models and animations in a Minecraft server by using generated resource packs and armorstands

Library which allows the use and rendering of Blockbench models and animations in a Minecraft server by using generated resource packs and armorstands

Hephaestus Engine Hephaestus Engine is a library which allows the visualization of block bench models and animations in a Minecraft server by the use

Dec 21, 2022

Adds value to towns, by giving each one a unique set of automatically-generated resources.

TownyResources TownyResources adds value to towns, by giving each one a unique set of automatically-produced resources which can be collected by playe

Dec 30, 2022

Automatic generated Jump and Run

Automatic generated Jump and Run

Project GenJumpAndRun An automatic generating Jump And Run 🔭 I’m currently working on Nothing 🌱 I’m currently learning Games developer (Java) 👨‍💻

Jul 13, 2022

Time-Based One-Time Password (RFC 6238) and HMAC-Based One-Time Password (RFC 4226) reference implementations and more.

Crypto Time-Based One-Time Password (RFC 6238) and HMAC-Based One-Time Password (RFC 4226) reference implementations and more. Getting Started TOTP ge

May 12, 2022

Compile Java byte-code to native CPU's.

Java Grinder Compile Java bytecode to microcontroller assembly. Currently supporting MSP430, dsPIC, 6502/6510, 68000, MIPS, TMS9900, and Z80 with plat

Dec 23, 2022

Download compiled jar from packages or compile it by yourself from sources

Download compiled jar from packages or compile it by yourself from sources

idle_codes Install Download compiled jar from packages or compile it by yourself from sources Put the jar file wherever you want. Make sure you have J

Dec 31, 2021

A library for IDEs and Code Editors to compile java projects faster dynamically

A library for IDEs and Code Editors to compile java projects faster dynamically

Feb 22, 2022

Rest.li is a REST+JSON framework for building robust, scalable service architectures using dynamic discovery and simple asynchronous APIs.

Rest.li is a REST+JSON framework for building robust, scalable service architectures using dynamic discovery and simple asynchronous APIs.

Rest.li is an open source REST framework for building robust, scalable RESTful architectures using type-safe bindings and asynchronous, non-blocking I

Dec 29, 2022

Automation Tests (REST-API with REST-ASSURED examples)

Automation Tests (REST-API with REST-ASSURED examples) Technology Stack IDEA Java Junit5 Gradle Selenide Allure Jenkins Rest-Assured See details: src/

Apr 11, 2022

Source code of course - Building Real-Time REST APIs with Spring Boot

springboot-blog-rest-api Learn how to build real-time REST APIs with Spring Boot by building a complete Blog App. Source code of Popular Building Real

Jan 6, 2023

EssentialClient is a client side mod originally forked from Carpet Client for 1.15.2 that implements new client side features

EssentialClient is a client side mod originally forked from Carpet Client for 1.15.2 that implements new client side features

EssentialClient EssentialClient is a client side only mod originally forked from Carpet Client for 1.15.2 that implements new client side features. Th

Jan 3, 2023

Joda-Time is the widely used replacement for the Java date and time classes prior to Java SE 8.

Joda-Time Joda-Time provides a quality replacement for the Java date and time classes. The design allows for multiple calendar systems, while still pr

Dec 27, 2022

Minecraft configurable plugin , which sends messages the first time a player logs into the server or the next time they log in.

JoinMessages Minecraft configurable plugin , which sends messages the first time a player logs into the server or the next time they log in or leave.

Aug 30, 2022

Just-In-Time Access is an AppEngine application that lets you manage just-in-time privileged access to Google Cloud projects.

Just-In-Time Access is an AppEngine application that lets you manage just-in-time privileged access to Google Cloud projects.

Just-In-Time Access Just-In-Time Access is an AppEngine application that lets you manage just-in-time privileged access to Google Cloud projects. Syno

Jan 3, 2023

Elasticsearch Java Rest Client.

JEST Jest is a Java HTTP Rest client for ElasticSearch. ElasticSearch is an Open Source (Apache 2), Distributed, RESTful, Search Engine built on top o

Jan 1, 2023

Produtos-api-client - Biblioteca de consumo de uma API Rest básica de produtos disponibilizada em outro repositório.

produtos-api-client Biblioteca de consumo de uma API Rest básica de produtos disponibilizada no repositório: clique aqui para acessar Com essa bibliot

Jan 4, 2022

OpenApi Generator - REST Client Generator

OpenApi Generator - REST Client Generator

Quarkus - Openapi Generator Welcome to Quarkiverse! Congratulations and thank you for creating a new Quarkus extension project in Quarkiverse! Feel fr

Jan 3, 2023
Releases(v0.4.1)
  • v0.4.1(Apr 2, 2022)

  • v0.4.0(Mar 29, 2022)

    New features

    Added a new dialect: JAX-RS Added a logging interceptor

    Bugfixes

    FormName was not respected when service changed, but not the model when the class already existed, resulting in invalid form names.

    Source code(tar.gz)
    Source code(zip)
  • v0.3.0(Feb 14, 2022)

    New features

    New dialects can be added by providing a service inheriting Dialect. This allows for usage of other annotations for service generation, for example Spring annotations like @GetMapping.

    (Potentially) Breaking features

    Single Form Converter class is no longer generated, instead pre-existing class is used for Map conversion, for classes and records converters will be added to the generated service subclass.

    Source code(tar.gz)
    Source code(zip)
  • v0.2.0(Feb 7, 2022)

    New features

    Added utility library for Spring Bean support. See Spring Boot section of Readme.md.

    Bugfixes

    Fixed an issue where nested class would not be instantiated due to how the class is generated.

    Source code(tar.gz)
    Source code(zip)
  • v0.1.0(Jan 23, 2022)

Owner
Žan Skamljič
Žan Skamljič
Microserver is a Java 8 native, zero configuration, standards based, battle hardened library to run Java Rest Microservices via a standard Java main class. Supporting pure Microservice or Micro-monolith styles.

Microserver A convenient modular engine for Microservices. Microserver plugins offer seamless integration with Spring (core), Jersey, Guava, Tomcat, G

AOL 936 Dec 19, 2022
Leading REST API framework for Java

Restlet Framework The leading REST API framework for Java Thanks to Restlet Framework's powerful routing and filtering capabilities, unified client an

Restlet Framework 633 Dec 18, 2022
Examples and server integrations for generating the Swagger API Specification, which enables easy access to your REST API

Swagger Core NOTE: If you're looking for Swagger Core 1.5.X and OpenAPI 2.0, please refer to 1.5 branch. NOTE: Since version 2.1.7 Swagger Core suppor

Swagger 7.1k Jan 5, 2023
Spring HATEOAS - Library to support implementing representations for hyper-text driven REST web services.

Spring HATEOAS This project provides some APIs to ease creating REST representations that follow the HATEOAS principle when working with Spring and es

Spring 996 Dec 28, 2022
Spring HATEOAS - Library to support implementing representations for hyper-text driven REST web services.

Spring HATEOAS This project provides some APIs to ease creating REST representations that follow the HATEOAS principle when working with Spring and es

Spring 920 Mar 8, 2021
Simple JSP Servlets REST api.

Simple JSP Servlets REST api.

MisterFunny01 3 May 9, 2021
A type-safe HTTP client for Android and the JVM

Retrofit A type-safe HTTP client for Android and Java. For more information please see the website. Download Download the latest JAR or grab from Mave

Square 41k Jan 5, 2023
Catch common Java mistakes as compile-time errors

Error Prone Error Prone is a static analysis tool for Java that catches common programming mistakes at compile-time. public class ShortSet { public

Google 6.3k Dec 31, 2022
Catch common Java mistakes as compile-time errors

Error Prone Error Prone is a static analysis tool for Java that catches common programming mistakes at compile-time. public class ShortSet { public

Google 6.3k Dec 31, 2022