Probablemente ya estes convencido de que utilizar GraphQL es una excelente opción para tu próxima API, pero todavía no sabes por dónde empezar. En este artículo vamos a ver cómo dar esos primeros pasos en la creación de una API con GraphQL. También vamos a ver cómo conectarnos a una base de datos hecha en MongoDB, ya preexistente, hecha en otro artículo llamado Cómo scrapear usando Puppeteer.js PASO a PASO.
Preparando el proyecto
Creamos una carpeta que se llame graphql-api
en el lugar que más nos guste y clonamos el siguiente repositorio de github. Este nos va a crear un proyecto muy simple de express.js.
git clone [email protected]:alotama/extremely-simple-express-app.git
Una vez hecho esto, agregamos las dependencias que vamos a necesitar para la construcción de nuestra primera API con GraphQL y MongoDB.
npm i graphql express-graphql mongoose
Estas dependencias son:
- graphql: contiene todo lo que vamos a necesitar para el desarrollo de nuestra API.
- express-graphql: contiene módulos que ya resuelven algunos procesos por nosotros.
- mongoose: nos va a permitir conectarnos a MongoDB y realizar las consultas que necesitemos.
Iniciando GraphQL
Al usar GraphQL, este nos provee un sandbox en el que nos permite simular todas las query que queramos. Para hacer esto, creamos un endpoint que puede llamarse como más nos guste, pero para este caso, vamos a ponerle /graphql
. Lo agregamos de la siguiente manera a nuestro proyecto:
const graphqlHTTP = require('express-graphql')
app.use('/graphql', graphqlHTTP({
schema: schemas,
rootValue: root,
graphiql: true
}))
En la primer línea importamos graphqlHTTP
. Lo que hace cuando la invocamos, es simplificar el proceso de creación del endpoint. Nada más debemos hacer una fragmentación de tres módulos.
- schema: es una instancia del
GraphQLSchema
de GraphQL. Aquí es donde estructuramos la información que vamos a solicitar. - rootValue: es una lista con todos nuestros schemas que van a estar disponibles para ser consultados.
- graphiql: es un boolean que simplemente habilita, o no, el sandbox que hablamos anteriormente.
Si lo dejamos así, no va a funcionar porque no declaramos ni los schemas, ni el root. Para eso incluimos nuestro "Hello world" en GraphQL.
let schema = buildSchema(`
type Query {
hello: String
}
`);
let root = {
hello: () => {
return 'Hello world!';
},
};
¡Excelente! Así de simple es cómo creamos una API con GraphQL.
En el siguiente paso, vamos a necesitar tener un MongoDB. Recomiendo leer el artículo que mencione al principio, cómo crear un scrapeador con puppeteer, para conseguir uno.

Te invito a que si quieres hacer todo paso a paso lo que haga, le eches una mirada. Es muy simple y no te va a llevar más de 10 o 15 minutos minutos crearlo.
Conexión a MongoDB
En mi caso use MongoDB Atlas, un servicio cloud gratuito. Copiamos el método de conexión por String y lo guardamos en una variable que lo llamamos mongoUrl
.
const mongoUrl = "mongodb+srv://<userName>:<password>@<cluster>.mongodb.net/<database>?ssl=true&retryWrites=true&w=majority"
Ahora importamos e invocamos mongoose y nos conectamos propiamente a la base de datos.
const mongoose = require('mongoose');
mongoose.connect(mongoUrl, { useNewUrlParser: true, useUnifiedTopology: true });
var db = mongoose.connection;
// Un mensaje que nos avise si se pudo conectar a la base da datos, o no.
!db ? console.log("Error connecting db") : console.log("Db connected successfully");
Ya conectados a la base de datos, ahora necesitamos crear un schema
del modelo del dato que vamos a consultar a MongoDB y lo exportamos para disponibilizarlo más adelante. Debemos declarar qué propiedades, tipos, y si es obligatorio que venga en la respuesta.
En mi caso, consulté los números finales de los campeones del juego Raid: Shadow Legends.
/*
Nuevo archivo:
championModel.js
*/
let mongoose = require('mongoose');
let Schema = mongoose.Schema;
let championSchema = new Schema({
name: {
type: String,
required: true
},
rarity: {
type: String,
},
faction: {
type: String,
},
type: {
type: String,
},
element: {
type: String,
},
stats: {
health: {
type: String,
required: true
},
attack: {
type: String,
required: true
},
defense: {
type: String,
required: true
},
criticalRate: {
type: String,
required: true
},
criticalDamage: {
type: String,
required: true
},
speed: {
type: String,
required: true
},
resistance: {
type: String,
required: true
},
accuracy: {
type: String,
required: true
},
}
});
module.exports = Champion = mongoose.model('champions', championSchema);
Volvemos al index.js
y creamos las funciones que van a realizar las consultas a MongoDB.
const Champion = require("./championMode")
const getChampion = (query) => {
return Champion.findOne({ name: query.name }, function (err, response) {
if(err) return err
return response;
})
}
const getAllChampions = (query) => {
let queryName = query.name && query.name.map(champion => {
let obj = { name: "" };
obj.name = champion;
return obj;
});
let filter = queryName && { $or: queryName }
return Champion.find(filter, function (err, docs) {
if (err) return err;
return docs
})
}
En getChampion()
recorre toda la respuesta proveniente de Champion
hasta encontrar el objeto que contiene el mismo name
que del parámetro de la query.name
.
Por otro lado, getAllChampions()
es un poco diferente a la anterior, porque en este caso, query
es un array. Entonces, antes de recorrer la respuesta de la consulta a MongoDB, debemos procesar la información de tal forma que la función find()
entienda que es lo que debe buscar.
Construcción de Schemas
Hecho ya todo lo referente a MongoDB, ahora pasamos a lo que nos falta de GraphQL. En la variable root
, reemplazamos la key hello
por:
const root = {
champion: getChampion,
allChampion: getAllChampions
}
La key puede ser cualquier cosa que se nos ocurra. Lo importante es que, como valor, se haga referencia a las funciones creadas anteriormente.
Por último, reemplazamos la query de la variable schema
por:
const schemas = buildSchema(`
type Query {
champion(name: String!): Champion
allChampion(name: [String!]):[Champion]
}
type Champion {
id: ID!
name: String!
rarity: String
faction: String
rating: Int
type: String
element: String
stats: Stats!
}
type Stats {
health: Int!
attack: Int!
defense: Int!
criticalRate: Int!
criticalDamage: Int!
speed: Int!
resistance: Int!
accuracy: Int!
}
`)
Sintaxis de los schemas
Para aclarar esta sintaxis es una manera simplificada de crear nuevos schemas. Existe otra manera, un poco más compleja, pero más flexible, que utiliza los módulos graphql/type
.
Link a la documentación de esta otra forma de crear schemas (graphql/type | API Reference).
En la forma simplificada, nada más hace falta declarar el nombre que queremos que tenga el nuevo type
. Luego de eso nada más declaramos una key y qué esperamos que sea. Estas pueden ser:
- String
- Int
- Boolean
- Float
- ID
Cuando hacemos, por ejemplo, String!
lo que hacemos es decir que ese campo no puede ser null
. Si por alguna razón ese campo no viene, la aplicación puede notificarlo con un error y/o romperse.
Nosotros podemos crear nuevas referencias escribiendo el nombre del type
que queremos que se muestre en esa key. Un ejemplo de esto es el código de más arriba. Sabemos que en type Champion
contiene stats: Stats!
.
Esto quiere decir que en la key stats
va a retornar el type Stats
declarado más abajo, que no puede ser null
.
Por otro lado, cuando creamos el type Query
le pasamos las dos keys que pusimos en la variable root
y declaramos que van a recibir un parámetro. En este caso, name
, que es del tipo String!
.
Por último, en el endpoint allChampions
además estamos declarando que el parámetro name
es un array que dentro va a contener strings [String!]
. Lo mismo ocurre con lo que esperamos que devuelva ese endpoint. Al envolver Champion
dentro de [ ]
lo que hacemos es decirle a GraphQL que lo que sea que contiene dentro va a ser un array.
Resumiendo
Creamos una API con GraphQL que se conecta a una base de datos en MongoDB Atlas y le realizamos todas las consultas que necesitemos. Por último declaramos las queries que se pueden hacer. También qué tipo de respuesta y cómo esperamos que sea esta.
Te invito a que me sigas en twitter, @alotama y me escribas qué te pareció el artículo. Si tenés alguna consulta también sos totalmente libre de enviarme tus consultas por allí.