Skip to content

What Is gRPC?

gRPC stands for gRPC Remote Procedure Call (yes, it’s recursive). The important half is RPC: the idea that calling code on another machine should look like calling a normal function.

Without RPC, a service-to-service call means: build a URL, choose a verb, serialize a body to JSON, send an HTTP request, check the status code, parse the response, and hope both sides agreed on the shape. RPC collapses all of that into one line:

user = userService.GetUser(id: 42)

You call a method. You get a typed result. The transport, serialization, and error handling are generated for you and hidden behind the call.

The thing that makes this possible is a contract written before any implementation — a .proto file. It declares the messages (the data) and the service (the methods):

syntax = "proto3";
package user.v1;
message GetUserRequest {
int64 id = 1;
}
message User {
int64 id = 1;
string name = 2;
string email = 3;
}
service UserService {
rpc GetUser(GetUserRequest) returns (User);
}

This file is the single source of truth. It is language-neutral: the same .proto produces a Go server, a Python client, and a Node service that all interoperate perfectly, because they were all generated from the same declaration.

The workflow has three steps, and only the middle one is code you write:

flowchart LR
  proto[".proto contract"] -->|protoc| stubs["generated stubs
(client + server)"]
  stubs --> impl["you implement
the server methods"]
  impl --> run["client calls
stub.GetUser(42)"]
  run -->|HTTP/2 + protobuf| server["server runs
GetUser, returns User"]
  server --> run
From contract to running call
  1. Write the .proto and run protoc (the protobuf compiler) to generate typed stubs.
  2. Implement the server — fill in the body of GetUser.
  3. Call from the client — invoke the generated stub method; the request is serialized to protobuf, sent over HTTP/2, and the typed response comes back.

Here is the client call in three languages. Notice how little there is: no URL, no JSON, no status-code checking — just a method call.

conn, _ := grpc.NewClient("localhost:50051", grpc.WithTransportCredentials(insecure.NewCredentials()))
defer conn.Close()
client := userv1.NewUserServiceClient(conn)
user, err := client.GetUser(ctx, &userv1.GetUserRequest{Id: 42})
if err != nil {
log.Fatal(err)
}
fmt.Println(user.Name) // typed field, no JSON parsing

Because everything flows from the contract, the framework can hand you a lot:

  • Type safety across the wire — the client and server can’t disagree about field names or types; they’re generated from the same schema.
  • Efficient binary encoding — protobuf is smaller and faster to parse than JSON.
  • Streaming — the same model extends to sending many messages in either direction (a whole later module).
  • Cross-language interop — 11+ official languages, all speaking the same protocol.
  • Built-in features — deadlines, cancellation, metadata, interceptors, and load balancing come with the framework, not as things you reinvent.
What does the "RPC" in gRPC mean for how you write code?
In the gRPC workflow, what is the single source of truth?
Why can a Go server and a Python client interoperate perfectly in gRPC?
Which of these does gRPC give you without extra work?