If you've ever wondered how different parts of an app talk to each other โ or how microservices "call" each other behind the scenes โ this one's for you.
In this post, I'll show you how to use gRPC in Python to build a menu service. We'll keep things light, use a kitchen/waiter analogy ๐ฝ๏ธ, and write some real working code together.
๐ What's gRPC? (But Make It Restaurant-Themed)
Let's say you're at a restaurant:
- Kitchen = the server (makes food)
- Waiter = the client (asks for food)
- Menu = the data structure (what's available)
The waiter goes, "Hey, what's on the menu today?"
The kitchen replies with delicious dishes.
This is literally what gRPC helps services do: ask for stuff, get a response โ but way faster and stricter than REST.
๐ ๏ธ Prerequisites
First, let's install what we need:
pip install grpcio grpcio-tools
๐ Step 1: Define the Language (menu.proto)
We need a shared language both waiter and kitchen understand.
Create a file called menu.proto:
syntax = "proto3";
package menu;
import "google/protobuf/empty.proto";
service MenuService {
rpc ListMenus (google.protobuf.Empty) returns (MenuList);
}
message Menu {
string id = 1;
string name = 2;
string description = 3;
double price = 4;
}
message MenuList {
repeated Menu items = 1;
}
โ๏ธ Step 2: Generate Python Code from the Proto
This turns your .proto file into real Python code.
Run this in terminal:
python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. menu.proto
Boom ๐ฅ โ now you've got menu_pb2.py and menu_pb2_grpc.py.
๐งโ๐ณ Step 3: Build the Server (Our Kitchen)
Create server.py:
import grpc
from concurrent import futures
from google.protobuf.empty_pb2 import Empty
import menu_pb2
import menu_pb2_grpc
class MenuService(menu_pb2_grpc.MenuServiceServicer):
def ListMenus(self, request, context):
return menu_pb2.MenuList(items=[
menu_pb2.Menu(
id="1",
name="Pizza Margherita",
description="Fresh tomatoes, mozzarella, basil",
price=12.99
),
menu_pb2.Menu(
id="2",
name="Classic Burger",
description="Beef patty, lettuce, tomato, cheese",
price=9.99
),
menu_pb2.Menu(
id="3",
name="Caesar Salad",
description="Crispy romaine, parmesan, croutons",
price=8.50
),
])
def serve():
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
menu_pb2_grpc.add_MenuServiceServicer_to_server(MenuService(), server)
server.add_insecure_port("[::]:50051")
server.start()
print("๐ฝ๏ธ gRPC Server running on port 50051")
server.wait_for_termination()
if __name__ == "__main__":
serve()
๐งพ Step 4: Create the Client (Our Waiter)
Create client.py:
import grpc
from google.protobuf.empty_pb2 import Empty
import menu_pb2
import menu_pb2_grpc
def run():
with grpc.insecure_channel("localhost:50051") as channel:
stub = menu_pb2_grpc.MenuServiceStub(channel)
try:
response = stub.ListMenus(Empty())
print("๐ Today's Menu:")
print("-" * 40)
for item in response.items:
print(f"๐ฝ๏ธ {item.name}")
print(f" {item.description}")
print(f" ๐ฐ ${item.price:.2f}")
print()
except grpc.RpcError as e:
print(f"โ Error: {e}")
if __name__ == "__main__":
run()
๐งช Step 5: Try It Out
- Run the server (in one terminal):
python server.py
- Run the client (in another terminal):
python client.py
You should see something like:
๐ Today's Menu:
----------------------------------------
๐ฝ๏ธ Pizza Margherita
Fresh tomatoes, mozzarella, basil
๐ฐ $12.99
๐ฝ๏ธ Classic Burger
Beef patty, lettuce, tomato, cheese
๐ฐ $9.99
๐ฝ๏ธ Caesar Salad
Crispy romaine, parmesan, croutons
๐ฐ $8.50
Yesss ๐ it's working!
๐ง What's Actually Happening?
+-----------+ ask for menu +-----------+
| Client | ----------------------> | Server |
| (Waiter) | | (Kitchen) |
| | <---------------------- | |
| | send back menu | |
+-----------+ +-----------+
The waiter (client) asks for the menu using a specific method call. The kitchen (server) replies with a structured list of menu items. No JSON parsing, no HTTP overhead โ just pure, efficient communication.
๐ค Why Use gRPC Instead of REST?
| Feature | REST | gRPC |
|---|---|---|
| Format | JSON (text) | Protocol Buffers (binary) |
| Speed | Good | โก Faster! |
| Type Safety | Runtime errors | Compile-time checks ๐ก๏ธ |
| Streaming | Limited | Bidirectional streams ๐ |
| Tooling | Manual setup | Auto-generated stubs ๐ช |
| Best For | Public APIs, web | Microservices, internal |
๐ Level Up: Add Error Handling
Want to make it production-ready? Here's how to add proper error handling to your server:
def ListMenus(self, request, context):
try:
# Simulate potential database call
menus = self._get_menus_from_database()
return menu_pb2.MenuList(items=menus)
except Exception as e:
context.set_code(grpc.StatusCode.INTERNAL)
context.set_details(f"Failed to fetch menus: {str(e)}")
return menu_pb2.MenuList()
def _get_menus_from_database(self):
# Your database logic here
return [
menu_pb2.Menu(id="1", name="Pizza", description="Yummy", price=12.99)
]
๐ก Quick Wins & Tips
File Structure:
your-project/
โโโ menu.proto
โโโ server.py
โโโ client.py
โโโ menu_pb2.py (generated)
โโโ menu_pb2_grpc.py (generated)
Pro Tips:
- Always use
with grpc.insecure_channel()for proper connection cleanup - Add proper logging with Python's
loggingmodule - Use virtual environments:
python -m venv grpc-env - For production, use secure channels with TLS
๐ฏ What's Next?
Now that you've got the basics down, here are some fun directions to explore:
- Add CRUD operations (Create, Update, Delete menu items)
- Connect to a real database (PostgreSQL, MongoDB)
- Add authentication with JWT tokens
- Implement streaming for real-time updates
- Deploy with Docker for easy distribution
๐ฌ Wanna Go Deeper?
Let me know in the comments if you want tutorials on:
- ๐ Securing gRPC APIs with authentication
- ๐ gRPC + PostgreSQL integration
- ๐ Streaming with gRPC (real-time data)
- ๐ณ Dockerizing gRPC services
- ๐ gRPC + REST gateway (best of both worlds)
๐ Got Questions?
Drop them below โ I love helping fellow devs learn backend stuff! Or tag me if you build something cool with gRPC!
Found this helpful? Give it a โค๏ธ and follow for more beginner-friendly backend tutorials ๐
Happy coding! ๐
Top comments (0)