FastAPI-style gRPC for Python
Project description
fastgrpcpy
FastAPI-style gRPC for Python.
Define services with type-annotated Python. Get the protobuf schema, generated stubs, and a running server — automatically.
Overview
fastgrpcpy treats your Python type hints as the source of truth for a gRPC service. The framework derives the .proto schema from your code, compiles the gRPC stubs, wires your handlers into the generated servicer, and runs the server — sync or async, with hot reload in development.
from dataclasses import dataclass
from fastgrpcpy import rpc, service
@dataclass
class User:
id: int
name: str
@service
class UserService:
@rpc
async def get_user(self, user_id: int) -> User:
return User(id=user_id, name="FastgRPC")
fastgrpcpy dev main.py
Call it from any gRPC client. With server reflection enabled in dev mode, grpcurl works with no extra setup:
grpcurl -plaintext -d '{"user_id": 42}' \
127.0.0.1:50051 fastgrpcpy.UserService/GetUser
# {"id": "42", "name": "Ashesh"}
That single command performs introspection, code generation, compilation, and serving in one step. No .proto files to write or maintain by hand.
Features
- Zero-boilerplate service definition — decorate a class with
@service, decorate methods with@rpc. fastgrpcpy handles the rest. - Hot reload in development, including transitive Python imports, powered by a Rust-backed file watcher.
- Automatic protobuf generation from Python type hints, including nested dataclasses,
Optional,list[T],dict[str, T], andAsyncIterator[T]. - Wire compatibility guarantees — field numbers are pinned in a lock file (
.fastgrpcpy.lock), preventing accidental wire-breaking changes. - Sync and async servers, auto-selected from your handler signatures (
grpc.aiofor async,grpc.serverfor sync). - All four streaming modes — unary-unary, unary-stream, stream-unary, stream-stream.
- gRPC server reflection enabled in dev mode, so
grpcurl, Postman, and BloomRPC work without any configuration. - Multi-file projects — split services, business logic, and models freely; reload follows imports.
Installation
Requires Python 3.11 or later.
pip install fastgrpcpy
For local development:
pip install -e '.[dev]'
Command-line interface
| Command | Purpose |
|---|---|
fastgrpcpy dev <file> |
Start the development server with hot reload and reflection |
fastgrpcpy run <file> |
Start the production server (no reload, no reflection) |
fastgrpcpy proto <file> |
Print the generated .proto schema to stdout |
fastgrpcpy compile <file> |
Write the .proto and compiled stubs to a directory |
fastgrpcpy dev
The development server selects the appropriate gRPC stack based on your handler signatures:
- Any
async defhandler →grpc.aio.server() - All
defhandlers →grpc.server(ThreadPoolExecutor) - Mixed sync and async → fails fast with a
ValidationError
fastgrpcpy dev examples/complex/main.py
fastgrpcpy dev examples/complex/main.py --host 0.0.0.0 --port 50051
On every file save the framework executes the following pipeline:
- The Rust-backed watcher detects a
.pychange in the project tree. - User modules are evicted from
sys.modulesso transitive imports reload cleanly. - The entry-point file is re-imported, re-running the
@serviceand@rpcdecorators. - The pipeline runs: inspector → field-number lock → validator → proto writer →
protoccompiler. - The previous server is gracefully stopped; a new one is built with the rewired handlers.
Reflection is enabled, so grpcurl -plaintext localhost:50051 list works out of the box.
fastgrpcpy run
Production server. No watcher, no reflection.
fastgrpcpy run main.py --host 0.0.0.0 --port 50051
fastgrpcpy proto
Print the generated .proto schema to stdout. Suitable for piping into a clients repository or copying into another build pipeline.
fastgrpcpy proto main.py
fastgrpcpy proto main.py > schemas/user.proto
fastgrpcpy proto main.py --package myco.users
The command consults .fastgrpcpy.lock to keep field numbers stable across runs.
fastgrpcpy compile
Produce a complete client bundle: the .proto source plus pre-compiled _pb2.py and _pb2_grpc.py modules.
fastgrpcpy compile main.py --out ./client/
client/
├── fastgrpcpy.proto
├── fastgrpcpy_pb2.py
└── fastgrpcpy_pb2_grpc.py
The output directory can be checked in to your clients repository or published as its own package.
Watch mode
fastgrpcpy dev runs a continuous build-and-serve loop:
edit main.py / business.py / models.py
↓ (Rust file watcher fires)
purge user modules from sys.modules
↓
re-import main.py — decorators re-register services
↓
inspector → lock → validator → proto writer → protoc compile
↓
stop the running server, start a new one with rewired handlers
Behavior worth knowing:
- Transitive imports reload. Editing
business.pyormodels.pytriggers a full rebuild. Without the explicitsys.modulespurge, Python would serve cached versions. - Framework modules persist.
fastgrpcpy,grpc, andgoogle.protobufare excluded from the purge to keep reload latency low; re-importing the generated grpc stubs every keystroke would be prohibitively slow. .pyfiles trigger reload. Generated.protoand_pb2.pyartifacts are filtered out by the watcher to prevent reload loops.- Reload errors are isolated. A failed rebuild (syntax error, validation failure, etc.) does not kill the running server; the previous server keeps serving traffic and the error is logged.
Project layout
A typical fastgrpcpy service:
my_service/
├── main.py # @service classes
├── business.py # business logic, imported by main.py
├── models.py # dataclasses, imported by both
├── .fastgrpcpy/ # gitignored, ephemeral build artifacts
│ ├── fastgrpcpy.proto # generated schema, refreshed every reload
│ ├── fastgrpcpy_pb2.py # compiled message classes
│ └── fastgrpcpy_pb2_grpc.py # compiled servicer base + stub
└── .fastgrpcpy.lock # committed; tracks protobuf field numbers
Everything inside .fastgrpcpy/ is regenerated on every fastgrpcpy dev reload. To export the schema for client teams, use fastgrpcpy proto (stdout) or fastgrpcpy compile --out <dir> (writes a clean bundle wherever you choose).
Field-number stability and .fastgrpcpy.lock
Protobuf identifies fields by number, not by name. If field numbers shift between releases — for example because a new field was inserted in the middle of a dataclass — every existing client will silently misinterpret the wire payload.
.fastgrpcpy.lock is the authoritative record of every field number ever assigned. New fields receive the next available number; removed fields are tombstoned and never reused.
[User]
id = 1
name = 2
[User.removed]
legacy_email = 3 # tombstoned — number 3 is permanently retired
Commit .fastgrpcpy.lock and review changes in pull requests, the same way you would treat a lock file or a database migration history.
Examples
The examples/ directory contains progressively larger services:
| Folder | Demonstrates |
|---|---|
hello_world/ |
Minimal async service |
async_basic/ |
Async handler with await asyncio.sleep |
sync/ |
Sync handler running on the grpc thread-pool server |
complex/ |
Multi-file project: main.py, business.py, models.py, two services, nested dataclasses |
streaming/ |
Server, client, and bidirectional streaming |
middleware/ |
Interceptor pattern (logging example) |
fastgrpcpy dev examples/complex/main.py --port 50065
grpcurl -plaintext 127.0.0.1:50065 list
grpcurl -plaintext -d '{"user_id": 1}' \
127.0.0.1:50065 fastgrpcpy.UserService/GetUser
Architecture
src/fastgrpcpy/
├── decorators.py # @service, @rpc, the registry
├── app.py # App configuration, interceptor registry
├── exceptions.py # gRPC-status-mapped exception classes
├── codegen/
│ ├── ir.py # ProtoFile / ProtoMessage / ProtoField (dataclasses)
│ ├── inspector.py # Python types → IR
│ ├── lock.py # field-number stability via .fastgrpcpy.lock
│ ├── validator.py # IR and registry sanity checks
│ ├── proto_writer.py # IR → .proto source string
│ └── compiler.py # invokes grpcio-tools / protoc
├── server/
│ ├── runner.py # build pipeline + lifecycle
│ ├── wiring.py # servicer synthesis, sync/async detection
│ ├── converter.py # dataclass ↔ protobuf recursive conversion
│ ├── watcher.py # watchfiles-based hot reload
│ └── reflection.py # gRPC server reflection setup
└── cli/
└── main.py # Typer CLI: dev, run, proto, compile
The codegen layer is a sequence of pure functions over an in-memory ProtoFile IR. The IR itself is never persisted; only the final .proto and .fastgrpcpy.lock are.
Development
pip install -e '.[dev]'
pytest # unit + integration tests
ruff check .
mypy src
The test suite includes end-to-end integration tests that spin up a real gRPC server (both sync and async) and exercise it over the wire.
Project status
Alpha. The core pipeline — code generation, lock-file management, sync and async server runtimes, hot reload, multi-file projects — is implemented and covered by tests. Known gaps include TLS support, OpenTelemetry tracing, and HTTP/JSON transcoding.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fastgrpcpy-0.1.0.tar.gz.
File metadata
- Download URL: fastgrpcpy-0.1.0.tar.gz
- Upload date:
- Size: 781.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6f8dd4a35e7c734d504d5eafa1247cd78d6bee5ce2690122048082ec91aaacb9
|
|
| MD5 |
870f9ab28a1eb03681c7ba2c73dc1479
|
|
| BLAKE2b-256 |
aa12e683b6e5237dd5308d92600573203e992b53e616da60a02fc1822d5e070a
|
Provenance
The following attestation bundles were made for fastgrpcpy-0.1.0.tar.gz:
Publisher:
publish.yml on asheshvidyut/fastgrpc
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fastgrpcpy-0.1.0.tar.gz -
Subject digest:
6f8dd4a35e7c734d504d5eafa1247cd78d6bee5ce2690122048082ec91aaacb9 - Sigstore transparency entry: 1488541544
- Sigstore integration time:
-
Permalink:
asheshvidyut/fastgrpc@20cda7818c0d57bf8537f971dd21628922073ffb -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/asheshvidyut
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@20cda7818c0d57bf8537f971dd21628922073ffb -
Trigger Event:
release
-
Statement type:
File details
Details for the file fastgrpcpy-0.1.0-py3-none-any.whl.
File metadata
- Download URL: fastgrpcpy-0.1.0-py3-none-any.whl
- Upload date:
- Size: 29.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8b3a89179aaf1844ea61469b5d9bd7a16196b2aa7aa17421e94740625d48a8a9
|
|
| MD5 |
c22d44bdc7a121fe3842e82ca6f8b5aa
|
|
| BLAKE2b-256 |
96e6684075dd0d9023046ca08adee628761c7bffe0d75473366af220c026cede
|
Provenance
The following attestation bundles were made for fastgrpcpy-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on asheshvidyut/fastgrpc
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fastgrpcpy-0.1.0-py3-none-any.whl -
Subject digest:
8b3a89179aaf1844ea61469b5d9bd7a16196b2aa7aa17421e94740625d48a8a9 - Sigstore transparency entry: 1488541588
- Sigstore integration time:
-
Permalink:
asheshvidyut/fastgrpc@20cda7818c0d57bf8537f971dd21628922073ffb -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/asheshvidyut
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@20cda7818c0d57bf8537f971dd21628922073ffb -
Trigger Event:
release
-
Statement type: