Multi-Descriptor Support
Combine multiple protobuf descriptor sets from different microservices into a unified GraphQL schema. This is essential for large microservice architectures where each team owns their proto files.
Overview
Instead of maintaining a single monolithic proto file, you can:
- Let each team generate their own descriptor file
- Combine them at gateway startup
- Serve a unified GraphQL API
Basic Usage
use grpc_graphql_gateway::{Gateway, GrpcClient};
// Load descriptor sets from different microservices
const USERS_DESCRIPTORS: &[u8] = include_bytes!("path/to/users.bin");
const PRODUCTS_DESCRIPTORS: &[u8] = include_bytes!("path/to/products.bin");
const ORDERS_DESCRIPTORS: &[u8] = include_bytes!("path/to/orders.bin");
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let gateway = Gateway::builder()
// Primary descriptor set
.with_descriptor_set_bytes(USERS_DESCRIPTORS)
// Add additional services from other teams
.add_descriptor_set_bytes(PRODUCTS_DESCRIPTORS)
.add_descriptor_set_bytes(ORDERS_DESCRIPTORS)
// Add clients for each service
.add_grpc_client("users.UserService",
GrpcClient::builder("http://users:50051").connect_lazy()?)
.add_grpc_client("products.ProductService",
GrpcClient::builder("http://products:50052").connect_lazy()?)
.add_grpc_client("orders.OrderService",
GrpcClient::builder("http://orders:50053").connect_lazy()?)
.build()?;
gateway.serve("0.0.0.0:8888").await?;
Ok(())
}
File-Based Loading
Load descriptors from files instead of embedding:
let gateway = Gateway::builder()
.with_descriptor_set_file("path/to/users.bin")?
.add_descriptor_set_file("path/to/products.bin")?
.add_descriptor_set_file("path/to/orders.bin")?
.build()?;
API Methods
| Method | Description |
|---|---|
with_descriptor_set_bytes(bytes) | Set primary descriptor (clears existing) |
add_descriptor_set_bytes(bytes) | Add additional descriptor |
with_descriptor_set_file(path) | Set primary descriptor from file |
add_descriptor_set_file(path) | Add additional descriptor from file |
descriptor_count() | Get number of configured descriptors |
Use Cases
Microservice Architecture
Each team generates their own descriptor:
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Users Team │ │ Products Team │ │ Orders Team │
│ │ │ │ │ │
│ users.proto │ │ products.proto │ │ orders.proto │
│ ↓ │ │ ↓ │ │ ↓ │
│ users.bin │ │ products.bin │ │ orders.bin │
└────────┬────────┘ └────────┬────────┘ └────────┬────────┘
│ │ │
└───────────────────────┼───────────────────────┘
│
┌────────────▼────────────┐
│ GraphQL Gateway │
│ │
│ Unified GraphQL Schema │
└─────────────────────────┘
Schema Stitching
Combine services at the gateway level:
# From users.bin
type Query {
user(id: ID!): User
}
# From products.bin
type Query {
product(upc: String!): Product
}
# From orders.bin
type Query {
order(id: ID!): Order
}
# Unified Schema (automatic)
type Query {
user(id: ID!): User
product(upc: String!): Product
order(id: ID!): Order
}
Independent Deployments
Update individual service descriptors without restarting:
// Hot-reload could be implemented by watching descriptor files
let gateway = Gateway::builder()
.with_descriptor_set_file("/config/users.bin")?
.add_descriptor_set_file("/config/products.bin")?
.build()?;
How It Works
- Primary descriptor is loaded with
with_descriptor_set_bytes/file - Additional descriptors are merged using
add_descriptor_set_bytes/file - Duplicate files are automatically skipped (same filename)
- Services and types from all descriptors are combined
- GraphQL schema is generated from the merged pool
Requirements
- All descriptors must include
graphql.protowith annotations - Service names should be unique across descriptors
- Type names are namespaced by their proto package
Logging
The gateway logs merge information:
INFO Merged 3 descriptor sets into unified schema (5 services, 42 types)
DEBUG Merged descriptor set #2 (15234 bytes) into schema pool
DEBUG Merged descriptor set #3 (8921 bytes) into schema pool
Error Handling
Common errors and solutions:
| Error | Cause | Solution |
|---|---|---|
at least one descriptor set is required | No descriptors provided | Add at least one with with_descriptor_set_bytes |
failed to merge descriptor set #N | Invalid protobuf data | Verify the descriptor file is valid |
missing graphql.schema extension | Annotations not found | Ensure graphql.proto is included |