¿Cómo implementar una aplicación Deno?

Existen varias opciones de implementación para aplicaciones web creadas con Deno. Sin embargo, la contenedorización como plataforma de servicios se ha convertido en una opción popular en los últimos tiempos debido a las diversas ventajas que ofrecen sobre otras opciones de implementación.

En este artículo, explorará Deno, sus ventajas y limitaciones. Además, creará una aplicación Deno simple y la implementará en contenedores Back4app.

¿Qué es Deno?

Deno es un tiempo de ejecución seguro y moderno para JavaScript y TypeScript creado para abordar las limitaciones y fallas de diseño encontradas en Node.js.

A diferencia de Node.js, enfatiza la seguridad de forma predeterminada, aplicando permisos granulares para el sistema de archivos y el acceso a la red.

Además, Deno admite de forma nativa TypeScript, lo que elimina la necesidad de pasos adicionales de configuración o transpilación, entre otras funciones.

Desde su lanzamiento en 2018, Deno ha atraído la atención y el interés de los desarrolladores debido a sus mejoras con respecto a Node.js.

Sin embargo, si bien Deno ofrece mejoras, Node.js sigue siendo un ecosistema maduro con un amplio soporte comunitario y un amplio repositorio de paquetes.

No obstante, Deno ha atraído a una creciente comunidad de desarrolladores que aprecian su enfoque y están explorando su potencial.

Ventajas de Deno

El ascenso de Deno a la popularidad se debe a algunas razones subyacentes. Algunas son las siguientes.

Seguridad mejorada sobre Node.js

Deno ofrece seguridad mejorada como ventaja clave, implementando un modelo de seguridad basado en permisos y ejecutando aplicaciones en un entorno aislado.

Implementa un modelo de seguridad basado en permisos, donde se requiere autorización explícita para acceder a recursos como el sistema de archivos y la red.

De forma predeterminada, Deno opera en modo restringido y ejecuta aplicaciones en un entorno aislado, lo que limita las acciones potencialmente riesgosas, las aísla del sistema subyacente y evita el acceso directo a recursos confidenciales.

Las auditorías de seguridad integrales y las revisiones meticulosas del código refuerzan aún más la sólida seguridad de Deno. Estas medidas le brindan una plataforma confiable y segura para crear aplicaciones, infundir confianza en su seguridad y proteger contra posibles vulnerabilidades.

Gestión de dependencias

Deno ofrece un enfoque distinto para la gestión de dependencias en comparación con los entornos de ejecución de JavaScript tradicionales como Node.js.

En lugar de depender de un registro de paquetes centralizado, Deno utiliza URL para importar módulos directamente desde la web.

Este enfoque simplifica el proceso al eliminar la necesidad de un administrador de paquetes separado como npm y mitigar las preocupaciones relacionadas con los conflictos de versiones y las complejidades de administrar la carpeta “node_modules”.

Puede especificar dependencias especificando las URL de los módulos que desean importar, lo que facilita compartir y distribuir código.

Este enfoque descentralizado para la gestión de dependencias en Deno promueve la simplicidad, reduce la fricción y ayuda a garantizar una experiencia de desarrollo más optimizada.

Compatibilidad con TypeScript lista para usar

Deno ofrece soporte nativo y perfecto para TypeScript, lo que lo convierte en una excelente opción si prefiere o necesita TypeScript en sus proyectos.

TypeScript es un superconjunto escrito de JavaScript que aporta escritura estática y otras características avanzadas del lenguaje al desarrollo de JavaScript. Con Deno, no hay necesidad de configuración adicional ni pasos de compilación para usar TypeScript.

Deno viene incluido con el compilador TypeScript, lo que le permite escribir y ejecutar código TypeScript directamente.

Este soporte nativo elimina las complejidades de configurar una cadena de herramientas TypeScript separada y simplifica el proceso de desarrollo.

Le permite aprovechar la verificación de tipos, las herramientas mejoradas y la experiencia de desarrollador mejorada de TypeScript mientras crea aplicaciones con Deno.

Limitaciones de Deno

Sin embargo, Deno tiene ciertas limitaciones que están afectando su adopción. Algunas de ellas son las siguientes.

Ecosistema inmaduro

Una de las limitaciones de Deno es la madurez de su ecosistema. En comparación con Node.js, que existe desde hace más tiempo, el ecosistema de Deno todavía es relativamente nuevo y está en evolución.

Esto significa que puede haber menos bibliotecas, marcos y herramientas de terceros diseñados específicamente para Deno. Es posible que necesite crear ciertas funcionalidades desde cero o adaptar paquetes de Node.js existentes para usarlos en Deno.

El tamaño más pequeño de la comunidad también significa que puede haber menos recursos, tutoriales y soporte comunitario disponibles en comparación con el ecosistema Node.js bien establecido.

Sin embargo, a medida que Deno gane popularidad y adopción, se espera que su ecosistema crezca y madure, ofreciendo una gama más amplia de bibliotecas y herramientas en el futuro.

Curva de aprendizaje pronunciada

Otra limitación de Deno es la curva de aprendizaje asociada con la transición desde otros entornos de ejecución de JavaScript, como Node.js.

Deno presenta nuevos conceptos, API y patrones con los que quizás necesite familiarizarse. Esto incluye comprender el sistema de módulos de Deno, el modelo de seguridad basado en permisos y las diferencias en cómo se implementan ciertas funcionalidades en comparación con otros tiempos de ejecución.

Es posible que los desarrolladores que ya dominan Node.js deban invertir tiempo y esfuerzo en aprender y adaptarse a las características y convenciones específicas de Deno.

Sin embargo, la curva de aprendizaje se puede gestionar consultando la documentación oficial de Deno, interactuando con la comunidad de Deno y explorando los recursos de aprendizaje disponibles.

Compatibilidad con las bibliotecas Node.js

La compatibilidad con las bibliotecas Node.js es otra limitación de Deno. Debido a las diferencias en los sistemas de módulos y los entornos de ejecución, no todas las bibliotecas y módulos de Node.js se pueden usar directamente en Deno sin modificaciones.

Deno usa módulos ES (módulos ECMAScript) como su sistema de módulos, mientras que Node.js tradicionalmente usa módulos CommonJS. Esta diferencia en los formatos de los módulos puede generar incompatibilidades al importar y usar módulos específicos de Node.js en Deno.

Es posible que los desarrolladores necesiten realizar ajustes o encontrar bibliotecas alternativas que estén diseñadas específicamente para funcionar con el sistema de módulos de Deno.

Si bien Deno proporciona una capa de compatibilidad para ejecutar algunos módulos de Node.js, es posible que no cubra todos los casos y es posible que se requieran modificaciones o adaptaciones manuales.

Opciones de implementación de Deno

Existen varias opciones de implementación para las aplicaciones Deno y algunas de ellas incluyen las siguientes.

Infraestructura como servicio (IaaS)

La infraestructura como servicio (IaaS) es un modelo de computación en la nube que ofrece recursos informáticos virtualizados.

Con IaaS, puede alquilar máquinas virtuales, almacenamiento y redes de proveedores de nube mediante un sistema de pago por uso.

Esto le permite configurar y administrar su propia infraestructura virtualizada sin invertir en hardware físico.

Las opciones de IaaS le permiten ejecutar aplicaciones Deno en máquinas virtuales. Las plataformas IaaS populares como AWS, Google Cloud y Microsoft Azure ofrecen soluciones flexibles y escalables, lo que le permite configurar la infraestructura de acuerdo con las necesidades específicas de su aplicación.

Sin embargo, si bien IaaS le otorga un mayor control y aislamiento de recursos, también exige una configuración y administración más manuales, lo que involucra tareas como el aprovisionamiento de servidores, actualizaciones de seguridad y monitoreo.

Por lo tanto, IaaS es una opción viable cuando necesita un control amplio sobre su infraestructura y tiene la experiencia para manejar sus complejidades de manera efectiva.

Contenedor como servicio (CaaS)

Container-as-a-Service (CaaS) es un modelo de computación en la nube que simplifica la implementación y gestión de aplicaciones en contenedores.

Con CaaS, puede concentrarse en crear e implementar aplicaciones sin preocuparse por la infraestructura subyacente.

Deno se pueden implementar dentro de contenedores, lo que garantiza coherencia y aislamiento. Los contenedores Back4app son una opción CaaS popular para la implementación de Deno.

Las plataformas CaaS ofrecen escalabilidad y aislamiento de recursos, con cada aplicación ejecutándose en su propio contenedor, lo que mejora la seguridad y la estabilidad.

La coherencia de los contenedores garantiza que las aplicaciones Deno se puedan implementar fácilmente en cualquier plataforma que admita contenedores.

Si bien las soluciones CaaS tienen una curva de aprendizaje, brindan beneficios significativos para aplicaciones que requieren escalamiento e implementación dinámicos en múltiples nodos o clústeres.

Proceso de instalación de Deno

Antes de poder utilizar Deno, debe descargarlo e instalarlo. La instalación de Deno varía según su sistema operativo.

En macOS y Linux, puede instalar Deno ejecutando el siguiente comando:

curl -fsSL <https://deno.land/x/install/install.sh> | sh

En Windows, puede instalar Deno usando Powershell ejecutando el siguiente comando:

irm <https://deno.land/install.ps1> | iex

Para confirmar que su instalación fue exitosa, puede ejecutar el siguiente comando y debería imprimir un número de versión en su terminal.

deno --version

Si no ve el número de versión, intente instalar Deno nuevamente.

Configurando un proyecto Deno

Para crear una API simple con Deno, necesitará un enrutador, un servidor y una base de datos.

Antes de seguir los pasos siguientes, cree una carpeta src en el directorio raíz de su proyecto. Esta carpeta contendrá todos los archivos fuente de su proyecto.

Paso 1: Crear un archivo de dependencia

A diferencia de Node.js, Deno utiliza administradores de paquetes como NPM o Yarn. Más bien, los paquetes se importan directamente desde su URL.

Para imitar las funciones de un archivo package.json, cree un deps.ts en el directorio raíz de su proyecto y agréguele el siguiente bloque de código.

export { Application, Router } from "https://deno.land/x/[email protected]/mod.ts";
export type { RouterContext} from "https://deno.land/x/[email protected]/mod.ts";
export { config as dotenvConfig } from "https://deno.land/x/[email protected]/mod.ts";
export { Client } from "https://deno.land/x/[email protected]/mod.ts";

El bloque de código anterior importa (instala) y exporta Application, Router, y RouterContex desde Oak. config de dotenv y Client de deno-postgres.

Paso 2: Crear un servidor

Para este paso, creará un servidor HTTP simple con Oak. Oak es un middleware para el servidor HTTP de Deno basado en Koa.js, un marco para Node.js similar a Express, pero más liviano.

Para crear un servidor HTTP con Oak, cree un archivo server.ts en su src y agréguele el bloque de código a continuación.

import { Application } from "../deps.ts";
import config from "../config/default.ts";

import router from "./router.ts";

const app = new Application();

app.use(router.routes());
app.use(router.allowedMethods());

await app.listen({ port: config.port });

El bloque de código anterior crea un servidor HTTP con Oak y registra un enrutador para manejar todo el tráfico entrante.

La línea app.use(router.routes()) registra las rutas del enrutador como middleware en la aplicación Oak. Todas las solicitudes entrantes se compararán con las rutas registradas y se ejecutarán los controladores correspondientes si se encuentra una coincidencia.

Si no se encuentra una coincidencia, la línea app.use(router.allowedMethods()) la maneja enviando respuestas apropiadas, como un 404 no encontrado o un 405 no permitido.

Paso 3: Gestión de variables ambientales

El almacenamiento de datos confidenciales, como claves API, credenciales de bases de datos, etc., en texto sin formato representa un riesgo para la seguridad.

Cualquiera que obtenga sus claves o credenciales podrá tener acceso sin restricciones a su aplicación. Esto puede resultar en pérdida y robo de datos, entre otras posibles vulnerabilidades.

Se considera una buena práctica almacenar datos confidenciales en variables ambientales para evitar situaciones como esta.

Cree un archivo .env en la carpeta raíz de su proyecto y almacene las credenciales de su base de datos y otra información confidencial en el archivo.

De esta forma:

#.env
DB_URI = <YOUR_POSTGRES_DB_URI>
PORT = 8000

Reemplace <YOUR_POSTGRES_DB_URI> con las credenciales de su base de datos.

A continuación, cree una carpeta config en la carpeta raíz de su proyecto y, en la carpeta de configuración, cree un archivo default.ts y agréguele el bloque de código a continuación.

//default.ts
import { dotenvConfig } from "../deps.ts";

dotenvConfig({
  export: true,
  path: "../.env",
});

const config = {
  db: {
    dbUri: Deno.env.get("DB_URI"),
  },
  port: 3000
};

export default config;

El bloque de código anterior recupera de forma segura los valores almacenados en su archivo .envy los expone al resto de su aplicación.

Paso 3: Conectarse a una base de datos

Para este paso, conectará su aplicación a una base de datos de Postgres. Necesitará la base de datos para almacenar y recuperar datos para su aplicación.

Cree un archivo db.ts en su carpeta src y agréguele el bloque de código a continuación.

//db.ts
import { Client } from "../deps.ts";
import config from "../config/default.ts";

let postgresConfiguration = config.db.dbUri;

const client = new Client(postgresConfiguration);

await client.connect();

export default client;

El bloque de código anterior intenta conectar su aplicación a una base de datos de Postgres utilizando el URI que proporcionó en su archivo .env.

Paso 4: Crear un repositorio de base de datos

Cree un archivo blogRepository en su carpeta src y agregue el siguiente código a su archivo.

//blogRepository.ts
import client from "./db.ts";

class BlogRepository {
  async createBlogTable() {
    const blog = await client.queryArray(
      `CREATE TABLE IF NOT EXISTS blogs (id SERIAL PRIMARY KEY, title VARCHAR(255), body VARCHAR(255), author VARCHAR(255))`
    );

    return blog;
  }

  async getAllBlogs() {
    const allBlogs = await client.queryArray("SELECT * FROM blogs");

    return allBlogs;
  }

  async getBlogById(id: string) {
    const blog = await client.queryArray(
      `SELECT * FROM blogs WHERE id = ${id}`
    );

    return blog;
  }

  async createBlog(title: string, body: string, author: string) {
    const blog = await client.queryArray(
      `INSERT INTO blogs (title, body, author) VALUES ('${title}', '${body}', '${author}')`
    );

    return blog;
  }

  async updateBlog(id: string, title: string, body: string, author: string) {
    const blog = await client.queryArray(
      `UPDATE blogs SET title = '${title}', body = '${body}', author = '${author}' WHERE id = ${id}`
    );

    return blog;
  }

  async deleteBlog(id: string) {
    const blog = await client.queryArray(`DELETE FROM blogs WHERE id = ${id}`);

    return blog;
  }
}

export default new BlogRepository();

El bloque de código anterior manejará todas las operaciones de la base de datos abstrayendo consultas SQL sin procesar y exponiendo métodos simples que puede usar para interactuar con su base de datos Postgres.

Paso 5: Crear controladores de ruta

Para este paso, creará controladores de ruta para manejar funciones CRUD simples para su aplicación. Las rutas admitidas son las siguientes:

  • GET /api/blogs: Devuelve todos los blogs de su base de datos.
  • GET /api/blog/:id: Devuelve un único blog con la identificación coincidente proporcionada en los parámetros de URL.
  • POST /api/blog/new: Crea un nuevo blog en su base de datos..
  • PUT /api/blog/:id: Actualiza un blog con la identificación coincidente proporcionada en los parámetros de URL.
  • DELETE /api/blog/:id: Elimina un blog con la identificación coincidente proporcionada en los parámetros de URL.

Cree un archivo router.ts en su carpeta src y agréguele las siguientes importaciones.

import { Router, RouterContext } from "../deps.ts";
import blogRepository from "./blogRepository.ts";

A continuación, cree una instancia de enrutador agregándole el siguiente bloque de código:

const router = new Router();

Para registrar los controladores de su enrutador, debe encadenarlos a la instancia del enrutador.

Por ejemplo (GET /api/blogs):

router
  .get("/api/blogs", async (ctx: RouterContext<"/api/blogs">) => {
    const data = await blogRepository.getAllBlogs();
    console.log(data);

    //format data
    const allBlogs = data.rows.map((blog) => {
      return {
        id: blog[0],
        title: blog[1],
        body: blog[2],
        author: blog[3]
      };
    });

    ctx.response.body = allBlogs;
  })

El bloque de código anterior crea un controlador de ruta para GET /api/blogs encadenando la lógica del controlador a la instancia del enrutador.

Encadenalas al método previamente encadenado para registrar el resto de rutas. De esta forma:

GET /api/blog/:id:

.get("/api/blog/:id", async (ctx: RouterContext<"/api/blog/:id">) => {
    try {
      const data = await blogRepository.getBlogById(ctx.params.id);
      console.log(data);

      //format data
      const blog = data.rows.map((blog) => {
        return {
          id: blog[0],
          title: blog[1],
          body: blog[2],
          author: blog[3]
        };
      });

      ctx.response.body = blog;
    } catch (error) {
      ctx.response.status = 500;
      ctx.response.body = {
        msg: "Error getting blog",
        error,
      };
    }
  })

POST /api/blog/new

.post("/api/blog/new", async (ctx: RouterContext<"/api/blog/new">) => {
    const resBody = ctx.request.body();
    const blog = await resBody.value;

    if (!blog) {
      ctx.response.status = 400;
      ctx.response.body = { msg: "Invalid data. Please provide a valid blog." };
      return;
    }

    const { title, body, author } = blog;

    if (!(title && body && author)) {
      ctx.response.status = 400;
      ctx.response.body = {
        msg: "Title or description missing. Please provide a valid blog.",
      };
      return;
    }

    try {
      await blogRepository.createBlog(title, body, author);

      ctx.response.status = 201;
      ctx.response.body = {
        msg: "blog added successfully",
      };
    } catch (error) {
      ctx.response.status = 500;
      ctx.response.body = {
        msg: "Error adding blog",
        error,
      };
    }
  })

PUT /api/blog/:id:

.put("/api/blog/:id", async (ctx: RouterContext<"/api/blog/:id">) => {
    try {
      const resBody = ctx.request.body();
      const blog = await resBody.value;

      if (!blog) {
        ctx.response.status = 400;
        ctx.response.body = {
          msg: "Invalid data. Please provide a valid blog.",
        };
        return;
      }

      const { title, body, author } = blog;

      if (!(title && body && author)) {
        ctx.response.status = 400;
        ctx.response.body = {
          msg: "Title or description missing. Please provide a valid blog.",
        };

        return;
      }

      await blogRepository.updateBlog(ctx.params.id, title, body, author);

      ctx.response.status = 200;
      ctx.response.body = {
        msg: "blog updated successfully",
      };
    } catch (error) {
      console.log(error);
      ctx.response.status = 500;
      ctx.response.body = {
        msg: "Error updating blog",
        error: error.message,
      };
    }
  })

DELETE /api/blog/:id:

.delete("/api/blog/:id", async (ctx: RouterContext<"/api/blog/:id">) => {
    await blogRepository.deleteBlog(ctx.params.id);

    ctx.response.status = 200;
    ctx.response.body = {
      msg: "blog deleted successfully",
    };
  });

Luego, exporte la instancia de su enrutador. De esta forma:

export default router;

Finalmente, modifique su archivo server.ts para crear una base de datos de blog cuando su aplicación se inicie por primera vez.

De esta forma:

import { Application } from "../deps.ts";
import config from "../config/default.ts";
import blogRepository from "./blogRepository.ts";

import router from "./router.ts";

const app = new Application();

(async () => {
  await blogRepository.createBlogTable();
})();

app.use(router.routes());
app.use(router.allowedMethods());

await app.listen({ port: config.port });

El código modificado agrega un IIFE que crea una nueva tabla de blog en su base de datos al archivo server.ts.

Implementación de su aplicación Deno en Back4app Containers

Para implementar su aplicación Deno en Back4app Containers, debe seguir los pasos a continuación:

Paso 1: Cree un Dockerfile

Un Dockerfile proporciona instrucciones específicas para crear una imagen de Docker. Estas instrucciones guían el proceso de construcción de la imagen.

Ejecute el siguiente comando para crear un Dockerfile:

touch Dockerfile

El comando anterior crea un Dockerfile en el directorio raíz de su proyecto.

A continuación, agregue el siguiente bloque de código a su Dockerfile:

FROM denoland/deno:latest

EXPOSE 8000

WORKDIR /app

COPY deps.ts .
RUN deno cache deps.ts

COPY . .

RUN deno cache src/server.ts

CMD ["run", "--allow-net", "--allow-env", "--allow-read", "src/server.ts"]

El Dockerfile anterior configura un entorno en contenedores para ejecutar una aplicación Deno.

Almacena en caché las dependencias de la aplicación y el punto de entrada, y luego ejecuta la aplicación Deno con los permisos especificados cuando se inicia el contenedor.

Se espera que la aplicación escuche en el puerto 8000, aunque la asignación del puerto real debe realizarse cuando se ejecuta el contenedor.

Finalmente, envíe su código a GitHub.

Paso 2: Cree una nueva aplicación Back4app

Para crear una aplicación Back4app, visite el sitio web oficial de Back4app. Una vez allí, ubique el botón Registrarse en la esquina superior derecha de la página de inicio de Back4app. Será dirigido a un formulario de registro al hacer clic en el botón Registrarse. Proceda a completar este formulario con los detalles requeridos, como su dirección de correo electrónico, nombre de usuario y contraseña. Asegúrese de proporcionar información precisa. Después de completar el formulario, envíelo.

Si ya tiene una cuenta, haga clic en Iniciar sesión.

Una vez que haya configurado correctamente su cuenta Back4app, inicie sesión para acceder al panel de su cuenta. Desde allí, ubique el botón “NUEVA APLICACIÓN” y haga clic en él.

Esta acción lo dirigirá a una página donde se le presentarán diferentes opciones para crear su nueva aplicación. Dado que su intención es implementar mediante contenedores, opte por la opción “Contenedores como servicio”.

Luego, conecte su cuenta de GitHub a su cuenta Back4app. Puede darle acceso a Back4app a todos los repositorios de su cuenta o a repositorios específicos.

Elija la aplicación que desea implementar, en este caso, la aplicación que creó en este tutorial, y haga clic en Seleccionar.

Choose repository to deploy

Al hacer clic en el botón Seleccionar, accederá a una página donde se le pedirá que complete cierta información sobre su aplicación, como el nombre, la rama, el directorio raíz, las opciones de implementación automática, el puerto, el estado y las variables ambientales.

Asegúrese de proporcionar todas las variables ambientales necesarias que su aplicación requiere para funcionar correctamente. Una vez que haya completado el llenado de la información requerida, proceda a hacer clic en el botón ‘Crear aplicación’.

Fill your app details

Esto iniciará el proceso de implementación y, después de un tiempo, su implementación debería estar lista. Si el proceso de implementación lleva mucho tiempo, puede consultar los registros para ver si se produjo un error con la implementación o consultar la guía de solución de problemas de Back4app.

Conclusión

En este artículo, exploró Deno, sus ventajas, limitaciones, sus opciones de implementación populares, cómo crear una aplicación con Deno e implementar su aplicación utilizando contenedores Back4app.

A pesar de sus limitaciones, debido a su modelo de seguridad, Deno puede ser ideal para crear aplicaciones muy seguras y sensibles. Además, su compatibilidad nativa con TypeScript elimina los problemas de configurar TypeScript en su proyecto.

Si sigue los pasos descritos en este artículo, podrá crear e implementar fácilmente su aplicación Deno en contenedores Back4app.


Leave a reply

Your email address will not be published.