He oído muchos rumores sobre trabajar con GraphQL y NoSQL, internamente dentro de Couchbase y externamente. Ya que suena como un tema candente, pensé en pasar algún tiempo aprendiendo sobre ello para ver cómo podría ser valioso a la hora de crear aplicaciones web.
Viniendo del mundo de las API RESTful, GraphQL es un concepto totalmente diferente para mí, aunque su objetivo es resolver un problema similar de acceso y mutación de datos a través de aplicaciones orientadas al cliente. Afortunadamente, Node.js y las bases de datos trabajan mano a mano, por lo que en el ejemplo de GraphQL-base de datos a continuación vamos a ser usando Node.js con Couchbase como nuestro Base de datos NoSQL capa.
En este momento, no soy un experto en GraphQL. Llevo muchos años desarrollando APIs RESTful con frameworks como Express y Hapi. Dicho esto, voy a tratar de explicar cómo he aprendido acerca de ellos.
Las API funcionan muy bien, pero cuando se trata de obtener la información que necesitas de ellas, las cosas se pueden hacer mejor. Por ejemplo, ¿qué pasa si necesitas un montón de datos no relacionados o poco relacionados para tu cliente? ¿Crea un punto final masivo que devuelva muchos datos o realiza una solicitud a cada punto final de API que pueda contener sus datos? ¿Y si sólo necesita un fragmento de los datos devueltos por un punto final concreto?
Aquí es donde GraphQL entra en juego. Mediante el lenguaje de consulta puedes especificar exactamente qué datos quieres que se devuelvan al cliente en cada momento sin tener que escribir un endpoint para todo.
Creación de una nueva aplicación Node.js con Express Framework
Vamos a utilizar Node.js para crear una interfaz para utilizar Consultas GraphQL. Empecemos creando un nuevo proyecto, obteniendo las dependencias y arrancando nuestro proyecto en preparación para algo de lógica real.
Con Node.js instalado, desde la línea de comandos, ejecute:
1 2 3 4 5 6 |
npm init -y npm instale couchbase --guardar npm instale express --guardar npm instale express-graphql --guardar npm instale graphql --guardar npm instale uuid --guardar |
Los comandos anteriores inicializarán un nuevo proyecto creando un archivo paquete.json e instalando las dependencias necesarias. De las dependencias, estamos instalando Couchbase y un paquete para generar cadenas UUID que más tarde representarán las claves de nuestros documentos. Los otros paquetes están relacionados con GraphQL y su vinculación con Express Framework.
Cree un archivo llamado app.js dentro de su proyecto e incluya el siguiente código:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
const Express = requiere("express"); const Couchbase = requiere("couchbase"); const ExpressGraphQL = requiere("express-graphql"); const ConstruirEsquema = requiere("graphql").construirEsquema; const UUID = requiere("uuid"); var esquema = ConstruirEsquema(``); var solucionadores = { }; var aplicación = Express(); aplicación.utilice("/graphql", ExpressGraphQL({ esquema: esquema, rootValue: solucionadores, graphiql: verdadero })); aplicación.escuche(3000, () => { consola.registro("Escuchando en :3000"); }); |
No vamos a preocuparnos por rellenar nuestro esquema y configurar nuestra base de datos todavía. En el código anterior, observa que hemos importado cada una de nuestras dependencias e inicializado Express. También hemos definido un único endpoint API que es como vamos a conseguir que Couchbase y GraphQL interactúen. Esencialmente, vamos a enviar cada consulta GraphQL a ese único endpoint y responderá con los datos correctos basados en nuestro esquema y resolvers.
Definición de un esquema GraphQL y Resolvers
Ahora que ya tenemos la base de la aplicación, vamos a definir un esquema y los resolvers para vincular los datos a posibles consultas y tipos de datos.
Para este ejemplo, vamos a crear una sencilla aplicación de blogging. Por esta razón, podría tener sentido tener los siguientes modelos de esquema:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
var esquema = ConstruirEsquema(` tipo Cuenta { id: Cadena, nombre: Cadena, apellido: Cadena, } tipo Blog { id: Cadena, cuenta: Cadena!, título: Cadena, contenido: Cadena } `); |
Sabemos que tendremos cuentas y sabemos que tendremos artículos de blog para cualquier cuenta dada. El signo de exclamación para cualquier tipo de dato significa que es una propiedad obligatoria.
Ahora necesitamos conectar nuestro modelo a Couchbase para poder consultar datos o incluso crearlos.
Consulta y mutación de datos desde Couchbase Server, la base de datos NoSQL
Vamos a suponer que usted ya tiene Couchbase funcionando. No hay ningún requisito especial de configuración de Couchbase para este ejemplo en particular.
El primer paso es conectarse a Couchbase Server desde la aplicación Node.js. Dentro del proyecto app.js añada las siguientes líneas:
1 2 3 |
var grupo = nuevo Couchbase.Grupo("couchbase://localhost"); grupo.autentifique("ejemplo", "123456"); var cubo = grupo.openBucket("ejemplo"); |
Las líneas anteriores asumen que Node.js y la base de datos - Couchbase, en este ejemplo - se están ejecutando en la máquina local. Vamos a utilizar un Bucket llamado ejemplo
y una cuenta de usuario RBAC denominada ejemplo
.
Ya tenemos los modelos de datos para GraphQL, pero no hemos definido las posibles consultas o mutaciones. Modifiquemos el esquema de nuestra aplicación para que tenga el siguiente aspecto:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
var esquema = ConstruirEsquema(` tipo Consulta { cuenta(id: Cadena!): Cuenta, cuentas: [Cuenta], blog(id: Cadena!): Blog, blogs(cuenta: Cadena!): [Blog] } tipo Cuenta { id: Cadena, nombre: Cadena, apellido: Cadena, } tipo Blog { id: Cadena, cuenta: Cadena!, título: Cadena, contenido: Cadena } tipo Mutación { crearCuenta(nombre: Cadena!, apellido: Cadena!): Cuenta crearBlog(cuenta: Cadena!, título: Cadena!, contenido: Cadena!): Blog } `); |
Tanto para las consultas como para las mutaciones, se llamará a funciones especiales de resolución que harán el trabajo pesado. Como resultado, o bien un Cuenta
o un Blog
será devuelto.
Empezando por las mutaciones, esperamos un crearCuenta
que espera dos parámetros. Puede tener el siguiente aspecto:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
var solucionadores = { crearCuenta: (datos) => { var id = UUID.v4(); datos.tipo = "cuenta"; devolver nuevo Promesa((resolver, rechace) => { cubo.insertar(id, datos, (error, resultado) => { si(error) { devolver rechace(error); } resolver({ "id": id }); }); }); } }; |
Observe que nuestra función resolver sólo tiene un parámetro a pesar de que estábamos pasando dos en nuestro esquema. Todos nuestros parámetros de datos residirán en el directorio datos
variable. La propia función espera que devolvamos una promesa.
Observará que devolvemos un id
y nada más. Recuerde, esperamos un Cuenta
a devolver, pero no todas las partes de Cuenta
debe estar presente. Sin embargo, puede ser una mejor práctica para devolver todo.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
var solucionadores = { crearBlog: (datos) => { var id = UUID.v4(); datos.tipo = "blog"; devolver nuevo Promesa((resolver, rechace) => { cubo.insertar(id, datos, (error, resultado) => { si(error) { devolver rechace(error); } resolver({ "id": id }); }); }); } }; |
En crearBlog
es prácticamente igual a la función anterior, con la excepción de que los datos de entrada son diferentes y la función tipo
tiene otro valor. Todas nuestras funciones estarán en el mismo solucionadores
objeto.
Una vez eliminadas las mutaciones, se pueden consultar los datos. Toma los cuenta
por ejemplo:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
var solucionadores = { cuenta: (datos) => { var id = datos.id; devolver nuevo Promesa((resolver, rechace) => { cubo.consiga(id, (error, resultado) => { si(error) { devolver rechace(error); } resolver(resultado.valor); }); }); } }; |
No ocurre nada realmente diferente entre esta función de consulta en particular y la función de mutación. Esperamos un id
y lo usamos para hacer una búsqueda.
Para recuperar todas las cuentas, podemos utilizar una consulta N1QL:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
var solucionadores = { cuentas: () => { var declaración = "SELECT META(cuenta).id, cuenta.* FROM `" + cubo.Nombre + "` AS account WHERE account.type = 'account'"; var consulta = Couchbase.N1qlQuery.fromString(declaración); devolver nuevo Promesa((resolver, rechace) => { cubo.consulta(consulta, (error, resultado) => { si(error) { devolver rechace(error); } resolver(resultado); }); }); } }; |
¿Puedes ver la tendencia ahora cuando se trata de consultar y mutar datos? Para terminar el trabajo, veamos nuestras dos funciones finales, una para obtener un solo blog y otra para obtener todos los blogs.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
var solucionadores = { blog: (datos) => { var id = datos.id; devolver nuevo Promesa((resolver, rechace) => { cubo.consiga(id, (error, resultado) => { si(error) { devolver rechace(error); } resolver(resultado.valor); }); }); }, blogs: (datos) => { var declaración = "SELECT META(blog).id, blog.* FROM `" + cubo.Nombre + "` AS blog WHERE blog.type = 'blog' AND blog.account = $account"; var consulta = Couchbase.N1qlQuery.fromString(declaración); devolver nuevo Promesa((resolver, rechace) => { cubo.consulta(consulta, { "cuenta": datos.cuenta }, (error, resultado) => { si(error) { devolver rechace(error); } resolver(resultado); }); }); } }; |
En este momento, nuestra aplicación está completa. Al menos lo suficientemente completa para este ejemplo en particular. Ahora probablemente deberíamos probarla con algunas consultas GraphQL.
Ejecución de consultas GraphQL con la interfaz gráfica
Si estás usando Node.js y el paquete GraphQL para Express, obtendrás una bonita interfaz gráfica para ejecutar consultas. Navega a http://localhost:3000/graphql en tu navegador web y deberías ver algo parecido a lo siguiente:
Así que vamos a crear una consulta de mutación que insertará los datos para nosotros.
1 2 3 4 5 |
mutación CrearCuenta($nombre: Cadena!, $apellido: Cadena!) { crearCuenta(nombre: $nombre, apellido: $apellido) { id } } |
La mutación anterior tomará dos parámetros obligatorios y luego ejecutará nuestro crearCuenta
pasando cada uno de esos parámetros. Los parámetros pueden establecerse en el Variables de consulta del explorador GraphQL. Los parámetros serían algo parecido a esto:
1 2 3 4 |
{ "nombre": "Erika", "apellido": "Raboy" } |
Tras una mutación exitosa, obtendremos el id
que es lo que hemos solicitado. Después de haber llenado nuestra base de datos con datos, podríamos hacer una consulta GraphQL más sofisticada como la siguiente:
1 2 3 4 5 6 7 8 9 10 |
consulta GetAccountData($id: Cadena!) { cuenta(id: $id) { nombre apellido } blogs(cuenta: $id) { título contenido } } |
Para la consulta anterior, introducirías una cuenta válida y te devolvería la información de la cuenta, así como la información del blog que esa cuenta ha escrito. A través de las consultas GraphQL puedes solicitar ciertas propiedades o todas las propiedades ahorrándote hacer múltiples peticiones.
Conclusión
Acabas de ver cómo crear una sencilla base de datos Node.js aprovechando GraphQL en Couchbase. Sólo porque GraphQL le da un medio para consultar los datos que necesita, no elimina la necesidad de escribir consultas de calidad que se comunican con su base de datos. Recuerda, GraphQL es sólo un lenguaje de consulta de cara al cliente, no un lenguaje de consulta de base de datos backend. En otras palabras, deja que Couchbase haga el trabajo pesado donde tenga sentido.
Si desea obtener más ayuda con el SDK de Node.js para Couchbase, consulte la sección Portal para desarrolladores de Couchbase.