Why GraphQL Federation Matters
Enterprises that split their domain logic into dozens of microservices often struggle with versioning, over‑fetching, and client‑side orchestration. A 2023 survey by the Cloud Native Computing Foundation reported that 68% of organizations consider data aggregation across services their top integration challenge. GraphQL federation solves this by letting each service expose its own schema while a central gateway composes a unified API contract.
Prerequisites and System Requirements
Before diving into code, make sure you have .NET 8 SDK installed, Docker Engine 24+, and an Azure subscription with permission to create an Azure Kubernetes Service (AKS) cluster. The Kubernetes node pool should run at least 2 vCPU and 4 GB RAM per node to handle the typical load of a small federation demo.
Setting Up .NET 8 and Hot Chocolate
Create a new ASP.NET Core Web API project targeting .NET 8 and add the Hot Chocolate packages that support Apollo federation.
dotnet new webapi -n ProductService -f net8.0
cd ProductService
dotnet add package HotChocolate.AspNetCore
dotnet add package HotChocolate.FederationIn Program.cs register the GraphQL server with federation support and define a simple query type.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddGraphQLServer()
.AddQueryType<Query>()
.AddApolloFederation();
var app = builder.Build();
app.MapGraphQL();
app.Run();
public class Query
{
public Product GetProductById(int id) => new Product { Id = id, Name = $"Product {id}" };
}
public class Product
{
[GraphQLKey]
public int Id { get; set; }
public string Name { get; set; }
}The [GraphQLKey] attribute marks the primary key that the gateway will use to resolve references across services.
Creating a Federated Service
Suppose you also have an InventoryService that stores stock levels. Its schema must expose the same Product type with the @key directive, then add a field for quantity.
builder.Services.AddGraphQLServer()
.AddQueryType<InventoryQuery>()
.AddTypeExtension<ProductInventoryExtension>()
.AddApolloFederation();
public class InventoryQuery
{
public Stock GetStock(int productId) => new Stock { ProductId = productId, Quantity = 42 };
}
public class Stock
{
public int ProductId { get; set; }
public int Quantity { get; set; }
}
[ExtendObjectType(typeof(Product))]
public class ProductInventoryExtension
{
public int Quantity([Parent] Product product, [Service] StockService stockService) =>
stockService.GetQuantity(product.Id);
}Both services now expose a Product type with the same key, allowing the federation gateway to stitch them together automatically.
Deploying to Azure Kubernetes Service
Package each service into a Docker image and push it to Azure Container Registry (ACR). The following Dockerfile works for both services.
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
WORKDIR /app
EXPOSE 80
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY ["ProductService/*.csproj", "ProductService/"]
RUN dotnet restore "ProductService/ProductService.csproj"
COPY . .
RUN dotnet publish "ProductService/ProductService.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "ProductService.dll"]After building and pushing, create a simple Kubernetes deployment and service manifest for each microservice.
apiVersion: apps/v1
kind: Deployment
metadata:
name: product-service
spec:
replicas: 2
selector:
matchLabels:
app: product-service
template:
metadata:
labels:
app: product-service
spec:
containers:
- name: product-service
image: myacr.azurecr.io/product-service:latest
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: product-service
spec:
type: ClusterIP
selector:
app: product-service
ports:
- port: 80
targetPort: 80Repeat the manifest for the inventory service, adjusting names and images accordingly. Deploy both manifests with kubectl apply -f deployment.yaml.
Configuring the Apollo Gateway on AKS
The gateway itself can be a lightweight Node.js container that reads the list of federated services from environment variables. Here is a minimal gateway.js script.
const { ApolloServer } = require('@apollo/server');
const { ApolloGateway } = require('@apollo/gateway');
const gateway = new ApolloGateway({
serviceList: [
{ name: 'product', url: process.env.PRODUCT_URL },
{ name: 'inventory', url: process.env.INVENTORY_URL }
]
});
const server = new ApolloServer({ gateway, subscriptions: false });
server.listen({ port: 4000 }).then(({ url }) => {
console.log(`Gateway ready at ${url}`);
});Build a Docker image for the gateway, push it to ACR, and expose it via an Azure Load Balancer service. In AKS, set the environment variables to the internal service DNS names, e.g., PRODUCT_URL=http://product-service.default.svc.cluster.local/graphql.
Testing the Federated API
Once the gateway is up, a client can request fields from both services in a single query:
query {
product(id: 1) {
id
name
quantity
}
}The gateway resolves product from the ProductService, then fetches quantity from InventoryService using the shared @key. Using GraphQL Playground or Insomnia you should see a combined response within 120 ms on a modest AKS node.
Performance and Monitoring Tips
1. Enable Hot Chocolate's built‑in DataLoader to batch reference lookups and reduce round‑trips between services. 2. Apply Azure Monitor's Application Insights to each pod; the distributed tracing feature automatically correlates gateway and downstream calls. 3. Set the Kubernetes Horizontal Pod Autoscaler (HPA) to scale on the cpu metric, targeting 70% utilization, which keeps latency under the 200 ms SLA observed in production environments.
Conclusion
GraphQL federation with Hot Chocolate on .NET 8 gives you a type‑safe, version‑friendly way to expose microservice data without sacrificing client flexibility. By containerizing each service, deploying to AKS, and wiring a lightweight Apollo gateway, you achieve a production‑ready architecture that scales horizontally and benefits from Azure’s observability stack. The key takeaway: invest in proper schema design and DataLoader patterns early, and the federation layer will handle the rest.
Sources
• Hot Chocolate Documentation – https://chillicream.com/docs/hotchocolate
• Azure Kubernetes Service Documentation – https://learn.microsoft.com/azure/aks
• Apollo Federation Specification – https://www.apollographql.com/docs/federation/
Author: Mahmut Sarıkaya — sarikayadev.com