Categorías
General Temporada 0

Log 0.5

Nota: Este log hace referencia a la parte 8d del curso https://fullstackopen.com/en/part8/fragments_and_subscriptions

Suscripciones en GraphQL

Resulta interesante este concepto para permitir la actualización/notificación de cambios desde el servidor hacia el cliente.

El servidor Apollo hace uso de WebSockets para ello y nos permite informar a quien esté suscrito, es decir, a los componentes que nosotros decidamos, si se produce algún cambio en nuestra base de datos.

Se programa, sobre todo, en la parte de cliente, que es quien se va a suscribir: para eso hacemos uso de una función propia de Apollo, useSubscription:

  export const BOOK_ADDED = gql`
  subscription{
    bookAdded{
      title
    author{
      name
      born
    }
    published
    genres
    id
    }
  }
`
useSubscription(BOOK_ADDED, {
  onSubscriptionData: ({ subscriptionData }) => {
   const addedBook = subscriptionData.data.bookAdded
   updateCacheWith(addedBook)
  }
})
  

En la parte del backend debemos «suscribirnos» a este suscriptor que ya habremos previamente definido en nuestro schema.

Nuestro mutation que añade libros debe cambiar, puesto que en el proceso de cambiar, debemos lanzar una llamada a nuestro suscriptor para avisarle que se va a producir un cambio y, en esa llamada, facilitarle qué estamos cambiando.

Mutation: {
    addBook: async (root, args, context) => {
      const author = await Author.findOne({name: args.author })
      const currentUser = context.currentUser
      
      if(!currentUser){
        throw new AuthenticationError("not authenticated")
      }
            
      const book = new Book({...args, author: author
      })
      try{
        await book.save()
        // Añadimos la suscripción -------------
        pubsub.publish('BOOK_ADDED', { bookAdded: book })
        // -------------------------------------
        return book 
      }catch (error) {
        throw new UserInputError(error.message, {
          invalidArgs: args,
        })
      }

Una vez se produce un cambio generado por el useMutation(ADD_BOOK…) salta el onSubscriptionData y podemos, por tanto, usar una función callback para gestionar qué hacemos con los datos añadidos:

const updateCacheWith = (addedBook) => {
    const includedIn = (set, object) => 
      set.map(p => p.id).includes(object.id)

    const dataInStore = client.readQuery({ query: ALL_INFO })
    if(!includedIn(dataInStore.allBooks, addedBook)) {
      try{
      client.writeQuery({
        query: ALL_INFO,
        data: { allBooks: dataInStore.allBooks.concat(addedBook)}
      })}catch(error){
        console.log(error)
      }
    }
  }

Este es un ejemplo de función que lo que hace es actualizar la caché de GraphQL con los datos añadidos, puesto que uno de los problemas que presenta GraphQL es precisamente la falta de actualización automática cuando se produce un cambio en la base de datos.

Utilizando este mecanismo nos aseguramos, por un lado, que la caché de GraphQL está siempre actualizada con los últimos datos que tiene la base de datos y, por otro, que todos los usuarios que simultáneamente están accediendo al servicio reciben esta actualización de forma inmediata.

GraphQL me ha parecido una solución tremendamente interesante para implementar Heres I, aunque todavía estoy en la parte más inicial del proyecto y tengo algo de margen para decidir qué camino escoger en relación a la forma que voy a utilizar para obtener los datos desde el servidor.

Categorías
Temporada 0

Log 0.4

GraphQL

Esta sección corresponde con la parte 8 del curso.

GraphQL plantea una forma de interactuar con los datos distinta a la que conocemos como REST.

Para poner en marcha un servicio de GraphQL hacemos uso del servidor Apolo

npm i apollo-server

Y montamos un archivo que lanzaremos con node que incluya tanto la definición de las estructuras de los datos como la de las llamadas:

Definimos las estructuras y las consultas:

const typeDefs = gql`
  type Book {
    title: String!
    published: Int!
    author: String!
    id: ID!
    genres: [String!]!
  }
  type Query {
    bookCount: Int!
     allBooks(author: String, genre: String): [Book!]!
  }
`

Además, debemos definir lo que se conoce como resolvers, que hacen uso de las estructuras de consulta que hemos definido para responder:

const resolvers = {
  Query: {
    bookCount: () => books.length,
    allBooks: (root, args) => {
      if(!args.author && !args.genre){
        return books
      } if (!args.genre) {
      return(books.filter(book => book.author === args.author))
      }
      if (!args.author){
        return(books.filter(book => book.genres.includes(args.genre) ))
      }
      return(books.filter(book => book.author === args.author && book.genres.includes(args.genre) ))
      },
    }

En estos resolvers podemos incluir toda la lógica que queramos para responder en función de nuestras necesidades.

Con ello, GraphQL opera como traductor entre la consulta y los datos adaptando las respuestas a las necesidades, previamente definidas, del cliente.

Además, durante el desarrollo, si trabajamos con el servidor Apollo, tendremos un entorno de pruebas donde realizar las consultas y ver qué responde el servidor de forma rápida y cómoda.

Conclusiones

Pese a que en un principio era algo reticente y consideraba que mis proyectos irían todos enfocados a servicios REST, esta solución me está pareciendo cada vez más interesante y más útil y es posible que me plantee incluirla en Heres I.