Backend desde cero con Express, graphQL y MongoDB

GraphQL junto con MongoDB de han convertido en dos de las tecnologías más usadas para proyectos de todo tipo. GraphQL por las ventajas que ofrece sobre el estándar API REST y MongoDB, por su versatilidad al momento de crear bases de datos. Las bases de datos hoy en día son más fáciles de administrar y de mantener si son NoSQL. Es por eso que me animé a escribir este tutorial.

En este tutorial aprenderemos a crear un backend sencillo utilizando MongoDB, GraphQL y Express. Este será el primero de una serie de tutoriales con estas tres tecnologías.

Índice

  1. Creando el proyecto y la configuración inicial
  2. Configurando GraphQL
  3. Creando el modelo de datos para MongoDB
  4. Creando el resolver de GraphQLL
  5. Creando un servidor y el Endpoint de nuestra API
  6. Tu turno

1. Creando el proyecto y la configuración inicial

Lo primero que necesitamos es crear un proyecto yarn para lo cual utilizamos el siguiente comando.

yarn init

Eso nos creará el archivo package.json con la configuración que utilizamos. La estructura que utilizaremos para este proyecto es la siguiente.

📦 node_modules
📂 graphql
 ├─📂resolver
 │  └─📜index.js
 ├─📂schema
 │  └─📜index.js
📂 models
 ├─📜article.js
📜app.js
📜nodemon.json
📜package.json
📜yarn.lock

Lo siguiente que haremos será instalar las dependencias que utilizaremos. Comenzaremos con Express, que es el framework JavaScript para backend que utilizaremos.

yarn add express

Lo segundo que necesitamos instalar el Nodemon, el cual lo utilizaremos como dependencia de desarrollo, por lo que utilizaremos el siguiente comando.

yarn add nodemon -D

También necesitamos instalar GraphQL, que es el que nos permitirá acceder a la base de datos.

yarn add graphql

Como base de datos utilizaremos MongoDB, para conectarnos a este necesitamos la dependencia llamada mongoose. Para lo cual usaremos el siguiente comando.

yarn add mongoose

Y por ultimo utilizaremos una dependencia que nos permitirá realizar peticiones GraphQL utilizando Express. Para esto utilizaremos express-graphql y lo instalamos de la siguiente forma.

yarn add express-graphql

Ahora que ya tenemos instalado todas las dependencias podemos revisar nuestro archivo package.json donde estarán todas las dependencias que estuvimos instalando. Pero aún debemos agregar algo más ahí y es el script que nos permitirá utilizas nodemon. Así que agregamos lo siguiente.

"main": "app.js",
"scripts": {
  "start": "nodemon app.js"
},

Por otro lado, Nodemon lo utilizamos como un monitor de scripts, que nos permite escuchar los cambios que hacemos en nuestro proyecto y volver a iniciar el servidor local cada vez que guardamos dichos cambios. Pero también tiene ciertas utilidades como permitirnos agregar variables de entorno, de tal forma que utilicemos dichas variables dentro de nuestro proyecto. Para declarar las variables de entorno lo hacemos dentro del archivo nodemon.json que creamos y donde debemos colocar el siguiente código.

{
  "env": {
    "MONGO_USER": "mongo_user",
    "MONGO_PASSWORD": "mongo_password",
    "MONGO_DB": "db_name"
  }
}

Cada una de esas variables son de MongoDB. Y para este punto también debemos tener creado un cluster en Mongo Atlas. Y eso sería toda la configuración inicial que debemos tener para comenzar con nuestro proyecto.

2. Configurando GraphQL

Comencemos explicando algunos términos importantes en GraphQL antes de comenzar. Un esquema o schema define la forma de los datos. Es decir define los tipos y los campos que tendrán cada uno de los documentos Json que se almacenarán en la base de datos. Entonces el código que utilizaremos en graphql/schema/index.js es el siguiente.

// Nos traemos la funciónn que utilizaremos de la dependencia de graphql
const { buildSchema } = require("graphql");

// Utilizamos este método para crear nuestros esquemas de la siguiente forma
module.exports = buildSchema(`

  type Article {
    _id: ID!
    title: String!
    body: String!
    createdAt: String!
  }

  input ArticleInput {
    title: String!
    body: String!
  }

  type Query {
    articles:[Article!]
  }

  type Mutation {
    createArticle(article:ArticleInput): Article
  }

  schema {
    query: Query
    mutation: Mutation
  }
`);

Lo primero que definimos es el tipo, en este caso Article, que nos permite definir la estructura de los objetos que tendremos en nuestros documentos MongoDB. Comenzando definiendo un id único, este lo creará MongoDB pero debes declararlo así _id : ID! ya que este campo es propio de MongoDB. Además agregamos el signo de ! para indicar que es un campo requerido. Los siguientes campos que utilizaremos son title, body y createAt que son de tipo String y también son requeridos.

Lo segundo que creamos en un input, este nos permite definir la forma de entrada de datos para enviarlos a la base de datos o para realizar mutaciones con GraphQL.

El tercer elemento que creamos es de tipo query, que como su nombre nos indica nos permite crear el modelo de consulta que podremos hacer en GraphQL. Este nos nos tendrá que devolver un arreglo de Artículos, ya que nuestra consulta tendrá que hacer justamente eso, entregarnos la lista de artículos almacenados en nuestra base de datos.

El cuarto elemento que creamos es un mutation. Las mutaciones en GraphQL son las que nos permiten crear, editar y eliminar registros en nuestra base de datos. Aqui nosotros primero definimos el nombre de la mutación y entre parentesis el Input que utilizamos para insertar los datos y después de los dos puntos el tipo de los datos que ingresamos.

Por último debemos crear el esquema schema donde declararemos la consulta query y el mutation.

##3. Creando el modelo de datos para MongoDB

Aquí modelaremos nuestros datos para poder enviárselos a MongoDB, por lo que utilizaremos la dependencia mongoose. Entonces iremos al archivo models/article.js y colocaremos el siguiente código que explicaremos a continuación.

// Traemos el objeto mongoose desde la dependencia
const mongoose = require("mongoose");

// Creamos una constante llamada Schema con un objeto de mongoose
const Schema = mongoose.Schema;

// Instanciamos el objeto Schema enviando como propiedad la estructura
const aricleSchema = new Schema(
  {
    title: {
      type: String,
      required: true,
    },

    body: {
      type: String,
      required: true,
    },
  },
  { timestamps: true }
);

// Exportamos el modelo del esquema
module.exports = mongoose.model("Article", aricleSchema);

Al crear la instancia de Schema definimos el esquema enviándole como primer parámetro un objeto con la forma de nuestro tipo de dato. Solo enviamos el title y el body porque el _id es propio de MongoDB y no tenemos que enviarlo, se generará automáticamente y del createdAt se encargará e siguiente parámetro que enviamos que es {timestamps: true} que se encarga de colocar tanto el createdAt como el updatedAt donde colocaremos la fecha y hora en el que se crea y actualiza nuestro registro.

4. Creando el resolver de GraphQL

Un resolver es una colección de funciones que ayudan a generar respuestas a las consultas de GraphQL, tanto a un query como a un mutation. Pero antes debemos saber que el nombre de la función de nuestro resolver debe ser igual al nombre del query o el mutation que va responder. Entonces precedamos a crear nuestro resolver, para ello nos dirigimos al archivo graphql/resolvers/index.js.

// Comenzamos traendo el modelo de datos
const Article = require("../../models/article");

// Exportamos las funciones que resolverá las peticiones
module.exports = {
  // Esta función es para los querys
  articles: async () => {
    try {
      // creamos una constante que mediante find me trae todo el arreglo de registros
      const articlesFetched = await Article.find();
      // Hacemos un map al arreglo y creamos otro arreglo pero con los datos que queremos mostrar
      return articlesFetched.map((article) => {
        return {
          ...article._doc,
          _id: article.id,
          createdAt: new Date(article._doc.createdAt).toISOString(),
        };
      });
    } catch (error) {
      throw error;
    }
  },

  // Esta otra función es para el mutation
  createArticle: async (args) => {
    try {
      // Creamos un objeto a partir de los args que son los que mandamos
      const { title, body } = args.article;
      // Creamos el objeto article con el objeto anterior
      const article = new Article({
        title,
        body,
      });
      // Hacemos un await guardando el articulo creado con save
      const newArticle = await article.save();
      // Retornamos un objeto con el resultado del await y el id
      return { ...newArticle._doc, _id: newArticle.id };
    } catch (error) {
      throw error;
    }
  },
};

Asi ya tenemos las funciones que responderá a nuestras peticiones de GrapgQL.

5. Creando un servidor y el Endpoint de nuestra API

Esto lo vamos hacer en el archivo principal de nuestro proyecto que es el app.js.

// Traemos las dependencias necesarias
const express = require("express");
const { graphqlHTTP } = require("express-graphql");
const mongoose = require("mongoose");
const graphqlSchema = require("./graphql/schema");
const graphqlResolvers = require("./graphql/resolvers");

// Instanciamos Express, necesario en toda aplicación Express
const app = express();

// Declaramos el endpoint y comos egundo parámetro el esquema y resolver de graphQL
app.use(
  "/graphql",
  graphqlHTTP({
    schema: graphqlSchema,
    rootValue: graphqlResolvers,
    graphiql: true,
  })
);
// Este es el uri para la conexion a MongoDB se trae desde Mongo Atlas
// Usamos process.env para obtener los valores desde Nodemon
const uri = `mongodb+srv://${process.env.MONGO_USER}:${process.env.MONGO_PASSWORD}@cluster0.ayw8p.mongodb.net/${process.env.MONGO_DB}?retryWrites=true&w=majority`;
// Declaramos las opciones para mongoose
const options = { useNewUrlParser: true, useUnifiedTopology: true };

// Usamos mongose para conectarnos al uri con las opciones y entonces escucharlas en el puerto 3000 usando app
mongoose
  .connect(uri, options)
  .then(() => app.listen(3000, console.log("Server is running")))
  .catch((error) => {
    throw error;
  });

Y listo ya tenemos nuestro endpoint usando GraphQL, Express y MongoDB solo debemos ir a la siguiente dirección http://localhost:3000/graphql. Aquí podríamos hacer dos tipos de peticiones comenzamos con el mutation, para crear un Articulo. Para eso dentro de la consola Graphiql utilizamos el siguiente código.

mutation {
  createArticle(article: { title: "Nuevo", body: "Nuevo prueba" }) {
    title
    body
    createdAt
  }
}

Y para realizar la consulta de los registros usamos el siguiente query.

{
  articles {
    title
    body
    createdAt
  }
}

6. Tu turno

Acabamos de crear nuestro primer endpoint utilizando MongoDb, Express y GraphQL. Quizás son tantas tecnologías juntas, pero así es como se estructuran los proyectos en el mundo real. Y usan más tecnologías que estas ya que son proyectos que han sido creados y mejorados con el paso de tiempo. Así que te invito a practicar utilizando estas tres tecnologías. Sin miedo al éxito!!