Skip to main content

gRPC Configuration

Complete reference for configuring Django-CFG gRPC integration.

📋 Configuration Structure

# api/config.py
from typing import Optional
from django_cfg import (
DjangoConfig,
GRPCConfig,
GRPCServerConfig,
GRPCAuthConfig,
GRPCProtoConfig,
)

class MyConfig(DjangoConfig):
grpc: Optional[GRPCConfig] = GRPCConfig(
enabled=True,
server=GRPCServerConfig(...),
auth=GRPCAuthConfig(...),
proto=GRPCProtoConfig(...),
auto_register_apps=True,
enabled_apps=[...],
)

⚙️ GRPCConfig

Main gRPC configuration container.

FieldTypeDefaultDescription
enabledboolFalseEnable/disable gRPC server
serverGRPCServerConfig-Server configuration
authGRPCAuthConfig-Authentication configuration
protoGRPCProtoConfig-Protocol Buffer configuration
auto_register_appsboolTrueAuto-discover services
enabled_appslist[str][]Apps to scan for services

Example

grpc: GRPCConfig = GRPCConfig(
enabled=True,
auto_register_apps=True,
enabled_apps=["apps.users", "apps.products", "apps.orders"],
)

🎯 Simplified Configuration (Flatten Fields)

For simpler configs, you can use flatten fields instead of nested config objects:

Traditional (Nested):

grpc = GRPCConfig(
enabled=True,
server=GRPCServerConfig(
host="[::]",
port=50051,
),
proto=GRPCProtoConfig(
package_prefix="api",
output_dir="protos",
),
enabled_apps=["apps.crypto"],
)

Simplified (Flatten):

grpc = GRPCConfig(
enabled=True,
enabled_apps=["apps.crypto"],
# Server fields (flatten)
host="[::]", # Instead of server=GRPCServerConfig(host=...)
port=50051,
# Proto fields (flatten)
package_prefix="api", # Instead of proto=GRPCProtoConfig(package_prefix=...)
# Environment integration
public_url=env.grpc_url,
)

Benefits:

  • Less boilerplate code
  • No need to import nested config classes
  • Cleaner configuration
  • Same functionality

When to use nested: When you need to organize complex configs or reuse config objects. When to use flatten: For simple configs (recommended for most cases).

🖥️ GRPCServerConfig

gRPC server settings.

FieldTypeDefaultDescription
hoststr"[::]"Server host address
portint50051Server port
max_workersint10Thread pool size
enable_reflectionboolTrueEnable server reflection
enable_health_checkboolTrueEnable health check service

Host Options

# All interfaces (IPv6) - Recommended
host="[::]"

# All interfaces (IPv4)
host="0.0.0.0"

# Localhost only
host="127.0.0.1"

# Specific IP
host="192.168.1.100"

Worker Threads

# Low traffic (development)
max_workers=5

# Medium traffic
max_workers=10

# High traffic (production)
max_workers=20

# Very high traffic
max_workers=50

Rule of thumb: Start with 10, increase if you see thread starvation.

Reflection

# Development: Enable for grpcurl testing
enable_reflection=True

# Production: Disable to hide service schema
enable_reflection=False

Health Check

# Always recommended for production
enable_health_check=True

# For load balancers
# GET /grpc.health.v1.Health/Check

Complete Example

server=GRPCServerConfig(
host="[::]", # All interfaces
port=50051, # Standard port
max_workers=20, # 20 concurrent requests
enable_reflection=True, # Enable grpcurl
enable_health_check=True, # Enable health checks
)

🔐 GRPCAuthConfig

Authentication configuration (supports API keys).

FieldTypeDefaultDescription
enabledboolFalseEnable authentication
require_authboolFalseRequire auth for all methods
API Key Settings
api_key_headerstr"x-api-key"Header name for API key
accept_django_secret_keyboolTrueAccept Django SECRET_KEY as valid API key (for dev/internal use)
General
public_methodslist[str][]Public methods (no auth required)

Authentication Modes

Mode 1: Disabled (Development)

auth=GRPCAuthConfig(
enabled=False, # No authentication
)

Mode 2: Optional Auth (Recommended)

auth=GRPCAuthConfig(
enabled=True,
require_auth=False, # Some methods can be public
api_key_header="x-api-key",
accept_django_secret_key=True, # Allow SECRET_KEY for dev
)

# In service:
# - Use self.get_user(context) for optional auth
# - Use self.require_user(context) for protected methods
# - Access context.api_key to check which API key was used

Mode 3: Required Auth (Strict - Production)

auth=GRPCAuthConfig(
enabled=True,
require_auth=True, # All methods require API key
api_key_header="x-api-key",
accept_django_secret_key=False, # Production: only DB keys
public_methods=[ # Except these
"/grpc.health.v1.Health/Check",
],
)

Public Methods

auth=GRPCAuthConfig(
enabled=True,
require_auth=False,
public_methods=[
# Health check (always public)
"/grpc.health.v1.Health/Check",
"/grpc.health.v1.Health/Watch",

# Your public methods
"/api.users.UserService/GetUser",
"/api.products.ProductService/ListProducts",
],
)

Complete Example

# Production configuration with API keys
auth=GRPCAuthConfig(
enabled=True,
require_auth=True, # Strict auth in production
# API key configuration
api_key_header="x-api-key",
accept_django_secret_key=False, # Disable SECRET_KEY in production
# Public methods
public_methods=[
"/grpc.health.v1.Health/Check",
"/grpc.health.v1.Health/Watch",
],
)

API Key Settings

Custom Header Name

auth=GRPCAuthConfig(
api_key_header="x-custom-api-key", # Change header name
)

Disable SECRET_KEY (Production)

auth=GRPCAuthConfig(
accept_django_secret_key=False, # Only accept keys from database
)

Enable SECRET_KEY (Development)

auth=GRPCAuthConfig(
accept_django_secret_key=True, # Allow using settings.SECRET_KEY
)

# Usage:
# grpcurl -H "x-api-key: $(python manage.py shell -c 'from django.conf import settings; print(settings.SECRET_KEY)')" ...

📄 GRPCProtoConfig

Protocol Buffer generation settings.

FieldTypeDefaultDescription
auto_generateboolFalseAuto-generate proto files
output_dirstr"protos"Proto output directory
package_prefixstr"api"Package prefix

Example

proto=GRPCProtoConfig(
auto_generate=True,
output_dir="protos",
package_prefix="api",
)

# Generates:
# protos/api/users.proto
# protos/api/products.proto

🎯 Environment-Specific Configs

Development

class DevelopmentConfig(DjangoConfig):
DEBUG = True

grpc: GRPCConfig = GRPCConfig(
enabled=True,
server=GRPCServerConfig(
host="127.0.0.1", # Localhost only
port=50051,
max_workers=5, # Low workers
enable_reflection=True, # Enable for testing
enable_health_check=True,
),
auth=GRPCAuthConfig(
enabled=True, # Enable auth even in dev
require_auth=False,
api_key_header="x-api-key",
accept_django_secret_key=True, # Allow SECRET_KEY for convenience
),
enabled_apps=["apps.users"], # Limited apps
)

Production

class ProductionConfig(DjangoConfig):
DEBUG = False

grpc: GRPCConfig = GRPCConfig(
enabled=True,
server=GRPCServerConfig(
host="[::]", # All interfaces
port=50051,
max_workers=20, # Higher workers
enable_reflection=False, # Disable in production
enable_health_check=True, # Keep health checks
),
auth=GRPCAuthConfig(
enabled=True, # Enable auth
require_auth=True, # Require by default
# API key configuration
api_key_header="x-api-key",
accept_django_secret_key=False, # Disable SECRET_KEY in production
public_methods=[
"/grpc.health.v1.Health/Check",
],
),
enabled_apps=[
"apps.users",
"apps.products",
"apps.orders",
"apps.payments",
],
)

Testing

class TestingConfig(DjangoConfig):
TESTING = True

grpc: GRPCConfig = GRPCConfig(
enabled=True,
server=GRPCServerConfig(
host="127.0.0.1",
port=50052, # Different port
max_workers=1, # Single worker for tests
enable_reflection=True,
enable_health_check=False, # Not needed in tests
),
auth=GRPCAuthConfig(
enabled=False, # Simplify testing
),
)

🌍 Environment Variables

Override config with environment variables:

# .env file
GRPC_ENABLED=true
GRPC_HOST=0.0.0.0
GRPC_PORT=50051
GRPC_MAX_WORKERS=20
GRPC_ENABLE_REFLECTION=false
GRPC_AUTH_ENABLED=true
GRPC_REQUIRE_AUTH=true
GRPC_API_KEY_HEADER=x-api-key
GRPC_ACCEPT_SECRET_KEY=false

In config:

import os

grpc: GRPCConfig = GRPCConfig(
enabled=os.getenv("GRPC_ENABLED", "true").lower() == "true",
server=GRPCServerConfig(
host=os.getenv("GRPC_HOST", "[::]"),
port=int(os.getenv("GRPC_PORT", "50051")),
max_workers=int(os.getenv("GRPC_MAX_WORKERS", "10")),
enable_reflection=os.getenv("GRPC_ENABLE_REFLECTION", "true").lower() == "true",
),
auth=GRPCAuthConfig(
enabled=os.getenv("GRPC_AUTH_ENABLED", "false").lower() == "true",
require_auth=os.getenv("GRPC_REQUIRE_AUTH", "false").lower() == "true",
api_key_header=os.getenv("GRPC_API_KEY_HEADER", "x-api-key"),
accept_django_secret_key=os.getenv("GRPC_ACCEPT_SECRET_KEY", "true").lower() == "true",
),
)

📊 Service Discovery

Auto-Discovery Locations

Services are discovered from these locations (in order):

  1. app/grpc_services.py
  2. app/grpc_handlers.py
  3. app/services/grpc.py
  4. app/handlers/grpc.py
  5. app/api/grpc.py

Enabled Apps

grpc: GRPCConfig = GRPCConfig(
auto_register_apps=True,
enabled_apps=[
# Full app paths
"apps.users",
"apps.products",

# Core Django apps (if they have gRPC services)
"django.contrib.auth",

# Third-party apps
"mylib.grpc_app",
],
)

Manual Registration

If auto-discovery doesn't work, register manually:

# api/grpc_handlers.py
def register_handlers(server):
from apps.users.grpc_services import UserService
from apps.users import user_pb2_grpc

user_pb2_grpc.add_UserServiceServicer_to_server(
UserService(),
server
)

Then in config:

grpc: GRPCConfig = GRPCConfig(
handlers_hook="api.grpc_handlers.register_handlers",
)

🚀 Command-Line Options

Override config when starting server:

# Custom host and port
python manage.py rungrpc --host 0.0.0.0 --port 50052

# More workers
python manage.py rungrpc --workers 20

# Verbose output
python manage.py rungrpc --verbosity 3

# Combined
python manage.py rungrpc \
--host 0.0.0.0 \
--port 50052 \
--workers 20 \
--verbosity 2

🎛️ Advanced Configuration

Custom Interceptors

Interceptor Order Matters!

Interceptors execute in the order listed. Authentication interceptors MUST come first to set context.user and context.api_key before logging interceptors use them.

Correct order:

  1. ApiKeyAuthInterceptor - Sets context.api_key and context.user
  2. RequestLoggerInterceptor - Logs request with api_key info
  3. LoggingInterceptor (dev mode)
  4. MetricsInterceptor (dev mode)
  5. Custom interceptors

Wrong order causes: API keys not tracked in logs, user info missing.

# In your config or settings
GRPC_INTERCEPTORS = [
# 1. Auth FIRST (sets context.api_key and context.user)
'django_cfg.apps.integrations.grpc.auth.ApiKeyAuthInterceptor',
# 2. Request logger (uses context.api_key from auth)
'django_cfg.apps.integrations.grpc.interceptors.RequestLoggerInterceptor',
# 3. Your custom interceptors
'myapp.interceptors.CustomInterceptor',
]

Connection Timeouts

# Client-side (in proto definition or client config)
# For long-running operations
deadline = 300 # 5 minutes

# Server-side (in service)
context.set_deadline(time.time() + 300)

Request Size Limits

# In server options
options = [
('grpc.max_send_message_length', 50 * 1024 * 1024), # 50MB
('grpc.max_receive_message_length', 50 * 1024 * 1024), # 50MB
]

server = grpc.server(
futures.ThreadPoolExecutor(max_workers=10),
interceptors=interceptors,
options=options,
)

📚 Configuration Checklist

Development ✅

  • enabled=True
  • host="127.0.0.1" (localhost)
  • enable_reflection=True (for grpcurl)
  • auth.enabled=False (easy testing)
  • max_workers=5 (low)

Production ✅

  • enabled=True
  • host="[::]" (all interfaces)
  • enable_reflection=False (security)
  • auth.enabled=True (security)
  • auth.require_auth=True (strict)
  • max_workers=20+ (high throughput)
  • Environment variables for secrets
  • Health check enabled
  • Monitoring/logging enabled

Configuration Tip: Start with defaults, measure performance, then tune based on actual traffic patterns. For production, always use API keys with require_auth=True.