Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
grpc-graphql-gateway
A high-performance Rust gateway that bridges gRPC services to GraphQL with full Apollo Federation v2 support.
Transform your gRPC microservices into a unified GraphQL API with zero GraphQL code. This gateway dynamically generates GraphQL schemas from protobuf descriptors and routes requests to your gRPC backends via Tonic, providing a seamless bridge between gRPC and GraphQL ecosystems.
โจ Features
Core Capabilities
- ๐ Dynamic Schema Generation - Automatic GraphQL schema from protobuf descriptors
- โก Full Operation Support - Queries, Mutations, and Subscriptions
- ๐ WebSocket Subscriptions - Real-time data via GraphQL subscriptions (
graphql-wsprotocol) - ๐ค File Uploads - Multipart form data support for file uploads
- ๐ฏ Type Safety - Leverages Rust's type system for robust schema generation
Federation & Enterprise
- ๐ Apollo Federation v2 - Complete federation support with entity resolution
- ๐ Entity Resolution - Production-ready resolver with DataLoader batching
- ๐ซ No N+1 Queries - Built-in DataLoader prevents performance issues
- ๐ All Federation Directives -
@key,@external,@requires,@provides,@shareable - ๐ Batch Operations - Efficient entity resolution with automatic batching
Developer Experience
- ๐ ๏ธ Code Generation -
protoc-gen-graphql-templategenerates starter gateway code - ๐ง Middleware Support - Extensible middleware for auth, logging, and observability
- ๐ Rich Examples - Complete working examples for all features
- ๐งช Well Tested - Comprehensive test coverage
Production Ready
- ๐ฅ Health Checks -
/healthand/readyendpoints for Kubernetes liveness/readiness probes - ๐ Prometheus Metrics -
/metricsendpoint with request counts, latencies, and error rates - ๐ญ OpenTelemetry Tracing - Distributed tracing with GraphQL and gRPC span tracking
- ๐ก๏ธ DoS Protection - Query depth and complexity limiting to prevent expensive queries
- ๐ Introspection Control - Disable schema introspection in production for security
- โก Rate Limiting - Built-in rate limiting middleware
- ๐ฆ Automatic Persisted Queries (APQ) - Reduce bandwidth with query hash caching
๐ Quick Start
Installation
[]
= "0.2"
= { = "1", = ["full"] }
= "0.12"
Basic Gateway
use ;
const DESCRIPTORS: & = include_bytes!;
async
Your gateway is now running!
- GraphQL HTTP:
http://localhost:8888/graphql - GraphQL WebSocket:
ws://localhost:8888/graphql/ws
Generate Descriptors
Add to your build.rs:
๐ Usage Examples
Queries, Mutations & Subscriptions
Annotate your proto file with GraphQL directives:
service UserService {
option (graphql.service) = {
host: "localhost:50051"
insecure: true
};
// Query
rpc GetUser(GetUserRequest) returns (User) {
option (graphql.schema) = {
type: QUERY
name: "user"
};
}
// Mutation
rpc CreateUser(CreateUserRequest) returns (User) {
option (graphql.schema) = {
type: MUTATION
name: "createUser"
request { name: "input" }
};
}
// Subscription (server streaming)
rpc WatchUser(WatchUserRequest) returns (stream User) {
option (graphql.schema) = {
type: SUBSCRIPTION
name: "userUpdates"
};
}
}
GraphQL operations:
# Query
query {
user(id: "123") {
id
name
email
}
}
# Mutation
mutation {
createUser(input: { name: "Alice", email: "alice@example.com" }) {
id
name
}
}
# Subscription
subscription {
userUpdates(id: "123") {
id
name
status
}
}
File Uploads
The gateway automatically supports GraphQL file uploads via multipart requests:
message UploadAvatarRequest {
string user_id = 1;
bytes avatar = 2; // Maps to Upload scalar in GraphQL
}
Field-Level Control
message User {
string id = 1 [(graphql.field) = { required: true }];
string email = 2 [(graphql.field) = { name: "emailAddress" }];
string internal_id = 3 [(graphql.field) = { omit: true }];
string password_hash = 4 [(graphql.field) = { omit: true }];
}
๐ Apollo Federation v2
Build federated GraphQL architectures with multiple subgraphs.
Defining Entities
message User {
option (graphql.entity) = {
keys: "id"
resolvable: true
};
string id = 1 [(graphql.field) = { required: true }];
string email = 2 [(graphql.field) = { shareable: true }];
string name = 3 [(graphql.field) = { shareable: true }];
}
message Product {
option (graphql.entity) = {
keys: "upc"
resolvable: true
};
string upc = 1 [(graphql.field) = { required: true }];
string name = 2 [(graphql.field) = { shareable: true }];
int32 price = 3 [(graphql.field) = { shareable: true }];
User created_by = 4 [(graphql.field) = {
name: "createdBy"
shareable: true
}];
}
Entity Resolution with DataLoader
The gateway includes production-ready entity resolution with automatic batching:
use ;
// Configure entity resolver with DataLoader batching
let resolver = builder
.register_entity_resolver
.build;
let gateway = builder
.with_descriptor_set_bytes
.enable_federation
.with_entity_resolver
.add_grpc_client
.serve
.await?;
Benefits:
- โ No N+1 Queries - DataLoader batches concurrent entity requests
- โ Automatic Batching - Multiple entities resolved in single operation
- โ Production Ready - Comprehensive error handling and logging
Extending Entities
message UserReviews {
option (graphql.entity) = {
extend: true
keys: "id"
};
string id = 1 [(graphql.field) = {
external: true
required: true
}];
repeated Review reviews = 2 [(graphql.field) = {
requires: "id"
}];
}
Federation Directives
| Directive | Purpose | Example |
|---|---|---|
@key |
Define entity key fields | keys: "id" |
@shareable |
Field resolvable from multiple subgraphs | shareable: true |
@external |
Field defined in another subgraph | external: true |
@requires |
Fields needed from other subgraphs | requires: "id email" |
@provides |
Fields this resolver provides | provides: "id name" |
Running with Apollo Router
# Start your federation subgraphs
# Compose the supergraph
# Run Apollo Router
Query the federated graph:
query {
product(upc: "123") {
upc
name
price
createdBy {
id
name
email # Resolved from User subgraph!
}
}
}
๐ง Advanced Features
Middleware
use ;
;
let gateway = builder
.add_middleware
.build?;
DoS Protection (Query Limits)
Protect your gateway and gRPC backends from malicious or expensive queries:
let gateway = builder
.with_descriptor_set_bytes
.with_query_depth_limit // Max nesting depth
.with_query_complexity_limit // Max query cost
.add_grpc_client
.build?;
Query Depth Limiting prevents deeply nested queries that could overwhelm your backends:
# This would be blocked if depth exceeds limit
query {
users { # depth 1
friends { # depth 2
friends { # depth 3
friends { # depth 4 - blocked if limit < 4
name
}
}
}
}
}
Query Complexity Limiting caps the total "cost" of a query (each field = 1 by default):
# Complexity = 4 (users + friends + name + email)
query {
users {
friends {
name
email
}
}
}
Recommended Values:
| Use Case | Depth Limit | Complexity Limit |
|---|---|---|
| Public API | 5-10 | 50-100 |
| Authenticated Users | 10-15 | 100-500 |
| Internal/Trusted | 15-25 | 500-1000 |
Health Checks
Enable Kubernetes-compatible health check endpoints:
let gateway = builder
.with_descriptor_set_bytes
.enable_health_checks // Adds /health and /ready endpoints
.add_grpc_client
.build?;
Endpoints:
| Endpoint | Purpose | Response |
|---|---|---|
GET /health |
Liveness probe | 200 OK if server is running |
GET /ready |
Readiness probe | 200 OK if gRPC clients are configured |
Kubernetes Deployment:
livenessProbe:
httpGet:
path: /health
port: 8888
initialDelaySeconds: 5
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8888
initialDelaySeconds: 5
periodSeconds: 10
Prometheus Metrics
Enable Prometheus-compatible metrics endpoint:
let gateway = builder
.with_descriptor_set_bytes
.enable_metrics // Adds /metrics endpoint
.add_grpc_client
.build?;
Metrics Exposed:
| Metric | Type | Description |
|---|---|---|
graphql_requests_total |
Counter | Total requests by operation type |
graphql_request_duration_seconds |
Histogram | Request latency |
graphql_errors_total |
Counter | Errors by type |
grpc_backend_requests_total |
Counter | gRPC backend calls |
grpc_backend_duration_seconds |
Histogram | gRPC latency |
Prometheus Scrape Config:
scrape_configs:
- job_name: 'graphql-gateway'
static_configs:
- targets:
metrics_path: '/metrics'
OpenTelemetry Tracing
Enable distributed tracing for end-to-end visibility:
use ;
// Initialize the tracer (do this once at startup)
let config = new
.with_service_name
.with_sample_ratio; // Sample all requests
let _provider = init_tracer;
let gateway = builder
.with_descriptor_set_bytes
.enable_tracing
.add_grpc_client
.build?;
// ... run your server ...
// Shutdown on exit
shutdown_tracer;
Spans Created:
| Span | Kind | Attributes |
|---|---|---|
graphql.query |
Server | graphql.operation.name, graphql.document |
graphql.mutation |
Server | graphql.operation.name, graphql.document |
grpc.call |
Client | rpc.service, rpc.method, rpc.grpc.status_code |
OTLP Export (Optional):
[]
= { = "0.1", = ["otlp"] }
Schema Introspection Control
Disable introspection in production for security:
let gateway = builder
.with_descriptor_set_bytes
.disable_introspection // Block __schema and __type queries
.add_grpc_client
.build?;
Environment-Based Toggle:
let is_production = var.map.unwrap_or;
let mut builder = builder
.with_descriptor_set_bytes;
if is_production
let gateway = builder.build?;
Automatic Persisted Queries (APQ)
Reduce bandwidth by caching queries on the server and allowing clients to send query hashes:
use ;
use Duration;
let gateway = builder
.with_descriptor_set_bytes
.with_persisted_queries
.add_grpc_client
.build?;
How APQ Works:
- First request: Client sends hash only โ Gateway returns
PERSISTED_QUERY_NOT_FOUND - Retry: Client sends hash + full query โ Gateway caches and executes
- Subsequent requests: Client sends hash only โ Gateway uses cached query
Client Request Format:
Benefits:
- โ Reduces request payload size by ~90% for large queries
- โ Compatible with Apollo Client's APQ implementation
- โ LRU eviction prevents unbounded memory growth
- โ Optional TTL for cache expiration
Custom Error Handling
let gateway = builder
.with_error_handler
.build?;
Response Plucking
Extract nested fields as top-level responses:
message ListUsersResponse {
repeated User users = 1;
int32 total = 2;
}
rpc ListUsers(ListUsersRequest) returns (ListUsersResponse) {
option (graphql.schema) = {
type: QUERY
name: "users"
response {
pluck: "users" // Returns [User] instead of ListUsersResponse
}
};
}
๐ Type Mapping
| Protobuf | GraphQL |
|---|---|
string |
String |
bool |
Boolean |
int32, uint32 |
Int |
int64, uint64 |
String (avoids precision loss) |
float, double |
Float |
bytes |
Upload (input) / String (output, base64) |
repeated T |
[T] |
message |
Object / InputObject |
enum |
Enum |
๐ ๏ธ Code Generation
Generate a starter gateway:
# Install the generator
# Generate gateway code
# Run the generated gateway
The generator creates:
- Complete gateway implementation
- Example queries/mutations/subscriptions
- Service configuration
- Ready-to-run code
๐ Examples
Greeter Example
Basic query, mutation, subscription, and file upload:
Open http://localhost:8888/graphql and try:
query { hello(name: "World") { message } }
mutation { updateGreeting(input: {name: "GraphQL", salutation: "Hey"}) { message } }
subscription { streamHello(name: "Stream") { message } }
Federation Example
Complete federated microservices with entity resolution:
Demonstrates:
- 3 federated subgraphs (User, Product, Review)
- Entity resolution with DataLoader batching
- Cross-subgraph queries
@shareablefields- Entity extensions
๐ฏ Best Practices
Federation
- Define Clear Boundaries - Each subgraph owns its entities
- Use @shareable Wisely - Mark fields resolved by multiple subgraphs
- Leverage DataLoader - Prevent N+1 queries with batch resolution
- Composite Keys - Use when entities need multiple identifiers
- Minimize @requires - Only specify truly required fields
Performance
- Enable Connection Pooling - Reuse gRPC connections
- Use Lazy Connections - Connect on first use
- Implement Caching - Cache frequently accessed entities
- Batch Operations - Use DataLoader for entity resolution
- Monitor Metrics - Track query performance and batch sizes
Security
- Validate Inputs - Use field-level validation
- Omit Sensitive Fields - Use
omit: truefor internal data - Implement Auth Middleware - Centralize authentication
- Rate Limiting - Protect against abuse
- TLS/SSL - Secure gRPC connections in production
๐งช Testing
# Run all tests
# Run with logging
RUST_LOG=debug
# Run specific test
๐ฆ Project Structure
grpc-graphql-gateway-rs/
โโโ src/
โ โโโ lib.rs # Public API
โ โโโ gateway.rs # Gateway implementation
โ โโโ schema.rs # Schema builder
โ โโโ federation.rs # Federation support
โ โโโ dataloader.rs # DataLoader for batching
โ โโโ grpc_client.rs # gRPC client management
โ โโโ middleware.rs # Middleware system
โ โโโ runtime.rs # HTTP/WebSocket server
โโโ proto/
โ โโโ graphql.proto # GraphQL annotations
โ โโโ *.proto # Your service definitions
โโโ examples/
โ โโโ greeter/ # Basic example
โ โโโ federation/ # Federation example
โโโ tests/ # Integration tests
๐ค Contributing
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
๐ License
This project is licensed under the MIT License - see the LICENSE file for details.
๐ Acknowledgments
- Inspired by grpc-graphql-gateway (Go)
- Built with async-graphql
- Powered by tonic
- Federation based on Apollo Federation v2
๐ Links
Made with โค๏ธ by Protocol Lattice