////
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
////
= Consuming a gRPC Service

include::./attributes.adoc[]

gRPC clients can be injected in your application code.

IMPORTANT: Consuming gRPC services requires the gRPC classes to be generated.
Place your `proto` files in `src/main/proto` and run `mvn compile`.

== Stubs and Injection

gRPC generation provides several stubs, providing different ways to consume a gRPC service.
You can inject:

* a service interface using the Mutiny API,
* a blocking stub using the gRPC API,
* a reactive stub based on Mutiny,
* the gRPC `io.grpc.Channel`, that lets you create other types of stubs.

[source, java]
----
import io.quarkus.grpc.GrpcClient;

import hello.Greeter;
import hello.GreeterGrpc.GreeterBlockingStub;
import hello.MutinyGreeterGrpc.MutinyGreeterStub;

class MyBean {

   // A service interface using the Mutiny API
   @GrpcClient("helloService")                   // <1>
   Greeter greeter;

   // A reactive stub based on Mutiny
   @GrpcClient("helloService")
   MutinyGreeterGrpc.MutinyGreeterStub mutiny;

   // A blocking stub using the gRPC API
   @GrpcClient
   GreeterGrpc.GreeterBlockingStub helloService; // <2>

   @GrpcClient("hello-service")
   Channel channel;

}
----
<1> A gRPC client injection point must be annotated with the `@GrpcClient` qualifier. This qualifier can be used to specify the name that is used to configure the underlying gRPC client. For example, if you set it to `hello-service`, configuring the host of the service is done using the `quarkus.grpc.clients.**hello-service**.host`.
<2> If the name is not specified via the `GrpcClient#value()` then the field name is used instead, e.g. `helloService` in this particular case.

The stub class names are derived from the service name used in your `proto` file.
For example, if you use `Greeter` as a service name as in:

[source]
----
option java_package = "hello";

service Greeter {
    rpc SayHello (HelloRequest) returns (HelloReply) {}
}
----

Then the service interface name is: `hello.Greeter`, the Mutiny stub name is: `hello.MutinyGreeterGrpc.MutinyGreeterStub` and the blocking stub name is: `hello.GreeterGrpc.GreeterBlockingStub`.

== Examples

=== Service Interface

[source, java]
----
import io.quarkus.grpc.GrpcClient;
import io.smallrye.mutiny.Uni;

import hello.Greeter;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/hello")
public class ExampleResource {

   @GrpcClient <1>
   Greeter hello;

   @GET
   @Path("/mutiny/{name}")
   public Uni<String> helloMutiny(@PathParam("name") String name) {
      return hello.sayHello(HelloRequest.newBuilder().setName(name).build())
            .onItem().transform(HelloReply::getMessage);
   }
}
----
<1> The service name is derived from the injection point - the field name is used. The `quarkus.grpc.clients.hello.host` property must be set.

=== Blocking Stub

[source, java]
----
import io.quarkus.grpc.GrpcClient;

import hello.GreeterGrpc.GreeterBlockingStub;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/hello")
public class ExampleResource {

   @GrpcClient("hello") <1>
   GreeterGrpc.GreeterBlockingStub blockingHelloService;

   @GET
   @Path("/blocking/{name}")
   public String helloBlocking(@PathParam("name") String name) {
      return blockingHelloService.sayHello(HelloRequest.newBuilder().setName(name).build()).getMessage();
   }
}
----
<1> The `quarkus.grpc.clients.hello.host` property must be set.

=== Handling streams

gRPC allows sending and receiving streams:

[source]
----
service Streaming {
    rpc Source(Empty) returns (stream Item) {} // Returns a stream
    rpc Sink(stream Item) returns (Empty) {}   // Reads a stream
    rpc Pipe(stream Item) returns (stream Item) {}  // Reads a streams and return a streams
}
----

Using the Mutiny stub, you can interact with these as follows:

[source, java]
----
package io.quarkus.grpc.example.streaming;

import io.grpc.examples.streaming.Empty;
import io.grpc.examples.streaming.Item;
import io.grpc.examples.streaming.MutinyStreamingGrpc;
import io.quarkus.grpc.GrpcClient;

import io.smallrye.mutiny.Multi;
import io.smallrye.mutiny.Uni;

import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/streaming")
@Produces(MediaType.APPLICATION_JSON)
public class StreamingEndpoint {

    @GrpcClient
    MutinyStreamingGrpc.MutinyStreamingStub streaming;

    @GET
    public Multi<String> invokeSource() {
        // Retrieve a stream
        return streaming.source(Empty.newBuilder().build())
                .onItem().transform(Item::getValue);
    }

    @GET
    @Path("sink/{max}")
    public Uni<Void> invokeSink(@PathParam("max") int max) {
        // Send a stream and wait for completion
        Multi<Item> inputs = Multi.createFrom().range(0, max)
                .map(i -> Integer.toString(i))
                .map(i -> Item.newBuilder().setValue(i).build());
        return streaming.sink(inputs).onItem().ignore().andContinueWithNull();
    }

    @GET
    @Path("/{max}")
    public Multi<String> invokePipe(@PathParam("max") int max) {
        // Send a stream and retrieve a stream
        Multi<Item> inputs = Multi.createFrom().range(0, max)
                .map(i -> Integer.toString(i))
                .map(i -> Item.newBuilder().setValue(i).build());
        return streaming.pipe(inputs).onItem().transform(Item::getValue);
    }

}

----

== Client configuration

For each gRPC service you inject in your application, you can configure the following attributes:

include::{generated-dir}/config/quarkus-grpc-config-group-config-grpc-client-configuration.adoc[opts=optional, leveloffset=+1]

The `client-name` is the name set in the `@GrpcClient` or derived from the injection point if not explicitly defined.

The following examples uses _hello_ as the client name.
Don't forget to replace it with the name you used in in the `@GrpcClient` annotation.

=== Enabling TLS

To enable TLS, use the following configuration.
Note that all paths in the configuration may either specify a resource on the classpath
(typically from `src/main/resources` or its subfolder) or an external file.

[source,properties]
----
quarkus.grpc.clients.hello.host=localhost

# either a path to a classpath resource or to a file:
quarkus.grpc.clients.hello.ssl.trust-store=tls/ca.pem
----

NOTE: When SSL/TLS is configured, `plain-text` is automatically disabled.

=== TLS with Mutual Auth

To use TLS with mutual authentication, use the following configuration:

[source,properties]
----
quarkus.grpc.clients.hello.host=localhost
quarkus.grpc.clients.hello.plain-text=false

# all the following may use either a path to a classpath resource or to a file:
quarkus.grpc.clients.hello.ssl.certificate=tls/client.pem
quarkus.grpc.clients.hello.ssl.key=tls/client.key
quarkus.grpc.clients.hello.ssl.trust-store=tls/ca.pem
----

=== Client Deadlines

It's always reasonable to set a deadline (timeout) for a gRPC client, i.e. to specify a duration of time after which the RPC times out and the client receives the status error `DEADLINE_EXCEEDED`.
You can specify the deadline via the `quarkus.grpc.clients."service-name".deadline` configuration property, e.g.: 

[source,properties]
----
quarkus.grpc.clients.hello.host=localhost
quarkus.grpc.clients.hello.deadline=2s <1>
----
<1> Set the  deadline for all injected clients.

== gRPC Headers
Similarly to HTTP, alongside the message, gRPC calls can carry headers.
Headers can be useful e.g. for authentication.

To set headers for a gRPC call, create a client with headers attached and then perform the call on this client:
[source,java]
----
import javax.enterprise.context.ApplicationScoped;

import examples.Greeter;
import examples.HelloReply;
import examples.HelloRequest;
import io.grpc.Metadata;
import io.quarkus.grpc.GrpcClient;
import io.quarkus.grpc.GrpcClientUtils;
import io.smallrye.mutiny.Uni;

@ApplicationScoped
public class MyService {
    @GrpcClient
    Greeter client;

    public Uni<HelloReply> doTheCall() {
        Metadata extraHeaders = new Metadata();
        if (headers) {
            extraHeaders.put("my-header", "my-interface-value");
        }

        Greeter alteredClient = GrpcClientUtils.attachHeaders(client, extraHeaders); // <1>

        return alteredClient.sayHello(HelloRequest.newBuilder().setName(name).build()); // <2>
    }
}
----
<1> Alter the client to make calls with the `extraHeaders` attached
<2> Perform the call with the altered client. The original client remains unmodified

`GrpcClientUtils` work with all flavors of clients.

== Client Interceptors

A gRPC client interceptor can be implemented by a CDI bean that also implements the `io.grpc.ClientInterceptor` interface.
You can annotate an injected client with `@io.quarkus.grpc.RegisterClientInterceptor` to register the specified interceptor for the particular client instance.
The `@RegisterClientInterceptor` annotation is repeatable.
Alternatively, if you want to apply the interceptor to any injected client then annotate the interceptor bean with `@io.quarkus.grpc.GlobalInterceptor`.

.Global Client Interceptor Example
[source, java]
----
import io.quarkus.grpc.GlobalInterceptor;

import io.grpc.ClientInterceptor;

@GlobalInterceptor <1>
@ApplicationScoped
public class MyInterceptor implements ClientInterceptor {

    @Override
    public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(MethodDescriptor<ReqT, RespT> method,
            CallOptions callOptions, Channel next) {
       // ...
    }
}
----
<1> This interceptor is applied to all injected gRPC clients.

TIP: Check the https://grpc.github.io/grpc-java/javadoc/io/grpc/ClientInterceptor.html[ClientInterceptor JavaDoc] to properly implement your interceptor.

.`@RegisterClientInterceptor` Example
[source, java]
----
import io.quarkus.grpc.GrpcClient;
import io.quarkus.grpc.RegisterClientInterceptor;

import hello.Greeter;

@ApplicationScoped
class MyBean {

    @RegisterClientInterceptor(MySpecialInterceptor.class) <1>
    @GrpcClient("helloService")
    Greeter greeter;
}
----
<1> Registers the `MySpecialInterceptor` for this particular client.

When you have multiple client interceptors, you can order them by implementing the `javax.enterprise.inject.spi.Prioritized` interface:

[source, java]
----
@ApplicationScoped
public class MyInterceptor implements ClientInterceptor, Prioritized {

    @Override
    public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(MethodDescriptor<ReqT, RespT> method,
            CallOptions callOptions, Channel next) {
       // ...
    }

    @Override
    public int getPriority() {
        return 10;
    }
}
----

Interceptors with the highest priority are called first.
The default priority, used if the interceptor does not implement the `Prioritized` interface, is `0`.

== gRPC Client metrics

=== Enabling metrics collection

gRPC client metrics are automatically enabled when the application also uses the xref:micrometer.adoc[`quarkus-micrometer`] extension.
Micrometer collects the metrics of all the gRPC clients used by the application.

As an example, if you export the metrics to Prometheus, you will get:

[source, text]
----
# HELP grpc_client_responses_received_messages_total The total number of responses received
# TYPE grpc_client_responses_received_messages_total counter
grpc_client_responses_received_messages_total{method="SayHello",methodType="UNARY",service="helloworld.Greeter",} 6.0
# HELP grpc_client_requests_sent_messages_total The total number of requests sent
# TYPE grpc_client_requests_sent_messages_total counter
grpc_client_requests_sent_messages_total{method="SayHello",methodType="UNARY",service="helloworld.Greeter",} 6.0
# HELP grpc_client_processing_duration_seconds The total time taken for the client to complete the call, including network delay
# TYPE grpc_client_processing_duration_seconds summary
grpc_client_processing_duration_seconds_count{method="SayHello",methodType="UNARY",service="helloworld.Greeter",statusCode="OK",} 6.0
grpc_client_processing_duration_seconds_sum{method="SayHello",methodType="UNARY",service="helloworld.Greeter",statusCode="OK",} 0.167411625
# HELP grpc_client_processing_duration_seconds_max The total time taken for the client to complete the call, including network delay
# TYPE grpc_client_processing_duration_seconds_max gauge
grpc_client_processing_duration_seconds_max{method="SayHello",methodType="UNARY",service="helloworld.Greeter",statusCode="OK",} 0.136478028
----

The service name, method and type can be found in the _tags_.

=== Disabling metrics collection

To disable the gRPC client metrics when `quarkus-micrometer` is used, add the following property to the application configuration:

[source, properties]
----
quarkus.micrometer.binder.grpc-client.enabled=false
----