-
Notifications
You must be signed in to change notification settings - Fork 10
Comenzando el desarrollo
Tanto el proyecto que iremos desarrollando en clases como los proyectos que realizarán ustedes estarán basados en un template utilizando koa. Esto es el equivalente a un generador de aplicaciones como rails new
, que les proveerá de un set de paquetes que funcionen bien en conjunto y de una estructura de cómo y dónde escribir su código.
El template tiene una serie de archivos en el directorio raíz que configuran aspectos generales del proyecto pero que normalmente no deberían necesitar alterar. Su trabajo estará concentrado principalmente dentro del directorio src
. A continuación explicaremos cada uno de los archivos y directorios dentro de src
.
En este archivo se construye la aplicación koa y se le configuran los middlewares generales (que se aplicarán a cualquier request). Si necesitan agregar algún otro middleware de carácter general pueden hacerlo aquí.
Es el lugar en donde se declara y configura el router principal (que finalmente da lugar a un middleware configurado en app.js
), que manejará la mayor parte de los requests a la aplicación. Este router delega diferentes paths a diferentes sub-routers (que se encargarán de manejar los requests en niveles más específicos de ese path al que fueron asignados), permitiéndoles organizar su aplicación a nivel de recursos (un router por recurso).
Configuración de la base de datos a la que se conectará su aplicación.
En este directorio irán agregando todas las migraciones que sea necesario realizar en su base de datos. Una migración es, en esencia, un objeto que contiene un método up
y un método down
. El primero controlará los cambios que se ejecutarán en la base de datos, mientras que el segundo una manera de revertirlos (aunque esto no siempre será factible). Un ejemplo simple: up
crea una tabla nueva, down
hará drop
de aquella tabla.
Es importante que cada cambio que quieran realizar en la base de datos sea hecho con una migración. De esa manera se asegurarán de mantener una consistencia entre todos los desarrolladores trabajando en una aplicación y, a la vez, asegurarse de que el código que ejecuten tiene correspondencia con la estructura de base de datos que se está utilizando.
Las migraciones están identificadas por un timestamp y su aplicación se almacena en la misma base de datos, lo que permite conocer el orden en que deben aplicarse y cuáles ya han sido aplicadas o aún faltan ser aplicadas.
Estas migraciones serán provistas por Sequelize, por lo que en su documentación podrán encontrar más detalles.
Este template utiliza Sequelize para construir el modelo. Es un Object-Relational Mapping que les permitirá tener una asociación entre "clases" en el mundo JavaScript a tablas en la base de datos, y de instancias en el mundo JavaScript a tuplas en la base de datos. Además, los ORMs suelen proveer de una gran cantidad de funcionalidad para facilitar la creación, actualización, obtención y eliminado de datos sin la necesidad de escribir código SQL directamente.
En este directorio tendrán declarados todos los modelos a nivel de ORM de su aplicación.
Este es el directorio en donde vivirán los sub-routers incluidos en routes.js
y que cumplirán la función de controllers en el patrón MVC. Normalmente las entidades en su aplicación serán expuestas a nivel de recursos a nivel HTTP, teniendo principalmente dos: una instancia de una entidad y la colección de instancias de una entidad.
La sugerencia es que tengan al menos un router por cada entidad; por ejemplo, un router para todas las acciones relacionadas a una ONG o colección de ONGs. Aún si esa entidad es presentada como un "sub-recurso" de otra entidad, amerita su propio sub-router, que probablemente será configurado como un "sub-sub-router" asociado a algún path controlado por el sub-router de ONGs; por ejemplo, iniciativas son un sub-recurso de una ONG.
Las seeds están orientadas a facilitar el desarrollo de su aplicación ayudándoles, por ejemplo, a crear datos ficticios con los cuales probar su código. Si bien la estructura de las seeds es muy similar a las migrations, las primeras están orientadas a datos únicamente. Si quieren modificar la estructura de la base de datos, deberán utilizar migraciones.
Si la aplicación que construyen requiere, además de una estructura de base de datos, de algunos datos básicos (como por ejemplo ciertos roles o categorías iniciales) es recomendable tener esa generación de información en una migración, que podrán asegurar se ejecutará antes de que su código las necesite. Mantengan las seeds únicamente para desarrollo, no para ser ejecutadas en producción.
Cuando quieran que los recursos disponibles en su aplicación tengan una representación que pueda ser rendereada en browsers necesitarán de este directorio. Aquí podrán incluir todos los templates que generarán contenido HTML con ayuda de los datos provistos por sus routers. La recomendación es organizarlos en sub-directorios por cada router que tengan.
Si ya instalaste PostgreSQL sólo te queda crear la base de datos que usarás para tu aplicación (createdb <database-name>
) y configurarla en src/config/database.js
. Ten en consideración que puedes ya sea escribir directamente los datos asociados a la base de datos en ese archivo (en la sección development
si es para desarrollo) o usar environment variables (¿ves los process.env.<ENV_VARIABLE_NAME>
que están en ese archivo?). Al menos para cosas como usuarios y contraseñas, esto último es lejos lo más recomendable (no quieres tener credenciales de acceso a nada en un repositorio).
package.json
es el archivo que describe tu aplicación y sus dependencias. Verás que tiene un nombre que no se adapta mucho a tu proyecto ("template"). Así que lo mínimo que deberías hacer es cambiar ese nombre. Si quieres sacarle más provecho a ese archivo puedes también configurar quiénes son los autores de la aplicación, la ubicación del repositorio, lugar en donde reportar issues, la licencia, etc. Más detalles en la documentación oficial.
Ahora estás listo para correr la aplicación. Tan sólo ejecuta
yarn start
o, directamente
node index.js
Y la aplicación quedará corriendo en el puerto 3000, así que puedes accederla escribiendo http://localhost:3000 en tu browser.
Para desarrollo es muy útil que la aplicación que estás ejecutando se de cuenta cuando modificas archivos y no tengas que terminar y comenzar el servidor con cada cambio que hagas. Para ello está instalado nodemon
como dependencia de desarrollo (devDependencies
). Puedes correr la aplicación con monitoreo de cambios de archivos ejecutando
yarn dev
o, directamente
nodemon index.js
Tu aplicación tendrá muchos modelos, con diferentes asociaciones entre sí (1-1, 1-N, N-1, N-N). Así que vamos a crear el primero de ellos.
Un package que viene en el template y que nos ayudará en esta tarea es sequelize-cli
. Nos permitirá crear un archivo de modelo y la migración necesaria para crear la tabla correspondiente a este modelo. El comando es el siguiente:
./node_modules/.bin/sequelize model:create --name ong --attributes name:string,logo:string,email:string
Luego veremos un archivo src/models/ong.js
y src/migrations/<timestamp>-create-ong.js
. Es posible que quieras ajustar estos archivos generados por Sequelize, especialmente para agregar elementos que no es posible especificarlos desde la línea de comando (como índices, claves foráneas, restricciones como not null, etc). Luego de eso, sólo te quedará ejecutar la migración con el comando sequelize para correr todas las migraciones que aún no han sido aplicadas (en este caso, sólo la recién creada):
./node_modules/.bin/sequelize db:migrate
Y ¡listo! Ahora podemos empezar a usar el modelo en nuestra aplicación.
Aunque... la tabla ongs recién creada no tiene ningún dato. Podemos arreglar eso, al menos en desarrollo, creando una seed.
./node_modules/.bin/sequelize seed:create --name add-ongs
El archivo src/seeds/<timestamp>-add-ongs.js
tendrá métodos up
y down
vacíos, que te permitirán elegir qué y cómo crear en la base de datos. Un package muy útil para este tipo de tareas es Faker.
Como verás a continuación, éste y todos los modelos que agregues estarán a tu disposición en el "contexto del request" de tu aplicación koa, específicamente en ctx.orm.ong
.
Ahora que tenemos la entidad "Ong" en la aplicación, podemos exponer dos recursos relevantes: una colección de ONGs y una ONG en particular. Por convención, identificaremos a estos dos recursos con los paths /ongs
y /ongs/:id
(siendo :id
el identificador de base de datos de una ONG), respectivamente. Sobre estos recursos podremos ejecutar varias operaciones que podremos expresar mediante métodos HTTP:
- Colección de ONGs:
/ongs
- Obtener la colección:
GET /ongs
- Agregar una nueva ONG:
POST /ongs
(incluyendo los datos de la nueva ONG en el body del request)
- Obtener la colección:
- ONG:
/ongs/:id
- Obtener la ONG:
GET /ongs/:id
- Reemplazar o editar la ONG:
PUT /ongs/:id
oPATCH /ongs/:id
- eliminar la ONG:
DELETE /ongs/:id
- Obtener la ONG:
Como ejemplo, expongamos la colección de ONGs. Para ello, crearemos un sub-router para la entidad ONG, en el archivo src/routes/ongs.js
, y en él agregaremos la siguiente definición de ruta:
const KoaRouter = require('koa-router');
const router = new KoaRouter();
router.get('ongs', '/', async (ctx) => {
const ongs = await ctx.orm.ong.findAll();
await ctx.render('ongs/index', { ongs });
});
module.exports = router;
Ahora sólo te queda crear el archivo src/views/ongs/index.html.ejs
y generar alguna vista con el lenguaje de templating EJS, teniendo presente que la variable ongs
, arreglo de varias instancias del modelo Ong
, estará disponible para que la uses dentro del template.
Para entender todo lo relacionado a definir rutas revisa la documentación de koa-router, el package que estamos usando para ello.