Symfony: Primeros pasos con overblog/GraphQLBundle

Aprende a desarrollar una API GraphQL con Symfony y GraphQLBndle

Image for post
Image for post

Esta semana la he dedicado a profundizar en el lenguaje GraphQL y en la forma en que podemos escribir una API con él y Symfony. Todas las notas que he tomado hasta hoy las he recopilado en este artículo en el que contaré la forma en que podemos definir la API de un blog mediante este lenguaje.

Para realizar la integración entre Symfony y GraphQL he escogido el bundle GraphQLBundle que cuenta con una extensa documentación y con varias charlas que sirven de mucha ayuda a la hora de entender todo el proceso.

Así que sin enrollarme más, ¡vamos a por ello!

Instalando overBlog/GraphQLBundle

El proceso para instalar el bundle es bastante sencillo. Primero ejecutaremos:

composer require overblog/graphql-bundle

y, si queremos disponer de una interfaz gráfica para probar la API instalaremos también el siguiente bundle:

composer require --dev overblog/graphiql-bundle

Nota. La documentación del bundle nos propone configurarlo para que emplee graphql como formato de definición, pero he tenido problemas definiendo resolvers (en unos párrafos veréis lo que son) en ese lenguaje, por lo que mi recomendación es que continuéis usando yaml .

Si queremos comprobar que todo está funcionando correctamente accederemos mediante un navegador a la raíz de nuestro proyecto y deberemos obtener un error similar al siguiente:

{"error":{"code":500,"message":"Internal Server Error",
"exception":[{"message":"Unknown type with alias \"Query\"
(verified service tag)","class":"Overblog\\
GraphQLBundle\\Resolver\\UnresolvableException","trace"...

La razón de que al acceder a la ruta / de nuestro proyecto obtengamos ese error es debido a que la “receta” de Flex que incorpora GraphQLBundle carga el siguiente archivo de rutas:

# config/routes/graphql.yamloverblog_graphql_endpoint:    resource: "@OverblogGraphQLBundle/Resources/config/routing/graphql.yml"

que añade directamente las siguientes rutas a nuestro proyecto:

Image for post
Image for post

Si queremos añadir un prefijo a las rutas gestionadas por GraphQLBundle bastará con añadir lo siguiente en dicho archivo:

overblog_graphql_endpoint:    resource: "@OverblogGraphQLBundle/Resources/config/routing/graphql.yml"    prefix: graphql

Configurando nuestro primer esquema

Ahora vamos a lo interesante. Si habéis llegado a este artículo seguramente sabréis que las API’s que emplean GraphQL son tipadas mediante la definición de esquemas en el formato definido por GraphQL:

Por tanto, lo primero que deberemos hacer será definir el esquema para nuestra entidad de Doctrine Article para lo cual crearemos un archivo Article.type.yaml en la carpeta config/graphql/types con el siguiente contenido:

Article:
type: object
config:
description: "An article"
fields:
id:
type: "Int!"
description: "The ID of the article."
title:
type: "String"
description: "Title of the article"
subtitle:
type: "String"
description: "Subtitle of the article"

A continuación definiremos el punto de entrada de las queries que componen nuestra API, es decir, las consultas que estamos exponiendo. Esto se realiza mediante el tipo especial de GraphQL query , del cual podéis leer más en los siguientes enlaces

Esto lo haremos creando un archivo Query.types.yaml en la carpeta config/graphql/types con el siguiente contenido:

Query:
type: object
config:
description: ''
fields:
allArticles:
type: '[Article]'

En este caso, estamos definiendo una la query allArticles la cual devolverá un array de entidades / tipos Article .

Por supuesto, en el momento en que nuestra API comience a crecer no será definir aquí todas las queries sino que el GraphQLBundle nos permitirá definir múltiples endpoints.

Si ahora accedemos en nuestro servidor a la ruta /graphiql veremos la interfaz gráfica que instalamos en el paso 1 de este artículo y podremos ejecutar una query similar a ésta:

{
allArticles {id, title}
}

pero que sin embargo devolverá:

{
"data": {
"allArticles": null
}
}

ya que de momento no hemos especificado la forma en que se deben obtener los datos para esa consulta.

Creando nuestro primer Resolver

Para conseguir que nuestra consulta anterior devuelva datos deberemos crear lo que se conoce como un Resolver el cual actuará de puente entre GraphQL y el servicio de almacenamiento de nuestros datos, por ejemplo Doctrine.

Para ello, modificaremos nuestro archivo Query.types.yaml añadiendo lo siguiente:

Query:
type: object
config:
description: ''
fields:
allArticles:
type: '[Article]'
resolve: "@=resolver('all_articles', [args])"

A continuación crearemos la clase correspondiente con la lógica para resolver la query a una entidad (o lista de entidades) procedentes de Doctrine. Esto lo haremos creando una clase en el directorio src/GraphQL/Resolver/ArticleResolver.php con el siguiente código:

Image for post
Image for post

Esta clase implementa dos interfaces: ResolverInterface y AliasedInterface . La primera de ellas permite que Symfony realice el autoconfigure de modo que el bundle GraphQL sepa que se trata de un resolver mientras que AliasedInterface nos permite declarar el resolver por medio de un alias. Es decir, si dado que nuestro Resolver lo declaramos de este modo:

resolve: "@=resolver('all_articles', [args])"

el método getAliases nos permite relacionar el alias all_articles con el método findAllArticles de nuestra clase. No obstante, existen más formas de declarar Resolvers que podéis encontrar en la propia documentación del bundle:

Con esto, ya podríamos ejecutar nuestra primera consulta en GraphQL por medio de la interfaz gráfica que se encuentra en el path /graphiql de nuestro proyecto:

{
allArticles {id, title}
}

Esto por ejemplo nos devolvería los campos id, title de todos los artículos que haya en base de datos.

Si ahora por ejemplo, quisiéramos añadir un método que nos permitiera obtener un artículo por id bastaría con modificar nuestro ArticleResolver del siguiente modo:

Image for post
Image for post

y modificar nuestro archivo Query.types.yaml para añadir:

Image for post
Image for post

Ahora ya podríamos volver a la interfaz de GraphiQL y lanzar la siguiente consulta:

{
articleById(id: 1) {id, title}
}

La cual nos devolverá el id y el título del artículo con id = 1 en el caso de que exista en base de datos.

Reusando tipos

Una de las ventajas que nos proporciona GraphQL es la posibilidad de declarar todos los tipos que queramos de modo que luego podamos reutilizarlos.

Por ejemplo, nuestra query allArticles devuelve una lista de artículos expresada como: [Article] .

Sin embargo, también podemos declarar un nuevo tipo ArticleList :

Image for post
Image for post

Para usar este nievo tipo, modificaremos la query allAssistants :

Image for post
Image for post

y en el ArticleResolver modificaremos el método findAll para que se adapte a este nuevo tipo:

public function findAll(Argument $args)
{
$articles = $this->em->getRepository(Article::class)
->findAll();
return ['articles' => $articles];
}

Ahora, en /graphiql podremos lanzar la consulta:

{
allArticles { article { id } }
}

para obtener el id de todos los artículos.

Conclusión

Como habréis podido observar, no resulta nada complicado comenzar a trabajar con GraphQL así que os animo a que lo probéis en algún proyecto y me digáis las impresiones.

En los siguientes artículos hablaré acerca de cómo podemos usar GraphQL para crear y modificar entidades y la forma en que podemos paginar los resultados.

¿Quieres recibir más artículos como este?

Suscríbete a nuestra newsletter:

Written by

Entre paseo y paseo con Simba desarrollo en Symfony y React

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store