Introducción
Las suscripciones en GraphQL permiten a los clientes recibir actualizaciones en tiempo real desde el servidor. Esto es especialmente útil para aplicaciones que requieren datos en tiempo real, como chats, notificaciones, o cualquier tipo de actualización en vivo.
Conceptos Clave
- Suscripción: Una operación de GraphQL que permite a los clientes escuchar eventos específicos en el servidor.
- Publicación/Suscripción (Pub/Sub): Un patrón de mensajería donde los emisores (publicadores) envían mensajes a un canal y los receptores (suscriptores) reciben esos mensajes.
- WebSocket: Un protocolo de comunicación que permite la interacción bidireccional entre el cliente y el servidor.
Configuración de Suscripciones en GraphQL
Paso 1: Configuración del Servidor
Para configurar suscripciones en un servidor GraphQL, generalmente se utiliza una biblioteca como Apollo Server. A continuación, se muestra cómo configurar un servidor con suscripciones:
const { ApolloServer, gql, PubSub } = require('apollo-server');
const pubsub = new PubSub();
const typeDefs = gql`
type Message {
id: ID!
content: String!
}
type Query {
messages: [Message!]
}
type Mutation {
postMessage(content: String!): ID!
}
type Subscription {
messagePosted: Message!
}
`;
const messages = [];
let id = 0;
const resolvers = {
Query: {
messages: () => messages,
},
Mutation: {
postMessage: (parent, { content }) => {
const message = { id: id++, content };
messages.push(message);
pubsub.publish('MESSAGE_POSTED', { messagePosted: message });
return message.id;
},
},
Subscription: {
messagePosted: {
subscribe: () => pubsub.asyncIterator(['MESSAGE_POSTED']),
},
},
};
const server = new ApolloServer({
typeDefs,
resolvers,
subscriptions: {
path: '/subscriptions',
},
});
server.listen().then(({ url, subscriptionsUrl }) => {
console.log(`Server ready at ${url}`);
console.log(`Subscriptions ready at ${subscriptionsUrl}`);
});Explicación del Código
-
Definición del Esquema:
typeDefsdefine los tiposMessage,Query,MutationySubscription.Subscriptiondefine un campomessagePostedque emite eventos de tipoMessage.
-
Publicador/Suscriptor:
PubSubes una implementación simple de pub/sub en memoria.pubsub.publish('MESSAGE_POSTED', { messagePosted: message })publica un evento cuando se crea un nuevo mensaje.
-
Resolvers:
postMessageenMutationcrea un nuevo mensaje y lo publica.messagePostedenSubscriptionse suscribe a los eventosMESSAGE_POSTED.
-
Servidor Apollo:
subscriptions: { path: '/subscriptions' }habilita las suscripciones en la ruta especificada.
Paso 2: Configuración del Cliente
Para suscribirse a eventos desde el cliente, se puede usar Apollo Client con WebSocket. A continuación, se muestra un ejemplo de configuración del cliente:
import { ApolloClient, InMemoryCache, HttpLink, split } from '@apollo/client';
import { WebSocketLink } from '@apollo/client/link/ws';
import { getMainDefinition } from '@apollo/client/utilities';
const httpLink = new HttpLink({
uri: 'http://localhost:4000/',
});
const wsLink = new WebSocketLink({
uri: `ws://localhost:4000/subscriptions`,
options: {
reconnect: true,
},
});
const splitLink = split(
({ query }) => {
const definition = getMainDefinition(query);
return (
definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
);
},
wsLink,
httpLink,
);
const client = new ApolloClient({
link: splitLink,
cache: new InMemoryCache(),
});
export default client;Explicación del Código
-
Enlaces HTTP y WebSocket:
HttpLinkse utiliza para consultas y mutaciones.WebSocketLinkse utiliza para suscripciones.
-
División de Enlaces:
splitse utiliza para enrutar las operaciones a través del enlace adecuado (HTTP o WebSocket) según el tipo de operación.
Ejemplo de Suscripción en el Cliente
import { gql, useSubscription } from '@apollo/client';
const MESSAGE_POSTED = gql`
subscription OnMessagePosted {
messagePosted {
id
content
}
}
`;
function Messages() {
const { data, loading } = useSubscription(MESSAGE_POSTED);
if (loading) return <p>Loading...</p>;
return (
<div>
<h2>Messages</h2>
{data.messagePosted.map((message) => (
<p key={message.id}>{message.content}</p>
))}
</div>
);
}
export default Messages;Explicación del Código
-
Definición de la Suscripción:
MESSAGE_POSTEDes una suscripción que escucha eventosmessagePosted.
-
Uso de la Suscripción:
useSubscriptionse utiliza para suscribirse aMESSAGE_POSTEDy actualizar la UI en tiempo real.
Ejercicio Práctico
Ejercicio
-
Configura un servidor GraphQL con suscripciones:
- Define un tipo
Commentcon camposidycontent. - Crea una mutación
postCommentque publique un eventocommentPosted. - Define una suscripción
commentPostedque emita eventos de tipoComment.
- Define un tipo
-
Configura un cliente que se suscriba a
commentPosted:- Usa
Apollo Clientpara configurar los enlaces HTTP y WebSocket. - Crea un componente que muestre los comentarios en tiempo real.
- Usa
Solución
Servidor
const { ApolloServer, gql, PubSub } = require('apollo-server');
const pubsub = new PubSub();
const typeDefs = gql`
type Comment {
id: ID!
content: String!
}
type Query {
comments: [Comment!]
}
type Mutation {
postComment(content: String!): ID!
}
type Subscription {
commentPosted: Comment!
}
`;
const comments = [];
let id = 0;
const resolvers = {
Query: {
comments: () => comments,
},
Mutation: {
postComment: (parent, { content }) => {
const comment = { id: id++, content };
comments.push(comment);
pubsub.publish('COMMENT_POSTED', { commentPosted: comment });
return comment.id;
},
},
Subscription: {
commentPosted: {
subscribe: () => pubsub.asyncIterator(['COMMENT_POSTED']),
},
},
};
const server = new ApolloServer({
typeDefs,
resolvers,
subscriptions: {
path: '/subscriptions',
},
});
server.listen().then(({ url, subscriptionsUrl }) => {
console.log(`Server ready at ${url}`);
console.log(`Subscriptions ready at ${subscriptionsUrl}`);
});Cliente
import { ApolloClient, InMemoryCache, HttpLink, split } from '@apollo/client';
import { WebSocketLink } from '@apollo/client/link/ws';
import { getMainDefinition } from '@apollo/client/utilities';
import { gql, useSubscription } from '@apollo/client';
const httpLink = new HttpLink({
uri: 'http://localhost:4000/',
});
const wsLink = new WebSocketLink({
uri: `ws://localhost:4000/subscriptions`,
options: {
reconnect: true,
},
});
const splitLink = split(
({ query }) => {
const definition = getMainDefinition(query);
return (
definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
);
},
wsLink,
httpLink,
);
const client = new ApolloClient({
link: splitLink,
cache: new InMemoryCache(),
});
const COMMENT_POSTED = gql`
subscription OnCommentPosted {
commentPosted {
id
content
}
}
`;
function Comments() {
const { data, loading } = useSubscription(COMMENT_POSTED);
if (loading) return <p>Loading...</p>;
return (
<div>
<h2>Comments</h2>
{data.commentPosted.map((comment) => (
<p key={comment.id}>{comment.content}</p>
))}
</div>
);
}
export default Comments;Conclusión
Las suscripciones de GraphQL son una poderosa herramienta para aplicaciones que requieren actualizaciones en tiempo real. Al comprender cómo configurar y utilizar suscripciones, puedes mejorar significativamente la interactividad y la experiencia del usuario en tus aplicaciones. En el siguiente módulo, exploraremos más herramientas y el ecosistema de GraphQL para seguir ampliando tus habilidades.
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
