Como levantar un backend en minutos con Parse + docker compose
Tags: parseserver, backend, spanish, docker
Published: 2023-04-18 17:07:08

## ¿Qué es backend as a service?

Es un servicio de backend que se presta a través de la nube y que se encarga de proveer apis para ser consumidos desde un frontend o app móvil según sea el caso, esto nos permite ahorrarnos el mantenimiento, la creación de plugins entre otras cosas, la lista de ventajas es muy larga y surge de la necesidad de ahorrar tiempo y costos en el desarrollo.

### ¿Por qué parse-sever?

Existen muchas opciones para tener un backend sin complicarnos en detalles técnicos como por ejemplo firebase, AWS amplify, Supabase entre otros más, estos nos ofrecen un servicio en la nube pero con la desventaja de que no podemos descargar el código fuente y por lo general nos limitamos a las opciones que nos ofrecen, en ocasiones necesitamos tener más control de lo que estamos haciendo y aquí es donde podemos utilizar parse-server, si lo necesitamos podemos descargar el código fuente y ejecutarlo desde express o correrlo desde una imagen de docker, otra de las ventajas es que existen muchos plugins gracias a la gran comunidad que hay detrás del proyecto, en nuestro caso utilizaremos docker compose porque crearemos funciones personalizadas y para ello solo debemos crear una carpeta llamada `src/cloud` desde allí crear nuestro archivo `main.js` que es el punto de entrada que reconoce parse-server, luego podemos compilar nuestra imagen, levantar la base de datos y levantar el dashboard para poder administrar nuestras clases todo desde docker compose.

Una vez dicho todo esto es hora empezar a configurar todo nuestro proyecto para así tener un backend el cual te daras cuenta es muy fácil de configurar y mantener.

### Creación del proyecto

Para este proyecto estoy utilizando pnpm el cual tiene varias ventajas frente a npm y una de las principales para mi es que descarga las dependencias en paralelo y esto permite compilar la imagen de docker mucho más rápido.

Para empezar debemos tener instalado pnpm y ejecutar el siguiente comando en nuestra terminal.

```sh
pnpm init
```

Nos preguntará el nombre del proyecto así como información general del proyecto, este comando nos creará el archivo `package.json`.

Luego debemos configurar babel para poder utilizar la sintaxis más reciente de javascript, para ello debemos instalar primero las dependencias en nuestro proyecto.

```sh
pnpm -i @babel/cli @babel/core @babel/node @babel/plugin-transform-runtime @babel/preset-env --save-dev

pnpm -i @babel/runtime --save
```

Una vez tengamos las dependencias instaladas, debemos agregar un archivo de configuración en la raíz del proyecto llamado `babel.config.json` y agregar el siguiente código:

```json
{
    "presets": [
        "@babel/preset-env"
    ],
    "plugins": [
        "@babel/plugin-transform-runtime"
    ],
    "env": {
        "debug": {
          "sourceMaps": "inline",
          "retainLines": true
        }
      }
}
```

En nuestro archivo `package.json` en la sección de scripts debemos agregar la siguiente línea para poder compilar nuestro código.

```json
"build": "npx babel src --out-dir dist --copy-files && cp package.json dist/",
```

Con esto ya podemos empezar a crear nuestras propias funciones, parse-server nos permite ejecutar funciones personalizadas solo debemos agregar nuestro código en el directorio llamado `src/cloud`, luego debemos crear un archivo llamado main.js, este archivo es el que utiliza parse-server como punto de entrada, para más información puedes consultar la guía oficial en este [link](https://docs.parseplatform.org/cloudcode/guide/)

Hasta el momento solo hemos creado la estructura del proyecto y la configuración de babel, pero aun no lo podemos probar, la idea es poder correr nuestro código desde una imagen de docker para ello necesitamos crear nuestro Dockerfile, y agregar las siguientes instrucciones para poder compilar nuestro código, descargar dependencias y luego copiar todo a la imagen de parse-server, el cual quedaría de esta manera. 

```docker
# ---------- Base ----------
FROM node:14-slim AS base
RUN apt-get update && apt-get install -y libpq-dev g++ make git openssh-client curl
RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm
# ---------- Builder ----------
# Creates:
# - node_modules: production dependencies (no dev dependencies)
# - dist: A production build compiled with Babel
FROM base AS builder
WORKDIR /app

COPY .npmrc package.json pnpm-lock.yaml babel.config.json ./
RUN pnpm install
COPY ./src ./src
RUN pnpm build

# Remove dev dependencies
RUN pnpm prune --prod

# ---------- Release ----------
FROM parseplatform/parse-server:5.4.1 AS release
COPY --from=builder /app/node_modules /parse-cloud/node_modules
COPY --from=builder /app/dist /parse-cloud/
```
Como te puedes dar cuenta estoy utilizando una imagen de nodejs v14 slim como primera capa de compilación, los pasos en el Dockerfile son los siguientes: Instalar pnpm, descargar las dependencias y compilar el proyecto, luego creamos una nueva imagen usando como base la imagen oficial de `parseplatform/parse-server`, (versión 5.4.1) esto lo hacemos porque debemos copiar nuestro código y para ello debemos guardar las dependencias junto con el código ya compilado en la carpeta `parse-cloud`, estar carpeta irá dentro de la imagen de parse, de esta manera podremos llamar a nuestras propias funciones.

En esta punto ya tenemos nuestra imagen solo la debemos compilarla, para probarla debemos pasarle los parámetros de la base de datos como variables a docker y listo, si bien es posible hacerlo de esa manera es mucho más cómodo levantar la base de datos y `parse-dashboard` (el administrador de parse-server) desde un docker compose, de esta manera con un par de comandos podemos tener corriendo todo fácilmente, para ello debemos crear nuestro archivo llamado `docker-compose.yml` y agregar el siguiente código.

```yml
version: "3.9"
services:
  mongo:
    image: mongo:4.2
    restart: always
    environment:
      MONGO_INITDB_ROOT_USERNAME: ${MONGO_ROOT_USERNAME}
      MONGO_INITDB_ROOT_PASSWORD: ${MONGO_ROOT_PASSWORD}
    networks:
      - app-network
    ports:
      - "27017:27017"
  parse:
    build: .
    environment:
      - PARSE_SERVER_APPLICATION_ID=${APP_ID}
      - PARSE_SERVER_MASTER_KEY=${MASTER_KEY}
      - PARSE_SERVER_REST_API_KEY=${REST_API_KEY}
      - PARSE_SERVER_CLIENT_KEY=${CLIENT_KEY}
      - PARSE_SERVER_DATABASE_URI=${DATABASE_URI}
      - PARSE_SERVER_MOUNT_PATH=/parse
      - PARSE_SERVER_CLOUD=/parse-cloud/cloud/main.js
      - PARSE_SERVER_MOUNT_GRAPHQL=true
      - PARSE_SERVER_MOUNT_PLAYGROUND=true
    ports:
      - "1337:1337"
    entrypoint: ["/bin/sh", "-c", "sleep 5;  node ./bin/parse-server"] #sleep few seconds for postgres to come up
    depends_on:
      - mongo
    networks:
      - app-network
  parse-dashboard:
    image: parseplatform/parse-dashboard
    ports:
      - '4040:4040'
    environment:
#      - PARSE_DASHBOARD_ALLOW_INSECURE_HTTP=true
      - PARSE_DASHBOARD_TRUST_PROXY=1
      - PARSE_DASHBOARD_COOKIE_SESSION_SECRET=${DASHBOARD_COOKIE_SESSION_SECRET}
      - PARSE_DASHBOARD_SERVER_URL=${DASHBOARD_SERVER_URL}
      - PARSE_DASHBOARD_MASTER_KEY=${MASTER_KEY}
      - PARSE_DASHBOARD_CLIENT_KEY=${CLIENT_KEY}
      - PARSE_DASHBOARD_APP_ID=${APP_ID}
      - PARSE_DASHBOARD_APP_NAME=MyParseApp
      - PARSE_DASHBOARD_USER_ID=${DASHBOARD_USER_ID}
      - PARSE_DASHBOARD_USER_PASSWORD=${DASHBOARD_USER_PASSWORD}
      - MOUNT_PATH=/dashboard/
      - PARSE_DASHBOARD_GRAPHQL_SERVER_URL=${DASHBOARD_GRAPHQL_SERVER_URL}
    command: parse-dashboard --dev
    depends_on:
      - parse
networks:
  app-network:
    driver: bridge
```

Lo que hacemos es levantar un contenedor de mongodb con las credenciales que nosotros queramos, luego se compila nuestra imagen y se levanta un contenedor con los parámetros de configuración, también levanta un contenedor con `parse-dashboard` que es una imagen con el administrador de parse y por último todo se comunica sobre la misma red, y así nos ahorramos tiempo en ejecutar varios comando para realizar la misma operación, por último cabe mencionar que las variables que utiliza docker compose se guardan en un archivo llamado .env que debe ser creado en la raíz del proyecto.

```
# .env file example
MONGO_ROOT_USERNAME=root
MONGO_ROOT_PASSWORD=example
APP_ID=parse
MASTER_KEY=parse@master123!
CLIENT_KEY=clientKey
REST_API_KEY=parseRestApiKey
DATABASE_URI=mongodb://root:example@mongo:27017/
DASHBOARD_SERVER_URL=http://localhost:1337/parse/
DASHBOARD_COOKIE_SESSION_SECRET=AB8849B6-D725-4A75-AA73-AB7103F0363F
DASHBOARD_APP_NAME=MyParseApp
DASHBOARD_USER_ID=admin
DASHBOARD_USER_PASSWORD=password
DASHBOARD_GRAPHQL_SERVER_URL=http://localhost:1337/graphql
```

Para levantar nuestro docker compose lo podemos hacer con el siguiente comando:

```sh
docker compose up --build --force-recreate
```

El comando anterior no es recomendado en producción, pero si necesitamos más documentación, en internet existen muchos tutoriales de comandos de docker compose en ambientes de producción.

De este manera tenemos un backend en pocos minutos, ya solo debemos concentrarnos en las funcionalidades de nuestra aplicación, espero y les sirva este pequeño tutorial, tambien dejo el codigo fuente en [github](https://github.com/kesmile/app-parse-server-backend), para quien desee consultarlo, tambien estare utilizando este backend con mas articulos que deseo escribir mas adelante.