This is a demonstration application showcasing how to use gRPC Federation to build a BFF (Backend for Frontend) API that aggregates data from multiple microservices.
The demo consists of:
- 3 Backend Services: User, Product, and Order services
- 1 BFF Service: Aggregates data from backend services using gRPC Federation
- Test Client: Demonstrates API usage
- Docker and Docker Compose
- Go 1.26+ (for development only;
go.modpins the build toolchain to Go 1.27) - Protocol Buffers compiler (protoc) (for development only)
# Build all Docker images
make build-all
# Start all services
make up-allThis will start:
- User Service on port 50051
- Product Service on port 50052
- Order Service on port 50053
- BFF Service on port 50054
Using grpcurl:
# Test GetUserWithOrders (includes success, USER_NOT_FOUND, USER_ACCOUNT_SUSPENDED)
make grpcurl-user
# Test GetOrderDetails (includes success, ORDER_NOT_FOUND, ORDER_CANCELLED, PRODUCT_NOT_FOUND, PRODUCT_OUT_OF_STOCK)
make grpcurl-order
# Test GetUserDashboard (includes success, USER_NOT_FOUND, USER_ACCOUNT_SUSPENDED)
make grpcurl-dashboard
# Or test all APIs and all error cases at once
make grpcurl-allTest Cases:
- Success cases: Normal user/order data
- FailedPrecondition cases: Various business logic errors (user not found, account suspended, order cancelled, product issues)
- Error handling: Demonstrates localized Japanese error messages with detailed error information
# Stop all services
make down-all
# Stop and remove volumes (clean everything)
make cleanBackend services only:
make build-backend
make up-backend
make down-backendBFF service only:
make build-bff
make up-bff
make down-bffThe BFF service provides three main endpoints that demonstrate gRPC Federation capabilities:
Fetches a user along with their orders by aggregating data from User and Order services.
Retrieves detailed order information including user details and product information by aggregating data from all three backend services.
Provides a comprehensive user dashboard with user info, order statistics, recent orders, and product recommendations.
The BFF service uses gRPC Federation options in the Protocol Buffer definitions to:
- Define service dependencies: Specify which backend services are needed
- Make parallel/sequential calls: Automatically optimize service calls
- Transform and aggregate data: Use CEL expressions to process responses
- Auto-bind fields: Map backend responses to BFF response fields
Example from proto/bff/bff.proto:
message GetUserWithOrdersResponse {
option (grpc.federation.message) = {
def {
name: "user_res"
call {
method: "backend.user.UserService/GetUser"
request {
field { field: "user_id" by: "$.user_id" }
}
}
}
def {
name: "orders_res"
call {
method: "backend.order.OrderService/ListOrdersByUser"
request {
field { field: "user_id" by: "$.user_id" }
}
}
}
};
backend.user.User user = 1 [(grpc.federation.field).by = "user_res.user"];
repeated backend.order.Order orders = 2 [(grpc.federation.field).by = "orders_res.orders"];
}Aggregation across a repeated field is written as a CEL expression, so no Go code
is needed on the BFF side. GetUserDashboard sums the order amounts with the
reduce macro from the grpc.federation.list library:
message UserStatistics {
option (grpc.federation.message) = {};
int32 total_orders = 1 [(grpc.federation.field).by = "$.total_orders"];
double total_spent = 2 [(grpc.federation.field).by =
"$.orders.reduce(accum, cur, accum + cur.total_amount, 0.0)"];
}$ make grpcurl-dashboard
"statistics": { "totalOrders": 2, "totalSpent": 889.95 }grpc-federation-demo/
├── proto/
│ ├── backend/ # Backend service definitions
│ │ ├── user.proto
│ │ ├── product.proto
│ │ └── order.proto
│ └── bff/
│ └── bff.proto # BFF service with federation
├── backend/ # Backend service implementations
│ ├── user/server.go
│ ├── product/server.go
│ └── order/server.go
└── bff/
└── server.go # BFF service implementation
This project demonstrates the typical workflow for implementing a BFF using grpc-federation:
1. Define your backend services (proto/backend/*.proto)
First, create standard gRPC service definitions for your backend microservices:
// proto/backend/user.proto
service UserService {
rpc GetUser(GetUserRequest) returns (GetUserResponse);
}2. Write BFF proto with federation annotations (proto/bff/bff.proto)
Add grpc-federation options to define how to aggregate backend services:
service BFFService {
option (grpc.federation.service) = {
dependencies: [
{ name: "user" service: "backend.user.UserService" },
{ name: "order" service: "backend.order.OrderService" }
]
};
rpc GetUserWithOrders(GetUserWithOrdersRequest) returns (GetUserWithOrdersResponse);
}
message GetUserWithOrdersResponse {
option (grpc.federation.message) = {
def {
name: "user_res"
call { method: "user.UserService/GetUser" ... }
}
def {
name: "orders_res"
call { method: "order.OrderService/ListOrdersByUser" ... }
}
};
User user = 1 [(grpc.federation.field).by = "user_res.user"];
repeated Order orders = 2 [(grpc.federation.field).by = "orders_res.orders"];
}3. Generate Go code
Run protoc with the grpc-federation plugin to auto-generate the BFF implementation:
make proto-bffThis generates:
bff.pb.go- Standard protobuf codebff_grpc.pb.go- Standard gRPC server/client codebff_grpc_federation.pb.go- Federation logic (auto-generated!)
4. Implement the BFF server (bff/server.go)
Simply register the auto-generated federation service:
func main() {
conn := /* create connections to backend services */
// The federation code handles all the aggregation logic!
federationServer := bff.NewBFFServiceFederationServer(
bff.BFFServiceFederationConfig(conn),
)
grpc.RegisterService(s, federationServer)
s.Serve(lis)
}Key Benefits:
- ✅ No manual service orchestration code
- ✅ Automatic parallel/sequential call optimization
- ✅ Type-safe field binding
- ✅ Built-in error handling and timeout support
If you want to modify the proto files or service implementations:
# Initialize project (install required tools)
make init
# Generate proto files after making changes
make proto
# Or generate individually
make proto-backend # Backend services only
make proto-bff # BFF service only
# Rebuild and restart services
make build-all
make down-all
make up-all# Protocol Buffer Generation
make proto # Generate all proto files
make proto-backend # Generate backend proto only
make proto-bff # Generate BFF proto only
# Docker Commands
make build-all # Build all Docker images
make up-all # Start all services
make down-all # Stop all services
make clean # Stop and remove all containers and volumes
# Testing Commands
make grpcurl-list # List available services
make grpcurl-user # Test GetUserWithOrders
make grpcurl-order # Test GetOrderDetails
make grpcurl-dashboard # Test GetUserDashboard
make grpcurl-all # Test all endpoints
# Setup
make init # Install required tools