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

  1. Suscripción: Una operación de GraphQL que permite a los clientes escuchar eventos específicos en el servidor.
  2. 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.
  3. 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

  1. Definición del Esquema:

    • typeDefs define los tipos Message, Query, Mutation y Subscription.
    • Subscription define un campo messagePosted que emite eventos de tipo Message.
  2. Publicador/Suscriptor:

    • PubSub es una implementación simple de pub/sub en memoria.
    • pubsub.publish('MESSAGE_POSTED', { messagePosted: message }) publica un evento cuando se crea un nuevo mensaje.
  3. Resolvers:

    • postMessage en Mutation crea un nuevo mensaje y lo publica.
    • messagePosted en Subscription se suscribe a los eventos MESSAGE_POSTED.
  4. 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

  1. Enlaces HTTP y WebSocket:

    • HttpLink se utiliza para consultas y mutaciones.
    • WebSocketLink se utiliza para suscripciones.
  2. División de Enlaces:

    • split se 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

  1. Definición de la Suscripción:

    • MESSAGE_POSTED es una suscripción que escucha eventos messagePosted.
  2. Uso de la Suscripción:

    • useSubscription se utiliza para suscribirse a MESSAGE_POSTED y actualizar la UI en tiempo real.

Ejercicio Práctico

Ejercicio

  1. Configura un servidor GraphQL con suscripciones:

    • Define un tipo Comment con campos id y content.
    • Crea una mutación postComment que publique un evento commentPosted.
    • Define una suscripción commentPosted que emita eventos de tipo Comment.
  2. Configura un cliente que se suscriba a commentPosted:

    • Usa Apollo Client para configurar los enlaces HTTP y WebSocket.
    • Crea un componente que muestre los comentarios en tiempo real.

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.

© Copyright 2024. Todos los derechos reservados