Overview
Protocol: HTTP/2 with Protocol Buffers Serialization: Binary (Protocol Buffers) Endpoint:api.petstoreapi.com:443
When to Use gRPC:
- ✅ High-performance requirements
- ✅ Microservice-to-microservice communication
- ✅ Strongly typed contracts needed
- ✅ Low latency, high throughput
- ✅ Polyglot environments (multiple languages)
- ❌ Browser applications (use REST/GraphQL instead)
- ❌ Simple CRUD operations (REST is simpler)
- ❌ Need HTTP caching (REST has better caching)
How gRPC Works
Copy
Client Server
│ │
├──── Unary Request ──────────>│
│ (protobuf binary) │
│ │
│ ┌────────┴────────┐
│ │ Deserialize │
│ │ Process │
│ │ Serialize │
│ └────────┬────────┘
│ │
│<──── Unary Response ─────────┤
│ (protobuf binary) │
- Binary serialization (smaller, faster)
- HTTP/2 (multiplexing, streaming)
- Strong typing with .proto files
- Built-in code generation
- Bi-directional streaming
Protocol Buffers
petstore.proto
Copy
syntax = "proto3";
package petstore.v1;
service PetService {
rpc GetPet(GetPetRequest) returns (Pet);
rpc ListPets(ListPetsRequest) returns (ListPetsResponse);
rpc CreatePet(CreatePetRequest) returns (Pet);
rpc UpdatePet(UpdatePetRequest) returns (Pet);
rpc DeletePet(DeletePetRequest) returns (DeletePetResponse);
}
message Pet {
string id = 1;
string name = 2;
Species species = 3;
string breed = 4;
int32 age_months = 5;
Status status = 6;
double adoption_fee = 7;
string currency = 8;
}
enum Species {
SPECIES_UNSPECIFIED = 0;
DOG = 1;
CAT = 2;
RABBIT = 3;
BIRD = 4;
REPTILE = 5;
OTHER = 6;
}
enum Status {
STATUS_UNSPECIFIED = 0;
AVAILABLE = 1;
PENDING = 2;
ADOPTED = 3;
NOT_AVAILABLE = 4;
}
message GetPetRequest {
string id = 1;
}
message ListPetsRequest {
Species species = 1;
Status status = 2;
int32 page = 3;
int32 limit = 4;
}
message ListPetsResponse {
repeated Pet data = 1;
Pagination pagination = 2;
}
message Pagination {
int32 page = 1;
int32 limit = 2;
int32 total_items = 3;
int32 total_pages = 4;
}
message CreatePetRequest {
string name = 1;
Species species = 2;
string breed = 3;
int32 age_months = 4;
Status status = 5;
double adoption_fee = 6;
string currency = 7;
}
message UpdatePetRequest {
string id = 1;
Pet pet = 2;
}
message DeletePetRequest {
string id = 1;
}
message DeletePetResponse {
bool success = 1;
string message = 2;
}
Code Examples
Python
Copy
import grpc
from petstore_pb2 import Pet, Species, Status, GetPetRequest, ListPetsRequest, CreatePetRequest
from petstore_pb2_grpc import PetServiceStub
# Create secure channel
credentials = grpc.ssl_channel_credentials()
channel = grpc.secure_channel('api.petstoreapi.com:443', credentials)
# Add authentication token
call_credentials = grpc.access_token_call_credentials('YOUR_JWT_TOKEN')
composite_credentials = grpc.composite_channel_credentials(credentials, call_credentials)
channel = grpc.secure_channel('api.petstoreapi.com:443', composite_credentials)
stub = PetServiceStub(channel)
# Get a pet
def get_pet(pet_id):
request = GetPetRequest(id=pet_id)
response = stub.GetPet(request)
print(f"Pet: {response.name}, {Species.Name(response.species)}")
return response
# List pets
def list_pets(species=None, status=None):
request = ListPetsRequest(
species=species or Species.SPECIES_UNSPECIFIED,
status=status or Status.STATUS_UNSPECIFIED,
page=1,
limit=20
)
response = stub.ListPets(request)
for pet in response.data:
print(f"{pet.name} - {Species.Name(pet.species)}")
return response
# Create a pet
def create_pet(name, species, breed, age_months):
request = CreatePetRequest(
name=name,
species=species,
breed=breed,
age_months=age_months,
status=Status.AVAILABLE
)
response = stub.CreatePet(request)
print(f"Created pet: {response.id}")
return response
# Usage
pet = get_pet('pet_fYrZzCW9E1WIOyGw')
pets = list_pets(species=Species.DOG, status=Status.AVAILABLE)
new_pet = create_pet('Buddy', Species.DOG, 'Golden Retriever', 24)
Go
Copy
package main
import (
"context"
"log"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/metadata"
pb "path/to/protos/petstore/v1"
)
func main() {
// Create secure connection
creds := credentials.NewTLS(nil)
conn, err := grpc.Dial("api.petstoreapi.com:443",
grpc.WithTransportCredentials(creds),
)
if err != nil {
log.Fatal(err)
}
defer conn.Close()
client := pb.NewPetServiceClient(conn)
// Add authentication
ctx := metadata.AppendToOutgoingContext(context.Background(),
"authorization", "Bearer YOUR_JWT_TOKEN")
// Get pet
ctx, cancel := context.WithTimeout(ctx, time.Second*5)
defer cancel()
pet, err := client.GetPet(ctx, &pb.GetPetRequest{
Id: "pet_fYrZzCW9E1WIOyGw",
})
if err != nil {
log.Fatal(err)
}
log.Printf("Pet: %s, %s", pet.Name, pet.Species)
// List pets
pets, err := client.ListPets(ctx, &pb.ListPetsRequest{
Species: pb.Species_DOG,
Status: pb.Status_AVAILABLE,
Page: 1,
Limit: 20,
})
if err != nil {
log.Fatal(err)
}
for _, p := range pets.Data {
log.Printf("%s - %s", p.Name, p.Species)
}
}
Java
Copy
import io.grpc.*;
import io.grpc.stub.StreamObserver;
import petstore.v1.*;
public class PetstoreClient {
private final PetServiceGrpc.PetServiceBlockingStub blockingStub;
public PetstoreClient(String host, int port, String token) {
// Create secure channel
ManagedChannel channel = ManagedChannelBuilder.forAddress(host, port)
.useTransportSecurity()
.build();
// Add authentication
Metadata metadata = new Metadata();
Metadata.Key<String> apiKey = Metadata.Key.of("authorization", Metadata.ASCII_STRING_MARSHALLER);
metadata.put(apiKey, "Bearer " + token);
blockingStub = MetadataUtils.attachHeaders(
PetServiceGrpc.newBlockingStub(channel),
metadata
);
}
public Pet getPet(String id) {
GetPetRequest request = GetPetRequest.newBuilder()
.setId(id)
.build();
Pet response = blockingStub.getPet(request);
System.out.println("Pet: " + response.getName() + ", " + response.getSpecies());
return response;
}
public void listPets() {
ListPetsRequest request = ListPetsRequest.newBuilder()
.setSpecies(Pet.Species.DOG)
.setStatus(Pet.Status.AVAILABLE)
.setPage(1)
.setLimit(20)
.build();
ListPetsResponse response = blockingStub.listPets(request);
for (Pet pet : response.getDataList()) {
System.out.println(pet.getName() + " - " + pet.getSpecies());
}
}
public static void main(String[] args) {
PetstoreClient client = new PetstoreClient(
"api.petstoreapi.com",
443,
"YOUR_JWT_TOKEN"
);
Pet pet = client.getPet("pet_fYrZzCW9E1WIOyGw");
client.listPets();
}
}
C#
Copy
using Grpc.Core;
using Grpc.Net.Client;
using Petstore.V1;
class Program
{
static async Task Main(string[] args)
{
// Create channel
var channel = GrpcChannel.ForAddress("https://api.petstoreapi.com");
// Create client with authentication
var headers = new Metadata
{
{ "Authorization", "Bearer YOUR_JWT_TOKEN" }
};
var client = new PetService.PetServiceClient(channel);
// Get pet
var pet = await client.GetPetAsync(
new GetPetRequest { Id = "pet_fYrZzCW9E1WIOyGw" },
headers
);
Console.WriteLine($"Pet: {pet.Name}, {pet.Species}");
// List pets
var pets = await client.ListPetsAsync(
new ListPetsRequest
{
Species = Species.Dog,
Status = Status.Available,
Page = 1,
Limit = 20
},
headers
);
foreach (var p in pets.Data)
{
Console.WriteLine($"{p.Name} - {p.Species}");
}
}
}
Node.js
Copy
const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader');
const path = require('path');
// Load proto file
const packageDefinition = protoLoader.loadSync('petstore.proto', {
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true
});
const proto = grpc.loadPackageDefinition(packageDefinition).petstore.v1;
// Create client
const credentials = grpc.credentials.createSsl();
const client = new proto.PetService('api.petstoreapi.com:443', credentials);
// Add authentication metadata
const metadata = new grpc.Metadata();
metadata.add('authorization', 'Bearer YOUR_JWT_TOKEN');
// Get pet
client.getPet({ id: 'pet_fYrZzCW9E1WIOyGw' }, metadata, (err, response) => {
if (err) {
console.error(err);
return;
}
console.log(`Pet: ${response.name}, ${response.species}`);
});
// List pets
client.listPets({
species: 'DOG',
status: 'AVAILABLE',
page: 1,
limit: 20
}, metadata, (err, response) => {
if (err) {
console.error(err);
return;
}
response.data.forEach(pet => {
console.log(`${pet.name} - ${pet.species}`);
});
});
Streaming
Server Streaming
Copy
service PetService {
rpc StreamPets(ListPetsRequest) returns (stream Pet);
}
Copy
# Server streaming
for pet in stub.StreamPets(ListPetsRequest()):
print(f"Received: {pet.name}")
Client Streaming
Copy
service PetService {
rpc CreatePets(stream CreatePetRequest) returns (CreatePetsResponse);
}
Copy
# Client streaming
def create_pets_generator():
yield CreatePetRequest(name='Buddy', species=Species.DOG)
yield CreatePetRequest(name='Whiskers', species=Species.CAT)
yield CreatePetRequest(name='Max', species=Species.DOG)
response = stub.CreatePets(create_pets_generator())
print(f"Created {response.count} pets")
Bidirectional Streaming
Copy
service ChatService {
rpc ChatStream(stream ChatMessage) returns (stream ChatMessage);
}
Authentication
Metadata Token
Copy
credentials = grpc.access_token_call_credentials('YOUR_JWT_TOKEN')
composite_credentials = grpc.composite_channel_credentials(
ssl_credentials,
credentials
)
Per-Call Authentication
Copy
# Add metadata to specific call
metadata = [('authorization', 'Bearer YOUR_TOKEN')]
response = stub.GetPet(request, metadata=metadata)
Error Handling
Copy
from grpc import StatusCode
try:
response = stub.GetPet(GetPetRequest(id='invalid_id'))
except grpc.RpcError as e:
if e.code() == StatusCode.NOT_FOUND:
print("Pet not found")
elif e.code() == StatusCode.PERMISSION_DENIED:
print("Access denied")
elif e.code() == StatusCode.INVALID_ARGUMENT:
print("Invalid argument")
else:
print(f"Error: {e.code()} - {e.details()}")
Best Practices
1. Reuse Channels
Copy
# Good - reuse channel
channel = grpc.secure_channel('api.petstoreapi.com:443', credentials)
stub = PetServiceStub(channel)
# Make multiple calls
pet1 = stub.GetPet(request1)
pet2 = stub.GetPet(request2)
2. Set Deadlines
Copy
from datetime import timedelta
try:
response = stub.GetPet(
request,
timeout=timedelta(seconds=5)
)
except grpc.RpcError as e:
if e.code() == StatusCode.DEADLINE_EXCEEDED:
print("Request timed out")
3. Handle Retries
Copy
from grpc import Retry
retry_policy = Retry(
initial=1.0, # Initial backoff in seconds
multiplier=2.0, # Backoff multiplier
max=10.0, # Maximum backoff in seconds
per_retry_timeout=1.0
)
response = stub.GetPet(
request,
retry=retry_policy,
timeout=timedelta(seconds=30)
)
Comparison with REST
| Feature | gRPC | REST |
|---|---|---|
| Serialization | Binary (Protobuf) | Text (JSON) |
| Performance | Very High | Medium |
| Code Generation | ✅ Built-in | ⚠️ Third-party |
| Streaming | ✅ Bidirectional | ⚠️ SSE only |
| Browser Support | ⚠️ Requires grpc-web | ✅ Native |
| Contract | .proto file | OpenAPI/Swagger |
| Payload Size | Small | Large |
| Learning Curve | Steeper | Easier |
Troubleshooting
Connection Issues
- Verify TLS certificate
- Check firewall rules
- Ensure correct port (443)
- Verify DNS resolution
Performance Issues
- Reuse channels (don’t recreate)
- Use streaming for bulk operations
- Implement proper retry logic
- Monitor gRPC metrics
Interactive Documentation
- Protocol Buffers: petstore.proto
- Protocol Overview: All Protocols
- REST API Guide: REST Protocol