Developers working with Python and gRPC often run into repetitive routines: writing .proto files, running protoc to generate code, and keeping generated files in sync with the schema. These steps can slow teams down and lead to mismatches, especially when building several services or collaborating. The usual workflow, as outlined by projects like matrix-contracts, means regenerating Python gRPC stubs with grpc_tools.protoc whenever a .proto file changes, which produces _pb2 and _pb2_grpc modules for sharing contracts.
Introducing fastgrpc3: A New Approach to Python gRPC
fastgrpc3 is an open-source Python framework from Edwin Alkins that simplifies gRPC development by using Pydantic models at the service boundary. This removes the need to handle generated _pb2 and _pb2_grpc classes directly. The engine behind it is protoruf, a Rust-based protobuf implementation that compiles .proto definitions in memory and maps protobuf bytes to Pydantic models. Alkins explains, "This tutorial builds the same service with fastgrpc3, an open-source gRPC framework I wrote on top of my Rust protobuf codec, protoruf." (fastgrpc3 project).
Setting Up fastgrpc3
To begin, install fastgrpc3 using pip:
pip install fastgrpc3
fastgrpc3 works with CPython versions 3.12 and 3.14, offering prebuilt wheels for Linux, macOS, and Windows. You do not need to install protoc separately, since the Rust-based codec comes bundled. The documentation notes, "There is no protoc to install: protobuf is compiled and encoded in Rust by protoruf, which ships as a wheel." (fastgrpc3 docs).
Defining Services with Pydantic Models
With fastgrpc3, you define your gRPC service using Pydantic models for requests and responses. Incoming data gets validated before your handler runs, and responses are checked for correctness. Here is an example of a simple order service:
from pydantic import BaseModel
from fastgrpc3 import GrpcApp, GrpcError, GrpcService, Insecure, StatusCode
class GetOrder(BaseModel):
order_number: str
class Order(BaseModel):
order_number: str
paid: bool = False
DB = {"A": Order(order_number="A", paid=True)}
service = GrpcService("Orders", package="orders.v1")
@service.unary()
async def Get(req: GetOrder) -> Order:
if req.order_number not in DB:
raise GrpcError(StatusCode.NOT_FOUND, "no such order")
return DB[req.order_number]
app = GrpcApp(address="[::]:50051", tls=Insecure())
app.add_service(service)
app.enable_reflection()Requests that do not match the model are rejected with an INVALID_ARGUMENT status. Errors raised as GrpcError are mapped to standard gRPC status codes, so clients in any language can handle them.
Running and Testing the Service
To start the service, use this command:
fastgrpc3 run orders:app
You can interact with the running service using grpcurl and server reflection:
grpcurl -plaintext -d '{"order_number": "A"}' localhost:50051 orders.v1.Orders/GetThis returns a JSON response with the order details if found.
Automatic Protobuf Contract Generation
Instead of writing .proto files by hand, fastgrpc3 generates them from your Pydantic models. Field numbers are managed and locked in a proto.lock file to keep compatibility over time. You can export standard .proto files for use by other teams or languages:
fastgrpc3 lock orders:app fastgrpc3 export orders:app -o proto/
This workflow helps maintain a consistent contract and supports integration with Go, Java, TypeScript, and other languages that use protobuf. The generator command fastgrpc3 generate proto/ -o src/myapp/gen produces Pydantic models, a service contract, and a typed client, moving code-generation tasks into the framework while still supporting protobuf schemas.
Typed Python Clients
fastgrpc3 lets you create strongly typed Python clients that use your Pydantic models. Here is an example client for the Orders service:
import asyncio
from fastgrpc3 import Channel, GrpcClient, GrpcError, Insecure
from orders import GetOrder, Order
class OrdersClient(GrpcClient):
package = "orders.v1"
service = "Orders"
get = GrpcClient.unary("Get", GetOrder, Order)
async def main() -> None:
async with Channel("localhost:50051", tls=Insecure()) as channel:
orders = OrdersClient(channel)
print(await orders.get(GetOrder(order_number="A")))
try:
await orders.get(GetOrder(order_number="Z"))
except GrpcError as error:
print(error.code.name, error.message)
if __name__ == "__main__":
asyncio.run(main())Responses come back as Pydantic models, making it easier to work with typed data and integrate with tools like mypy and modern editors.
Dependency Injection and Advanced Features
fastgrpc3 supports dependency injection similar to FastAPI. You can define dependencies for database sessions or user context. Cleanup for yield-based dependencies is handled automatically. The framework also supports sub-dependencies, per-call caching, blocking I/O providers, and test overrides.
from collections.abc import AsyncIterator
from fastgrpc3 import Context, Depends
async def session() -> AsyncIterator[dict[str, Order]]:
yield DB
def current_user(ctx: Context) -> str:
return str(ctx.metadata.get("x-user", "anonymous"))
@service.unary()
async def Pay(
req: GetOrder,
db: dict[str, Order] = Depends(session),
user: str = Depends(current_user),
) -> Order:
order = db[req.order_number]
order.paid = True
return orderHooks, middleware, and exception handlers can be added around every call, supporting robust service design and testing.
Testing with Real Servers
fastgrpc3 includes a TestClient that launches the app on a local port, so you can test the actual gRPC wire protocol:
from fastgrpc3.testing import TestClient
from orders import GetOrder, app
from client import OrdersClient
async def test_get() -> None:
async with TestClient(app) as tc:
order = await tc.client(OrdersClient).get(GetOrder(order_number="A"))
assert order.paidThis approach ensures your tests cover real network interactions, not just mocks.
Production-Ready Deployment and High Performance
For production, you can enable TLS, health checks, reflection, and documentation endpoints. fastgrpc3 can run on the standard grpc.aio server or switch to a Rust-based transport built on tonic for higher throughput. The fastgrpc3-transport project describes a tonic-based Rust gRPC server transport where HTTP/2, gRPC framing, TLS, compression, and flow control run in Rust, while Python's asyncio loop receives calls in batches and dispatches them through fastgrpc3's runtime. The transport is marked "experimental" and provides a parity matrix for differences from grpc.aio. CI-tested wheels are available for Linux x86_64 and aarch64, macOS arm64 and x86_64, and Windows x86_64, supporting CPython 3.12 and 3.14.
Benchmarks from the project state that encoding a 20-field message takes about 1 microsecond, which is 55 times faster than using google.protobuf with Pydantic. These numbers come from the author and reflect the Rust-backed codec's performance under specific test conditions.
How fastgrpc3 Works Under the Hood
The core of fastgrpc3 is the protoruf codec, which compiles protobuf schemas in memory and maps them to Pydantic models. This setup removes the need for generated _pb2 classes and speeds up encoding and decoding, with performance gains over traditional methods. The framework's generator command can produce Pydantic models, service contracts, and typed clients directly from protobuf definitions, moving code-generation tasks into the framework itself.
Compatibility and Ecosystem Considerations
While fastgrpc3 offers a model-first workflow and Rust-powered speed, the conventional generated-stub workflow is still widely used in Python projects, especially where broad ecosystem compatibility and multi-language support matter. Teams using established CI pipelines or distributing contracts across languages may stick with protoc-based tooling. The fastgrpc3 project is at version 0.1 and is described as an early-stage open-source framework. Feedback and compatibility discovery are ongoing, and teams considering adoption are encouraged to test schema evolution, error semantics, streaming, TLS, compression, generated-client interoperability, and operational behavior against the documented parity matrix and known issues.
Getting Started
To try fastgrpc3, install the package with tonic support:
pip install "fastgrpc3[tonic]"
For more details, visit the official documentation or the project's GitHub repository. fastgrpc3 is currently at version 0.1, and feedback from developers is encouraged to help improve the framework.
