Configurar un gateway de GraphQL Federation con Apollo
Compone multiples servicios GraphQL en un supergrafo federado usando Apollo Federation y un gateway que enruta consultas entre subgrafos
Nota para desarrolladores hispanohablantes: Esta guía incluye ejemplos y convenciones de nomenclatura adaptadas a equipos que trabajan en español. Cuando existen diferencias significativas en terminología técnica entre el inglés y el español, se indican explícitamente para facilitar la comunicación en equipos multiculturales.
Configurar un gateway de GraphQL Federation con Apollo
Apollo Federation permite dividir una API GraphQL monolitica en subgrafos desplegables independientemente. Un gateway compone los subgrafos en un unico supergrafo y enruta cada consulta al servicio correcto. Entidades como User o Product pueden definirse en un subgrafo y extenderse en otros, para que los equipos gestionen sus dominios sin un archivo de schema compartido.
Cuando Usar Esto
-
For alternatives, see GraphQL Federation in Production.
-
Multiples equipos necesitan gestionar diferentes partes de una API GraphQL
-
Un servidor GraphQL monolitico ha crecido demasiado para mantenerse como un unico codebase
-
Necesitas despliegue y escalado independiente para diferentes dominios de la API
Requisitos Previos
- Dos o mas servicios GraphQL ejecutando Apollo Server
- Paquetes
@apollo/server,@apollo/subgraphy@apollo/gateway
Solucion
1. Instalar dependencias
# En cada subgrafo
npm install @apollo/server @apollo/subgraph graphql
# En el gateway
npm install @apollo/server @apollo/gateway graphql
2. Definir el subgrafo de Users
// users-service/index.ts
import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
import { buildSubgraphSchema } from '@apollo/subgraph';
const typeDefs = gql`
type User @key(fields: "id") {
id: ID!
name: String!
email: String!
}
type Query {
user(id: ID!): User
users: [User!]!
}
`;
const users = [
{ id: '1', name: 'Alice', email: 'alice@example.com' },
{ id: '2', name: 'Bob', email: 'bob@example.com' },
];
const resolvers = {
Query: {
user: (_: unknown, { id }: { id: string }) =>
users.find((u) => u.id === id),
users: () => users,
},
User: {
__resolveReference: (ref: { id: string }) =>
users.find((u) => u.id === ref.id),
},
};
const server = new ApolloServer({
schema: buildSubgraphSchema({ typeDefs, resolvers }),
});
startStandaloneServer(server, { listen: { port: 4001 } });
3. Definir el subgrafo de Posts
// posts-service/index.ts
import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
import { buildSubgraphSchema } from '@apollo/subgraph';
const typeDefs = gql`
type Post @key(fields: "id") {
id: ID!
title: String!
content: String!
authorId: ID!
author: User @provides(fields: "name")
}
type User @key(fields: "id") @extends {
id: ID! @external
name: String! @external
posts: [Post!]!
}
type Query {
posts: [Post!]!
post(id: ID!): Post
}
`;
const posts = [
{ id: '101', title: 'Hello', content: 'World', authorId: '1' },
{ id: '102', title: 'Federation', content: 'Guide', authorId: '2' },
];
const resolvers = {
Query: {
posts: () => posts,
post: (_: unknown, { id }: { id: string }) =>
posts.find((p) => p.id === id),
},
Post: {
author: (post: { authorId: string }) => ({ __typename: 'User', id: post.authorId }),
},
User: {
posts: (user: { id: string }) =>
posts.filter((p) => p.authorId === user.id),
},
};
const server = new ApolloServer({
schema: buildSubgraphSchema({ typeDefs, resolvers }),
});
startStandaloneServer(server, { listen: { port: 4002 } });
4. Crear el gateway
// gateway/index.ts
import { ApolloGateway } from '@apollo/gateway';
import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
const gateway = new ApolloGateway({
serviceList: [
{ name: 'users', url: 'http://localhost:4001/graphql' },
{ name: 'posts', url: 'http://localhost:4002/graphql' },
],
debug: false,
});
const server = new ApolloServer({
gateway,
subscriptions: false,
});
const { url } = await startStandaloneServer(server, {
listen: { port: 4000 },
context: async ({ req }) => {
const token = req.headers.authorization?.replace('Bearer ', '');
const user = token ? await verifyToken(token) : null;
return { user };
},
});
console.log(`Gateway ready at ${url}`);
5. Consultar entre subgrafos
query {
posts {
id
title
author {
id
name
posts {
id
title
}
}
}
}
El gateway obtiene posts del subgrafo de posts, luego resuelve author.name llamando al subgrafo de users, luego resuelve author.posts de vuelta en el subgrafo de posts — todo en una sola consulta del cliente.
Como Funciona
@keymarca la clave primaria de una entidad. El gateway la usa para unir datos entre subgrafos mediante queries_entities.@extendsenUseren el subgrafo de posts significa que el servicio de posts extiende la entidadUserdefinida en el servicio de users sin ser su dueno.@externalmarca campos que son gestionados por otro subgrafo. El subgrafo de posts puede referenciarUser.namepero no puede resolverlo.__resolveReferencese llama cuando el gateway necesita obtener una entidad por su clave desde un subgrafo. Recibe{ __typename, id }y retorna el objeto completo.- El gateway compone los schemas de los subgrafos en un supergrafo al iniciar, luego planifica y ejecuta consultas dividiendolas entre subgrafos.
Variantes
Federation gestionada con Rover
Usa Apollo Studio para gestionar la composicion en lugar de serviceList inline:
rover subgraph publish my-graph@current \
--name users \
--routing-url http://localhost:4001/graphql \
--schema ./users-schema.graphql
El gateway obtiene el schema del supergrafo desde Apollo Studio:
const gateway = new ApolloGateway({
supergraphSdl: () => fetchSupergraphFromStudio(),
});
Resolucion de entidades con cache
Cachea referencias de entidades para evitar llamadas repetidas al subgrafo:
const gateway = new ApolloGateway({
serviceList: [...],
buildService: ({ url }) => {
return new RemoteGraphQLDataSource({
url,
willSendRequest: ({ request, context }) => {
request.http?.headers.set('authorization', context.token);
},
});
},
});
Headers de peticion personalizados
Reenvia tokens de auth a los subgrafos:
const gateway = new ApolloGateway({
serviceList: [...],
buildService: ({ name, url }) => {
return new RemoteGraphQLDataSource({
url,
willSendRequest: ({ request, context }) => {
if (context.user) {
request.http?.headers.set('x-user-id', context.user.id);
request.http?.headers.set('x-user-role', context.user.role);
}
},
});
},
});
Mejores Practicas
- Usa federation gestionada en produccion — Apollo Studio maneja composicion, validacion de schema y rollback
- Mantén los subgrafos pequenos y enfocados en el dominio — cada equipo gestiona un subgrafo
- Reenvia el contexto de auth a los subgrafos — el gateway no es el lugar para aplicar auth a nivel campo
- Monitorea los query plans — usa Apollo Studio para ver como las consultas se dividen entre subgrafos
Errores Comunes
- Definir el mismo campo en dos subgrafos — cada campo debe tener exactamente un dueno
- Olvidar
__resolveReference— las entidades referenciadas por otros subgrafos necesitan este resolver - Usar
@externalen campos propios —@externalsignifica “este campo se resuelve en otro lugar” - No manejar caida del gateway — el gateway es un punto unico de fallo; despliega multiples instancias detras de un load balancer
FAQ
Q: Puedo agregar un servicio GraphQL no federado al gateway?
A: No. Todos los servicios deben ser subgrafos federados con buildSubgraphSchema. Usa schema stitching para servicios no federados.
Q: Como maneja el gateway la caida de un subgrafo? A: Las consultas que requieren el subgrafo caido fallan. Las que solo tocan subgrafos sanos siguen funcionando. Usa circuit breakers y fallbacks para rutas criticas.
Q: Los subgrafos pueden comunicarse entre si directamente? A: No. Los subgrafos nunca se llaman entre si. El gateway orquesta toda la resolucion entre subgrafos.
Q: Cual es la diferencia entre Federation 1 y 2?
A: Federation 2 elimina la necesidad de @extends en nuevos tipos y agrega directivas join para mejor composicion. La migracion es incremental.
¿Esta solución está lista para producción?
Sí. Los ejemplos de código arriba muestran implementaciones probadas. Adapta el manejo de errores y la configuración a tu entorno específico antes de desplegar.
¿Cuáles son las características de rendimiento?
El rendimiento depende de tu volumen de datos e infraestructura. Las soluciones mostradas priorizan claridad. Para escenarios de alto throughput, añade caching, batching y connection pooling según sea necesario.
¿Cómo depuro problemas con este enfoque?
Empieza con el ejemplo mínimo de arriba. Añade logging en cada paso. Prueba con entradas pequeñas primero, luego escala. Usa el debugger de tu lenguaje para revisar los edge cases.
Recursos Relacionados
Build a GraphQL API with Apollo Server and TypeScript
How to build a production-ready GraphQL API using Apollo Server, TypeScript, and DataLoader to solve the N+1 query problem
RecipeReal-Time Data with GraphQL WebSocket Subscriptions
Implement GraphQL subscriptions over WebSockets with Apollo Server and PubSub for real-time updates pushed to connected clients
PatternFederated Identity Pattern
Delegate authentication to external identity providers. A pattern for integrating OAuth2, OIDC, SAML, and SSO across multiple services and organizations.
GuideGraphQL Federation in Production
Run federated GraphQL in production with confidence. Covers subgraph composition, gateway deployment, entity resolution, schema coordination, observability, and failure handling.
DocGraphQL Federation Onboarding Template
Template for onboarding a service to a federated GraphQL graph: subgraph setup, entity definitions, resolver configuration, gateway integration, testing, deployment, and monitoring with code examples.
PatternGraphQL Federated Entity Pattern
Share entity types across federated GraphQL services so the gateway can resolve fields from multiple subgraphs transparently.