Una introducción a GraphQL en Elixir usando Absinthe — Parte II

Diego Jose Villa
Mar 5 · 4 min read

En Una introducción a GraphQL en Elixir usando Absinthe — Parte I creamos un servidor para una API GraphQL y realizamos queries básicas. En esta parte exploraremos JSONB de Postgres y realizaremos queries más complejas como filtros para usuarios y posts.

Resolviendo datos en JSONB

En el modelo de datos cada post tiene asociado un atributo details, y este es declarado con el tipo map. Los maps son almacenados en Postgres como JSONB. Para poder accesar a los valores de details debemos crear un nuevo helper y un nuevo tipo, de esta manera Absinthe podrá procesar queries en donde se soliciten valores almacenados en la estructura JSONB.

En el archivo blog_types.ex agregaremos un nuevo objeto :post_detail con los siguientes campos.

Absinthe desconoce el tipo :map, por lo que no podemos asociar un campo con el tipo :map al objeto :post, pero sí podemos declarar nuevos tipos. Al declarar un nuevo tipo podemos modelar la representación de la estructura JSONB en Absinthe.

En el módulo BlogTypes hemos definido un nuevo objeto, :post_detail, que contiene la descripción de los campos que puede contener la estructura JSONB.

Si entramos a http://localhost:4000/graphiql y ejecutamos la siguiente query:

BlogAPI nos retornará la siguiente respuesta:

El valor de site se retorna como null, y no es el comportamiento que esperamos. Esto sucede porque Absinthe sabe que site es un atributo de details que puede ser consultado, pero no sabe cómo consultar ese campo en la estructura JSONB. Para arreglar este error necesitamos crear un nuevo resolver.

En el directorio graphql_api_web/resolvers crearemos un nuevo archivo helper_resolver.ex, y en este archivo definiremos lo siguiente:

La función key(key_name) retornará un función; esta función recibirá la estructura que esta almacenada en details y usando la función Map.get() consultará el valor para la llave que hayamos especificado.

Ahora podemos usar la función key(key_name) en blog_type.ex, esto se hace importando HelperResolver y pasando la función al atributo resolve asociado a un campo de post_field.

Ahora si ejecutamos la misma query, el servidor resolverá el valor de site en la estructura JSONB y lo mostrará, el resultado será el siguiente:

Filtrar usuarios

Hasta ahora podemos consultar los usuarios y sus valores, pero no podemos realizar filtros sobre un valor en especifico. Supongamos que ahora queremos habilitar la funcionalidad para filtrar los usuarios por tipo de subscripción; podríamos tener una query como la siguiente:

Para poder realizar este tipo de queries debemos hacer algunos cambios. Para empezar debemos agregar un input_object en admin_types.ex.

Un input_object permite definir los campos que pueden ser utilizados como parámetros de queries. En AdminTypes definimos :user_filter como input_object y subscription como un parámetro de una query.

En el archivo admin_queries.ex tenemos que modificar :users para que reciba el argumento :filter y este solo podrá contener campos que estén definidos en :user_filter.

También tenemos que modificar el archivo user_resolver.ex y admin.ex. UserResolver ahora tiene que recibir el argumento filter y enviarlo como parámetro a Admin.list_users.

list_users(filter) ahora incluye un Enum.reduce que recibe una función anónima que esta utilizando pattern matching para realizar el mapeo de filter: {subscription: “gold”} con {:filter, filter}. Si el mapeo anterior se cumple se llama a la función filter_with(entity, filter) que aplicará el filtro especificado.

Ahora si corremos de nuevo la query, BlogAPI estará filtrando los usuarios por tipo de subscripción.

Filtrar posts

Aplicar un filtro a un posts es muy similar a lo que hemos realizado para filtrar usuarios, solo que ahora tenemos que utilizar fragmentos para poder procesar los valores en JSONB.

En el archivo blog_types.ex debemos agregar un nuevo input_object :post_filter que contendrá los campos que pueden ser filtrados en el atributo details de un post.

En blog_queries.ex debemos modificar el campo :posts para que ahora pueda recibir un argumento llamado :filter.

Y debemos modificar el resolver post_resolver.ex para que ahora la función list_posts reciba los filtros especificados en la query.

La función filter_with(entity, filter) es muy similar a la definida para filtrar usuarios, solo que ahora incluimos fragment. Los fragmentos son utilizados cuando no podemos representar una query utilizando Ecto, un fragmento es enviado directamente a la base de datos.

Ahora si ingresamos una query como la siguiente, BlogAPI estará filtrando los posts por los atributos que se especifiquen.


Repositorio del proyecto: https://github.com/diegoajv/BlogAPI

Diego Jose Villa

Written by

Software Engineer @ecaresoft. ITC’18 — @TecdeMonterrey. Making great stuff combining design and software engineering. Always ready to contribute.

The Backlog by Ecaresoft

Sharing our journey: from software development to company culture and productivity hacks.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade