grpc-server-kit
Batteries-optional async gRPC server toolkit (app facade, builder, TLS, lifecycle, health, interceptors, observability)
grpc-server-kit gives you a clean, production-grade foundation for building
grpc.aio servers: a one-object GrpcApp facade, validated channel options,
TLS/mTLS credential loading, graceful signal-driven shutdown, streaming-aware
interceptors, and gRPC health checking — with optional extras for
reflection, channelz, Prometheus metrics, OpenTelemetry tracing, Sentry,
Dishka DI, and typed pydantic settings.
The core depends only on grpcio. Every integration is an opt-in extra, so you
install exactly what you use.
Installation
pip install grpc-server-kit # core (grpcio only)
pip install "grpc-server-kit[settings]" # + pydantic settings models
pip install "grpc-server-kit[health]" # + gRPC health checking
pip install "grpc-server-kit[metrics,tracing]" # + Prometheus + OpenTelemetry
pip install "grpc-server-kit[all]" # everything
Requirements: Python 3.12+
Quick start
import asyncio
from grpc_server_kit import GrpcApp
app = GrpcApp(port=50051)
app.add_servicer(MyServicer(), add_MyServiceServicer_to_server)
asyncio.run(app.run()) # build + bind + SIGINT/SIGTERM + graceful shutdown
That's the whole server. GrpcApp builds the grpc.aio server, binds the port
(TLS-aware), installs signal handlers, and serves until termination. More knobs
when you need them:
from grpc_server_kit import GrpcApp, GrpcServerConfig
from grpc_server_kit.aio.interceptors import (
AsyncContextInterceptor, AsyncExceptionHandlerInterceptor,
AsyncMetricsInterceptor, AsyncRequestLoggerInterceptor, HeaderConfig,
)
from grpc_server_kit.observability.metrics import get_grpc_server_metrics
config = GrpcServerConfig(host="[::]", port=50051, grace_period=10.0) # stdlib dataclass
app = GrpcApp(config, interceptors=[
AsyncMetricsInterceptor(metrics=get_grpc_server_metrics(), service_name="my.pkg.MyService"),
AsyncContextInterceptor([HeaderConfig("x-request-id", "request_id")]),
AsyncRequestLoggerInterceptor(),
AsyncExceptionHandlerInterceptor(MY_ERROR_STATUS_MAP), # domain error → gRPC status
])
app.add_servicer(MyServicer(), add_MyServiceServicer_to_server)
app.enable_health(checkers=[DatabaseHealthChecker(session_maker)]) # [health] extra
app.enable_reflection(["my.pkg.MyService"]) # [reflection] extra
asyncio.run(app.run())
Prefer pydantic-validated, env-friendly settings? Use
grpc_server_kit.settings.BaseGrpcServerSettings (the [settings] extra) —
GrpcServerConfig and BaseGrpcServerSettings are interchangeable everywhere
the kit accepts settings.
Streaming-aware interceptors
Every interceptor wraps the whole RPC for all four call kinds — unary-unary, unary-stream, stream-unary, stream-stream. Durations cover the full stream, errors raised mid-stream are mapped to proper gRPC statuses, and cleanup always runs. Write your own by implementing a single async-generator hook:
from grpc_server_kit.aio.interceptors import AsyncServerInterceptor, RpcCall
class TimingInterceptor(AsyncServerInterceptor):
async def around_call(self, call: RpcCall):
start = time.perf_counter()
try:
yield # the RPC (or the full response stream) runs here
finally:
log.info("%s took %.3fs", call.method_name, time.perf_counter() - start)
Canonical chain order (outermost first): metrics → context → logger → tracing → exception handler → sentry. The exception handler maps exception types to
gRPC statuses over the MRO and sends safe, non-leaking details; deliberate
context.abort() calls pass through untouched. Sentry sits inside the
exception handler so it observes raw handler exceptions before they become
aborts (outside it would never capture anything), with a capture filter that
reports only server-class errors.
Advanced API
GrpcApp is sugar over small, composable pieces you can use directly — for DI
containers, custom lifecycles, or embedding:
from grpc_server_kit import bind_server_port
from grpc_server_kit.aio import AsyncGrpcServerBuilder, run_async_grpc_server
server = (
AsyncGrpcServerBuilder(settings)
.with_interceptors(interceptors)
.with_servicers(lambda s: add_MyServiceServicer_to_server(MyServicer(), s))
.with_reflection(["my.pkg.MyService"])
.build()
)
bind_server_port(server, settings) # honors settings.ssl_enabled (TLS/mTLS)
await run_async_grpc_server(server, address=f"{settings.host}:{settings.port}")
Dishka DI providers (cut the per-service boilerplate)
If your service uses Dishka, the kit ships ready-made
providers so you register a handful of providers instead of hand-writing the interceptor
list, settings aliasing, and a metrics provider. grpc_server_providers(...) bundles the
standard set in one call:
from dishka import make_async_container
from grpc_server_kit.dishka import GrpcioProvider, FromDishka, inject
from grpc_server_kit.aio.dishka import grpc_server_providers
from grpc_server_kit.aio.server import AsyncServer
from grpc_server_kit.aio import run_async_grpc_server
from grpc_server_kit import bind_server_port
container = make_async_container(
*grpc_server_providers( # settings + metrics + sentry/tracer seams
settings.grpc, # + the canonical interceptor chain + AsyncServer
service_name="my.pkg.MyService",
error_status_map=MY_ERROR_STATUS_MAP, # service-specific domain map (optional)
metrics_enabled=settings.grpc.metrics_enabled,
),
GrpcioProvider(), # request-scoped ServicerContext for FromDishka
MyDomainProvider(), # your use-cases / repos
)
server = await container.get(AsyncServer) # interceptors + options already wired
add_MyServiceServicer_to_server(MyServicer(), server) # AsyncServer works directly
bind_server_port(server, settings.grpc)
await run_async_grpc_server(server, address=f"{settings.grpc.host}:{settings.grpc.port}")
The interceptor chain is built in the canonical order
(metrics → context → logger → tracing → exception → sentry → DishkaAioInterceptor), with
include_* toggles and extra_outer / extra_inner slots on
GrpcServerInterceptorsProvider. Providers degrade gracefully: the metrics / Sentry / tracer
seams resolve to None when disabled or their extra is absent, and the interceptors no-op.
SDK initialization (sentry_sdk.init, OpenTelemetry setup, the /metrics HTTP server) is a
process-wide concern and is not done by these providers. Requires [dishka]
(+ [metrics]/[sentry]/[tracing] for those seams).
Granular providers (GrpcServerSettingsProvider, PrometheusGrpcServerMetricsProvider,
SentryAdapterProvider, TracerAdapterProvider, GrpcServerInterceptorsProvider,
AsyncGrpcServerProvider) are available for finer control.
What's inside
Layout convention: all async code lives under a single
grpc_server_kit.aiopackage (mirroringgrpc.aio), organized by domain (aio.health,aio.interceptors,aio.observability,aio.dishka). Modules that work for both sync and async stay at the top level — either directly (config,options,credentials,server,protocols,signals,settings) or as shared domain packages (interceptors→ constants/utils,observability→ protocols/metrics/sentry,dishka→ providers).
| Module | Responsibility | Extra |
|---|---|---|
aio.GrpcApp |
One-object facade: config → servicers → run | core |
config.GrpcServerConfig |
Zero-dependency settings dataclass | core |
aio.builder.AsyncGrpcServerBuilder |
Fluent builder for an AsyncServer |
core |
aio.create_async_grpc_server / aio.AsyncServer |
Server factory + typed wrapper | core |
aio.ServerLifecycleManager / aio.run_async_grpc_server |
Start/serve/stop with graceful shutdown | core |
aio.interceptors.* |
streaming-aware base + context, logger, metrics, tracing, sentry, exception-handler | core |
options / credentials / server / signals / protocols |
Channel options, TLS/mTLS, port binding, SIGINT/SIGTERM, seams | core |
settings.BaseGrpcServerSettings |
Pydantic config model | settings |
aio.health.* |
gRPC Health v1 servicer, orchestrator, TTL cache, pg/redis checkers | health |
observability.metrics.GrpcServerMetrics |
Prometheus metric definitions | metrics |
aio.observability.tracing.instrument_aio_server |
OpenTelemetry instrumentation | tracing |
observability.sentry.SentrySdkAdapter |
Sentry adapter for the sentry interceptor | sentry |
aio.dishka.* / dishka.* |
DI providers + DishkaAioInterceptor re-export |
dishka |
Optional dependencies
| Extra | Pulls in | Enables |
|---|---|---|
reflection |
grpcio-reflection |
server reflection |
channelz |
grpcio-channelz |
channelz debugging |
settings |
pydantic |
BaseGrpcServerSettings / BaseHealthSettings |
health |
grpcio-health-checking |
health servicer / orchestrator / cache |
postgres |
sqlalchemy[asyncio] |
DatabaseHealthChecker |
redis |
redis |
RedisHealthChecker |
metrics |
prometheus-client |
GrpcServerMetrics |
tracing |
opentelemetry-instrumentation-grpc |
instrument_aio_server |
sentry |
sentry-sdk |
SentrySdkAdapter |
dishka |
dishka, protobuf |
DI providers + DishkaAioInterceptor |
all |
all of the above | everything |
Examples
Runnable, self-contained scripts in examples/ — each prints what it's doing and exits on its own:
minimal_server.py— the smallest real server:GrpcApp, one servicer,asyncio.run(app.run()).observability_chain.py— the canonical interceptor chain (metrics → context → logger → tracing → exception handler → sentry) wired by hand.custom_interceptor.py— writing your own streaming-aware interceptor withAsyncServerInterceptor.around_call.health_checks.py—app.enable_health()with a custom dependency checker, driven through the realgrpc_health.v1stubs.
Documentation
Full documentation at bedrock-python.github.io/grpc-server-kit.
License
Apache 2.0 — see LICENSE.
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 grpc_server_kit-0.1.0.tar.gz.
File metadata
- Download URL: grpc_server_kit-0.1.0.tar.gz
- Upload date:
- Size: 59.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
uv/0.12.3 {"installer":{"name":"uv","version":"0.12.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b54b96a5350a473bedfbc7ab1e6a7591399f55779fd4ca2451f9074a6046fc96
|
|
| MD5 |
eea02ef3a5f0b8aec1e562ec8c0ab436
|
|
| BLAKE2b-256 |
1638ffa27b8fb9e676b8e0408bcbdfb07d89738a9c068991c6c9d8afb2d1ce0b
|
File details
Details for the file grpc_server_kit-0.1.0-py3-none-any.whl.
File metadata
- Download URL: grpc_server_kit-0.1.0-py3-none-any.whl
- Upload date:
- Size: 85.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
uv/0.12.3 {"installer":{"name":"uv","version":"0.12.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
39d3151d2a5ce454f7a5b94a7dc05e73a1234e1aefa98f9f88900585eadc3a8b
|
|
| MD5 |
b88135d33f87fe24b70f3d63d061600d
|
|
| BLAKE2b-256 |
7172902527bde2131fef35f386f0a35aa7f59f388f909e641c61676f0f4eb116
|