Interceptors in gRPC-Web
We’re pleased to announce support for interceptors in gRPC-web as of release 1.1.0. While the current design is based on gRPC client interceptors available from other gRPC languages, it also includes gRPC-web specific features that should make interceptors easy to adopt and use alongside modern web frameworks.
Introduction
Similar to other gRPC languages, gRPC-web supports unary and
server-streaming interceptors. For each kind of interceptor, we’ve defined an
interface containing a single intercept() method:
UnaryInterceptorStreamInterceptor
This is how the UnaryInterceptor interface is declared:
/*
* @interface
*/
const UnaryInterceptor = function() {};
/**
* @template REQUEST, RESPONSE
* @param {!Request<REQUEST, RESPONSE>} request
* @param {function(!Request<REQUEST,RESPONSE>):!Promise<!UnaryResponse<RESPONSE>>}
* invoker
* @return {!Promise<!UnaryResponse<RESPONSE>>}
*/
UnaryInterceptor.prototype.intercept = function(request, invoker) {};
The intercept() method takes two parameters:
- A
requestof type grpc.web.Request - An
invoker, which performs the actual RPC when invoked
The StreamInterceptor interface declaration is similar, except that the
invoker return type is ClientReadableStream instead of Promise. For
implementation details, see interceptor.js.
Note
AStreamInterceptor can be applied to any RPC with a ClientReadableStream
return type, whether it’s a unary or a server-streaming RPC.What can I do with an interceptor?
An interceptor allows you to do the following:
- Update the original gRPC request before passing it along — for example, you might inject extra information such as auth headers
- Manipulate the behavior of the original invoker function, such as bypassing the call so that you can use a cached result instead
- Update the response before it’s returned to the client
You’ll see some examples next.
Unary interceptor example
The code given below illustrates a unary interceptor that does the following:
- It prepends a string to the gRPC request message before the RPC.
- It prepends a string to the gRPC response message after it’s received.
This simple unary interceptor is defined as a class that implements the
UnaryInterceptor interface:
/**
* @constructor
* @implements {UnaryInterceptor}
*/
const SimpleUnaryInterceptor = function() {};
/** @override */
SimpleUnaryInterceptor.prototype.intercept = function(request, invoker) {
// Update the request message before the RPC.
const reqMsg = request.getRequestMessage();
reqMsg.setMessage('[Intercept request]' + reqMsg.getMessage());
// After the RPC returns successfully, update the response.
return invoker(request).then((response) => {
// You can also do something with response metadata here.
console.log(response.getMetadata());
// Update the response message.
const responseMsg = response.getResponseMessage();
responseMsg.setMessage('[Intercept response]' + responseMsg.getMessage());
return response;
});
};
Stream interceptor example
More care is needed to intercept server-streamed responses from a
ClientReadableStream using a StreamInterceptor. These are the main steps to
follow:
- Create a
ClientReadableStream-wrapper class, and use it to intercept stream events such as the reception of server responses. - Create a class that implements
StreamInterceptorand that uses the stream wrapper.
The following sample stream-wrapper class intercepts responses and prepends a string to response messages:
/**
* A ClientReadableStream wrapper.
*
* @template RESPONSE
* @implements {ClientReadableStream}
* @constructor
* @param {!ClientReadableStream<RESPONSE>} stream
*/
const InterceptedStream = function(stream) {
this.stream = stream;
};
/** @override */
InterceptedStream.prototype.on = function(eventType, callback) {
if (eventType == 'data') {
const newCallback = (response) => {
// Update the response message.
const msg = response.getMessage();
response.setMessage('[Intercept response]' + msg);
// Pass along the updated response.
callback(response);
};
// Register the new callback.
this.stream.on(eventType, newCallback);
} else {
// You can also override 'status', 'end', and 'error' eventTypes.
this.stream.on(eventType, callback);
}
return this;
};
/** @override */
InterceptedStream.prototype.cancel = function() {
this.stream.cancel();
return this;
};
The intercept() method of the sample interceptor returns a wrapped stream:
/**
* @constructor
* @implements {StreamInterceptor}
*/
const TestStreamInterceptor = function() {};
/** @override */
TestStreamInterceptor.prototype.intercept = function(request, invoker) {
return new InterceptedStream(invoker(request));
};
Binding interceptors
By passing an array of interceptor instances using an appropriate option key, you can bind interceptors to a client when the client is instantiated:
const promiseClient = new MyServicePromiseClient(
host, creds, {'unaryInterceptors': [interceptor1, interceptor2, interceptor3]});
const client = new MyServiceClient(
host, creds, {'streamInterceptors': [interceptor1, interceptor2, interceptor3]});
Note
Interceptors are executed in reverse order for request processing, and in order for response processing, as illustrated here:

Feedback
Found a problem with grpc-web or need a feature? File an issue over the
grpc-web repository. If you have general questions or comments, then
consider posting to the gRPC mailing list or sending us an email at
grpc-web-team@google.com.