Skip to main content

Pipe

Pipes transform a parameter after it is extracted and before the RPC method runs.

interface Pipe<TInput = unknown, TOutput = unknown> {
execute(input: TInput, metadata: PipeMetadata): Promise<TOutput> | TOutput;
}

metadata tells you the class, method, and parameter index. Throw a GrpcError to finish the call. The RPC method does not run.

Pipes run only for parameters created with @Call(), @Callback(), or createCustomParameterDecorator(). A method that takes the raw (call, callback) arguments has no pipe step.

Global pipes from useGlobalPipe() run before the pipes passed to that parameter decorator. A global pipe sees every decorated parameter, so it has to accept each of those values. A string pipe registered globally will receive the call when the method also uses @Call().

import {
createCustomParameterDecorator,
type Pipe,
RPC,
Service,
} from '@inversifyjs/grpc-core';
import { injectable } from 'inversify';

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

@injectable()
export class UppercasePipe implements Pipe<string, string> {
public execute(input: string): string {
return input.toUpperCase();
}
}

@injectable()
export class PrefixPipe implements Pipe<string, string> {
public execute(input: string): string {
return `hero:${input}`;
}
}

export const heroId: ParameterDecorator = createCustomParameterDecorator(
(call: { request: HeroRequest }): string => call.request.id,
PrefixPipe,
);

@Service(heroServiceDefinition)
export class PipedHeroService {
@RPC('GetHero')
public getHero(@heroId id: string): HeroResponse {
return {
name: id,
};
}
}

Bind PrefixPipe. GetHero with id abc returns { name: 'hero:abc' }.

Register UppercasePipe globally before build() and bind it too:

adapter.useGlobalPipe(UppercasePipe);

The same request then returns { name: 'hero:ABC' }, because the global pipe runs first.