En el artículo de introducción a GraphQL te expliqué los conceptos fundamentales sobre esta tecnología y hoy te enseñaré a crear un backend con Nodejs. Para este fin usaremos:
- Apollo Server
- Paquete graphql (implementación de la referencia de GraphQL para Nodejs)
Para empezar nuestro proyecto tenemos que instalar Apollo Server y graphql:
1npm i apollo-server graphql 2
Definiendo nuestro esquema de GraphQL
Creamos el archivo typeDefs.js
y definimos nuestro esquema, (queries, mutaciones y tipo)
1const { gql } = require('apollo-server'); 2 3const typeDefs = gql` 4 type Task { 5 id: Int! 6 name: String! 7 description: String! 8 completed: Boolean! 9 } 10 11 type Query { 12 tasks: [Task]! 13 task(id: Int!): Task 14 } 15 16 type Mutation { 17 addTask( 18 name: String!, 19 description: String!, 20 completed: Boolean! 21 ): Task! 22 } 23`; 24 25module.exports = typeDefs; 26
Usamos gql
para definir un esquema de GraphQL, en este hay tres componentes principales:
Task
. Dentro del cual definimos los camposid
,name
,description
ycompleted
.- Las queries que soportará nuestro servicio. Son
tasks
ytask
, estas consiguen una lista de tareas y una tarea en específico dado su id. - Mutaciones:
addTask
que recibirá los argumentosname
,description
ycompleted
y a su vez retornará una tarea.
Al final exportamos la definición de nuestro esquema.
Definiendo nuestros resolvers
Los resolvers son las funciones que satisfacen las queries y mutaciones. Es en los resolvers donde estaremos implementando la lógica para conseguir y agregar las tareas. Para hacerlo crearemos un archivo llamado resolvers.js
:
1const tasks = []; 2 3module.exports = { 4 Query: { 5 tasks: (parent, args, context) => tasks, 6 task: (parent, args, context) => { 7 const task = tasks.find((task) => task.id === args.id); 8 return task; 9 }, 10 }, 11 Mutation: { 12 addTask: (parent, args, context) => { 13 const newTask = { 14 id: tasks.length, 15 name: args.name, 16 description: args.description, 17 completed: args.completed, 18 }; 19 20 tasks.push(newTask); 21 return newTask; 22 }, 23 }, 24}; 25
En este archivo definimos un array tasks
que usaremos como fuente de datos en nuestra aplicación.
Ten presente que GraphQL es independiente de la base o fuente de datos que estemos usando. Puedes usar bases de datos como mongo o bases de datos SQL, incluso puedes obtener la información a través de otras apis.
Después de creado el array, exportaremos un objeto que tendrá las propiedades Query
y Mutation
Query
1 Query: { 2 tasks: (parent, args, context) => tasks, 3 task: (parent, args, context) => { 4 const task = tasks.find((task) => task.id === args.id); 5 return task; 6 }, 7 }, 8
Aquí es donde estarán definidas todos los resolvers para devolver la información en las queries que nos hagan nuestros clientes. Las funciones dentro de la propiedad Query
deben llamarse igual a la query que van a satisfacer.
En nuestro esquema definimos la query tasks
así que en la propiedad Query
de nuestros resolvers crearemos la propiedad tasks
, que tendrá la función que nos devuelve las tareas cuando alguno de nuestros clientes haga alguna query a tasks
.
Luego definimos otro resolver para la query de task
y en esta función usamos los parámetros que nos provee apollo para todos nuestros resolvers (parent
, args
y context
). Para más información acerca de estos parámetros los invito a leer este artículo en la documentación de apollo. En el caso del resolver para task
solo haremos uso del parámetro args
, en este nos llegará el argumento id que se definió en la query task
de nuestro esquema:
1// En nuestro esquema definimos que en esta query ibamos a recibir un campo id de tipo Int 2// Y que devolveriamos una tarea 3task(id: Int!): Task 4
Lo que hace args
es reunir esos argumentos definidos en nuestro esquema en un objeto que es lo que representa args
y con el valor que se le pase a id
buscamos en nuestra lista de tareas alguna que tenga el mismo id
y lo retornamos, este retorno es el que dará la respuesta a la petición del cliente.
Mutation
1Mutation: { 2 addTask: (parent, args, context) => { 3 const newTask = { 4 id: tasks.length, 5 name: args.name, 6 description: args.description, 7 completed: args.completed, 8 }; 9 10 tasks.push(newTask); 11 return newTask; 12 }, 13 }, 14
Solo nos queda nuestra mutación para agregar una nueva tarea, esta debe ubicarse dentro de la propiedad Mutation
y al igual que en las queries debe de llamarse igual a la mutación definida en nuestro esquema que espera satisfacer. Aquí también recibimos en args
los argumentos name
, description
y completed
(así está definido en el esquema). Con estos campos formamos el objeto que agregaremos en el array y lo retornamos, es muy importante que este objeto cumpla con la estructura definida en el type Task
, en caso contrario tendríamos errores.
Levantar el servidor GraphQL
para eso tenemos el siguiente código en el archivo index.js
:
1const { ApolloServer } = require('apollo-server'); 2const typeDefs = require('./typeDefs'); 3const resolvers = require('./resolvers'); 4 5const server = new ApolloServer({ 6 typeDefs, 7 resolvers, 8 playground: true, 9}); 10 11server.listen().then(() => { 12 console.log('Corriendo aplicación graphQL en <http://localhost:4000/grapqhql>'); 13}); 14
- Aquí importamos la definición de nuestro esquema y nuestros resolvers y los usamos para construir una instancia de
ApolloServer
, - Además ponemos la propiedad
playground
entrue
que permite tener un entorno gráfico donde interactuaremos con el servidor. - Y para levantar el servidor usamos la función
listen()
.
Si abrimos http://localhost:4000/grapqhql
podremos ver lo siguiente:
Este playground que nos da apollo nos servirá para probar nuestro servicio, por ejemplo: ejecutamos la mutación addTask
para agregar una tarea al array.
Al lado izquierdo escribimos nuestra mutación pasándole los argumentos que nos pide para crear la tarea y diciéndole que de la tarea resultante queremos conseguir el id y el nombre. Al darle click al botón de play conseguiremos como respuesta lo que tenemos a la derecha, donde podemos ver solamente el id y el nombre como lo indiqué cuando escribí la mutación.
Ahora veamos la query de tasks
:
Como ves estoy usando la query de tasks
, está como la definimos en el esquema, nos retorna un array con todas las tareas, en este caso solo retorna la que acabamos de crear y con los campos que le especifiqué: id
, name
y description
.
Al igual podemos probar la query task
:
Aquí usamos la query task
buscando una tarea por un id en especial, 0
en este caso, al lado derecho recibimos un único objeto task
con los campos id
, name
y completed
como lo definí al momento de hacer la query.
Eso es todo, espero que te haya gustado este artículo. Si quieres ver el código, te dejo el repositorio con todo lo que hicimos. Próximamente te enseñaré a consumir este servicio desde frontend con vanilla js y React. ¡Hasta pronto!