grpcurl is your Swiss Army knife for interacting with gRPC services directly from the command line. Think of it as curl but for gRPC.
Let’s say you have a running gRPC server and you want to test a specific method. You’ve got the .proto file defining the service, but no client application to speak of. This is where grpcurl shines. Instead of spinning up a whole client or writing boilerplate code, you can just grpcurl it.
Imagine a simple Greeter service with a SayHello method:
// greeter.proto
syntax = "proto3";
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
Here’s how you’d call SayHello using grpcurl, assuming your server is running on localhost:50051:
grpcurl -plaintext localhost:50051 greeter.Greeter/SayHello -d '{"name": "World"}'
The output will be:
{
"message": "Hello, World!"
}
Breaking this down:
-plaintext: This tellsgrpcurlto communicate over plain TCP, not TLS. For secure connections, you’d omit this and potentially use-insecureif the server uses self-signed certs and you want to bypass verification (though not recommended for production).localhost:50051: This is the address of your gRPC server.greeter.Greeter/SayHello: This is the fully qualified name of the gRPC method you want to call. It follows the patternpackage.ServiceName/MethodName.-d '{"name": "World"}': This is the request payload. For gRPC, it’s typically JSON, butgrpcurlcan also handle Protobuf binary format. The structure of the JSON must match the fields defined in yourHelloRequestmessage.
grpcurl can also introspect services. If you don’t know the exact method name or the structure of the request, you can list the services and methods available on the server:
grpcurl -plaintext localhost:50051 list
This might output something like:
greeter.Greeter
And to see the methods for a specific service:
grpcurl -plaintext localhost:50051 greeter.Greeter list
Output:
SayHello rpc (greeter.HelloRequest) returns (greeter.HelloReply)
This is incredibly useful for debugging and understanding how a gRPC API is structured. You can even describe a specific method to see its request and response types in detail:
grpcurl -plaintext localhost:50051 greeter.Greeter.SayHello describe
Output:
rpc SayHello (greeter.HelloRequest) returns (greeter.HelloReply)
greeter.HelloRequest is a message:
string name = 1;
greeter.HelloReply is a message:
string message = 1;
This gives you the schema for the request and response messages, which is crucial for constructing valid payloads.
One of grpcurl’s most powerful features is its ability to stream data. If your gRPC method is a client-streaming, server-streaming, or bidirectional-streaming RPC, grpcurl handles it gracefully. For a server-streaming RPC, grpcurl will print each message as it arrives. For client-streaming or bidirectional, you’ll typically need to provide multiple -d flags or use a file as input for the data.
Consider a hypothetical Chat service with a StreamMessages method that’s bidirectional:
// chat.proto
syntax = "proto3";
service Chat {
rpc StreamMessages (stream ChatMessage) returns (stream ChatMessage) {}
}
message ChatMessage {
string sender = 1;
string content = 2;
}
You could interact with this using multiple -d flags, but it quickly becomes unwieldy. A more practical approach for streaming is to feed data from a file. Let’s say you have messages.jsonl (JSON Lines format, where each line is a valid JSON object):
{"sender": "Alice", "content": "Hello server!"}
{"sender": "Alice", "content": "How are you?"}
And you want to see the server’s responses. You can use grpcurl like this:
cat messages.jsonl | grpcurl -plaintext -import-path . -proto chat.proto localhost:50051 chat.Chat/StreamMessages
In this case, -import-path . and -proto chat.proto are important. grpcurl needs to know where to find the .proto file to understand the message structures, especially for streaming. It will then send each line from messages.jsonl as a separate ChatMessage and print the incoming ChatMessage responses from the server.
The ability to use -import-path and -proto allows grpcurl to resolve dependencies and understand complex Protobuf definitions, not just simple ones. This makes it suitable for interacting with even the most intricate gRPC APIs.
A subtle but crucial point: when you’re dealing with Protobuf’s Any type, grpcurl can serialize and deserialize it. This means you can send complex, nested, or polymorphic messages if your API uses Any for flexible payloads. You’d typically represent the "type URL" and the serialized message within the Any field.
The next logical step after mastering direct calls is to integrate grpcurl into your automated workflows, perhaps for health checks or basic integration tests.