🚀 Oferta especial: 60% OFF no CrazyStack - Últimas vagas!Garantir vaga →
Módulo 5 - Aula 3

GraphQL Federation com tRPC

Domine GraphQL Federation com tRPC: schema stitching, resolvers distribuídos, gateway unificado e integração seamless entre microserviços para arquiteturas enterprise.

120 min
Expert
GraphQL Federation

🎯 Por que GraphQL Federation é essencial para SaaS enterprise?

API Unificada: Um único endpoint GraphQL que agrega todos os microserviços, simplificando consumo para o frontend.

Autonomia dos Times: Cada microserviço mantém seu próprio schema, permitindo desenvolvimento independente.

Type Safety Distribuída: Toda a type safety do tRPC é preservada através da federation, mantendo DX excepcional.

Escalabilidade: Gateway otimiza queries distribuídas automaticamente, suportando milhões de requests.

⚠️ Conceitos Importantes para Entender

Apollo Federation:

Especificação que permite compor múltiplos schemas GraphQL em um único gateway.

Schema Stitching:

Técnica para combinar schemas de diferentes serviços em uma API unificada.

Subgraph:

Cada microserviço que expõe um schema GraphQL partial que faz parte da federation.

Query Planning:

Otimização automática que distribui queries entre serviços de forma eficiente.

🌐 GraphQL Gateway Setup

🔧 Apollo Federation Gateway

gateway/apollo-federation-gateway.ts
// 📁 gateway/apollo-federation-gateway.ts
import { ApolloGateway, IntrospectAndCompose } from '@apollo/gateway';
import { ApolloServer } from 'apollo-server-express';
import { Logger } from '@/infrastructure/logging/logger';

// 🎯 Interface para serviços federados
export interface FederatedService {
  name: string;
  url: string;
  schema?: string;
  healthCheckPath?: string;
  headers?: Record<string, string>;
}

// 🌐 Gateway Configuration
export interface GatewayConfig {
  services: FederatedService[];
  introspectionInterval?: number;
  debug?: boolean;
  playground?: boolean;
  cors?: boolean;
  subscriptions?: boolean;
}

// 🏗️ Apollo Federation Gateway
export class TRPCGraphQLGateway {
  private gateway: ApolloGateway;
  private server: ApolloServer;
  private logger: Logger;
  private config: GatewayConfig;
  
  constructor(config: GatewayConfig, logger: Logger) {
    this.config = config;
    this.logger = logger;
    this.setupGateway();
  }
  
  // 🔧 Setup Apollo Gateway
  private setupGateway(): void {
    this.gateway = new ApolloGateway({
      supergraphSdl: new IntrospectAndCompose({
        subgraphs: this.config.services.map(service => ({
          name: service.name,
          url: service.url,
          headers: service.headers || {},
        })),
        introspectionHeaders: {
          'Apollo-Require-Preflight': 'true',
        },
        pollIntervalInMs: this.config.introspectionInterval || 30000,
      }),
      
      // 🔍 Service health monitoring
      serviceHealthCheck: true,
      
      // 📊 Execution metrics
      experimental_approximateQueryPlanStoreMiB: 30,
      
      // 🚨 Error handling personalizado
      buildService: ({ url, name }) => {
        return {
          process: ({ request, context }) => {
            // 🔐 Add authentication headers
            return fetch(url, {
              method: 'POST',
              headers: {
                'Content-Type': 'application/json',
                'Authorization': context.authorization || '',
                'X-User-ID': context.userId || '',
                'X-Organization-ID': context.organizationId || '',
                'X-Trace-ID': context.traceId || '',
                ...request.http?.headers,
              },
              body: JSON.stringify({
                query: request.query,
                variables: request.variables,
                operationName: request.operationName,
              }),
            })
            .then(response => response.json())
            .catch(error => {
              this.logger.error('Service request failed', {
                service: name,
                url,
                error: error.message,
                traceId: context.traceId,
              });
              throw error;
            });
          },
        };
      },
    });
  }

🚀 Gateway Startup e Health Check

gateway/apollo-federation-gateway.ts
// 🚀 Start Gateway Server
async start(port: number = 4000): Promise<void> {
  try {
    // 🔧 Create Apollo Server
    this.server = new ApolloServer({
      gateway: this.gateway,
      subscriptions: this.config.subscriptions || false,
      
      // 📝 Context creation
      context: async ({ req }) => {
        return {
          authorization: req.headers.authorization,
          userId: req.headers['x-user-id'],
          organizationId: req.headers['x-organization-id'],
          traceId: req.headers['x-trace-id'] || this.generateTraceId(),
        };
      },
      
      // 🎭 Playground configuration
      introspection: this.config.debug || false,
      playground: this.config.playground && this.config.debug,
      
      // 📊 Plugins for monitoring
      plugins: [
        {
          requestDidStart() {
            return {
              didResolveOperation(requestContext) {
                console.log('🔍 Operation:', requestContext.operationName);
              },
              didEncounterErrors(requestContext) {
                console.error('❌ GraphQL Errors:', requestContext.errors);
              },
            };
          },
        },
      ],
      
      // 🛡️ Security configuration
      cors: this.config.cors || {
        origin: process.env.ALLOWED_ORIGINS?.split(',') || 'http://localhost:3000',
        credentials: true,
      },
    });
    
    await this.server.listen({ port });
    
    this.logger.info('🌐 GraphQL Gateway started', {
      port,
      services: this.config.services.length,
      playground: this.config.playground && this.config.debug,
    });
    
    // 📊 Log federated services
    for (const service of this.config.services) {
      this.logger.info('📡 Federated service registered', {
        name: service.name,
        url: service.url,
      });
    }
  } catch (error) {
    this.logger.error('Failed to start GraphQL Gateway', {
      error: error.message,
      stack: error.stack,
    });
    throw error;
  }
}

// 🔍 Health Check completo
async healthCheck(): Promise<{
  status: string;
  services: Array<{
    name: string;
    status: 'healthy' | 'unhealthy' | 'unknown';
    latency?: number;
    error?: string;
  }>;
}> {
  const serviceChecks = await Promise.allSettled(
    this.config.services.map(async (service) => {
      const start = Date.now();
      
      try {
        const healthUrl = service.healthCheckPath 
          ? `${service.url.replace('/graphql', '')}${service.healthCheckPath}`
          : `${service.url.replace('/graphql', '')}/health`;
        
        const response = await fetch(healthUrl, {
          method: 'GET',
          timeout: 5000,
        });
        
        const latency = Date.now() - start;
        
        return {
          name: service.name,
          status: response.ok ? 'healthy' : 'unhealthy',
          latency,
        };
      } catch (error) {
        return {
          name: service.name,
          status: 'unhealthy',
          latency: Date.now() - start,
          error: error.message,
        };
      }
    })
  );
  
  const services = serviceChecks.map((result, index) => {
    if (result.status === 'fulfilled') {
      return result.value;
    } else {
      return {
        name: this.config.services[index].name,
        status: 'unknown',
        error: result.reason?.message || 'Unknown error',
      };
    }
  });
  
  const allHealthy = services.every(s => s.status === 'healthy');
  
  return {
    status: allHealthy ? 'healthy' : 'degraded',
    services,
  };
}

⚙️ Configuração de Produção

gateway/production-config.ts
// 📁 gateway/production-config.ts
// 🔧 Gateway Configuration para Produção

const gatewayConfig: GatewayConfig = {
  services: [
    {
      name: 'user-service',
      url: process.env.USER_SERVICE_URL || 'http://localhost:4001/graphql',
      healthCheckPath: '/health',
      headers: {
        'X-API-Key': process.env.INTERNAL_API_KEY || '',
        'X-Service-Name': 'gateway',
      },
    },
    {
      name: 'organization-service',
      url: process.env.ORGANIZATION_SERVICE_URL || 'http://localhost:4002/graphql',
      healthCheckPath: '/health',
      headers: {
        'X-API-Key': process.env.INTERNAL_API_KEY || '',
        'X-Service-Name': 'gateway',
      },
    },
    {
      name: 'billing-service',
      url: process.env.BILLING_SERVICE_URL || 'http://localhost:4003/graphql',
      healthCheckPath: '/health',
      headers: {
        'X-API-Key': process.env.INTERNAL_API_KEY || '',
        'X-Service-Name': 'gateway',
      },
    },
    {
      name: 'analytics-service',
      url: process.env.ANALYTICS_SERVICE_URL || 'http://localhost:4004/graphql',
      healthCheckPath: '/health',
      headers: {
        'X-API-Key': process.env.INTERNAL_API_KEY || '',
        'X-Service-Name': 'gateway',
      },
    },
  ],
  introspectionInterval: 30000, // 30 seconds
  debug: process.env.NODE_ENV === 'development',
  playground: process.env.NODE_ENV === 'development',
  cors: true,
  subscriptions: false, // tRPC handles subscriptions
};

// 🚀 Start Gateway with error handling
async function startGateway() {
  const logger = new Logger('GraphQL Gateway');
  const gateway = new TRPCGraphQLGateway(gatewayConfig, logger);
  
  try {
    await gateway.start(parseInt(process.env.GATEWAY_PORT || '4000'));
    
    // 📊 Periodic health checks
    setInterval(async () => {
      const health = await gateway.healthCheck();
      logger.info('🏥 Gateway health check', health);
      
      // 🚨 Alert se serviços estão down
      const unhealthyServices = health.services.filter(s => s.status !== 'healthy');
      if (unhealthyServices.length > 0) {
        logger.error('🚨 Unhealthy services detected', { unhealthyServices });
        // Aqui você poderia integrar com alerting (Slack, PagerDuty, etc.)
      }
    }, 60000); // Every minute
    
    // 🔄 Graceful shutdown
    process.on('SIGTERM', async () => {
      logger.info('📛 Received SIGTERM, shutting down gracefully');
      await gateway.stop();
      process.exit(0);
    });
    
    process.on('SIGINT', async () => {
      logger.info('📛 Received SIGINT, shutting down gracefully');
      await gateway.stop();
      process.exit(0);
    });
  } catch (error) {
    logger.error('💥 Failed to start gateway', error);
    process.exit(1);
  }
}

if (require.main === module) {
  startGateway();
}

🌉 tRPC to GraphQL Bridge

🔧 Bridge Adapter Implementation

bridge/trpc-graphql-adapter.ts
// 📁 bridge/trpc-graphql-adapter.ts
import { initTRPC } from '@trpc/server';
import { buildSubgraphSchema } from '@apollo/subgraph';
import { gql } from 'apollo-server-core';
import { GraphQLResolveInfo } from 'graphql';

// 🎯 Types para bridge
export interface TRPCToGraphQLConfig {
  serviceName: string;
  trpcRouter: any;
  context: any;
  enableIntrospection?: boolean;
}

export interface GraphQLFieldMapping {
  trpcProcedure: string;
  graphqlField: string;
  inputMapping?: Record<string, string>;
  outputMapping?: Record<string, string>;
}

// 🌉 tRPC to GraphQL Adapter
export class TRPCGraphQLBridge {
  private config: TRPCToGraphQLConfig;
  private fieldMappings: Map<string, GraphQLFieldMapping> = new Map();
  
  constructor(config: TRPCToGraphQLConfig) {
    this.config = config;
  }
  
  // 📝 Register field mapping
  addFieldMapping(mapping: GraphQLFieldMapping): void {
    this.fieldMappings.set(mapping.graphqlField, mapping);
  }
  
  // 🏗️ Build GraphQL Schema from tRPC Router
  buildSchema(): any {
    const typeDefs = this.generateTypeDefs();
    const resolvers = this.generateResolvers();
    
    return buildSubgraphSchema([
      {
        typeDefs,
        resolvers,
      },
    ]);
  }
  
  // 📞 Call tRPC Procedure Helper
  private async callTRPCProcedure(
    procedurePath: string,
    input: any,
    context: any
  ): Promise<any> {
    try {
      // 🎯 Navigate to tRPC procedure
      const pathParts = procedurePath.split('.');
      let procedure = context.trpc;
      
      for (const part of pathParts) {
        procedure = procedure[part];
        if (!procedure) {
          throw new Error(`tRPC procedure not found: ${procedurePath}`);
        }
      }
      
      // 📞 Execute procedure
      const result = await procedure(input);
      
      context.logger?.debug('tRPC procedure executed', {
        procedure: procedurePath,
        input,
        success: true,
      });
      
      return result;
    } catch (error) {
      context.logger?.error('tRPC procedure failed', {
        procedure: procedurePath,
        input,
        error: error.message,
      });
      throw error;
    }
  }
}

📄 Federation Schema Definition

bridge/trpc-graphql-adapter.ts
// 📄 Generate GraphQL Type Definitions
private generateTypeDefs(): any {
  return gql`
    # 👤 User Entity (Federated)
    type User @key(fields: "id") {
      id: ID!
      email: String!
      name: String!
      role: UserRole!
      organizationId: ID
      isActive: Boolean!
      createdAt: String!
      updatedAt: String!
      metadata: JSON
      
      # 🔗 Federation: Relations to other services
      organization: Organization @provides(fields: "name")
      billingProfile: BillingProfile @provides(fields: "plan")
      analytics: UserAnalytics @provides(fields: "lastActive")
    }
    
    # 🏢 Organization Entity (Federated)
    type Organization @key(fields: "id") {
      id: ID!
      name: String!
      plan: String!
      isActive: Boolean!
      createdAt: String!
      
      # 🔗 Relations
      users: [User!]! @provides(fields: "id email name")
      owner: User! @provides(fields: "id name email")
      billing: BillingProfile @provides(fields: "plan status")
    }
    
    # 💳 Billing Profile (External reference)
    type BillingProfile @key(fields: "organizationId") @extends {
      organizationId: ID! @external
      plan: String!
      status: BillingStatus!
      nextBillingDate: String
      usage: UsageMetrics
    }
    
    # 📊 User Analytics (External reference)
    type UserAnalytics @key(fields: "userId") @extends {
      userId: ID! @external
      lastActive: String
      sessionsCount: Int!
      averageSessionDuration: Int!
      featuresUsed: [String!]!
    }
    
    # 🎯 Enums
    enum UserRole {
      USER
      ADMIN
      SUPER_ADMIN
    }
    
    enum BillingStatus {
      ACTIVE
      PAST_DUE
      CANCELED
      TRIAL
    }
    
    # 📊 Custom Scalars
    scalar JSON
    scalar DateTime
    
    # 📝 Input Types
    input CreateUserInput {
      email: String!
      name: String!
      role: UserRole = USER
      organizationId: ID
      metadata: JSON
    }
    
    input UpdateUserInput {
      id: ID!
      email: String
      name: String
      role: UserRole
    }
    
    input UserFilters {
      organizationId: ID
      role: UserRole
      isActive: Boolean
      search: String
    }
    
    input PaginationInput {
      page: Int = 1
      limit: Int = 20
      sortBy: String = "createdAt"
      sortOrder: SortOrder = DESC
    }
    
    enum SortOrder {
      ASC
      DESC
    }
    
    # 📋 Response Types
    type UserConnection {
      nodes: [User!]!
      totalCount: Int!
      pageInfo: PageInfo!
    }
    
    type PageInfo {
      currentPage: Int!
      totalPages: Int!
      hasNextPage: Boolean!
      hasPreviousPage: Boolean!
    }
    
    # 📋 Queries
    type Query {
      # 👤 User Queries
      user(id: ID!): User
      users(filters: UserFilters, pagination: PaginationInput): UserConnection!
      userStats(organizationId: ID): UserStats!
      
      # 🔍 Search
      searchUsers(query: String!, organizationId: ID): [User!]!
    }
    
    # ✏️ Mutations
    type Mutation {
      # 👤 User Management
      createUser(input: CreateUserInput!): User!
      updateUser(input: UpdateUserInput!): User!
      deactivateUser(id: ID!, reason: String!): Boolean!
      
      # 🔄 Bulk Operations
      bulkUpdateUsers(userIds: [ID!]!, updates: UpdateUserInput!): [User!]!
    }
    
    # 📡 Subscriptions (handled by tRPC)
    type Subscription {
      userCreated(organizationId: ID): User!
      userUpdated(userId: ID): User!
      userDeactivated(organizationId: ID): User!
    }
  `;
}

🔧 Federation Resolvers

bridge/trpc-graphql-adapter.ts
// 🔧 Generate GraphQL Resolvers
private generateResolvers(): any {
  return {
    // 🎯 Entity resolvers
    User: {
      // 🔑 Federation key resolver
      __resolveReference: async (reference: { id: string }, context: any) => {
        try {
          // 📞 Call tRPC procedure
          const user = await this.callTRPCProcedure(
            'user.queries.getById', 
            { id: reference.id }, 
            context
          );
          return user;
        } catch (error) {
          context.logger?.error('Failed to resolve User reference', {
            userId: reference.id,
            error: error.message,
          });
          return null;
        }
      },
      
      // 🔗 Relation resolvers
      organization: async (user: any, args: any, context: any) => {
        if (!user.organizationId) return null;
        
        // 🎯 This will be resolved by organization service
        return { __typename: 'Organization', id: user.organizationId };
      },
      
      billingProfile: async (user: any, args: any, context: any) => {
        if (!user.organizationId) return null;
        
        // 🎯 This will be resolved by billing service
        return { __typename: 'BillingProfile', organizationId: user.organizationId };
      },
      
      analytics: async (user: any, args: any, context: any) => {
        // 🎯 This will be resolved by analytics service
        return { __typename: 'UserAnalytics', userId: user.id };
      },
    },
    
    Organization: {
      __resolveReference: async (reference: { id: string }, context: any) => {
        try {
          const organization = await this.callTRPCProcedure(
            'organization.queries.getById', 
            { id: reference.id }, 
            context
          );
          return organization;
        } catch (error) {
          context.logger?.error('Failed to resolve Organization reference', {
            organizationId: reference.id,
            error: error.message,
          });
          return null;
        }
      },
      
      users: async (organization: any, args: any, context: any) => {
        const users = await this.callTRPCProcedure(
          'user.queries.list',
          {
            organizationId: organization.id,
            page: 1,
            limit: 100,
          },
          context
        );
        return users.users;
      },
      
      owner: async (organization: any, args: any, context: any) => {
        const owner = await this.callTRPCProcedure(
          'user.queries.getById', 
          { id: organization.ownerId }, 
          context
        );
        return owner;
      },
    },
    
    // 📋 Query resolvers
    Query: {
      user: async (parent: any, args: { id: string }, context: any) => {
        return this.callTRPCProcedure('user.queries.getById', args, context);
      },
      
      users: async (parent: any, args: any, context: any) => {
        const result = await this.callTRPCProcedure('user.queries.list', args, context);
        
        return {
          nodes: result.users,
          totalCount: result.total,
          pageInfo: {
            currentPage: result.page,
            totalPages: result.totalPages,
            hasNextPage: result.page < result.totalPages,
            hasPreviousPage: result.page > 1,
          },
        };
      },
      
      searchUsers: async (parent: any, args: any, context: any) => {
        return this.callTRPCProcedure('user.queries.search', args, context);
      },
    },
    
    // ✏️ Mutation resolvers
    Mutation: {
      createUser: async (parent: any, args: { input: any }, context: any) => {
        return this.callTRPCProcedure('user.commands.create', args.input, context);
      },
      
      updateUser: async (parent: any, args: { input: any }, context: any) => {
        return this.callTRPCProcedure('user.commands.update', args.input, context);
      },
      
      deactivateUser: async (parent: any, args: { id: string; reason: string }, context: any) => {
        const result = await this.callTRPCProcedure('user.commands.deactivate', args, context);
        return result.success;
      },
    },
    
    // 📡 Subscription resolvers (delegate to tRPC)
    Subscription: {
      userCreated: {
        // 🔗 tRPC subscription bridge
        subscribe: async (parent: any, args: any, context: any) => {
          return context.trpc.user.subscriptions.userCreated(args);
        },
      },
      
      userUpdated: {
        subscribe: async (parent: any, args: any, context: any) => {
          return context.trpc.user.subscriptions.userUpdated(args);
        },
      },
    },
  };
}

🎯 User Service Bridge Implementation

bridge/user-service-bridge.ts
// 📁 bridge/user-service-bridge.ts
// 🎯 User Service Bridge Implementation

import { TRPCGraphQLBridge } from './trpc-graphql-adapter';
import { userCQRSRouter } from '@/server/routers/user-cqrs-router';

// 🔧 Create User Service Bridge
export function createUserServiceBridge() {
  const bridge = new TRPCGraphQLBridge({
    serviceName: 'user-service',
    trpcRouter: userCQRSRouter,
    context: {}, // Will be populated by Apollo
    enableIntrospection: process.env.NODE_ENV === 'development',
  });
  
  // 📝 Register field mappings
  bridge.addFieldMapping({
    trpcProcedure: 'user.queries.getById',
    graphqlField: 'user',
    inputMapping: { id: 'id' },
  });
  
  bridge.addFieldMapping({
    trpcProcedure: 'user.queries.list',
    graphqlField: 'users',
    inputMapping: { 
      filters: 'filters',
      pagination: 'pagination',
    },
  });
  
  bridge.addFieldMapping({
    trpcProcedure: 'user.commands.create',
    graphqlField: 'createUser',
    inputMapping: { input: 'input' },
  });
  
  bridge.addFieldMapping({
    trpcProcedure: 'user.commands.update',
    graphqlField: 'updateUser',
    inputMapping: { input: 'input' },
  });
  
  bridge.addFieldMapping({
    trpcProcedure: 'user.commands.deactivate',
    graphqlField: 'deactivateUser',
    inputMapping: { id: 'id', reason: 'reason' },
  });
  
  return bridge.buildSchema();
}

// 📁 services/user-service.ts
// 🚀 User Service com Federation

import express from 'express';
import { ApolloServer } from 'apollo-server-express';
import { createUserServiceBridge } from './bridge/user-service-bridge';
import { createContext } from './context';

async function startUserService() {
  const app = express();
  
  // 🌉 Create federated schema
  const schema = createUserServiceBridge();
  
  // 🚀 Apollo Server setup
  const server = new ApolloServer({
    schema,
    context: createContext,
    plugins: [
      {
        requestDidStart() {
          return {
            didResolveOperation(requestContext) {
              console.log('🎯 User Service Operation:', requestContext.operationName);
            },
          };
        },
      },
    ],
  });
  
  await server.start();
  server.applyMiddleware({ app, path: '/graphql' });
  
  // 🏥 Health check endpoint
  app.get('/health', (req, res) => {
    res.json({ 
      status: 'healthy', 
      service: 'user-service',
      timestamp: new Date().toISOString(),
    });
  });
  
  const port = process.env.USER_SERVICE_PORT || 4001;
  
  app.listen(port, () => {
    console.log(`🚀 User Service running on http://localhost:${port}/graphql`);
    console.log(`🏥 Health check: http://localhost:${port}/health`);
  });
}

if (require.main === module) {
  startUserService().catch(console.error);
}

🧩 Schema Stitching e Resolvers Distribuídos

🏗️ Schema Stitching Composer

stitching/schema-composer.ts
// 📁 stitching/schema-composer.ts
import { stitchSchemas } from '@graphql-tools/stitch';
import { introspectSchema, wrapSchema } from '@graphql-tools/wrap';
import { fetch } from 'cross-fetch';
import { print } from 'graphql';
import { ExecutionResult } from 'graphql';

// 🎯 Service Configuration
export interface ServiceConfig {
  name: string;
  endpoint: string;
  headers?: Record<string, string>;
  transforms?: any[];
  merge?: Record<string, any>;
}

// 🧩 Schema Stitching Composer
export class SchemaStitchingComposer {
  private services: ServiceConfig[];
  private stitchedSchema: any = null;
  private logger: Logger;
  
  constructor(services: ServiceConfig[], logger: Logger) {
    this.services = services;
    this.logger = logger;
  }
  
  // 🏗️ Compose unified schema
  async composeSchema(): Promise<any> {
    try {
      const subschemas = await Promise.all(
        this.services.map(service => this.createSubschema(service))
      );
      
      this.stitchedSchema = stitchSchemas({
        subschemas,
        
        // 🔗 Cross-service type merging
        typeMerging: {
          User: {
            // 🎯 Merge User type from multiple services
            fieldName: 'user',
            selectionSet: '{ id }',
            key: ({ id }: { id: string }) => id,
            argsFromKeys: (keys: string[]) => ({ ids: keys }),
          },
          
          Organization: {
            fieldName: 'organization',
            selectionSet: '{ id }',
            key: ({ id }: { id: string }) => id,
            argsFromKeys: (keys: string[]) => ({ ids: keys }),
          },
        },
        
        // 🔄 Merge configurations
        mergeTypes: true,
        
        // 🛡️ Error handling
        onTypeConflict: (left, right, info) => {
          this.logger.warn('Type conflict detected', {
            leftType: left.name,
            rightType: right.name,
            info: info?.message,
          });
          return left; // Prefer left schema
        },
      });
      
      this.logger.info('✅ Schema stitching completed', {
        servicesCount: this.services.length,
        typesCount: Object.keys(this.stitchedSchema.getTypeMap()).length,
      });
      
      return this.stitchedSchema;
    } catch (error) {
      this.logger.error('❌ Schema stitching failed', {
        error: error.message,
        stack: error.stack,
      });
      throw error;
    }
  }

🔧 Subschema Creation e Wrapping

stitching/schema-composer.ts
// 🔧 Create subschema for service
private async createSubschema(service: ServiceConfig): Promise<any> {
  try {
    // 🔍 Introspect remote schema
    const schema = await introspectSchema(
      async (query) => this.executeRemoteQuery(service, query)
    );
    
    // 🌯 Wrap schema with service-specific logic
    const wrappedSchema = wrapSchema({
      schema,
      executor: async ({ document, variables, context, info }) => {
        const query = print(document);
        return this.executeRemoteQuery(service, query, variables, context);
      },
      
      // 🔄 Schema transforms
      transforms: service.transforms || [],
    });
    
    this.logger.info('📡 Subschema created', {
      service: service.name,
      endpoint: service.endpoint,
      typesCount: Object.keys(schema.getTypeMap()).length,
    });
    
    return {
      schema: wrappedSchema,
      merge: service.merge || {},
      batch: true, // Enable query batching
      
      // 🔐 Context transformation
      createProxyingResolver: ({ subschemaConfig, operation, transformedSchema }) => {
        return async (root, args, context, info) => {
          // 🎯 Add service-specific context
          const serviceContext = {
            ...context,
            service: service.name,
            traceId: context.traceId || this.generateTraceId(),
          };
          
          return operation({ 
            root, 
            args, 
            context: serviceContext, 
            info,
            schema: transformedSchema,
          });
        };
      },
    };
  } catch (error) {
    this.logger.error('Failed to create subschema', {
      service: service.name,
      endpoint: service.endpoint,
      error: error.message,
    });
    throw error;
  }
}

// 📞 Execute remote GraphQL query
private async executeRemoteQuery(
  service: ServiceConfig,
  query: string,
  variables?: any,
  context?: any
): Promise<ExecutionResult> {
  try {
    const response = await fetch(service.endpoint, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'User-Agent': 'GraphQL-Schema-Stitching/1.0',
        ...service.headers,
        ...(context?.authorization && {
          'Authorization': context.authorization,
        }),
        ...(context?.traceId && {
          'X-Trace-ID': context.traceId,
        }),
      },
      body: JSON.stringify({
        query,
        variables,
        operationName: null,
      }),
    });
    
    if (!response.ok) {
      throw new Error(`HTTP ${response.status}: ${response.statusText}`);
    }
    
    const result = await response.json();
    
    // 📊 Log successful request
    this.logger.debug('Remote query executed', {
      service: service.name,
      operationType: query.includes('mutation') ? 'mutation' : 'query',
      hasErrors: !!result.errors,
      variables,
    });
    
    return result;
  } catch (error) {
    this.logger.error('Remote query failed', {
      service: service.name,
      endpoint: service.endpoint,
      error: error.message,
      query: query.substring(0, 200), // Log first 200 chars
    });
    
    return {
      errors: [{
        message: `Service ${service.name} is unavailable: ${error.message}`,
        extensions: {
          code: 'SERVICE_UNAVAILABLE',
          service: service.name,
        },
      }],
    };
  }
}

🔗 Distributed Resolvers Manager

stitching/distributed-resolvers.ts
// 📁 stitching/distributed-resolvers.ts
// 🎯 Distributed Resolvers for Cross-Service Relations

export class DistributedResolverManager {
  private services: Map<string, ServiceConfig> = new Map();
  private logger: Logger;
  
  constructor(logger: Logger) {
    this.logger = logger;
  }
  
  // 📝 Register service
  registerService(config: ServiceConfig): void {
    this.services.set(config.name, config);
    this.logger.info('Service registered for distributed resolvers', {
      service: config.name,
      endpoint: config.endpoint,
    });
  }
  
  // 🔗 Create cross-service resolvers
  createDistributedResolvers(): Record<string, any> {
    return {
      // 👤 User distributed resolvers
      User: {
        // 🏢 Resolve organization from organization-service
        organization: async (user: any, args: any, context: any) => {
          if (!user.organizationId) return null;
          
          return this.resolveFromService('organization-service', {
            query: `
              query GetOrganization($id: ID!) {
                organization(id: $id) {
                  id
                  name
                  plan
                  isActive
                  createdAt
                }
              }
            `,
            variables: { id: user.organizationId },
            context,
          });
        },
        
        // 💳 Resolve billing from billing-service
        billingProfile: async (user: any, args: any, context: any) => {
          if (!user.organizationId) return null;
          
          return this.resolveFromService('billing-service', {
            query: `
              query GetBillingProfile($organizationId: ID!) {
                billingProfile(organizationId: $organizationId) {
                  organizationId
                  plan
                  status
                  nextBillingDate
                  usage {
                    apiCalls
                    storageUsed
                    bandwidthUsed
                  }
                }
              }
            `,
            variables: { organizationId: user.organizationId },
            context,
          });
        },
        
        // 📊 Resolve analytics from analytics-service
        analytics: async (user: any, args: any, context: any) => {
          return this.resolveFromService('analytics-service', {
            query: `
              query GetUserAnalytics($userId: ID!) {
                userAnalytics(userId: $userId) {
                  userId
                  lastActive
                  sessionsCount
                  averageSessionDuration
                  featuresUsed
                }
              }
            `,
            variables: { userId: user.id },
            context,
            fallback: {
              userId: user.id,
              lastActive: null,
              sessionsCount: 0,
              averageSessionDuration: 0,
              featuresUsed: [],
            },
          });
        },
      },
      
      // 🏢 Organization distributed resolvers
      Organization: {
        // 👥 Resolve users from user-service
        users: async (organization: any, args: any, context: any) => {
          const result = await this.resolveFromService('user-service', {
            query: `
              query GetOrganizationUsers($organizationId: ID!, $pagination: PaginationInput) {
                users(
                  filters: { organizationId: $organizationId }
                  pagination: $pagination
                ) {
                  nodes {
                    id
                    email
                    name
                    role
                    isActive
                    createdAt
                  }
                  totalCount
                }
              }
            `,
            variables: { 
              organizationId: organization.id,
              pagination: args.pagination || { page: 1, limit: 50 }
            },
            context,
          });
          
          return result?.users?.nodes || [];
        },
        
        // 👑 Resolve owner from user-service
        owner: async (organization: any, args: any, context: any) => {
          if (!organization.ownerId) return null;
          
          return this.resolveFromService('user-service', {
            query: `
              query GetUser($id: ID!) {
                user(id: $id) {
                  id
                  email
                  name
                  role
                  isActive
                }
              }
            `,
            variables: { id: organization.ownerId },
            context,
          });
        },
      },
    };
  }

📞 Service Resolution Logic

stitching/distributed-resolvers.ts
// 📞 Resolve from specific service
private async resolveFromService(
  serviceName: string,
  options: {
    query: string;
    variables?: any;
    context: any;
    fallback?: any;
  }
): Promise<any> {
  const service = this.services.get(serviceName);
  if (!service) {
    this.logger.error('Service not found for distributed resolver', {
      service: serviceName,
      availableServices: Array.from(this.services.keys()),
    });
    return options.fallback || null;
  }
  
  try {
    const response = await fetch(service.endpoint, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        ...service.headers,
        ...(options.context?.authorization && {
          'Authorization': options.context.authorization,
        }),
        ...(options.context?.traceId && {
          'X-Trace-ID': options.context.traceId,
        }),
      },
      body: JSON.stringify({
        query: options.query,
        variables: options.variables,
      }),
    });
    
    if (!response.ok) {
      throw new Error(`HTTP ${response.status}: ${response.statusText}`);
    }
    
    const result = await response.json();
    
    if (result.errors) {
      this.logger.warn('GraphQL errors in distributed resolver', {
        service: serviceName,
        errors: result.errors,
        variables: options.variables,
      });
      return options.fallback || null;
    }
    
    // 🎯 Extract the first field from data
    const dataKeys = Object.keys(result.data || {});
    return dataKeys.length > 0 ? result.data[dataKeys[0]] : options.fallback;
  } catch (error) {
    this.logger.error('Distributed resolver failed', {
      service: serviceName,
      error: error.message,
      variables: options.variables,
    });
    return options.fallback || null;
  }
}

// 📁 stitching/production-setup.ts
// 🚀 Production Schema Stitching Setup

export async function createProductionStitchingGateway() {
  const logger = new Logger('Schema Stitching');
  
  const services: ServiceConfig[] = [
    {
      name: 'user-service',
      endpoint: process.env.USER_SERVICE_URL + '/graphql',
      headers: {
        'X-API-Key': process.env.INTERNAL_API_KEY,
        'X-Service-Version': '1.0.0',
      },
    },
    {
      name: 'organization-service',
      endpoint: process.env.ORGANIZATION_SERVICE_URL + '/graphql',
      headers: {
        'X-API-Key': process.env.INTERNAL_API_KEY,
        'X-Service-Version': '1.0.0',
      },
    },
    {
      name: 'billing-service',
      endpoint: process.env.BILLING_SERVICE_URL + '/graphql',
      headers: {
        'X-API-Key': process.env.INTERNAL_API_KEY,
        'X-Service-Version': '1.0.0',
      },
    },
    {
      name: 'analytics-service',
      endpoint: process.env.ANALYTICS_SERVICE_URL + '/graphql',
      headers: {
        'X-API-Key': process.env.INTERNAL_API_KEY,
        'X-Service-Version': '1.0.0',
      },
    },
  ];
  
  // 🏗️ Create schema composer
  const composer = new SchemaStitchingComposer(services, logger);
  const resolverManager = new DistributedResolverManager(logger);
  
  // 📝 Register services for distributed resolvers
  services.forEach(service => resolverManager.registerService(service));
  
  // 🧩 Compose unified schema
  const stitchedSchema = await composer.composeSchema();
  
  // 🔗 Add distributed resolvers
  const distributedResolvers = resolverManager.createDistributedResolvers();
  
  // 🔄 Merge with existing resolvers
  const finalSchema = stitchSchemas({
    subschemas: [{ schema: stitchedSchema }],
    resolvers: distributedResolvers,
  });
  
  logger.info('🎉 Production stitching gateway ready', {
    servicesCount: services.length,
    hasDistributedResolvers: Object.keys(distributedResolvers).length > 0,
  });
  
  return finalSchema;
}

🔌 Client Integration: tRPC + GraphQL

🔗 Unified Client Configuration

client/unified-client.ts
// 📁 client/unified-client.ts
import { createTRPCNext } from '@trpc/next';
import { createTRPCProxyClient, httpBatchLink } from '@trpc/client';
import { ApolloClient, InMemoryCache, createHttpLink, from } from '@apollo/client';
import { setContext } from '@apollo/client/link/context';
import { onError } from '@apollo/client/link/error';
import { RetryLink } from '@apollo/client/link/retry';
import type { AppRouter } from '@/server/routers/_app';

// 🎯 Unified Client Configuration
interface UnifiedClientConfig {
  trpcEndpoint: string;
  graphqlEndpoint: string;
  wsEndpoint?: string;
  apiKey?: string;
  enableBatching?: boolean;
  enableCache?: boolean;
  retryAttempts?: number;
}

// 🔗 tRPC Client Setup
export const trpc = createTRPCNext<AppRouter>({
  config({ ctx }) {
    return {
      links: [
        httpBatchLink({
          url: process.env.NEXT_PUBLIC_TRPC_ENDPOINT || 'http://localhost:3000/api/trpc',
          
          // 📝 Headers for authentication
          headers: async () => {
            const token = typeof window !== 'undefined' 
              ? localStorage.getItem('auth-token')
              : ctx?.req?.headers?.authorization;
            
            return {
              'Content-Type': 'application/json',
              ...(token && { 'Authorization': `Bearer ${token}` }),
              'X-Client-Type': 'web',
              'X-Client-Version': process.env.NEXT_PUBLIC_APP_VERSION || '1.0.0',
            };
          },
          
          // 🔄 Request/Response transformation
          fetch: async (url, options) => {
            const startTime = Date.now();
            
            try {
              const response = await fetch(url, {
                ...options,
                timeout: 30000, // 30 second timeout
              });
              
              // 📊 Log request metrics
              console.debug('tRPC Request completed', {
                url,
                method: options?.method,
                status: response.status,
                duration: Date.now() - startTime,
              });
              
              return response;
            } catch (error) {
              console.error('tRPC Request failed', {
                url,
                error: error.message,
                duration: Date.now() - startTime,
              });
              throw error;
            }
          },
        }),
      ],
      
      // 🔄 React Query configuration
      queryClientConfig: {
        defaultOptions: {
          queries: {
            staleTime: 1000 * 60 * 5, // 5 minutes
            cacheTime: 1000 * 60 * 10, // 10 minutes
            retry: 3,
            refetchOnWindowFocus: false,
            refetchOnMount: false,
          },
          mutations: {
            retry: 1,
          },
        },
      },
    };
  },
  ssr: false,
});

🌐 Apollo Client Setup

client/unified-client.ts
// 🌐 GraphQL Apollo Client Setup
const createApolloClient = (config: UnifiedClientConfig) => {
  // 🔗 HTTP Link
  const httpLink = createHttpLink({
    uri: config.graphqlEndpoint,
    credentials: 'include',
    fetch: async (uri, options) => {
      const startTime = Date.now();
      
      try {
        const response = await fetch(uri, options);
        
        console.debug('GraphQL Request completed', {
          uri,
          status: response.status,
          duration: Date.now() - startTime,
        });
        
        return response;
      } catch (error) {
        console.error('GraphQL Request failed', {
          uri,
          error: error.message,
          duration: Date.now() - startTime,
        });
        throw error;
      }
    },
  });
  
  // 🔐 Auth Link
  const authLink = setContext(async (_, { headers }) => {
    const token = typeof window !== 'undefined' 
      ? localStorage.getItem('auth-token')
      : null;
    
    const organizationId = typeof window !== 'undefined'
      ? localStorage.getItem('current-organization-id')
      : null;
    
    return {
      headers: {
        ...headers,
        'Content-Type': 'application/json',
        ...(token && { 'Authorization': `Bearer ${token}` }),
        ...(organizationId && { 'X-Organization-ID': organizationId }),
        ...(config.apiKey && { 'X-API-Key': config.apiKey }),
        'X-Client-Type': 'web',
        'X-Client-Version': process.env.NEXT_PUBLIC_APP_VERSION || '1.0.0',
        'X-Trace-ID': `trace_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`,
      },
    };
  });
  
  // 🚨 Error Link
  const errorLink = onError(({ graphQLErrors, networkError, operation, forward }) => {
    if (graphQLErrors) {
      graphQLErrors.forEach(({ message, locations, path, extensions }) => {
        console.error('GraphQL Error', {
          message,
          locations,
          path,
          extensions,
          operation: operation.operationName,
          variables: operation.variables,
        });
        
        // 🔐 Handle authentication errors
        if (extensions?.code === 'UNAUTHENTICATED') {
          if (typeof window !== 'undefined') {
            localStorage.removeItem('auth-token');
            window.location.href = '/login';
          }
        }
      });
    }
    
    if (networkError) {
      console.error('GraphQL Network Error', {
        error: networkError.message,
        operation: operation.operationName,
        variables: operation.variables,
      });
    }
  });
  
  // 🔄 Retry Link
  const retryLink = new RetryLink({
    delay: {
      initial: 300,
      max: Infinity,
      jitter: true,
    },
    attempts: {
      max: config.retryAttempts || 3,
      retryIf: (error, _operation) => {
        return !!error && !error.message.includes('UNAUTHENTICATED');
      },
    },
  });

💾 Apollo Cache Configuration

client/unified-client.ts
// 💾 Cache configuration
const cache = new InMemoryCache({
  typePolicies: {
    User: {
      keyFields: ['id'],
      fields: {
        analytics: {
          merge: true,
        },
        organization: {
          merge: true,
        },
        billingProfile: {
          merge: true,
        },
      },
    },
    Organization: {
      keyFields: ['id'],
      fields: {
        users: {
          keyArgs: ['filters'],
          merge(existing = [], incoming = []) {
            return [...existing, ...incoming];
          },
        },
        billing: {
          merge: true,
        },
      },
    },
    UserConnection: {
      keyFields: false,
      fields: {
        nodes: {
          merge: false,
        },
      },
    },
    BillingProfile: {
      keyFields: ['organizationId'],
      fields: {
        usage: {
          merge: true,
        },
      },
    },
    UserAnalytics: {
      keyFields: ['userId'],
      fields: {
        featuresUsed: {
          merge: false,
        },
      },
    },
  },
  
  // 🔧 Custom cache resolver
  possibleTypes: {
    Node: ['User', 'Organization', 'BillingProfile'],
  },
});

return new ApolloClient({
  link: from([errorLink, retryLink, authLink, httpLink]),
  cache,
  defaultOptions: {
    watchQuery: {
      errorPolicy: 'all',
      fetchPolicy: config.enableCache ? 'cache-first' : 'network-only',
    },
    query: {
      errorPolicy: 'all',
      fetchPolicy: config.enableCache ? 'cache-first' : 'network-only',
    },
    mutate: {
      errorPolicy: 'all',
    },
  },
  connectToDevTools: process.env.NODE_ENV === 'development',
});

🔗 Unified Client Hook

client/unified-client.ts
// 🔗 Unified Client Hook
export function useUnifiedClient(config?: Partial<UnifiedClientConfig>) {
  const defaultConfig: UnifiedClientConfig = {
    trpcEndpoint: process.env.NEXT_PUBLIC_TRPC_ENDPOINT || 'http://localhost:3000/api/trpc',
    graphqlEndpoint: process.env.NEXT_PUBLIC_GRAPHQL_ENDPOINT || 'http://localhost:4000/graphql',
    enableBatching: true,
    enableCache: true,
    retryAttempts: 3,
    ...config,
  };
  
  const apolloClient = createApolloClient(defaultConfig);
  
  return {
    // 🎯 tRPC Client
    trpc,
    
    // 🌐 GraphQL Client
    graphql: apolloClient,
    
    // 🔄 Unified operations
    operations: {
      // 📊 Get user with full analytics (GraphQL)
      async getUserWithAnalytics(userId: string) {
        const { data } = await apolloClient.query({
          query: gql`
            query GetUserWithAnalytics($userId: ID!) {
              user(id: $userId) {
                id
                email
                name
                role
                isActive
                createdAt
                
                organization {
                  id
                  name
                  plan
                }
                
                analytics {
                  lastActive
                  sessionsCount
                  averageSessionDuration
                  featuresUsed
                }
                
                billingProfile {
                  plan
                  status
                  usage {
                    apiCalls
                    storageUsed
                    bandwidthUsed
                  }
                }
              }
            }
          `,
          variables: { userId },
        });
        
        return data.user;
      },
      
      // ⚡ Create user (tRPC para speed)
      async createUser(input: any) {
        return trpc.user.commands.create.mutate(input);
      },
      
      // 📋 List users with relations (GraphQL para relacionamentos)
      async listUsersWithRelations(filters?: any, pagination?: any) {
        const { data } = await apolloClient.query({
          query: gql`
            query ListUsersWithRelations($filters: UserFilters, $pagination: PaginationInput) {
              users(filters: $filters, pagination: $pagination) {
                nodes {
                  id
                  email
                  name
                  role
                  isActive
                  
                  organization {
                    id
                    name
                    plan
                  }
                  
                  analytics {
                    lastActive
                    sessionsCount
                  }
                }
                totalCount
                pageInfo {
                  currentPage
                  totalPages
                  hasNextPage
                }
              }
            }
          `,
          variables: { filters, pagination },
        });
        
        return data.users;
      },

📊 Dashboard Operations

client/unified-client.ts
// 📊 Get organization dashboard (GraphQL para dados relacionais complexos)
async getOrganizationDashboard(organizationId: string) {
  const { data } = await apolloClient.query({
    query: gql`
      query GetOrganizationDashboard($organizationId: ID!) {
        organization(id: $organizationId) {
          id
          name
          plan
          isActive
          createdAt
          
          users {
            id
            email
            name
            role
            isActive
            analytics {
              lastActive
              sessionsCount
            }
          }
          
          billing {
            plan
            status
            nextBillingDate
            usage {
              apiCalls
              storageUsed
              bandwidthUsed
              features {
                feature
                usageCount
                lastUsed
              }
            }
          }
        }
        
        userStats(organizationId: $organizationId) {
          total
          active
          inactive
          byRole {
            role
            count
          }
          recentSignups
        }
      }
    `,
    variables: { organizationId },
  });
  
  return {
    organization: data.organization,
    stats: data.userStats,
  };
},

// ⚡ Real-time user events (tRPC subscription)
subscribeToUserEvents(organizationId: string, onUpdate: (user: any) => void) {
  return trpc.user.subscriptions.userUpdated.subscribe(
    { organizationId },
    {
      onData: onUpdate,
      onError: (error) => {
        console.error('User subscription error:', error);
      },
    }
  );
},

// 🔄 Hybrid search (tRPC para performance, GraphQL para relações)
async hybridUserSearch(query: string, includeRelations = false) {
  if (includeRelations) {
    // 🌐 Use GraphQL for complex relations
    const { data } = await apolloClient.query({
      query: gql`
        query SearchUsersWithRelations($query: String!) {
          searchUsers(query: $query) {
            id
            email
            name
            role
            organization {
              id
              name
              plan
            }
            analytics {
              lastActive
              sessionsCount
            }
          }
        }
      `,
      variables: { query },
    });
    
    return data.searchUsers;
  } else {
    // ⚡ Use tRPC for fast simple searches
    return trpc.user.queries.search.query({ query });
  }
},

🪝 React Hooks para Federation

client/hooks/use-unified-mutations.ts
// 📁 client/hooks/use-unified-mutations.ts
// 🔄 Unified Mutations Hook

import { useMutation, useQueryClient } from '@apollo/client';
import { gql } from '@apollo/client';

export function useUnifiedMutations() {
  const queryClient = useQueryClient();
  const { trpc, graphql } = useUnifiedClient();
  
  return {
    // 👤 User mutations
    createUser: useMutation(
      gql`
        mutation CreateUser($input: CreateUserInput!) {
          createUser(input: $input) {
            id
            email
            name
            role
            organizationId
            isActive
            createdAt
          }
        }
      `,
      {
        onSuccess: (data) => {
          // 🔄 Invalidate related queries
          queryClient.invalidateQueries(['users']);
          queryClient.invalidateQueries(['userStats']);
          
          // 🔄 Also invalidate tRPC cache
          trpc.user.queries.list.invalidate();
          
          console.log('User created successfully', { userId: data.createUser.id });
        },
      }
    ),
    
    updateUser: trpc.user.commands.changeEmail.useMutation({
      onSuccess: () => {
        // 🔄 Invalidate both tRPC and GraphQL caches
        trpc.user.queries.getById.invalidate();
        trpc.user.queries.list.invalidate();
        queryClient.invalidateQueries(['user']);
        queryClient.invalidateQueries(['users']);
      },
    }),
    
    // 🏢 Organization mutations
    updateOrganization: trpc.organization.commands.update.useMutation({
      onSuccess: () => {
        queryClient.invalidateQueries(['organization']);
        queryClient.invalidateQueries(['organizationDashboard']);
      },
    }),
    
    // 🔄 Bulk operations (preferir GraphQL para bulk)
    bulkUpdateUsers: useMutation(
      gql`
        mutation BulkUpdateUsers($userIds: [ID!]!, $updates: UpdateUserInput!) {
          bulkUpdateUsers(userIds: $userIds, updates: $updates) {
            id
            name
            role
            isActive
          }
        }
      `,
      {
        onSuccess: () => {
          queryClient.invalidateQueries(['users']);
          queryClient.invalidateQueries(['userStats']);
          trpc.user.queries.list.invalidate();
        },
      }
    ),
  };
}

// 📁 client/hooks/use-federation-queries.ts
// 🔍 Federation Queries Hook

export function useFederationQueries() {
  const { trpc, graphql } = useUnifiedClient();
  
  return {
    // 📊 User with full context (GraphQL federation)
    useUserWithContext: (userId: string) => {
      return useQuery({
        queryKey: ['userWithContext', userId],
        queryFn: async () => {
          const { data } = await graphql.query({
            query: gql`
              query GetUserWithContext($userId: ID!) {
                user(id: $userId) {
                  id
                  email
                  name
                  role
                  isActive
                  
                  organization {
                    id
                    name
                    plan
                    billing {
                      status
                      nextBillingDate
                    }
                  }
                  
                  analytics {
                    lastActive
                    sessionsCount
                    averageSessionDuration
                    featuresUsed
                  }
                  
                  billingProfile {
                    plan
                    status
                    usage {
                      apiCalls
                      storageUsed
                      bandwidthUsed
                    }
                  }
                }
              }
            `,
            variables: { userId },
          });
          
          return data.user;
        },
        enabled: !!userId,
      });
    },
    
    // ⚡ Fast user lookup (tRPC)
    useFastUser: trpc.user.queries.getById.useQuery,
    
    // 📋 Users list with pagination (hybrid)
    useUsersList: (filters: any, useRelations = true) => {
      if (useRelations) {
        return useQuery({
          queryKey: ['usersWithRelations', filters],
          queryFn: async () => {
            const { data } = await graphql.query({
              query: gql`
                query GetUsersWithRelations($filters: UserFilters, $pagination: PaginationInput) {
                  users(filters: $filters, pagination: $pagination) {
                    nodes {
                      id
                      email
                      name
                      role
                      isActive
                      organization {
                        id
                        name
                        plan
                      }
                      analytics {
                        lastActive
                        sessionsCount
                      }
                    }
                    totalCount
                    pageInfo {
                      currentPage
                      totalPages
                      hasNextPage
                    }
                  }
                }
              `,
              variables: { filters, pagination: { page: 1, limit: 20 } },
            });
            
            return data.users;
          },
        });
      } else {
        return trpc.user.queries.list.useQuery(filters);
      }
    },
  };
}

✅ O que você conquistou nesta aula

Apollo Federation Gateway configurado com health checks
tRPC to GraphQL Bridge com federation directives
Schema Stitching para combinar múltiplos serviços
Distributed Resolvers para cross-service relations
Unified Client combinando tRPC + GraphQL
Apollo Cache Strategy com type policies otimizadas
Error Handling distribuído com fallbacks
Production Monitoring com tracing e health checks

⚡ Performance e Monitoring para Federation

Query Planning:Apollo Gateway otimiza automaticamente queries distribuídas entre serviços.

Caching Strategy:Cache em múltiplas camadas: Apollo Cache + tRPC Cache + Service Cache.

Error Boundaries:Falhas em um serviço não afetam outros, com fallbacks automáticos.

Tracing Distribuído:Trace IDs permitem rastrear requests através de todos os serviços.

🚀 Próximos Passos

Na próxima aula:

  • • Real-time com WebSockets
  • • tRPC Subscriptions
  • • Event-driven architecture
  • • Live notifications

Para consolidar:

  • • Implemente federation em projeto próprio
  • • Configure monitoring e alerting
  • • Teste performance com carga
  • • Documente architecture decisions

🎯 Continue Sua Jornada

Agora você domina GraphQL Federation e pode criar APIs unificadas enterprise-level!

Federation ExpertSchema StitchingDistributed SystemsEnterprise Architect

Navegação do Curso

Progresso do Módulo 53/5 aulas