Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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:

  1. Let each team generate their own descriptor file
  2. Combine them at gateway startup
  3. 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

MethodDescription
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

  1. Primary descriptor is loaded with with_descriptor_set_bytes/file
  2. Additional descriptors are merged using add_descriptor_set_bytes/file
  3. Duplicate files are automatically skipped (same filename)
  4. Services and types from all descriptors are combined
  5. GraphQL schema is generated from the merged pool

Requirements

  • All descriptors must include graphql.proto with 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:

ErrorCauseSolution
at least one descriptor set is requiredNo descriptors providedAdd at least one with with_descriptor_set_bytes
failed to merge descriptor set #NInvalid protobuf dataVerify the descriptor file is valid
missing graphql.schema extensionAnnotations not foundEnsure graphql.proto is included