Skip to content

Commit d4207b9

Browse files
Merge branch 'AlexDaSoul-feature/add-grpc-metadata'
2 parents 3ee09b7 + dda88fa commit d4207b9

File tree

1 file changed

+85
-10
lines changed

1 file changed

+85
-10
lines changed

content/microservices/grpc.md

Lines changed: 85 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ Next, we need to implement the service. To define a handler that fulfills this d
123123
@Controller()
124124
export class HeroesController {
125125
@GrpcMethod('HeroesService', 'FindOne')
126-
findOne(data: HeroById, metadata: any): Hero {
126+
findOne(data: HeroById, metadata: Metadata, call: ServerUnaryCall<any>): Hero {
127127
const items = [
128128
{ id: 1, name: 'John' },
129129
{ id: 2, name: 'Doe' },
@@ -135,7 +135,7 @@ export class HeroesController {
135135
@Controller()
136136
export class HeroesController {
137137
@GrpcMethod('HeroesService', 'FindOne')
138-
findOne(data, metadata) {
138+
findOne(data, metadata, call) {
139139
const items = [
140140
{ id: 1, name: 'John' },
141141
{ id: 2, name: 'Doe' },
@@ -145,11 +145,12 @@ export class HeroesController {
145145
}
146146
```
147147

148-
> info **Hint** The `@GrpcMethod()` decorator is imported from the `@nestjs/microservices` package.
148+
> info **Hint** The `@GrpcMethod()` decorator is imported from the `@nestjs/microservices` package, while `Metadata` and `ServerUnaryCall` from the `grpc` package.
149149
150150
The decorator shown above takes two arguments. The first is the service name (e.g., `'HeroesService'`), corresponding to the `HeroesService` service definition in `hero.proto`. The second (the string `'FindOne'`) corresponds to the `FindOne()` rpc method defined within `HeroesService` in the `hero.proto` file.
151151

152-
The `findOne()` handler method takes two arguments, the `data` passed from the caller and `metadata` that stores gRPC request metadata.
152+
The `findOne()` handler method takes three arguments, the `data` passed from the caller, `metadata` that stores gRPC
153+
request metadata and `call` to obtain the `GrpcCall` object properties such as `sendMetadata` for send metadata to client.
153154

154155
Both `@GrpcMethod()` decorator arguments are optional. If called without the second argument (e.g., `'FindOne'`), Nest will automatically associate the `.proto` file rpc method with the handler based on converting the handler name to upper camel case (e.g., the `findOne` handler is associated with the `FindOne` rpc call definition). This is shown below.
155156

@@ -158,7 +159,7 @@ Both `@GrpcMethod()` decorator arguments are optional. If called without the sec
158159
@Controller()
159160
export class HeroesController {
160161
@GrpcMethod('HeroesService')
161-
findOne(data: HeroById, metadata: any): Hero {
162+
findOne(data: HeroById, metadata: Metadata, call: ServerUnaryCall<any>): Hero {
162163
const items = [
163164
{ id: 1, name: 'John' },
164165
{ id: 2, name: 'Doe' },
@@ -170,7 +171,7 @@ export class HeroesController {
170171
@Controller()
171172
export class HeroesController {
172173
@GrpcMethod('HeroesService')
173-
findOne(data, metadata) {
174+
findOne(data, metadata, call) {
174175
const items = [
175176
{ id: 1, name: 'John' },
176177
{ id: 2, name: 'Doe' },
@@ -187,7 +188,7 @@ You can also omit the first `@GrpcMethod()` argument. In this case, Nest automat
187188
@Controller()
188189
export class HeroesService {
189190
@GrpcMethod()
190-
findOne(data: HeroById, metadata: any): Hero {
191+
findOne(data: HeroById, metadata: Metadata, call: ServerUnaryCall<any>): Hero {
191192
const items = [
192193
{ id: 1, name: 'John' },
193194
{ id: 2, name: 'Doe' },
@@ -199,7 +200,7 @@ export class HeroesService {
199200
@Controller()
200201
export class HeroesService {
201202
@GrpcMethod()
202-
findOne(data, metadata) {
203+
findOne(data, metadata, call) {
203204
const items = [
204205
{ id: 1, name: 'John' },
205206
{ id: 2, name: 'Doe' },
@@ -306,6 +307,21 @@ call() {
306307
}
307308
```
308309

310+
To send gRPC metadata (along with the request), you can pass a second argument, as follows:
311+
312+
```typescript
313+
call(): Observable<any> {
314+
const metadata = new Metadata();
315+
metadata.add('Set-Cookie', 'yummy_cookie=choco');
316+
317+
return this.heroesService.findOne({ id: 1 }, metadata);
318+
}
319+
```
320+
321+
> info **Hint** The `Metadata` class is imported from the `grpc` package.
322+
323+
Please note that this would require updating the `HeroesService` interface that we've defined a few steps earlier.
324+
309325
A full working example is available [here](https://github.com/nestjs/nest/tree/master/sample/04-grpc).
310326

311327
#### gRPC Streaming
@@ -370,7 +386,7 @@ The `@GrpcStreamMethod()` decorator provides the function parameter as an RxJS `
370386

371387
```typescript
372388
@GrpcStreamMethod()
373-
bidiHello(messages: Observable<any>): Observable<any> {
389+
bidiHello(messages: Observable<any>, metadata: Metadata, call: ServerDuplexStream<any, any>): Observable<any> {
374390
const subject = new Subject();
375391

376392
const onNext = message => {
@@ -386,7 +402,9 @@ bidiHello(messages: Observable<any>): Observable<any> {
386402
}
387403
```
388404

389-
> info **Hint** For supporting full-duplex interaction with the `@GrpcStreamMethod()` decorator, the controller method must return an RxJS `Observable`.
405+
> warning **Warning** For supporting full-duplex interaction with the `@GrpcStreamMethod()` decorator, the controller method must return an RxJS `Observable`.
406+
407+
> info **Hint** The `Metadata` and `ServerUnaryCall` classes/interfaces are imported from the `grpc` package.
390408
391409
According to the service definition (in the `.proto` file), the `BidiHello` method should stream requests to the service. To send multiple asynchronous messages to the stream from a client, we leverage an RxJS `ReplySubject` class.
392410

@@ -440,3 +458,60 @@ lotsOfGreetings(requestStream: any, callback: (err: unknown, value: HelloRespons
440458
```
441459

442460
Here we used the `callback` function to send the response once processing of the `requestStream` has been completed.
461+
462+
#### gRPC Metadata
463+
464+
Metadata is information about a particular RPC call in the form of a list of key-value pairs, where the keys are strings and the values are typically strings but can be binary data. Metadata is opaque to gRPC itself - it lets the client provide information associated with the call to the server and vice versa. Metadata may include authentication tokens, request identifiers and tags for monitoring purposes, and data information such as the number of records in a data set.
465+
466+
To read the metadata in `@GrpcMethod()` handler, use the second argument (metadata), which is of type `Metadata` (imported from the `grpc` package).
467+
468+
To send back metadata from the handler, use the `ServerUnaryCall#sendMetadata()` method (third handler argument).
469+
470+
```typescript
471+
@@filename(heroes.controller)
472+
@Controller()
473+
export class HeroesService {
474+
@GrpcMethod()
475+
findOne(data: HeroById, metadata: Metadata, call: ServerUnaryCall<any>): Hero {
476+
const serverMetadata = new Metadata();
477+
const items = [
478+
{ id: 1, name: 'John' },
479+
{ id: 2, name: 'Doe' },
480+
];
481+
482+
serverMetadata.add('Set-Cookie', 'yummy_cookie=choco');
483+
call.sendMetadata(serverMetadata);
484+
485+
return items.find(({ id }) => id === data.id);
486+
}
487+
}
488+
@@switch
489+
@Controller()
490+
export class HeroesService {
491+
@GrpcMethod()
492+
findOne(data, metadata, call) {
493+
const serverMetadata = new Metadata();
494+
const items = [
495+
{ id: 1, name: 'John' },
496+
{ id: 2, name: 'Doe' },
497+
];
498+
499+
serverMetadata.add('Set-Cookie', 'yummy_cookie=choco');
500+
call.sendMetadata(serverMetadata);
501+
502+
return items.find(({ id }) => id === data.id);
503+
}
504+
}
505+
```
506+
507+
Likewise, to read the metadata in handlers annotated with the `@GrpcStreamMethod()` handler ([subject strategy](microservices/grpc#subject-strategy)), use the second argument (metadata), which is of type `Metadata` (imported from the `grpc` package).
508+
509+
To send back metadata from the handler, use the `ServerDuplexStream#sendMetadata()` method (third handler argument).
510+
511+
To read metadata from within the [call stream handlers](microservices/grpc#call-stream-handler) (handlers annotated with `@GrpcStreamCall()` decorator), listen to the `metadata` event on the `requestStream` reference, as follows:
512+
513+
```typescript
514+
requestStream.on('metadata', (metadata: Metadata) => {
515+
const meta = metadata.get('X-Meta');
516+
});
517+
```

0 commit comments

Comments
 (0)