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
logging
module - 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)