Saltar al contenido

Deno es una poderosa herramienta con la que podemos hacer grandes cosas. En esta entrada vamos a ver cómo desarrollar un pequeño sistema MVC con Deno y Alosaur, algo sencillo pero que nos ofrezca una idea de lo que Alosaur nos ofrece.



Para llevar a cabo este sistema MVC con Deno vamos a utilizar MySQL para tener acceso a una base de datos y Handlebars para el sistema de vistas, algo sencillo que cubrirá todas las necesidades para tener la base de un proyecto MVC.

No te pierdas el curso gratuito de Deno Aprende a desarrollar una sencilla API REST con Deno


La estructura de nuestro sistema ¿¡SVC!? La siguiente será la estructura que utilizaremos para nuestro proyecto, tendremos areas, controladores, servicios, vistas, variables de entorno y el archivo de configuración de denon.


Conectar Deno y MySQL Para poder conectar con una base de datos MySQL desde Deno primero debemos crear nuestro archivo .env para almacenar variables de entorno.

DATABASE_HOST=127.0.0.1
DATABASE_PORT=3006
DATABASE_NAME=deno
DATABASE_USER=root
DATABASE_PASSWORD=

Ahora simplemente debemos crear un archivo Database.ts que nos permita configurar la conexión con MySQL y un método query.

import {Client} from "https://deno.land/x/mysql/mod.ts";
import "https://deno.land/x/dotenv/load.ts";

export class Database {
    client: Client;

    constructor() {
        this.client = new Client();
        this.client.connect({
            hostname: Deno.env.get('DATABASE_HOST'),
            db: Deno.env.get('DATABASE_NAME'),
            username: Deno.env.get('DATABASE_USER'),
            password: Deno.env.get('DATABASE_PASSWORD'),
        });
    }

    async query(sql: string) {
        return await this.client.query(sql);
    }
}

Inyección de dependencias con Deno: UserService Ahora vamos a crear un servicio llamado UserService haciendo uso de DI para utilizar nuestra clase Database.

import { Database } from '../database.ts';
import { Injectable } from 'https://deno.land/x/alosaur/mod.ts';

export interface User {
    id: number;
    name: string;
}

@Injectable()
export class UserService {
    constructor(private db: Database){}

    async getUsers(): Promise<User[]> {
        return (await this.db.query("SELECT * FROM users"));
    }
}

El decorador @Injectable nos permitirá que más adelante podamos utilizar el UserService en el constructor de nuestro Controller y que esta dependencia quede resuelta automáticamente.
Controladores en Alosaur con decoradores: HomeController Ahora crea el controlador HomeController y añade el siguiente código.

import { Controller, Get, View } from "https://deno.land/x/alosaur/mod.ts";
import {UserService} from "../../services/UserService.ts";

@Controller('/home')
export class HomeController {
    constructor(private service: UserService) {}

    @Get('/')
    async index() {
        const users = await this.service.getUsers();
        return View('index', { users });
    }
}

Utilizando el decorador @Controller le decimos a nuestra clase que se convierta en un controlador de Alosaur, inyectamos el servicio UserService en el constructor.
En el método index obtenemos utilizamos el decorador @Get('/') para decirle a este método que atienda una petición GET a la raíz, obtenemos los usuarios de base de datos y los pasamos a una vista index que todavía no tenemos.
Áreas en Alosaur, ¿módulos de Angular? Alosaur está muy enfocado al diseño de aplicaciones que utiliza Angular, controladores, módulos, servicios etcétera, lo verás a continuación. Para ello crea el archivo HomeArea y añade el siguiente código.

import { Area } from "https://deno.land/x/alosaur/mod.ts";
import { HomeController } from "./HomeController.ts";

@Area({
    controllers: [HomeController]
})
export class HomeArea {}

Y ya tenemos aquí una aplicación Angular, no bien bien, ¡pero sí se parece mucho! El motor de vistas Handlebars con Deno y Alosaur Para que todo funcione necesitamos definir las templates de Handlebar, para ello empecemos por el layout, así que crea el archivo main.hbs y añade el siguiente código.

{{> head }}

{{{ body }}}

{{> foot }}

La idea es utilizar 2 archivos parciales, el head y el foot, y dentro añadir el contenido dinámico, así que crea los archivos foot.hbs y head.hbs respectivamente.

</div>

<footer class="footer">
    <div class="container">
        <span class="text-muted">www.cursosdesarrolloweb.es</span>
    </div>
</footer>

<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
</body>

</html>
<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <title>Alosaur 🦕</title>
    <meta name="author" content="">
    <meta name="description" content="">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">
    <link rel="stylesheet" href="/statics/styles.css">
</head>

<body>
    <nav class="navbar navbar-expand-lg navbar-light bg-light">
        <a class="navbar-brand" href="#">Alosaur Framework 🦕</a>
        <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
        </button>

        <div class="collapse navbar-collapse justify-content-end" id="navbarSupportedContent">
            <ul class="navbar-nav">
                <li class="nav-item active">
                    <a class="nav-link" href="#">Inicio <span class="sr-only">(current)</span></a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="#">Login</a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="#">Registro</a>
                </li>
            </ul>
        </div>
    </nav>
    <div class="container">

Finalmente crea la vista index.hbs la cual nos permitirá mostrar los usuarios que hemos obtenido de base de datos.

<div class="card mt-3">
    <div class="card-body">
        <h5 class="card-title">Bienvenido a Alosaur 🦕</h5>
        <p class="card-text">
            ¡Un Framework para el desarrollo de aplicaciones web con Deno!
        </p>
        <ul>
            {{#each users}}
                <li>ID: {{id}} - Name: {{name}}</li>
            {{/each}}
        </ul>
        <a href="#" class="btn btn-primary">¡Haz algo grande!</a>
    </div>
</div>

Configurar TypeScript para utilizar decoradores Para poder utilizar decoradores en Deno simplemente crea en la raíz de tu proyecto un archivo tsconfig.json con el siguiente contenido.

{
  "compilerOptions": {
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  }
}

Denon, Nodemon para Deno Ahora vamos a crear un archivo denon.json que nos ayudará mucho mientras estamos desarrollando aplicaciones en Deno, gracias a Denon todos los cambios que hagamos en nuestro código serán reflejados en el navegador sin necesidad de volver a reiniciar nuestro servidor local.

{
  "scripts": {
    "start": {
      "cmd": "deno run --unstable --allow-net --allow-read --allow-env app.ts",
      "tsconfig": "tsconfig.json"
    }
  }
}

Lo único que hacemos aquí es definir un script llamado start que al ejecutarlo ejecutará lo que hay dentro de la clave cmd utilizando el archivo de configuración tsconfig.json.
Configurar el punto de entrada de Deno Para finalizar este pequeño proyecto sólo nos queda definir el archivo app.ts y añadir el siguiente código.

import { App, ViewRenderConfig } from "https://deno.land/x/alosaur/mod.ts";
import { Handlebars } from 'https://deno.land/x/handlebars/mod.ts';
import { HomeArea } from "./areas/home/HomeArea.ts";

const app = new App({
    areas: [HomeArea],
})

// Basedir path
const viewPath = 'views';

// Create Handlebars config
const handle = new Handlebars({
    baseDir: viewPath,
    extname: '.hbs',
    layoutsDir: 'layouts/',
    partialsDir: 'partials/',
    defaultLayout: 'main',
    helpers: undefined,
    compilerOptions: undefined,
});

app.useViewRender({
    type: 'handlebars',
    basePath: viewPath,
    getBody: async (path: string, model: any, config: ViewRenderConfig) => await handle.renderView(path, model),
});

app.useStatic({
    root: 'statics',
    baseRoute: '/statics/',
});

app.listen(":5000");

¡Voilà! Ya lo tenemos todo listo. En este archivo simplemente utilizamos el módulo Home, configuramos el sistema de vistas para utilizar Handlebars, le decimos a nuestra APP que los archivos estáticos estarán en el directorio statics y lanzamos el proyecto en el puerto 5000.
Para ver todo lo que hemos estado haciendo en el navegador simplemente ejecuta el siguiente comando en tu proyecto.

denon start

Espero que te haya gustado esta entrada y, sobretodo, que hayas aprendido a desarrollar un pequeño sistema MVC con Deno y Alosaur, si es así, te invito a que te registres en la plataforma para que puedas acceder a los cursos gratuitos que tenemos disponibles y estar atento a todo el material que estaremos publicando, entre ellos los nuevos cursos de Deno.

Rutas de aprendizaje