Skip to content

Quickstart

Build and run your first fastgrpc service.

Install

pip install -e '.[dev]'

Requires Python 3.11 or later.

Write a service

Create main.py:

from dataclasses import dataclass
from fastgrpc 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="Ashesh")

That's the entire service. No .proto, no protoc, no servicer subclass.

Run it

fastgrpc dev main.py

You should see:

✓ Serving on grpc://127.0.0.1:50051 (async)
Watching . for changes...

fastgrpc auto-detected async def, so it picked grpc.aio.server(). If you'd written def get_user(...) instead, it would have used the sync grpc.server thread-pool server.

Call it

In another terminal:

grpcurl -plaintext -d '{"user_id": 42}' \
  127.0.0.1:50051 fastgrpc.UserService/GetUser
{
  "id": "42",
  "name": "Ashesh"
}

What just happened

When you ran fastgrpc dev, the framework:

  1. Imported main.py — the @service and @rpc decorators registered UserService and get_user.
  2. Inspected the type hints to derive a protobuf schema.
  3. Wrote .fastgrpc/fastgrpc.proto and ran protoc to compile _pb2.py and _pb2_grpc.py stubs.
  4. Synthesized a UserServiceImpl subclass at runtime that wraps your get_user with proto↔dataclass conversion.
  5. Started a grpc.aio.server() on port 50051 and registered the implementation.
  6. Started a Rust-backed file watcher that triggers steps 1–5 again on every .py change.

Inspect the generated proto:

fastgrpc proto main.py
syntax = "proto3";

package fastgrpc;

message GetUserRequest {
  int64 user_id = 1;
}

message User {
  int64 id = 1;
  string name = 2;
}

service UserService {
  rpc GetUser (GetUserRequest) returns (User);
}

Next steps