La federación de GraphQL es una técnica avanzada que permite dividir un esquema de GraphQL en múltiples servicios independientes, cada uno responsable de una parte del esquema. Esto facilita la escalabilidad y la modularidad en aplicaciones grandes y complejas.
¿Qué es la Federación de GraphQL?
La federación de GraphQL permite que múltiples servicios colaboren para resolver una única consulta GraphQL. En lugar de tener un único servidor monolítico que maneje todas las resoluciones, la federación permite que diferentes servicios manejen diferentes partes del esquema.
Beneficios de la Federación de GraphQL
- Escalabilidad: Permite escalar diferentes partes del esquema de manera independiente.
- Modularidad: Facilita la división del código en módulos más manejables.
- Desarrollo Independiente: Equipos diferentes pueden trabajar en diferentes servicios sin interferir entre sí.
- Despliegue Independiente: Permite desplegar servicios de manera independiente, reduciendo el riesgo de errores en el despliegue.
Conceptos Clave
Gateway
El Gateway es el punto de entrada único para todas las consultas GraphQL. Se encarga de delegar las resoluciones a los servicios correspondientes.
Servicios Federados
Son los servicios individuales que manejan partes específicas del esquema. Cada servicio define su propio esquema y resolvers.
Extensiones de Esquema
Permiten que un servicio extienda tipos definidos en otro servicio. Esto es crucial para la colaboración entre servicios.
Configuración de un Gateway Federado
Paso 1: Configuración del Gateway
Primero, configuramos el Gateway utilizando Apollo Server.
const { ApolloServer } = require('apollo-server'); const { ApolloGateway } = require('@apollo/gateway'); const gateway = new ApolloGateway({ serviceList: [ { name: 'users', url: 'http://localhost:4001/graphql' }, { name: 'products', url: 'http://localhost:4002/graphql' }, ], }); const server = new ApolloServer({ gateway }); server.listen().then(({ url }) => { console.log(`🚀 Gateway ready at ${url}`); });
Paso 2: Configuración de Servicios Federados
Cada servicio federado debe definir su propio esquema y resolvers. Aquí hay un ejemplo de un servicio de usuarios.
Esquema del Servicio de Usuarios
# users/schema.graphql type User @key(fields: "id") { id: ID! name: String! email: String! } extend type Query { user(id: ID!): User }
Resolvers del Servicio de Usuarios
// users/index.js const { ApolloServer, gql } = require('apollo-server'); const { buildFederatedSchema } = require('@apollo/federation'); const typeDefs = gql` type User @key(fields: "id") { id: ID! name: String! email: String! } extend type Query { user(id: ID!): User } `; const resolvers = { Query: { user(_, { id }) { return users.find(user => user.id === id); }, }, User: { __resolveReference(user) { return users.find(u => u.id === user.id); }, }, }; const server = new ApolloServer({ schema: buildFederatedSchema([{ typeDefs, resolvers }]), }); server.listen({ port: 4001 }).then(({ url }) => { console.log(`🚀 Users service ready at ${url}`); });
Paso 3: Extender Tipos en Otros Servicios
Un servicio puede extender tipos definidos en otro servicio. Aquí hay un ejemplo de un servicio de productos que extiende el tipo User
.
Esquema del Servicio de Productos
# products/schema.graphql extend type User @key(fields: "id") { id: ID! @external products: [Product] } type Product { id: ID! name: String! price: Float! } extend type Query { product(id: ID!): Product }
Resolvers del Servicio de Productos
// products/index.js const { ApolloServer, gql } = require('apollo-server'); const { buildFederatedSchema } = require('@apollo/federation'); const typeDefs = gql` extend type User @key(fields: "id") { id: ID! @external products: [Product] } type Product { id: ID! name: String! price: Float! } extend type Query { product(id: ID!): Product } `; const resolvers = { User: { products(user) { return products.filter(product => product.userId === user.id); }, }, Query: { product(_, { id }) { return products.find(product => product.id === id); }, }, }; const server = new ApolloServer({ schema: buildFederatedSchema([{ typeDefs, resolvers }]), }); server.listen({ port: 4002 }).then(({ url }) => { console.log(`🚀 Products service ready at ${url}`); });
Ejercicio Práctico
Ejercicio
- Configura un Gateway federado que incluya dos servicios:
orders
ycustomers
. - Define un esquema y resolvers para cada servicio.
- Asegúrate de que el servicio
orders
pueda extender el tipoCustomer
definido en el serviciocustomers
.
Solución
Esquema del Servicio de Clientes
# customers/schema.graphql type Customer @key(fields: "id") { id: ID! name: String! email: String! } extend type Query { customer(id: ID!): Customer }
Resolvers del Servicio de Clientes
// customers/index.js const { ApolloServer, gql } = require('apollo-server'); const { buildFederatedSchema } = require('@apollo/federation'); const typeDefs = gql` type Customer @key(fields: "id") { id: ID! name: String! email: String! } extend type Query { customer(id: ID!): Customer } `; const resolvers = { Query: { customer(_, { id }) { return customers.find(customer => customer.id === id); }, }, Customer: { __resolveReference(customer) { return customers.find(c => c.id === customer.id); }, }, }; const server = new ApolloServer({ schema: buildFederatedSchema([{ typeDefs, resolvers }]), }); server.listen({ port: 4003 }).then(({ url }) => { console.log(`🚀 Customers service ready at ${url}`); });
Esquema del Servicio de Pedidos
# orders/schema.graphql extend type Customer @key(fields: "id") { id: ID! @external orders: [Order] } type Order { id: ID! product: String! quantity: Int! } extend type Query { order(id: ID!): Order }
Resolvers del Servicio de Pedidos
// orders/index.js const { ApolloServer, gql } = require('apollo-server'); const { buildFederatedSchema } = require('@apollo/federation'); const typeDefs = gql` extend type Customer @key(fields: "id") { id: ID! @external orders: [Order] } type Order { id: ID! product: String! quantity: Int! } extend type Query { order(id: ID!): Order } `; const resolvers = { Customer: { orders(customer) { return orders.filter(order => order.customerId === customer.id); }, }, Query: { order(_, { id }) { return orders.find(order => order.id === id); }, }, }; const server = new ApolloServer({ schema: buildFederatedSchema([{ typeDefs, resolvers }]), }); server.listen({ port: 4004 }).then(({ url }) => { console.log(`🚀 Orders service ready at ${url}`); });
Configuración del Gateway
const { ApolloServer } = require('apollo-server'); const { ApolloGateway } = require('@apollo/gateway'); const gateway = new ApolloGateway({ serviceList: [ { name: 'customers', url: 'http://localhost:4003/graphql' }, { name: 'orders', url: 'http://localhost:4004/graphql' }, ], }); const server = new ApolloServer({ gateway }); server.listen().then(({ url }) => { console.log(`🚀 Gateway ready at ${url}`); });
Conclusión
La federación de GraphQL es una poderosa técnica para manejar esquemas grandes y complejos de manera modular y escalable. Al dividir el esquema en múltiples servicios independientes, se facilita el desarrollo, despliegue y mantenimiento de aplicaciones grandes. Con la práctica y la correcta implementación, la federación puede mejorar significativamente la eficiencia y la colaboración en equipos de desarrollo.
Curso de GraphQL
Módulo 1: Introducción a GraphQL
- ¿Qué es GraphQL?
- GraphQL vs REST
- Configuración de un Servidor GraphQL
- Conceptos Básicos del Esquema de GraphQL
Módulo 2: Conceptos Fundamentales
Módulo 3: Diseño Avanzado de Esquemas
Módulo 4: Trabajando con Datos
Módulo 5: Rendimiento y Seguridad
Módulo 6: Herramientas y Ecosistema
Módulo 7: Pruebas y Despliegue
- Pruebas Unitarias de Resolvers
- Pruebas de Integración
- Integración Continua
- Despliegue de Servidores GraphQL