gRPC · Testing · Protobuf

A gRPC client in the browser: test gRPC APIs online

How to call and test a gRPC API from the browser with Swastik Codes: loading the schema by reflection or .proto upload, all four call types, metadata, deadlines and swc.test scripts.

By · · Updated · 8 min read

Browsers cannot speak native gRPC: the protocol needs full control over HTTP/2 frames and trailers, which the fetch API does not give a web page. That is why "test gRPC online" usually means installing a desktop tool or running grpcurl in a terminal. Swastik Codes takes another route. Its web app hands the call to the swastik.codes server, which makes a real gRPC call over HTTP/2 and streams the results back to your tab. You get a full gRPC client in the browser: load services by server reflection or from .proto files, make unary, server-streaming, client-streaming and bidirectional calls, set metadata and deadlines, and write test scripts.

This guide uses the public test server at grpcb.in, which supports server reflection on port 9000 (plaintext) and 9001 (TLS), so you can follow along before pointing the client at your own service.

What you need

  • A gRPC server reachable from the public internet. The relay refuses private, loopback and other reserved addresses, so localhost:50051 or an internal IP will not work from the web app. Use a staging host or a tunnel with a public hostname. The desktop app for Windows and Linux calls services from your own machine.
  • Either server reflection enabled on the server, or the .proto files that describe it.
  • A free Swastik Codes account. The web app needs one, because calls run through the server.

Step 1: create a gRPC request

  1. Open the app and sign in.
  2. Click + in the tab bar and choose gRPC from the New request menu.
  3. Enter the server address. Any of these forms work: host:port, grpc://host:port (plaintext), grpcs://host:port (TLS), or http:// and https://. For the demo, enter grpcb.in:9001 and turn on Use TLS, or simply enter grpcs://grpcb.in:9001.

The address field also accepts a pasted grpcurl command, and the code button next to it copies the current call as a grpcurl command. That makes it easy to move between a teammate's terminal and the app.

Step 2: load the service definition

gRPC messages are Protobuf, so the client needs the schema before it can turn your JSON into bytes. Open the Service definition tab and pick a Schema source.

Server reflection

Choose Server reflection and click Reflect (or Load services). Swastik Codes asks the server for its services through the reflection API (grpc.reflection.v1, falling back to v1alpha for servers that only implement the older version). With grpcb.in you will see services such as hello.HelloService, grpcbin.GRPCBin and addsvc.Add. Reflection services themselves are hidden from the list.

If your server requires authentication even for reflection, add the metadata first (see Step 4); it is sent with the reflection request too.

.proto files

Many production servers disable reflection. In that case choose .proto files and:

  • click Add .proto files to upload one or more files, or add a whole folder so relative import paths are kept, or
  • paste a URL to a raw .proto file and click Fetch.

If your files import each other from different roots, list the directories under Import paths, one per line, exactly like protoc -I. Imports are resolved against those paths first and then by path suffix or file name. The files are compiled in memory on the server, and well-known types such as google/protobuf/timestamp.proto are available.

Click Load protos. The method dropdown in the address bar now lists every service and method, grouped by service.

Step 3: make a unary call

Select hello.HelloService/SayHello. A badge next to the tabs tells you the call shape: unary, server streaming, client streaming or bidirectional streaming.

On the Message tab, click Use example message to get a JSON skeleton generated from the request type. For SayHello it has one field, greeting. Edit it:

JSON
{ "greeting": "Swastik" }

Press Invoke. The response panel shows the received message:

JSON
{ "reply": "hello Swastik" }

along with the response metadata (headers), trailers and the final status, for example 0 OK, plus the time to the first message and the total duration. Variables work in the message, the metadata and the address, so {"greeting": "{{name}}"} is fine.

Step 4: metadata, auth and deadlines

gRPC metadata is the equivalent of HTTP headers. Add keys on the Metadata tab, for example x-request-id or x-tenant. On the Authorization tab you can choose Bearer, Basic, API key or OAuth 2.0, which become metadata entries such as authorization: Bearer …, or inherit auth from the collection.

Under Settings you will find:

  • Deadline (ms): the call is cancelled with DEADLINE_EXCEEDED if it takes longer. Set it to a small value to check how your service handles a slow downstream dependency.
  • TLS options: server certificate verification, a Server name override for SNI, and PEM fields for a custom CA certificate, a client certificate and a client key (for mutual TLS).
  • Max send message size and Max receive message size in MB, a Keepalive interval for HTTP/2 pings, and whether to include fields with default values in the response. Proto3 omits default values on the wire, so turning this on shows 0, "" and false fields explicitly.

Step 5: streaming calls

Streaming is where browser-based tools usually give up, and where the relay design pays off.

Server streaming. Select hello.HelloService/LotsOfReplies, send {"greeting": "Swastik"} and press Invoke. The server sends ten replies, and each one appears in the log as it arrives, followed by the final status.

Client streaming and bidirectional streaming. Select LotsOfGreetings or BidiHello. Now Invoke opens the stream without sending anything yet. Write a message and click Send message as often as you like; each message is sent on the open stream. Click End stream to half-close your side. For a bidirectional method you see the server's replies interleaved with your messages; for client streaming the single reply arrives after you end the stream.

Saved messages help here. Click save in the composer to keep a message with the request, then send it again from the Saved tab in one click.

Step 6: write gRPC tests

The Scripts tab has three hooks, each with a snippet menu:

  • Before invoke runs before the call. Change swc.request.metadata or set variables, for example to compute a signature.
  • On message runs for every message the server sends, with the message in swc.message.
  • After response runs once the call has ended, with the status, metadata, trailers and all messages in swc.response.

A typical After response script for the server-streaming call above:

JavaScript
swc.test('Status code is 0 (OK)', function () {
  swc.response.to.have.statusCode(0);
});

swc.test('Ten replies', function () {
  swc.expect(swc.response.messages.count()).to.equal(10);
});

swc.test('Every message has a reply', function () {
  swc.response.messages.to.have.property('reply');
});

swc.test('Fast enough', function () {
  swc.response.to.have.responseTime.below(2000);
});

And an On message check that runs per message:

JavaScript
swc.test('Reply mentions the greeting', function () {
  swc.expect(swc.message.data.reply).to.include('Swastik');
});

Results appear in the response panel as passed and failed tests. Because gRPC requests are saved in collections like any other request, the collection runner and monitors run them too, scripts included. The syntax is the same pm.* API used for HTTP tests; see writing API tests with pm scripts for the full picture.

A minimal gRPC server with reflection

To test your own service, enable reflection in development and staging. In Node.js with @grpc/grpc-js:

JavaScript
// server.mjs  (npm install @grpc/grpc-js @grpc/proto-loader @grpc/reflection)
import * as grpc from '@grpc/grpc-js';
import * as loader from '@grpc/proto-loader';
import { ReflectionService } from '@grpc/reflection';

const def = loader.loadSync('greeter.proto', { keepCase: true });
const proto = grpc.loadPackageDefinition(def);

const server = new grpc.Server();
server.addService(proto.demo.Greeter.service, {
  sayHello(call, callback) {
    callback(null, { message: `Hello, ${call.request.name}` });
  },
});
new ReflectionService(def).addToServer(server);

server.bindAsync(`0.0.0.0:${process.env.PORT || 50051}`, grpc.ServerCredentials.createInsecure(), (err) => {
  if (err) throw err;
  console.log('gRPC server ready');
});

with greeter.proto:

Protobuf
syntax = "proto3";
package demo;

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply);
}
message HelloRequest { string name = 1; }
message HelloReply { string message = 1; }

In Go, register reflection.Register(s) from google.golang.org/grpc/reflection; in Python, use grpc_reflection.v1alpha.reflection.enable_server_reflection. Put the server behind a TLS-terminating proxy that supports HTTP/2 end to end, or serve TLS from the process itself.

Troubleshooting

"Load services first" stays in the method list. Reflection or proto compilation has not run yet, or it failed. Check the error under the Service definition tab.

Reflection fails with UNIMPLEMENTED. The server does not have reflection enabled. Upload .proto files instead.

The call fails with UNAVAILABLE straight away. Usually a TLS mismatch: TLS on for a plaintext port, or off for a TLS port. Many gRPC hosts behind a load balancer only accept TLS on 443.

Import errors when loading protos. Add the directory that the import statements are relative to under Import paths, or add the whole folder so relative paths are kept.

RESOURCE_EXHAUSTED on large messages. Raise the max send or receive message size in Settings. The relay caps a single message at 16 MiB.

Fields with zero values are missing from the response. That is proto3 behaviour. Turn on the option to include default-valued fields.

Frequently asked questions

Can a browser make gRPC calls directly?

Not native gRPC. Browsers do not expose the HTTP/2 trailers that gRPC relies on, which is why gRPC-Web and proxies exist. Swastik Codes runs the real gRPC call on its server and streams the results to the browser, so your service does not need gRPC-Web.

Do I need server reflection?

No. Reflection is the quickest way to load a schema, but you can always upload .proto files or fetch them from a URL instead.

Are streaming RPCs supported?

Yes: unary, server streaming, client streaming and bidirectional streaming. For streams that you send on, Invoke opens the stream, you send messages one by one, and End stream half-closes it.

Can I test gRPC services on localhost?

Not from the web app, because the relay refuses private and loopback addresses. Use the desktop app, or expose the service on a public hostname for testing.

Can I run gRPC tests on a schedule?

Yes. gRPC requests saved in a collection run in the collection runner and in scheduled monitors, with their scripts, so you can track a gRPC API over time and get an email or webhook alert when it fails.

Try it

Open Swastik Codes, create a gRPC request, enter grpcs://grpcb.in:9001, reflect and invoke SayHello. You will need a free account to send calls. The gRPC client feature page summarises every option, and if your service also exposes HTTP or GraphQL endpoints, the REST API client and the GraphQL client live in the same workspace.

Try it on your own API

Swastik Codes runs in the browser and is free to use. Create an account to send requests, sync your workspaces and invite your team.