Koa js directo a la práctica
En el post anterior hablamos largo y tendido sobre este increÃble framework, te contamos que es koa js, mencionamos las caracterÃsticas que lo hacÃan una muy buena opción frente a otros frameworks, sobretodo nos centramos como funcionaba por debajo y por qué los middlewares de koa2 son una obra de arte, eso nos da una buena base, asà que te recomiendo que des una vuelta por ese post y luego regreses.
Bien, asumo que ya lo hiciste, en este post trataremos ya las funcionalidades en sà que trae koa, es decir a un nivel superior, empezando desde cero(0), hasta complicarlo de a pocos, sin más preámbulo, vamos a ello!
¿Cómo instalar koa?
Es tan sencillo como ubicarte en tu carpeta de trabajo y ejecutar:
npm install koajs --save // en npm
yarn add koa --save // en yarn
Tu package.json debe verse de esta forma:

¿Cómo se crea una aplicación básica?
Sencillamente con estas 3 lineas:
const Koa = require('koa'); // (1)
const app = new Koa(); // (2)
app.use(async ctx => { // (3)
ctx.body = 'Saludos desde eldevsin.site';
});
app.listen(3000); // (4)
Primero importamos el paquete que acabamos de instalar (1), luego creamos una instancia de koa (2), con la instancia creada podemos agregar un middleware super sencillo (3) que le devuelve un texto al cliente, para finalmente inicializar el servidor haciendo que la aplicación escuche en el puerto 3000 (4), o el puerto libre que quieras. Ten en cuenta que este es el ejemplo más sencillo que podrás encontrar, analÃzalo para pasar al siguiente punt0.
Sobre app.listen();
Es la función muy aparte que inicia la escucha del servidor, abstrae la forma en que se ejecuta un servidor con el paquete nativo http, asà que nos evita de estar haciendo lo siguiente:
const http = require('http');
const Koa = require('koa');
const app = new Koa();
http.createServer(app.callback()).listen(3000); // reemplaza esta parte
Sobre app.callback();
Ya lo vimos en el post anterior, ve y revÃsalo xD, pero en pocas palabras se encarga de devolver una función completamente compatible con la librerÃa nativa http para manejar la solicitudes del servidor.
Es más, si lo pensamos bien, podemos utilizar la funcionalidad ya programada con koa para representar el servidor en diferentes formas:
const http = require('http');
const https = require('https');
// lo mismo con koa
const Koa = require('koa');
const app = new Koa();
http.createServer(app.callback()).listen(3000); // (1)
https.createServer(app.callback()).listen(3001); // (2)
Utilizar esto en las librerÃas de http (1) o https (2), o en diferentes puertos es un juego de niños.
¿Cómo agregar un middleware en koa?
Hasta este momento ya lo deberÃas tener claro, pero si estuviste un poco despistado, no te preocupes, eso se logra a través de app.use(), de la siguiente forma:
app.use(async ctx => { // -.- si, tuvimos que volverlo a dejar claro
ctx.body = 'Saludos desde eldevsin.site';
});
¿Qué es el context(ctx) de koa?
Básicamente el context, es un objeto que tiene propiedades útiles de koa, como los objetos request o response ...¿Sólo eso? Pues no, hay muchas mas propiedades útiles que tiene este objeto, vamos a revisándolas:
- ctx.req: Objeto request de nodejs.
- ctx.res: Objeto response de nodejs.
- ctx.request: Objeto request de koajs.
- ctx.response: Objeto response de koajs.
- ctx.state: Objeto recomendado para pasar datos entre middlewares.
- ctx.app: Referencia de la instancia actual de koa.
- ctx.cookies: Instancia de paquete cookies, permite hacer  agregar/obtener cookies firmadas.
- ctx.throw: Función útil para lanzar errores en la aplicación, por defecto devuelve el status 500.
- ctx.assert: Función similar a throw, la diferencia es que assert evalúa la existencia de un valor pasado como parámetro.
¿Puedo añadirle propiedades personalizadas a ctx?
Puedes hacerlo, sin embargo es considerado un anti patrón, si tienes esa necesidad es recomendable hacerlo a través de la propiedad state, ¡que para eso fue hecha!, iremos viendo más adelante como se usa ésta y otras caracterÃsticas propias de koa.
Los alias de koa
-¿Alias...de qué hablas? apura que quiero ir a comer.
-Keep calm, vamos por la mitad recién.
Cuando nos referimos a alias estamos hablando de ciertas propiedades y métodos que pertenecen al objeto request y al objeto response que están como una vÃa de acceso rápido en el objeto context. En la siguiente imagen puedes apreciarlo mejor:

Nota que las propiedades están accesibles directamente desde context, por ejemplo body, status, length, pertenecientes a response, y por otro lado query, headers, url que pertenecen a request.
Alias de request
La lista completa de accesores agregados de solicitud es la siguiente:
ctx.header
ctx.headers
ctx.method
ctx.method=
ctx.url
ctx.url=
ctx.originalUrl
ctx.origin
ctx.href
ctx.path
ctx.path=
ctx.query
ctx.query=
ctx.querystring
ctx.querystring=
ctx.host
ctx.hostname
ctx.fresh
ctx.stale
ctx.socket
ctx.protocol
ctx.secure
ctx.ip
ctx.ips
ctx.subdomains
ctx.is()
ctx.accepts()
ctx.acceptsEncodings()
ctx.acceptsCharsets()
ctx.acceptsLanguages()
ctx.get()
Alias de response
La lista completa de accesores de respuesta es la siguiente:
ctx.body
ctx.body=
ctx.status
ctx.status=
ctx.message
ctx.message=
ctx.length=
ctx.length
ctx.type=
ctx.type
ctx.headerSent
ctx.redirect()
ctx.attachment()
ctx.set()
ctx.append()
ctx.remove()
ctx.lastModified=
ctx.etag=
Como hacer que las cosas ocurran
Después de conocer las herramientas de nivel superior que tenemos disponibles en koa, en esta sección meteremos mano a diferentes casos y preguntas que comúnmente te vas a hacer utilizando este framework, ¡empecemos!
¿Como se usa el state en koa?
Como lo comentamos anteriormente el state es un objeto visible entre middlewares, entonces por tanto podemos meter lo que quisiéramos dentro de él y que persista mientras dure la petición, éste es un ejemplo super sencillo:
...
app.use(async (ctx, next) => {
ctx.state.myCustomKey = Math.random();
await next();
});
app.use(async (ctx, next) => {
ctx.state.myCustomKey += ' - eldevsin.site';
await next();
});
app.use(async ctx => {
ctx.body = 'el valor de myCustomKey es: ' + ctx.state.myCustomKey;
});
app.listen(3000);
Estamos utilizando 3 middlewares, el primero, para crear en el state una propiedad llamada myCustomKey con un valor random, el segundo middleware se va a encargar de concatenar un texto (autobombo detected xD), y finalmente ese valor lo enviamos al cliente. El resultado será algo similar a:

¿Como manejar las cookies en koajs?
Este punto nuevamente es pan comido, para esto tenemos disponible el objeto cookies del contexto de koa(ctx.cookies), dicho objeto diene disponible la función set(), que sirve para crear cookies en el cliente de tu aplicación.
Cookies sin firmar
Como primer ejemplo crearemos una cookie sin firmar, el uso que le puedes dar a este tipo de cookie es para agregar algún indicador donde no tengas información confidencial o importante, como por ejemplo cuando necesitas saber si el usuario ya acepto las condiciones del servicio al entrar a tu web. Miremos el siguiente código:
...
app.use(async (ctx, next) => {
ctx.cookies.set('myCustomCookie', 'eldevsinsite');
await next();
ctx.body = 'algun valor';
});
app.listen(3000);
Al ingresar a tu servidor este creará una cookie y almacenará del lado del cliente, como lo podemos comprobar inspeccionando en el navegador:

La cookie que acabamos de crear no esconde/protege de ninguna manera el valor que tiene asignado.
Cookies firmados
Estas cookies son útiles cuando quieres ocultar su información, como por ejemplo id de sesiones de usuario. En el siguiente ejemplo puedes como crearlos:
...
app.use(async (ctx, next) => {
ctx.cookies.set('myCustomCookie', 'eldevsinsite', { signed: true });
await next();
ctx.body = 'algun valor';
});
app.listen(3000);
La diferencia radica en que en las opciones se pasa la propiedad signed: true, esto le indica a koa que debe firmar la cookie. El resultado es el siguiente:

¡Ey! no te asustes, es normal...pero...¿por qué sucede esto?, bien koa esta intentando encriptar la cookie, sin embargo necesita una llave para hacerlo, para lograrlo debes especificarlo en app.keys, es decir a nivel de aplicación, a esa propiedad debes asignarle un array con al menos un valor, pues actuara como semilla, de la siguiente manera:
app.keys = ['mySuperClave'];
Al inspeccionar nuevamente en el navegador veremos:

Bien, logramos firmar la cookie, pero .. ¡se esta viendo la información!, en este momento tal vez piensas que te he fallado, pero no es asÃ, te tengo noticias... lo que sucede es que este ejemplo se esta desarrollando localmente con http, por eso la información no viaja encriptada, pero en un servidor real con https se verá de la siguiente manera:

Genial, no?, tu cookie fue firmada correctamente y la transmisión de la información es completamente segura.
Obteniendo el valor de la cookie
Una vez creada la cookie, posteriormente será necesario acceder a esa información, para hacerlo existe la función get() dentro del objeto cookies, como en el siguiente ejemplo:
...
app.use(async (ctx, next) => {
ctx.cookies.set('myCustomCookie', 'eldevsinsite', { signed: true });
await next();
});
app.use(async (ctx, next) => {
ctx.body =
'siguenos en redes sociales - ' + ctx.cookies.get('myCustomCookie');
});
app.listen(3000);
Si tu cookie esta firmada o no, es lo de menos, ahora puedes obtener el valor fácilmente.
Eliminando una cookie
Para hacerlo simplemente debes decirle a koa que el valor de la cookie myCustomCookie (o la que has definido) es nulo, asÃ:
...
app.use(async (ctx, next) => {
ctx.cookies.set('myCustomCookie', null, { signed: true });
await next();
});
app.use(async (ctx, next) => {
ctx.body =
'siguenos en redes sociales - ' + ctx.cookies.get('myCustomCookie');
});
app.listen(3000);
Por tanto al inspeccionar el navegador:

¿Cómo manejo los errores en koa?
Una de las cosas mas terrorÃficas para los programadores es enterarse de un error 500 en la aplicación, sobretodo en plena madrugada, y no es para menos, sin embargo, koa nos tiene una caracterÃstica interesante para estar pendientes de este problema, pues fácilmente se puede centralizar los eventos de error, ¿cómo?, de la siguiente forma:
...
app.use(async (ctx, next) => {
ctx.body = 'suscribete en la web - eldevsin.site';
throw new Error('Wish we could turn back time'); // producimos el error
});
app.on('error', e => {
console.log('error centralizado: ', e);
});
app.listen(3000);
Obtendremos la siguiente salida al consultar al servidor:

Entonces podemos aprovechar ese evento para hace muchas cosas, como notificarte por email, por sms, por registrar esos errores en aws, o lo que se ocurra.
Ojo, pero hay un punto importante, existe una forma que evita que se ejecute el evento on('error'), y es cuando agregas un try/catch en un middleware, tomamos el mismo ejemplo pero con una versión extendida:
...
app.use(async (ctx, next) => {
try {
await next();
} catch (e) {
ctx.body = 'sucedio un error...pero lo atrapamos';
}
});
app.use(async (ctx, next) => {
ctx.body = 'suscribete en la web - eldevsin.site';
throw new Error('Wish we could turn back time'); // producimos el error
});
app.on('error', e => {
console.log('error centralizado: ', e);
});
...
Miremos el navegador a ver el resultado:

Cuando sucede el error, el try/catch lo atrapa y evita que se ejecute el escuchador de eventos de error, en cambio envÃa un mensaje personalizado al cliente, ¿cómo lo sabemos?, pues mira tu terminal...esta más que limpia.
¿Cómo obtenemos los valores de los parámetros de consulta o querystring?
Estos casos con muy comunes cuando se utilizan filtros, por ejemplo imagina que quieres listar a todos los elefantes pequeños de color morado:

Para obtener esos valores en el backend debemos apalancarnos de ctx.query:
...
app.use(async (ctx, next) => {
ctx.body = ctx.query;
});
app.listen(3000);
Lo que estamos haciendo aquà es mostrar lo que nos trae query enviándolo al cliente, ojo, es es solo por efectos del ejemplo, obteniendo el siguiente resultado:

Luego de eso podemos usar el objeto para hacer consultas a la base de datos de tu preferencia, obviamente luego de aplicar validaciones y otras cosas necesarias.
¿Cómo definir rutas?
Cuando realizamos una aplicación es importante distribuir la información en diferentes lugares de la url para organizar los servicios, en koa podemos hacerlo de dos formas, la manera dura o de la manera mas sencilla.
La manera dura
Nos referimos a la manera dura porque básicamente, se evalúa lo que trae ctx.path, el siguiente es un ejemplo super básico:
...
app.use(async (ctx, next) => {
const path = ctx.path;
let response = 'Ruta no encontrada.';
if (path.includes('home')) {
response = 'Hola desde el home.';
}
if (path.includes('contact')) {
response = 'Hola desde el contact.';
}
return (ctx.body = response);
});
...
Por defecto se devuelve que la ruta no fue encontrada, si embargo si la solicitud coincide con home o contact, se devolverá un saludo, asà será el resultado:

Ahora bien, este ejemplo no sirve de mucho si pensamos en que en una ruta puedes posiblemente repetir palabras o tener incluso todas ellas, entonces para eso tendrÃamos que hacer coincidir las cosas con expresiones regulares y muchas más validaciones. Awww, shit, muchos dolores de cabeza si utilizas este camino.
La forma sencilla (y recomendada)
Para evitar una catástrofe en tu código, es mejor utilizar una librerÃa que junto con koa la hacen linda, nos referimos a koa-router, con esta librerÃa podemos construir módulos de rutas y devolver un middleware para inyectarlo a koa.
Una vez entendido esa parte, empezaremos instalando koa-router:
npm install koa-router --save // con npm
yarn add koa-router --save // con yarn
Tu package.json deberÃa tener un aspecto similar a:

El siguiente punto es importar la dependencia y crear una instancia del enrutador:
const Koa = require('koa');
const Router = require('koa-router');
const router = new Router();
Creando las rutas
Los métodos mas utilizados para realizar las consultas http son: GET, POST, PUT, DELETE, existen otros más que por ahora no vamos a tener en cuenta, y todos ellos están disponibles en koa-router, bien, buscaremos obtener el mismo resultado que realizamos en la forma anterior, de la siguiente manera:
const Koa = require('koa');
const Router = require('koa-router');
const app = new Koa();
const router = new Router();
router.get('/home', async ctx => { // 1
ctx.body = 'Hola desde el home.';
});
router.get('/contact', async ctx => { // 2
ctx.body = 'Hola desde el contact.';
});
app.use(router.routes()); // 3
app.use(async ctx => { // 4
return (ctx.body = 'Ruta no encontrada.');
});
app.listen(3000);
En resumen lo que estamos haciendo en el código anterior es:
Le decimos al enrutador(router) que queremos definir dos rutas /home(1) y /contact(2), ambos son accedidos mediante el método GET, luego el enrutador le pasa a koa todas las rutas mediante un middleware(3), finalmente, si no llega a coincidir ninguna ruta pasa al último middleware que devuelve al cliente "ruta no encontrada"(4).
Si bien hemos visto implementar rutas solo con koa es una tarea complicada para nosotros la mayorÃa de mortales, la ayuda de koa-router es crucial, pues permite hacerlo de una forma muy intuitiva.
Por ahora dejaremos hasta allà el post para no hacerlo más extenso, ha sido una jornada maratónica pero con mucho valor por las maravillosas cosas que puedes empezar a hacer con koa y también considerarla como buena alternativa frente a express js, meteor js, totaljs, entre otras.
Via GIPHY
Si consideras que este post te ha aportado mucho valor, compártelo con tus amigos para que ellos también lo aprovechen, deja tu comentario pues amamos saber lo que piensas, y sÃguenos en nuestras redes sociales, nos vemos en una próxima entrega.