Skip to main content

RPC

@RPC() marks a method as the handler for one RPC name from the service definition.

function RPC(name: string): MethodDecorator

name is the definition key. With the ts-proto output from Getting started, rpc GetHero is getHero. An empty name throws when the decorator runs. A name missing from the definition, or a definition method with no @RPC(), throws when the server is built.

The definition's requestStream and responseStream flags choose how the method receives the call.

KindRequestResponseMethod shape
Unarysingle messagesingle messageReturn the response, or use @Callback()
Client streamingstreamsingle messageReturn the response when the input stream ends, or use @Callback()
Server streamingsingle messagestreamWrite messages on the call, then end()
BidirectionalstreamstreamRead and write the same call

Unary and client-streaming methods that return a message let the adapter send it. Return undefined when you have already finished the call yourself. Server-streaming and bidirectional methods always own the call: the adapter does not send their return value.

These streaming handlers load hero.proto with @grpc/proto-loader, so @RPC() uses the .proto name, such as ListHeroes.

Client streaming​

import { type ServerReadableStream } from '@grpc/grpc-js';
import { RPC, Service } from '@inversifyjs/grpc-core';

import {
heroUploadServiceDefinition,
type UploadRequest,
type UploadResponse,
} from './loadHeroServiceDefinition.js';

@Service(heroUploadServiceDefinition)
export class HeroUploadService {
@RPC('UploadHeroes')
public async uploadHeroes(
call: ServerReadableStream<UploadRequest, UploadResponse>,
): Promise<UploadResponse> {
const names: string[] = [];

return new Promise<UploadResponse>(
(
resolve: (response: UploadResponse) => void,
reject: (error: Error) => void,
): void => {
call.on('data', (request: UploadRequest): void => {
names.push(request.name);
});
call.on('end', (): void => {
resolve({
names,
});
});
call.on('error', (error: Error): void => {
reject(error);
});
},
);
}
}

Server streaming​

import { type ServerWritableStream } from '@grpc/grpc-js';
import { RPC, Service } from '@inversifyjs/grpc-core';

import {
heroListServiceDefinition,
type HeroRequest,
type HeroResponse,
} from './loadHeroServiceDefinition.js';

@Service(heroListServiceDefinition)
export class HeroListService {
@RPC('ListHeroes')
public listHeroes(
call: ServerWritableStream<HeroRequest, HeroResponse>,
): void {
const id: string = call.request.id;

call.write({
name: `${id}-a`,
});
call.write({
name: `${id}-b`,
});
call.end();
}
}

Bidirectional streaming​

import { type ServerDuplexStream } from '@grpc/grpc-js';
import { RPC, Service } from '@inversifyjs/grpc-core';

import {
type ChatMessage,
heroChatServiceDefinition,
} from './loadHeroServiceDefinition.js';

@Service(heroChatServiceDefinition)
export class HeroChatService {
@RPC('Chat')
public chat(call: ServerDuplexStream<ChatMessage, ChatMessage>): void {
call.on('data', (message: ChatMessage): void => {
call.write({
text: message.text,
});
});
call.on('end', (): void => {
call.end();
});
}
}

A unary handler can return the response directly:

@RPC('getHero')
public getHero(call: { request: HeroRequest }): HeroResponse {
return {
name: call.request.id,
};
}

The full unary server is in Getting started. To take the @grpc/grpc-js call and callback explicitly, see Parameters.