////
This guide is maintained in the main Quarkus repository
and pull requests should be submitted there:
https://github.com/quarkusio/quarkus/tree/main/docs/src/main/asciidoc
////
= Using the REST Client

include::./attributes.adoc[]

This guide explains how to use the RESTEasy REST Client in order to interact with REST APIs
with very little effort.

TIP: there is another guide if you need to write server xref:rest-json.adoc[JSON REST APIs].

== Prerequisites

include::includes/devtools/prerequisites.adoc[]

== Solution

We recommend that you follow the instructions in the next sections and create the application step by step.
However, you can go right to the completed example.

Clone the Git repository: `git clone {quickstarts-clone-url}`, or download an {quickstarts-archive-url}[archive].

The solution is located in the `rest-client-quickstart` {quickstarts-tree-url}/rest-client-quickstart[directory].

== Creating the Maven project

First, we need a new project. Create a new project with the following command:

:create-app-artifact-id: rest-client-quickstart
:create-app-extensions: resteasy,resteasy-jackson,rest-client,rest-client-jackson
include::includes/devtools/create-app.adoc[]

This command generates the Maven project with a REST endpoint and imports:

* the `resteasy` and `resteasy-jackson` extensions for the REST server support;
* the `rest-client` and `rest-client-jackson` extensions for the REST client support.

If you already have your Quarkus project configured, you can add the `rest-client` and the `rest-client-jackson` extensions
to your project by running the following command in your project base directory:

:add-extension-extensions: rest-client,rest-client-jackson
include::includes/devtools/extension-add.adoc[]

This will add the following to your `pom.xml`:

[source,xml,role="primary asciidoc-tabs-target-sync-cli asciidoc-tabs-target-sync-maven"]
.pom.xml
----
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-rest-client</artifactId>
</dependency>
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-rest-client-jackson</artifactId>
</dependency>
----

[source,gradle,role="secondary asciidoc-tabs-target-sync-gradle"]
.build.gradle
----
implementation("io.quarkus:quarkus-rest-client")
implementation("io.quarkus:quarkus-rest-client-jackson")
----

== Setting up the model

In this guide we will be demonstrating how to consume part of the REST API supplied by the link:https://stage.code.quarkus.io[stage.code.quarkus.io] service.
Our first order of business is to set up the model we will be using, in the form of an `Extension` POJO.

Create a `src/main/java/org/acme/rest/client/Extension.java` file and set the following content:

[source,java]
----
package org.acme.rest.client;

import java.util.List;

public class Extension {

    public String id;
    public String name;
    public String shortName;
    public List<String> keywords;

}
----

The model above is only a subset of the fields provided by the service, but it suffices for the purposes of this guide.

== Create the interface

Using the RESTEasy REST Client is as simple as creating an interface using the proper JAX-RS and MicroProfile annotations. In our case the interface should be created at `src/main/java/org/acme/rest/client/ExtensionsService.java` and have the following content:

[source, java]
----
package org.acme.rest.client;

import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import org.jboss.resteasy.annotations.jaxrs.QueryParam;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import java.util.Set;

@Path("/extensions")
@RegisterRestClient
public interface ExtensionsService {

    @GET
    Set<Extension> getById(@QueryParam String id);
}
----

The `getById` method gives our code the ability to get an extension by id from the Code Quarkus API. The client will handle all the networking and marshalling leaving our code clean of such technical details.

The purpose of the annotations in the code above is the following:

* `@RegisterRestClient` allows Quarkus to know that this interface is meant to be available for
CDI injection as a REST Client
* `@Path`, `@GET` and `@PathParam` are the standard JAX-RS annotations used to define how to access the service

[NOTE]
====
When a JSON extension is installed such as `quarkus-rest-client-jackson` or `quarkus-rest-client-jsonb`, Quarkus will use the `application/json` media type
by default for most return values, unless the media type is explicitly set via
`@Produces` or `@Consumes` annotations (there are some exceptions for well known types, such as `String` and `File`, which default to `text/plain` and `application/octet-stream`
respectively).

If you don't want JSON by default you can set `quarkus.resteasy-json.default-json=false` and the default will change back to being auto-negotiated. If you set this
you will need to add `@Produces(MediaType.APPLICATION_JSON)` and `@Consumes(MediaType.APPLICATION_JSON)` to your endpoints in order to use JSON.

If you don't rely on the JSON default, it is heavily recommended to annotate your endpoints with the `@Produces` and `@Consumes` annotations to define precisely the expected content-types.
It will allow to narrow down the number of JAX-RS providers (which can be seen as converters) included in the native executable.
====

=== Path Parameters

If the GET request requires path parameters you can leverage the `@PathParam("parameter-name")` annotation instead of (or in addition to) the `@QueryParam`. Path and query parameters can be combined, as required, as illustrated in a mock example below.

[source, java]
----
package org.acme.rest.client;

import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import org.jboss.resteasy.annotations.jaxrs.PathParam;
import org.jboss.resteasy.annotations.jaxrs.QueryParam;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import java.util.Set;

@Path("/extensions")
@RegisterRestClient
public interface ExtensionsService {

    @GET
    @Path("/stream/{stream}")
    Set<Extension> getByStream(@PathParam String stream, @QueryParam("id") String id);
}
----


== Create the configuration

In order to determine the base URL to which REST calls will be made, the REST Client uses configuration from `application.properties`.
The name of the property needs to follow a certain convention which is best displayed in the following code:

[source,properties]
----
# Your configuration properties
quarkus.rest-client."org.acme.rest.client.ExtensionsService".url=https://stage.code.quarkus.io/api # // <1>
quarkus.rest-client."org.acme.rest.client.ExtensionsService".scope=javax.inject.Singleton # // <2>
----

<1> Having this configuration means that all requests performed using `ExtensionsService` will use `https://stage.code.quarkus.io` as the base URL.
Using the configuration above, calling the `getById` method of `ExtensionsService` with a value of `io.quarkus:quarkus-rest-client` would result in an HTTP GET request being made to `https://stage.code.quarkus.io/api/extensions?id=io.quarkus:quarkus-rest-client`.
<2> Having this configuration means that the default scope of `ExtensionsService` will be `@Singleton`. Supported scope values are `@Singleton`, `@Dependent`, `@ApplicationScoped` and `@RequestScoped`. The default scope is `@Dependent`.
The default scope can also be defined on the interface.

Note that `org.acme.rest.client.ExtensionsService` _must_ match the fully qualified name of the `ExtensionsService` interface we created in the previous section.

[NOTE]
====
The standard MicroProfile Rest Client properties notation can also be used to configure the client:

[source,properties]
----
org.acme.rest.client.ExtensionsService/mp-rest/url=https://stage.code.quarkus.io/api
org.acme.rest.client.ExtensionsService/mp-rest/scope=javax.inject.Singleton
----

If a property is specified via both the Quarkus notation and the MicroProfile notation, the Quarkus notation takes a precedence.
====


To facilitate the configuration, you can use the `@RegisterRestClient` `configKey` property that allows to use another configuration root than the fully qualified name of your interface.

[source, java]
----

@RegisterRestClient(configKey="extensions-api")
public interface ExtensionsService {
    [...]
}
----

[source,properties]
----
# Your configuration properties
quarkus.rest-client.extensions-api.url=https://stage.code.quarkus.io/api
quarkus.rest-client.extensions-api.scope=javax.inject.Singleton
----

=== Disabling Hostname Verification

To disable the SSL hostname verification for a specific REST client, add the following property to your configuration:

[source,properties]
----
quarkus.rest-client.extensions-api.hostname-verifier=io.quarkus.restclient.NoopHostnameVerifier
----

=== Disabling SSL verifications

To disable all SSL verifications, add the following property to your configuration:

[source,properties]
----
quarkus.tls.trust-all=true
----
[WARNING]
====
This setting should not be used in production as it will disable any kind of SSL verification.
====

== Create the JAX-RS resource

Create the `src/main/java/org/acme/rest/client/ExtensionsResource.java` file with the following content:

[source,java]
----
package org.acme.rest.client;

import org.eclipse.microprofile.rest.client.inject.RestClient;
import org.jboss.resteasy.annotations.jaxrs.PathParam;

import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import java.util.Set;

@Path("/extension")
public class ExtensionsResource {

    @Inject
    @RestClient
    ExtensionsService extensionsService;

    @GET
    @Path("/id/{id}")
    public Set<Extension> id(@PathParam String id) {
        return extensionsService.getById(id);
    }
}
----

Note that in addition to the standard CDI `@Inject` annotation, we also need to use the MicroProfile `@RestClient` annotation to inject `ExtensionsService`.

== Update the test

We also need to update the functional test to reflect the changes made to the endpoint.
Edit the `src/test/java/org/acme/rest/client/ExtensionsResourceTest.java` file and change the content of the `testExtensionIdEndpoint` method to:


[source, java]
----
package org.acme.rest.client;

import static io.restassured.RestAssured.given;
import static org.hamcrest.CoreMatchers.hasItem;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.greaterThan;

import org.acme.rest.client.resources.WireMockExtensionsResource;
import org.junit.jupiter.api.Test;

import io.quarkus.test.common.QuarkusTestResource;
import io.quarkus.test.junit.QuarkusTest;

@QuarkusTest
@QuarkusTestResource(WireMockExtensionsResource.class)
public class ExtensionsResourceTest {

    @Test
    public void testExtensionsIdEndpoint() {
        given()
            .when().get("/extension/id/io.quarkus:quarkus-rest-client")
            .then()
            .statusCode(200)
            .body("$.size()", is(1),
                "[0].id", is("io.quarkus:quarkus-rest-client"),
                "[0].name", is("REST Client"),
                "[0].keywords.size()", greaterThan(1),
                "[0].keywords", hasItem("rest-client"));
    }
}
----

The code above uses link:http://rest-assured.io/[REST Assured]'s link:https://github.com/rest-assured/rest-assured/wiki/GettingStarted#jsonpath[json-path] capabilities.


== Async Support

The rest client supports asynchronous rest calls.
Async support comes in 2 flavors: you can return a `CompletionStage` or a `Uni` (requires the `quarkus-rest-client-mutiny` extension).
Let's see it in action by adding a `getByIdAsync` method in our `ExtensionsService` REST interface. The code should look like:

[source, java]
----
package org.acme.rest.client;

import java.util.Set;
import java.util.concurrent.CompletionStage;

import javax.ws.rs.GET;
import javax.ws.rs.Path;

import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import org.jboss.resteasy.annotations.jaxrs.QueryParam;

@Path("/extensions")
@RegisterRestClient
public interface ExtensionsService {

    @GET
    Set<Extension> getById(@QueryParam String id);

    @GET
    CompletionStage<Set<Extension>> getByIdAsync(@QueryParam String id);

}
----

Open the `src/main/java/org/acme/rest/client/ExtensionsResource.java` file and update it with the following content:

[source,java]
----
package org.acme.rest.client;

import java.util.Set;
import java.util.concurrent.CompletionStage;

import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;

import org.eclipse.microprofile.rest.client.inject.RestClient;
import org.jboss.resteasy.annotations.jaxrs.PathParam;

@Path("/extension")
public class ExtensionsResource {

    @Inject
    @RestClient
    ExtensionsService extensionsService;

    @GET
    @Path("/id/{id}")
    public Set<Extension> id(@PathParam String id) {
        return extensionsService.getById(id);
    }

    @GET
    @Path("/id-async/{id}")
    public CompletionStage<Set<Extension>> idAsync(@PathParam String id) {
        return extensionsService.getByIdAsync(id);
    }

}
----

To test asynchronous methods, add the test method below in `ExtensionsResourceTest`:
[source,java]
----
@Test
public void testExtensionIdAsyncEndpoint() {
    given()
        .when().get("/extension/id-async/io.quarkus:quarkus-rest-client")
        .then()
        .statusCode(200)
        .body("$.size()", is(1),
            "[0].id", is("io.quarkus:quarkus-rest-client"),
            "[0].name", is("REST Client"),
            "[0].keywords.size()", greaterThan(1),
            "[0].keywords", hasItem("rest-client"));
}
----

The `Uni` version is very similar:

[source, java]
----
package org.acme.rest.client;

import java.util.Set;
import java.util.concurrent.CompletionStage;

import javax.ws.rs.GET;
import javax.ws.rs.Path;

import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import org.jboss.resteasy.annotations.jaxrs.QueryParam;

import io.smallrye.mutiny.Uni;

@Path("/extensions")
@RegisterRestClient
public interface ExtensionsService {

    // ...

    @GET
    Uni<Set<Extension>> getByIdAsUni(@QueryParam String id);
}
----

The `ExtensionsResource` becomes:

[source,java]
----
package org.acme.rest.client;

import java.util.Set;
import java.util.concurrent.CompletionStage;

import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;

import org.eclipse.microprofile.rest.client.inject.RestClient;
import org.jboss.resteasy.annotations.jaxrs.PathParam;

import io.smallrye.mutiny.Uni;

@Path("/extension")
public class ExtensionsResource {

    @Inject
    @RestClient
    ExtensionsService extensionsService;


    // ...

    @GET
    @Path("/id-uni/{id}")
    public Uni<Set<Extension>> idMutiny(@PathParam String id) {
        return extensionsService.getByIdAsUni(id);
    }
}
----

[TIP]
.Mutiny
====
The previous snippet uses Mutiny reactive types.
If you are not familiar with Mutiny, check xref:mutiny-primer.adoc[Mutiny - an intuitive reactive programming library].
====

When returning a `Uni`, every _subscription_ invokes the remote service.
It means you can re-send the request by re-subscribing on the `Uni`, or use a `retry` as follows:

[source, java]
----

@Inject @RestClient ExtensionsService extensionsService;

// ...

extensionsService.getByIdAsUni(id)
    .onFailure().retry().atMost(10);
----

If you use a `CompletionStage`, you would need to call the service's method to retry.
This difference comes from the laziness aspect of Mutiny and its subscription protocol.
More details about this can be found in https://smallrye.io/smallrye-mutiny/#_uni_and_multi[the Mutiny documentation].

== Custom headers support

The MicroProfile REST client allows amending request headers by registering a `ClientHeadersFactory` with the `@RegisterClientHeaders` annotation.

Let's see it in action by adding a `@RegisterClientHeaders` annotation pointing to a `RequestUUIDHeaderFactory` class in our `ExtensionsService` REST interface:

[source, java]
----
package org.acme.rest.client;

import java.util.Set;
import java.util.concurrent.CompletionStage;

import javax.ws.rs.GET;
import javax.ws.rs.Path;

import org.eclipse.microprofile.rest.client.annotation.RegisterClientHeaders;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import org.jboss.resteasy.annotations.jaxrs.QueryParam;

import io.smallrye.mutiny.Uni;

@Path("/extensions")
@RegisterRestClient
@RegisterClientHeaders(RequestUUIDHeaderFactory.class)
public interface ExtensionsService {

    @GET
    Set<Extension> getById(@QueryParam String id);

    @GET
    CompletionStage<Set<Extension>> getByIdAsync(@QueryParam String id);

    @GET
    Uni<Set<Extension>> getByIdAsUni(@QueryParam String id);
}
----

And the `RequestUUIDHeaderFactory` would look like:

[source, java]
----
package org.acme.rest.client;

import org.eclipse.microprofile.rest.client.ext.ClientHeadersFactory;

import javax.enterprise.context.ApplicationScoped;
import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.MultivaluedMap;
import java.util.UUID;

@ApplicationScoped
public class RequestUUIDHeaderFactory implements ClientHeadersFactory {

    @Override
    public MultivaluedMap<String, String> update(MultivaluedMap<String, String> incomingHeaders, MultivaluedMap<String, String> clientOutgoingHeaders) {
        MultivaluedMap<String, String> result = new MultivaluedHashMap<>();
        result.add("X-request-uuid", UUID.randomUUID().toString());
        return result;
    }
}
----

As you see in the example above, you can make your `ClientHeadersFactory` implementation a CDI bean by
annotating it with a scope-defining annotation, such as `@Singleton`, `@ApplicationScoped`, etc.


=== Default header factory

You can also use `@RegisterClientHeaders` annotation without any custom factory specified. In that case the `DefaultClientHeadersFactoryImpl` factory will be used and all headers listed in `org.eclipse.microprofile.rest.client.propagateHeaders` configuration property will be amended. Individual header names are comma-separated.
[source, java]
----
@Path("/extensions")
@RegisterRestClient
@RegisterClientHeaders
public interface ExtensionsService {

    @GET
    Set<Extension> getById(@QueryParam String id);

    @GET
    CompletionStage<Set<Extension>> getByIdAsync(@QueryParam String id);

    @GET
    Uni<Set<Extension>> getByIdAsUni(@QueryParam String id);
}

----

[source,properties]
----
org.eclipse.microprofile.rest.client.propagateHeaders=Authorization,Proxy-Authorization
----

== Package and run the application

Run the application with:

include::includes/devtools/dev.adoc[]

Open your browser to http://localhost:8080/extension/id/io.quarkus:quarkus-rest-client.

You should see a JSON object containing some basic information about the REST Client extension.

As usual, the application can be packaged using:

include::includes/devtools/build.adoc[]

And executed with `java -jar target/quarkus-app/quarkus-run.jar`.

You can also generate the native executable with:

include::includes/devtools/build-native.adoc[]

== REST Client and RESTEasy interactions

In Quarkus, the REST Client extension and xref:rest-json.adoc[the RESTEasy extension] share the same infrastructure.
One important consequence of this consideration is that they share the same list of providers (in the JAX-RS meaning of the word).

For instance, if you declare a `WriterInterceptor`, it will by default intercept both the servers calls and the client calls,
which might not be the desired behavior.

However, you can change this default behavior and constrain a provider to:

* only consider *client* calls by adding the `@ConstrainedTo(RuntimeType.CLIENT)` annotation to your provider;
* only consider *server* calls by adding the `@ConstrainedTo(RuntimeType.SERVER)` annotation to your provider.

[#using-a-mock-http-server-for-tests]
== Using a Mock HTTP Server for tests

Setting up a mock HTTP server, against which tests are run, is a common testing pattern.
Examples of such servers are link:http://wiremock.org/[Wiremock] and link:https://docs.hoverfly.io/projects/hoverfly-java/en/latest/index.html[Hoverfly].
In this section we'll demonstrate how Wiremock can be leveraged for testing the `ExtensionsService` which was developed above.

First of all, Wiremock needs to be added as a test dependency. For a Maven project that would happen like so:

[source,xml,role="primary asciidoc-tabs-target-sync-cli asciidoc-tabs-target-sync-maven"]
.pom.xml
----
<dependency>
    <groupId>com.github.tomakehurst</groupId>
    <artifactId>wiremock-jre8</artifactId>
    <scope>test</scope>
    <version>${wiremock.version}</version> <1>
</dependency>
----
<1> Use a proper Wiremock version. All available versions can be found link:https://search.maven.org/artifact/com.github.tomakehurst/wiremock-jre8[here].

[source,gradle,role="secondary asciidoc-tabs-target-sync-gradle"]
.build.gradle
----
testImplementation("com.github.tomakehurst:wiremock-jre8:$wiremockVersion") <1>
----
<1> Use a proper Wiremock version. All available versions can be found link:https://search.maven.org/artifact/com.github.tomakehurst/wiremock-jre8[here].

In Quarkus tests when some service needs to be started before the Quarkus tests are ran, we utilize the `@io.quarkus.test.common.QuarkusTestResource`
annotation to specify a `io.quarkus.test.common.QuarkusTestResourceLifecycleManager` which can start the service and supply configuration
values that Quarkus will use.

[NOTE]
====
For more details about `@QuarkusTestResource` refer to  xref:getting-started-testing.adoc#quarkus-test-resource[this part of the documentation].
====

Let's create an implementation of `QuarkusTestResourceLifecycleManager` called `WiremockExtensions` like so:

[source,java]
----
package org.acme.rest.client;

import java.util.Collections;
import java.util.Map;

import com.github.tomakehurst.wiremock.WireMockServer;
import io.quarkus.test.common.QuarkusTestResourceLifecycleManager;

import static com.github.tomakehurst.wiremock.client.WireMock.*; // <1>

public class WireMockExtensions implements QuarkusTestResourceLifecycleManager {  // <2>

    private WireMockServer wireMockServer;

    @Override
    public Map<String, String> start() {
        wireMockServer = new WireMockServer();
        wireMockServer.start(); // <3>

        stubFor(get(urlEqualTo("/extensions?id=io.quarkus:quarkus-rest-client"))   // <4>
                .willReturn(aResponse()
                        .withHeader("Content-Type", "application/json")
                        .withBody(
                            "[{" +
                            "\"id\": \"io.quarkus:quarkus-rest-client\"," +
                            "\"name\": \"REST Client\"" +
                            "}]"
                        )));

        stubFor(get(urlMatching(".*")).atPriority(10).willReturn(aResponse().proxiedFrom("https://stage.code.quarkus.io/api")));   // <5>

        return Collections.singletonMap("quarkus.rest-client.\"org.acme.rest.client.ExtensionsService\".url", wireMockServer.baseUrl()); // <6>
    }

    @Override
    public void stop() {
        if (null != wireMockServer) {
            wireMockServer.stop();  // <7>
        }
    }
}
----

<1> Statically importing the methods in the Wiremock package makes it easier to read the test.
<2> The `start` method is invoked by Quarkus before any test is run and returns a `Map` of configuration properties that apply during the test execution.
<3> Launch Wiremock.
<4> Configure Wiremock to stub the calls to `/extensions?id=io.quarkus:quarkus-rest-client` by returning a specific canned response.
<5> All HTTP calls that have not been stubbed are handled by calling the real service. This is done for demonstration purposes, as it is not something that would usually happen in a real test.
<6> As the `start` method returns configuration that applies for tests, we set the rest-client property that controls the base URL which is used by the implementation
of `ExtensionsService` to the base URL where Wiremock is listening for incoming requests.
<7> When all tests have finished, shutdown Wiremock.


The `ExtensionsResourceTest` test class needs to be annotated like so:

[source,java]
----
@QuarkusTest
@QuarkusTestResource(WireMockExtensions.class)
public class ExtensionsResourceTest {

}
----

[WARNING]
====
`@QuarkusTestResource` applies to all tests, not just `ExtensionsResourceTest`.
====

== Further reading

 * link:https://download.eclipse.org/microprofile/microprofile-rest-client-2.0/microprofile-rest-client-spec-2.0.html[MicroProfile Rest Client specification]
