0
# Client Usage
1
2
Client functionality for consuming gRPC services with reactive programming support and configuration management. Clients are injected as CDI beans and provide reactive method signatures using Mutiny types.
3
4
## Capabilities
5
6
### @GrpcClient Annotation
7
8
Qualifies an injected gRPC client. The annotation provides configuration name resolution and supports programmatic creation of annotation literals.
9
10
```java { .api }
11
/**
12
* Qualifies an injected gRPC client.
13
*/
14
@Qualifier
15
@Target({ FIELD, PARAMETER })
16
@Retention(RUNTIME)
17
public @interface GrpcClient {
18
19
/**
20
* Constant value for {@link #value()} indicating that the annotated element's name should be used as-is.
21
*/
22
String ELEMENT_NAME = "<<element name>>";
23
24
/**
25
* The name is used to configure the gRPC client, e.g. the location, TLS/SSL, etc.
26
*
27
* @return the client name
28
*/
29
String value() default ELEMENT_NAME;
30
31
final class Literal extends AnnotationLiteral<GrpcClient> implements GrpcClient {
32
private static final long serialVersionUID = 1L;
33
private final String value;
34
35
/**
36
* Creates a new instance of {@link Literal}.
37
*
38
* @param value the name of the gRPC service, must not be {@code null}, must not be {@code blank}
39
* @return the literal instance.
40
*/
41
public static Literal of(String value);
42
43
/**
44
* @return the service name.
45
*/
46
public String value();
47
}
48
}
49
```
50
51
**Usage Examples:**
52
53
```java
54
import io.quarkus.grpc.GrpcClient;
55
import jakarta.inject.Inject;
56
57
public class GreetingClientService {
58
59
// Using field name as client name
60
@Inject
61
@GrpcClient
62
MutinyGreetingGrpc greeting;
63
64
// Using explicit client name
65
@Inject
66
@GrpcClient("user-service")
67
MutinyUserGrpc userService;
68
69
// Using method parameter injection
70
public void processGreeting(@GrpcClient("greeting") MutinyGreetingGrpc client) {
71
// Use client
72
}
73
}
74
```
75
76
### MutinyClient Interface
77
78
Represents a convenient Mutiny client generated for a gRPC service. Provides access to the underlying gRPC stub and supports creating new instances with custom stubs.
79
80
```java { .api }
81
/**
82
* Represents a convenient Mutiny client generated for a gRPC service.
83
*/
84
public interface MutinyClient<T extends AbstractStub<T>> {
85
86
/**
87
*
88
* @return the underlying stub
89
*/
90
T getStub();
91
92
MutinyClient<T> newInstanceWithStub(T stub);
93
}
94
```
95
96
**Usage Examples:**
97
98
```java
99
import io.quarkus.grpc.GrpcClient;
100
import io.quarkus.grpc.MutinyClient;
101
import io.grpc.stub.AbstractStub;
102
import io.smallrye.mutiny.Uni;
103
104
public class ProductClientService {
105
106
@Inject
107
@GrpcClient("product-service")
108
MutinyClient<ProductServiceGrpc.ProductServiceStub> productClient;
109
110
public Uni<Product> getProduct(Long productId) {
111
ProductRequest request = ProductRequest.newBuilder()
112
.setId(productId)
113
.build();
114
115
return productClient.getStub().getProduct(request);
116
}
117
118
public MutinyClient<ProductServiceGrpc.ProductServiceStub> withDeadline(Duration timeout) {
119
var stubWithDeadline = productClient.getStub()
120
.withDeadlineAfter(timeout.toMillis(), TimeUnit.MILLISECONDS);
121
return productClient.newInstanceWithStub(stubWithDeadline);
122
}
123
}
124
```
125
126
### Client Configuration
127
128
Clients are configured through application properties using the client name:
129
130
```properties
131
# Basic configuration
132
quarkus.grpc.clients.greeting.host=localhost
133
quarkus.grpc.clients.greeting.port=9000
134
135
# TLS configuration
136
quarkus.grpc.clients.user-service.host=secure-service.example.com
137
quarkus.grpc.clients.user-service.port=443
138
quarkus.grpc.clients.user-service.ssl.trust-store=path/to/truststore.p12
139
quarkus.grpc.clients.user-service.ssl.trust-store-password=secret
140
141
# Load balancing
142
quarkus.grpc.clients.payment-service.host=payment-service
143
quarkus.grpc.clients.payment-service.port=9000
144
quarkus.grpc.clients.payment-service.load-balancing-policy=round_robin
145
```
146
147
### Client Utilities
148
149
Utility class for working with gRPC clients, including header attachment and proxy handling.
150
151
```java { .api }
152
/**
153
* gRPC client utilities
154
*/
155
public class GrpcClientUtils {
156
157
/**
158
* Attach headers to a gRPC client.
159
*
160
* To make a call with headers, first invoke this method and then perform the intended call with the <b>returned</b> client
161
*
162
* @param client any kind of gRPC client
163
* @param extraHeaders headers to attach
164
* @param <T> type of the client
165
* @return a client with headers attached
166
*/
167
public static <T> T attachHeaders(T client, Metadata extraHeaders);
168
169
public static <T> T getProxiedObject(T client);
170
}
171
```
172
173
**Usage Examples:**
174
175
```java
176
import io.quarkus.grpc.GrpcClientUtils;
177
import io.grpc.Metadata;
178
import io.grpc.Metadata.Key;
179
180
public class AuthenticatedClientService {
181
182
@Inject
183
@GrpcClient("secure-service")
184
MutinySecureServiceGrpc secureClient;
185
186
public Uni<SecureResponse> callSecureService(String token, SecureRequest request) {
187
// Create headers
188
Metadata headers = new Metadata();
189
Key<String> authKey = Key.of("authorization", Metadata.ASCII_STRING_MARSHALLER);
190
headers.put(authKey, "Bearer " + token);
191
192
// Attach headers to client
193
var clientWithHeaders = GrpcClientUtils.attachHeaders(secureClient, headers);
194
195
// Make the call
196
return clientWithHeaders.secureCall(request);
197
}
198
199
public Uni<UserInfo> getUserInfo(String userId) {
200
// Get actual client instance (unwrap proxy if needed)
201
var actualClient = GrpcClientUtils.getProxiedObject(secureClient);
202
203
UserRequest request = UserRequest.newBuilder()
204
.setUserId(userId)
205
.build();
206
207
return actualClient.getUserInfo(request);
208
}
209
}
210
```
211
212
### Reactive Call Patterns
213
214
Clients support all gRPC call patterns using Mutiny reactive types:
215
216
```java
217
public class ComprehensiveClientExample {
218
219
@Inject
220
@GrpcClient("data-service")
221
MutinyDataServiceGrpc dataClient;
222
223
// Unary call: request -> Uni<response>
224
public Uni<DataResponse> getData(String id) {
225
DataRequest request = DataRequest.newBuilder().setId(id).build();
226
return dataClient.getData(request);
227
}
228
229
// Server streaming: request -> Multi<response>
230
public Multi<DataItem> streamData(String query) {
231
StreamRequest request = StreamRequest.newBuilder().setQuery(query).build();
232
return dataClient.streamData(request);
233
}
234
235
// Client streaming: Multi<request> -> Uni<response>
236
public Uni<UploadResponse> uploadData(Multi<DataChunk> chunks) {
237
return dataClient.uploadData(chunks);
238
}
239
240
// Bidirectional streaming: Multi<request> -> Multi<response>
241
public Multi<ProcessedData> processData(Multi<RawData> rawData) {
242
return dataClient.processData(rawData);
243
}
244
}
245
```
246
247
### Error Handling
248
249
Client errors are propagated as Mutiny failures and can be handled using standard reactive patterns:
250
251
```java
252
public class ErrorHandlingExample {
253
254
@Inject
255
@GrpcClient("unreliable-service")
256
MutinyUnreliableServiceGrpc client;
257
258
public Uni<ServiceResponse> callWithRetry(ServiceRequest request) {
259
return client.callService(request)
260
.onFailure(StatusRuntimeException.class)
261
.retry().withBackOff(Duration.ofSeconds(1)).atMost(3)
262
.onFailure().transform(ex -> {
263
if (ex instanceof StatusRuntimeException) {
264
StatusRuntimeException sre = (StatusRuntimeException) ex;
265
return new ServiceException("Service call failed: " + sre.getStatus());
266
}
267
return ex;
268
});
269
}
270
}
271
```
272
273
### Programmatic Client Creation
274
275
For dynamic client creation, use CDI programmatic lookup with GrpcClient.Literal:
276
277
```java
278
import jakarta.enterprise.inject.Instance;
279
import io.quarkus.grpc.GrpcClient;
280
281
@ApplicationScoped
282
public class DynamicClientService {
283
284
@Inject
285
Instance<Object> instance;
286
287
public <T> T getClient(String serviceName, Class<T> clientType) {
288
return instance.select(clientType, GrpcClient.Literal.of(serviceName)).get();
289
}
290
291
public Uni<GreetingResponse> greetDynamically(String serviceName, String name) {
292
MutinyGreetingGrpc client = getClient(serviceName, MutinyGreetingGrpc.class);
293
294
GreetingRequest request = GreetingRequest.newBuilder()
295
.setName(name)
296
.build();
297
298
return client.greet(request);
299
}
300
}
301
```