Hono: Framework para aplicaciones web

Cuando me enfrento a un nuevo proyecto, ya sea una API para una aplicación móvil o un backend para un sitio web, siempre busco herramientas que me hagan la vida más simple y fácil. No quiero mantener configuraciones complejas ni enfocar esfuerzos a optimizar código con un framework pesado que consuma más recursos de los necesarios. Mis prioridades son simples: quiero que sea rápido, ligero y que se integre sin problemas con Deno, que es el entorno de ejecución de TypeScript que he adoptado para casi todo.

Y ahí es donde entra Hono.

Descubrí Hono buscando una alternativa moderna a Express.js (un framework que usé durante años en el mundo de Node.js). Aunque Express es casi de facto el framework web para node.js, sentía que se estaba quedando desactualizado no encajaba del todo con la simplicidad y seguridad que me ofrecía Deno. Necesitaba algo que se sintiera más nativo, más ágil y moderno.

Lo que me convenció de Hono fue precisamente eso. Es increíblemente liviano, casi no añade overhead a mi aplicación, y su rendimiento es espectacular. Pero la verdadera joya de la corona, para mí, es su versatilidad. Aunque mi ecosistema principal es Deno, Hono me da la tranquilidad de saber que mi código es portable. Si el día de mañana decido mover una API a Cloudflare Workers para que corra en el edge (más cerca de los usuarios), o si un cliente me pide desplegarla en un servidor tradicional con Node.js, puedo hacerlo con cambios mínimos. Esa libertad es algo que valoro muchísimo.

Como vengo del mundo de Express, empezar con Hono fue muy sencillo. La forma de definir rutas, gestionar peticiones (req) y respuestas (res) y usar middlewares es tan intuitiva y familiar que no sentí que hiciera un esfuerzo adicional, es decir, si vienes del mundo Express, la curva de aprendizaje es rápida.

En este post quiero contarte, desde mi experiencia, por qué Hono se ha convertido en mi framework de cabecera para casi cualquier proyecto web que involucre un backend.

Manos a la Obra: Tu Primer Proyecto con Hono y Deno

Ya te conté por qué me gusta Hono, ahora vamos a la práctica para que veas lo rápido que es empezar.

Primero, abre tu terminal, crea una carpeta para el proyecto y entra en ella:

1
mkdir mi-proyecto-hono && cd mi-proyecto-hono

Deno tiene un comando muy práctico para inicializar proyectos que nos ahorra bastante trabajo. Usaremos el siguiente:

1
deno init

Este comando te creará una estructura básica con un main.ts y un deno.json. Ahora, para instalar Hono, agregamos la dependencia en el archivo deno.json y luego simplemente lo importamos en nuestro main.ts. Deno lo descargará y cacheará la primera vez que ejecutes el código.

Puedes agregar Hono como dependencia usando JSR de la siguiente forma, pero debes editar el archivo deno.json para corregir el alias en el import.

1
deno add jsr:@hono/hono

El corazón de tu proyecto - deno.json

Antes de ver el código, echemos un vistazo al deno.json. Aquí es donde Deno gestiona las dependencias y tareas. Para usar Hono, podemos añadirlo a la sección imports o usar la línea de comandos de deno para agregar la dependencia:

1
2
3
4
5
6
7
8
{
"imports": {
"hono": "jsr:@hono/hono@^4.10.3" // <-- ojo con el alias
},
"tasks": {
"start": "deno run --allow-net main.ts"
}
}

Un detalle importante es que, como ves, Hono está disponible en JSR (el nuevo registro de paquetes de JavaScript), lo que hace que manejar las versiones sea muy limpio.

Primer controlador

Ahora sí, el código de main.ts. Es tan simple y elegante como esto:

1
2
3
4
5
6
7
8
9
import { Hono } from "hono";

const app = new Hono();

app.get("/", (c) => {
return c.text("Hello Hono!");
});

Deno.serve(app.fetch);

¿Qué hace este código?

  1. Importa Hono.
  2. Crea una nueva instancia de la aplicación.
  3. Define una ruta para el método GET en la URL raíz (/).
  4. Cuando alguien visita esa ruta, le devuelve el texto “Hello Hono!”.
  5. Finalmente, Deno.serve inicia el servidor.

Para ponerlo en marcha, usamos la tarea que definimos en deno.json:

1
deno task start

Como Deno es seguro por defecto, te pedirá permiso para acceder a la red. ¡Y listo! Ya tienes un servidor corriendo. Si usas una herramienta como httpie, verás esto:

1
2
3
4
5
6
$ http :8000/

HTTP/1.1 200 OK
...

Hello Hono!

¿Y si quiero devolver JSON?

Fácil. Hono tiene un método específico para eso. Simplemente cambia c.text() por c.json():

1
2
3
app.get("/", (c) => {
return c.json({ message: "Hello Hono!" });
});

El resultado ahora será una respuesta JSON perfecta:

1
2
3
4
5
6
7
8
9
$ http :8000/

HTTP/1.1 200 OK
content-type: application/json; charset=UTF-8
...

{
"message": "Hello Hono!"
}

En solo unos minutos, hemos montado un servidor web funcional. Esta simplicidad y rapidez es lo que me terminó de convencer de la combinación de Hono y Deno.

¿ Cómo cambiar el puerto del servidor ?

Esto me costó pillarlo en su momento, así que dejo aquí cómo se hace:

1
Deno.serve({ port: 8080 }, app.fetch);

Revisemos algunos middlewares útiles

logger

Un middleware bien útil es el de logger, permite registrar (o loggear) lo que entra y lo que sale a Hono (obviamente depende de la ubicación en el archivo principal de la aplicación.)

1
2
3
4
5
6
import { Hono } from "hono";
import { logger } from "hono/logger";

const app = new Hono();

app.use(logger());

Con esta configuración se puede obtener este tipo de logs.

1
2
3
Listening on http://0.0.0.0:8000/ (http://localhost:8000/)
<-- GET /
--> GET / 200 0ms

etag

La cabecera o header ETag (que viene de Entity Tag o “etiqueta de entidad”) es básicamente una huella digital o un identificador único que el servidor le asigna a una versión específica de un recurso (como una página, una imagen o un archivo JSON).

Imagina que tienes un archivo en tu servidor. Cada vez que modificas y guardas ese archivo, su ETag cambia. Es como el número de versión de ese recurso en un momento exacto.

¿Y para qué sirve?

Su propósito principal es hacer que el cacheo sea mucho más eficiente y ahorrar ancho de banda. El flujo es el siguiente:

  1. Primera Visita: Cuando tu navegador pide un recurso (ej: styles.css) por primera vez, el servidor se lo envía completo y, además, incluye el header ETag con su “huella digital”. Por ejemplo: ETag: “v1-a2b3c4d5”.
  2. El Navegador Guarda la Huella: Tu navegador guarda el archivo styles.css en su caché y también anota su ETag.
  3. Siguientes Visitas: La próxima vez que necesite ese mismo archivo, el navegador no lo pide a ciegas. En su lugar, le pregunta al servidor: “Oye, tengo una versión de styles.css con la huella v1-a2b3c4d5. ¿Sigue siendo la buena?”. Esto lo hace enviando un header llamado If-None-Match: “v1-a2b3c4d5”.
  4. El Servidor Decide:
    • Si la “huella digital” del archivo en el servidor coincide con la que envió el navegador, significa que el archivo no ha cambiado. El servidor responde con un 304 Not Modified. Este mensaje es muy liviano, no contiene el archivo, y básicamente le dice al navegador: “Todo sigue igual, usa la copia que ya tienes en tu caché”.
    • Si la “huella digital” no coincide, significa que el archivo fue actualizado. El servidor responde con un 200 OK, envía la nueva versión del archivo y, por supuesto, su nuevo ETag.

En resumen, el ETag permite al navegador evitar volver a descargar recursos que no han cambiado, haciendo que la navegación sea mucho más rápida y consuma menos datos. Además ayuda a las capas superiores de la aplicación a tomar mejores decisiones respecto del cache de la aplicación.

Ahora la configuración para Hono:

1
2
3
4
5
6
import { Hono } from "hono";
import { etag } from "hono/etag";

const app = new Hono();

app.use(etag());

Finalmente esta será la respuesta donde aparece una cabecera con el etag.

1
2
3
4
5
6
7
8
9
10
11
http :8000/
HTTP/1.1 200 OK
content-length: 25
content-type: application/json
date: Tue, 28 Oct 2025 03:25:31 GMT
etag: "6e488564c25da2b4d7326d5ebfa92e957af61dcb"
vary: Accept-Encoding

{
"message": "Hello Hono!"
}

cors

Probablemente, uno de los “problemas” más comunes al empezar a desarrollar una API es toparse con un error de CORS en el navegador.

Imagina que los sitios web son como reinos amurallados. Por seguridad, un reino (tudominio.com) no puede simplemente enviar un mensajero a pedirle recursos a otro reino (api.otrodominio.com). Esta regla se llama Política del Mismo Origen (Same-Origin Policy) y es fundamental para la seguridad en la web.

El middleware de CORS de Hono es el pasaporte diplomático que nos permite configurar qué “reinos” externos tienen permiso para comunicarse con nuestra API.

Por ejemplo, una configuración de CORS muy específica y segura podría ser así:

1
2
3
4
5
6
7
8
9
10
11
import { Hono } from 'hono';
import { cors } from 'hono/cors';

app.use('/api/*', cors({
origin: 'http://example.com',
allowMethods: ['POST', 'GET', 'OPTIONS'],
allowHeaders: ['X-Custom-Header', 'Upgrade-Insecure-Requests'],
exposeHeaders: ['Content-Length', 'X-Kuma-Revision'],
maxAge: 600,
credentials: true,
}));

Analicemos las reglas que hemos definido en este bloque:

  • app.use('/api/*', ...): Primero, le indicamos a Hono que aplique este middleware únicamente a las rutas que comiencen con /api/. Esto es muy práctico para tener políticas de seguridad distintas para diferentes partes de tu aplicación.
  • origin: 'http://example.com': La regla más importante. Se especifica que solo las peticiones que vengan desde http://example.com están permitidas.
  • allowMethods: [...]: Define qué métodos HTTP están autorizados (POST, GET, OPTIONS). Un DELETE sería bloqueado.
  • allowHeaders: [...]: Una lista blanca de las cabeceras HTTP que el cliente tiene permitido enviar.
  • exposeHeaders: [...]: Permite que el código JavaScript del cliente pueda leer cabeceras de la respuesta que, por defecto, el navegador oculta por seguridad, como Content-Length.
  • maxAge: 600: Una optimización de rendimiento. Indica al navegador que puede guardar en caché el resultado de la petición de “verificación” (preflight) durante 600 segundos (10 minutos).
  • credentials: true: Esencial si tu frontend necesita enviar credenciales como cookies o cabeceras de autenticación (Authorization).

En resumen, este middleware te da un control muy granular para configurar una política de CORS segura, una práctica mucho más recomendable que simplemente abrir el acceso a todo el mundo con origin: '*'.

Palabras al Cierre

Como hemos visto en este recorrido, Hono se presenta como una alternativa muy atractiva en el ecosistema de JavaScript y TypeScript. Su filosofía minimalista, su rendimiento y, sobre todo, su capacidad para adaptarse a cualquier entorno de ejecución, lo convierten en una herramienta que vale la pena tener en nuestro arsenal.

Personalmente, la combinación de la simplicidad de Hono con la seguridad y el entorno moderno de Deno ha hecho que desarrollar APIs vuelva a ser una experiencia ágil y productiva.

Pero esto es solo el comienzo. Este artículo es el primero de una serie donde exploraremos a fondo el ecosistema de Deno y Hono. En las próximas entregas, veremos temas un poco más avanzados como:

  • Creación de nuestros propios middlewares.
  • Helpers de Hono
  • Rutas avanzadas y manejo de grupos.
  • Estrategias de testing para nuestras APIs.

Te invito a que experimentes con lo que hemos visto hoy y, por supuesto, a que te mantengas atento para los próximos artículos de la serie.


Referencias Útiles

Aquí te dejo una lista de enlaces que te serán de gran ayuda para profundizar en Hono y Deno:

Deno: ¿Una alternativa real a Node.js?

Si te mueves en el mundo de JavaScript, seguro que Node.js es tu pan de cada día. Pero, ¿has oído hablar de Deno? Y no, no es solo “Node” al revés (aunque el guiño es genial). Deno es un runtime para JavaScript y TypeScript creado por el mismísimo Ryan Dahl, el padre de Node.js.

Imagínate que creas algo increíble, pero con el tiempo te das cuenta de que podrías haberlo hecho mejor. Eso es Deno para Ryan Dahl. Es su oportunidad de redimirse y arreglar lo que él considera los “errores de diseño” de Node.js. El resultado es un entorno de ejecución más moderno, seguro y con una experiencia de desarrollo que te va a sorprender.

¿Qué hace a Deno tan especial?

¡A programar desde el minuto cero!

Una de las cosas que más me gustan de Deno es que viene con todo lo que necesitas para empezar a trabajar. ¿Cansado de configurar ESLint, Prettier, Jest y un montón de dependencias de desarrollo cada vez que empiezas un proyecto?

Deno incluye out of the box:

  • Un formateador de código (deno fmt): Para que tu código siempre luzca impecable y consistente.
  • Un linter (deno lint): Para detectar problemas y mantener la calidad del código.
  • Un runner de tests (deno test): Con soporte para tests unitarios y de integración, al estilo BDD (como Jest) o al estilo de Deno con Deno.test.
  • Soporte nativo para TypeScript: Sin necesidad de instalar tsc ni configurar tsconfig.json. Escribes en TypeScript y Deno se encarga del resto.
  • Un repositorio de librerías (JSR), Deno tiene su propio ecosistema de dependencias llamado JSR. Pero en las últimas versiones ahora soporta librerías del ecosistema de NPM.

Esto significa menos tiempo configurando y más tiempo programando. ¡Productividad al máximo desde el primer momento! Toda la configuración del entorno queda en el archivo deno.json y de esta forma es fácil de transportar, lo subes al repositorio y todo tu equipo lo usa (y lo va mejorando).

Seguridad: Tú tienes el control

Otro de los pilares de Deno es la seguridad. A diferencia de Node.js, donde un script puede acceder a tu sistema de archivos, a la red o a las variables de entorno sin que te des cuenta, Deno ejecuta el código en un sandbox.

Por defecto, un script de Deno no tiene permisos para hacer nada “peligroso”. Si necesita acceder a la red, al sistema de archivos o a cualquier otra cosa, tienes que dárselo explícitamente con flags al ejecutar el comando:

1
2
3
4
5
6
7
8
# Permite leer del sistema de archivos
deno run --allow-read mi_script.ts

# Permite conexiones de red
deno run --allow-net mi_script.ts

# ¡Permite todo! (Úsalo con cuidado)
deno run -A mi_script.ts

Este modelo de permisos explícitos te da un control total sobre lo que tus dependencias pueden y no pueden hacer, evitando sorpresas desagradables.

Gestión de dependencias

En Deno, no existe la carpeta node_modules ni el archivo package.json. Las dependencias se importan directamente desde una URL, como en el navegador:

1
import { serve } from "https://deno.land/std@0.140.0/http/server.ts";

La forma de hacer los imports con las urls en los archivos TS, a mi modo de ver, es poco mantenible. Se hicieron varios esfuerzos para que sea una vía simple de mantener, pero al parecer llegaron a algo como un administrador de dependencias centralizado en el archivo deno.json. En las últimas versiones, además, agregaron soporte para incluir dependencias del ecosistema de NPM, de esta forma, se amplió la cantidad de librerías que puedes usar.

Si vas a usar dependencias de NPM, a veces se genera un archivo package.json muy básico con el propósito de agregar dependencias solamente.

Deno descarga y cachea las dependencias la primera vez que se ejecuta el script. Si quieres asegurarte de que las dependencias no cambien, puedes usar un archivo deno.json (o deno.jsonc) para gestionar las versiones, de forma similar a como lo harías con un package.json, pero mucho más simple.

Un ejemplo rápido

La mejor forma de entenderlo es probándolo. ¡Vamos a crear un pequeño proyecto!

1. Instalación

Deno está disponible para la mayoría de los sistemas operativos. Pásate por la página de instalación, busca tu SO y sigue los pasos.

2. Inicializando el proyecto

Una vez instalado, crear un proyecto es tan fácil como esto:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ mkdir mi-proyecto-deno && cd mi-proyecto-deno
$ deno init

✅ Project initialized

Run these commands to get started

# Run the program
deno run main.ts

# Run the program and watch for file changes
deno task dev

# Run the tests
deno test

¡Y ya está! Deno te crea tres archivos:

  • deno.json: El corazón de tu proyecto, similar al package.json.
  • main.ts: Tu punto de entrada.
  • main_test.ts: Un archivo de ejemplo para tus tests.

3. El código

El archivo main.ts que se genera es súper simple:

1
2
3
4
5
6
7
8
export function add(a: number, b: number): number {
return a + b;
}

// Learn more at https://docs.deno.com/runtime/manual/examples/module_metadata#concepts
if (import.meta.main) {
console.log("2 + 3 =", add(2, 3));
}

Puedes ejecutarlo con deno run main.ts y verás en la consola:

1
2 + 3 = 5

4. Los tests

Y el archivo de test, main_test.ts, no se queda atrás en simplicidad:

1
2
3
4
5
6
import { assertEquals } from "@std/assert";
import { add } from "./main.ts";

Deno.test(function addTest() {
assertEquals(add(2, 3), 5);
});

Ejecútalo con deno test y verás que todo funciona como la seda:

1
2
3
4
5
Check file:///Users/pcollaog/mi-proyecto-deno/main_test.ts
running 1 test from ./main_test.ts
addTest ... ok (0ms)

ok | 1 passed | 0 failed (3ms)

Conclusión

Como ves, Deno no es solo un capricho. Es una propuesta seria que replantea el desarrollo del lado del servidor con JavaScript/TypeScript, poniendo el foco en la seguridad, la simplicidad y una experiencia de desarrollo moderna y sin fricciones.

Si vienes de Node.js, la curva de aprendizaje es mínima y las ventajas son enormes. Menos configuración, más seguridad y un conjunto de herramientas unificado que te hará la vida más fácil.

¿Mi recomendación? Dale una oportunidad. Puede que te sorprendas y encuentres a tu nuevo mejor amigo para tus proyectos de backend.

¿Y tú, ya has probado Deno? ¡Cuéntame tu experiencia en los comentarios!

En un próximo artículo, profundizaré en mi experiencia con los frameworks web más populares para Deno.


Referencias

conventional commits y algunas herramientas de apoyo

¿ qué es conventional commits ?

CommitLint es una herramienta que ayuda a adoptar una convención para escribir commits de forma estandarizada. esta convención se llama Conventional Commits y esta relacionada con un estándar a la hora de versionar artefactos llamada Semantic Versioning.

La especificación de Conventional Commits es una convención sobre cómo escribir los mensajes de commits (específicamente el título del commit). Además nos provee de un conjunto sencillo de reglas para crear un CHANGELOG de commits explícito; lo que hace más fácil escribir herramientas encima del historial. Esta convención encaja con SemVer, al describir en los mensajes de los commits las funcionalidades, parches y cambios de ruptura hechos.

El mensaje del commit debe ser estructurado de la siguiente manera:

1
2
3
4
5
<tipo>(ámbito opcional): <descripción>

[cuerpo opcional]

[nota(s) al pie opcional(es)]

Por ejemplo:

1
2
3
4
5
fix(api): se corrige validacion de email

Se agregan nuevos dominios a la validación del correo

Issue #2

Los tipos que más se utilizan en Conventional Commits son los siguientes:

  • feat: Se agrega una nueva característica
  • fix: Se corrige algún problema
  • chore: Se modifican archivos que rodean el código fuente (configuraciones, cambios de versión)
  • refactor: Refactorización del código
  • docs: se agrega o modifica documentación
  • test: Se agrega o modifican tests

Los tipos relevantes a la hora de generar valor en el CHANGELOG son los feat y los fix, en base a esos tipos se construye de forma automática la bitácora de cambios relevantes (útil al usar standard-version, leer mas adelante)

Instalar commitlint

1
yarn add -D @commitlint/{cli,config-conventional} 

Si estas usando commonsjs debes cambiar el export default por module.exports. debes revisar el atributo type dentro del archivo package.json.

Ejecuta el siguiente comando para crear el archivo commitlint.config.js

1
$ echo "export default { extends: ['@commitlint/config-conventional'] };" > commitlint.config.js

O agregar este archivo en la raíz del proyecto:

1
2
3
export default { 
extends: ['@commitlint/config-conventional']
};

Demás esta decir que puedes extender la configuración y acomodarla a tus necesidades, sólo considerar que las herramientas que se basan en estas convenciones también deberás adaptarlas y en otros casos es posible que no te sirvan.

husky

Husky es una herramienta que permite gestionar de forma simple git-hooks y que nos permite que cada vez que hagamos un commit, por ejemplo, corra los test unitarios o que nos revise si el commit message cumple con la convención de conventional commits (que es nuestro caso).

1
2
3
4
5
yarn add -D husky 
yarn husky init

npm pkg set scripts.commitlint="commitlint --edit"
echo "yarn commitlint \${1}" > .husky/commit-msg

El ultimo comando (el echo), genera un archivo cuyo contenido es la ejecución de commitlint y será ubicado en el directorio de los hooks de husky.

Nota: Cuando ejecutas el comando yarn husky init este te genera un directorio llamando .husky y dentro quedará un archivo llamado pre-commit con el siguiente contenido:

1
yarn test

Esto quiere decir que antes de que se realice el commit, husky ejecurata el comando yarn test y si este comando sale sin problemas, se efectuará el commit, en caso contrario los archivos no serán agregados al historial.

standard version

Otra herramienta que nos puede ayudar a mejorar la forma en que realizamos los releases, es una utilidad llamada standard-version (es algo vieja pero funciona perfectamente). Según su propia descripción:

Una utilidad para el versionamiento usando semver y generación de CHANGELOG potenciados por conventional commits.

1
yarn add -D standard-version

Una vez agregada la librería se debe agregar un comando en la sección de scripts en el package.json (sólo para mayor comodidad).

1
2
3
4
5
"scripts": {
...
"release": "standard-version",
"pre-release": "standard-version --dry-run"
}

Con eso ya puedes usar los comandos release y pre-release, el primero para concretar el release y el segundo es para ver como quedará de forma verbosa pero sin afectar el repositorio. Por ejemplo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
$ yarn pre-release

yarn run v1.22.19
$ standard-version --dry-run
✔ bumping version in package.json from 1.0.0 to 1.1.0
✔ created CHANGELOG.md
✔ outputting changes to CHANGELOG.md

---
## 1.1.0 (2024-04-22)


### Features

* se agrega busqueda de app por su id
* se agrega funcion para eliminar aplicacion
* se agrega persistencia (find,delete,create) de app
* se agrega persistencia de eventos por app
* se agrega validacion de esquema para applicacion
* se agregan controladores para aplicaciones y logs


### Bug Fixes

* **ci:** se corrige nombre de la imagen mongodb
* **ci:** se corrige script para test, falta instalar deps
* se corrigen test unitarios
* se incluye close connection
---

✔ committing package.json and CHANGELOG.md
✔ tagging release v1.1.0
ℹ Run `git push --follow-tags origin develop` to publish

Nótese que el comando de pre-release nos indica exactamente lo que hará el comando release, donde hay que resaltar la linea bumping version in package.json from 1.0.0 to 1.1.0 que indica, en función de commits messages, califican para cambio de minor (feat agregados) o patch (fix agregados). Por ejemplo… si entre versiones solo tienes commits messages con fix cuando hagas el release, lo hara modificando el dígito del patch, por otro lado, si agregas un nuevo feature (feat) te modificará el minor en la versión.

Si requieres hacer un versionamiento manual debes ejecutar el siguiente comando:

1
yarn release --releas-as 1.1.1

Con eso fuerzas a que la versión sea la que indicas como argumento.

GitFlow

Si usas git-flow como modelo de ramas, te sugiero configurar standard-version para que no realice el tag una vez que hagas el release, de esta forma le delegas dicha función a git-flow. Les dejo el archivo de configuración que se llama .versionrc y debe estar ubicado en la raíz del proyecto:

1
2
3
4
5
{
"skip": {
"tag": true
}
}

Con esta configuración, puedes lanzar los comandos de release en la rama hotfix o release y luego finalizas las rama con git-flow.

Palabras al cierre

Espero les sirva este pequeño post sobre herramientas de desarrollo que utilizo en el día a día y sus configuraciones.

Les dejo los links de las referencias:

AWS Lambda - ESM Node.js

Algunas generalidades previas

En la mayoría de los paradigmas de programación, podemos encontrar sistemas que nos permiten auto organizar el código en pequeñas piezas o para incorporar librerías/bibliotecas de terceros a nuestro código. Al combinar todo lo anterior resulta una pieza de código más grande y compleja.

Desde un inicio Javascript utilizó el sistema de carga de módulos llamado CommonsJS (CJS) y es parte integral de Node.js hasta la version v8.5.0 donde se incorpora un nuevo sistema de carga de módulos, ESM. A partir de la version v13.2.0 de Node.js fue estabilizado e incorporado como un nuevo estándar.

¿ Por qué deberíamos usar ESM en AWS Lambdas ?

James Beswick (Principal Developer Advocate for the AWS Serverless Team) escribió un artículo titulado Using Node.js ES modules and top-level await in AWS Lambda ,donde detalla el por qué y en qué casos deberías usar ESM como cargador de módulos de Javascript en el contexto de un AWS Lambda. Uno de los motivos más importantes de usar ESM, es que la carga en frío de un lambda tarda casi un tercio en comparación con CommonsJS.

Dejo aquí la comparativa entre CJS y ESM donde en el p99 (carga en frío) la partida se reduce a un tercio mejorando el rendimiento en un 43,5%. Para esta prueba, todas las métricas de ESM salieron por debajo (mejores en tiempo) de las de CJS es simplemente marginal (alrededor de 2-5ms).

¿ Cómo usamos ESM en AWS Lambdas ?

Configuración del artefacto - package.json

Primero que todo se deben ajustar un par de atributos en el archivo package.json para indicar que el módulo es del tipo ESM.

package.json
1
2
3
4
5
6
7
8
9
{
"name": "serverless-lambda-esm",
"version": "1.0.0-beta",
"description": "Serverless Lambda ESM",
"main": "src/Handler.js", // <-- Sera cargado como módulo
"license": "UNLICENSED",
"private": true,
"type": "module", // <-- Este atributo debe tener el valor: module
}

Con este ajuste le indicamos al cargador que trate los archivos como ES módulos según su extensión, es decir, con el atributo type y valor en module todos los archivos con extensión .js serán tratados como ESM. Si por alguna razón quieres mezclar ambos mundos debes hacerlo de forma explícita usando como extensión .cjs. Por el contrario, si no utilizas el atributo type o lo dejas con valor commonjs, todos los archivos con extension .js serán tratados como CJS y si quieres utilizar ESM estos deben tener extension .mjs.

Preparamos el Handler

Handler.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { DateTime } from "luxon";

/**
*
* @param {*} event
* @returns
*/
export const handler = async (event, context) => {
const date = DateTime.now().toFormat("yyyy-MM-dd");
console.log("Date: ", date);
console.log(JSON.stringify(event));

return {
statusCode: 200,
body: JSON.stringify({ message: "ok" })
};
};

El handler debe estar expuesto de esa forma para que sea cargado como ESM. Se puede apreciar que ya no usamos la sentencia require para cargar una dependencia externa (en esta caso luxon) y en su reemplazo utilizamos import.

Algunos detallitos que he ido aprendiendo en el camino y que no está muy explícito en la documentación, es que los imports de nuestros archivos, es decir, el código que está dentro del proyecto, deben ser cargados y nombrados con su extensión, dejo un ejemplo:

Handler.js
1
2
import { DateTime } from "luxon"; // <-- Sin la extension (lib)
import Service from './service/Service.js'; // <-- Con la extension

Export de los módulos

Service.js
1
2
3
4
5
6
7
8
9
10
11

const Service = {
/**
* Funcion
*/
doSomething: async(event)=>{
console.log("Event: ", JSON.stringify(event));
}
};

export default Service; // <-- Así se exponen los módulos al código

Palabras al cierre

Espero les sea útil para sus desarrollos de lambdas con Node.js. Más adelante ire dejando nuevos artículos con ejemplos e ideas para implementar con AWS Lambdas. Pase y deje su comentario.

Enlaces de interés

Escuchando Subterranean Homesick Alien del disco OK Computer

Radiohead

Adiós 2017... bienvenido 2018

Un año más, que se va,
un año más, cuántos se han ido.
Un año más, que más da,
cuántos se han ido ya…

Hernán Gallardo Pavéz

Esta canción es casi un himno patrio de fin de año y no hay fiesta donde no se toque esta canción, que dicho sea de paso, ha sido interpretada por varias bandas. Aquí les dejo un link para que lean acerca de la historia de ésta canción.

Symbiose SpA y Tecnología

Este año ha sido particularmente extraño en lo que a negocios y pega se refiere. Tuvimos elecciones presidenciales y que durante lo que dura la “incertidumbre”, los proyectos y las inversiones en tecnología se aletargar paralizan bastante. Una vez pasada la “incertidumbre” (para bien o para mal) se vuelven a activar los proyectos. En Symbiose SpA hemos sentidos esos embates del mundo económico y político aunque no tengamos que ver con ninguno de los dos.

En lo técnico quizás lo mas novedoso ha sido programar en nuevos frameworks y lenguajes. Uno de los nuevos frameworks con los que he tenido que sufrir lidiar ha sido Angular + TypeScript. Solo les puedo comentar que este último lenguaje ha sido vilipendiado de la peor forma en el trabajo y básicamente por lo odioso que es el “compilador” y los mensajes de error que salen en la consola de desarrollo del browser (sólo si es que salen). Hace mucho tiempo que no rabiaba tanto con algo que me gusta hacer.

En comparación, usando AngularJS como framework, es bastante mas legible y decente los errores que salen en la consola y los errores típicos son los dolores de cabeza que te da Javascript, que de alguna u otra forma, muchos developers tenemos interiorizados.

Sin lugar a duras en temas tecnológicos el uso de contenedores ha sido protagonista en el desarrollo de microservicios y la aceptación de esta arquitectura que viene de hace hace un par de años. En este ámbito Docker ha sido una de las plataformas que se mantiene en la pelea de la “containerización” junto a Kubernetes.

El uso que le damos a diario a estas herramientas van de la mano con Continuous Integration y Continuous Delivery evitando así HH preciadas de los desarrolladores peleando contra los servidores de aplicación, bases datos y cuanta aplicación ofrezca pelea. Para resumir este punto, que mejor que utilizar la palabra AUTOMATIZACIÓN.

Para finalizar lo técnico, sigo acumulando horas de vuelo SpringFramework y toda sus componentes orientados a crear microservicios y ambientes cloud. Cada release nueva de estos componentes traen un montón de nuevas funcionalidades que nos hace la vida mas fácil a la hora de pensar en soluciones Cloud. Kudos para los developers de SpringFramework.

Libros varios

Un hobby que había dejado totalmente en el olvido era el de leer. Tenía mi Kindle juntando polvo en algún rincón hasta que me puse la meta de leer al menos 1 libro al mes (empezando como en septiembre) y puedo decir que me fue bastante bien. Les dejo un listado de libros interesantes que pude leer o terminar de leer durante el año pasado:

Cabe destacar todos estos libros son de escritores Chilenos y que por cierto disfruté cada uno en su ámbito. El libro de Patricio Bañados es como estar tras bambalinas de TVN en los tiempos donde nuestro país paso por un periodo triste de su historia. Pero lo mas interesante es cuando superamos el periodo de la dictadura y las historias de codazos, volteretas y bajezas políticas se hacen presente en la vida del Relator.

Los libros de Ciencias de José Maza, Gabriel León y María Teresa Ruíz todos tienen la característica de un lenguaje muy simple y de fácil comprensión. Todos ellos muy entretenidos. Ciencia Pop tiene muchas anécdotas científicas y descubrimientos que se han hecho de casualidad (como la mayoría de los descubrimientos científicos). Somos polvo de estrellas y Hijos de las estrellas nos invitan a reflexionar sobre el origen del universo, sobre el origen de la vida y de qué estamos hechos, literalmente… Polvo de estrellas.

Sobre la Naturaleza del Software, también es un libro de fácil lectura con muchas anécdotas del mundo informático. También invita a reflexionar sobre la evolución de la Ingeniería Informática, los errores recurrentes en esta área, de lo joven que es esta rama de la ingeniería y las comparaciones odiosas con otras ramas. Destaca a grandes personajes que han participado en la evolución del cómo se construye software hasta el día de hoy.

En proceso de lectura aún:

Sobre “Contacto” de Carl Sagan, es un libro lleno de detalles y abundante en información de los personajes… muy muy descriptivo (a veces aburre un poco). Lo que me chocó es que uno espera una cierta similitud con la película Contacto y dicha similitud no es tal. La película la habré visto al menos una docena de veces y siempre emociona el momento en que empieza a escuchar los pulsos que provienen de la estrella Vega o al final cuando les habla apasionadamente a los niños sobre el universo. El libro es totalmente diferente o mejor dicho… la película es “basada” en la novela original de Carl Sagan.

Lo mismo me pasó con “Yo, Robot”, la película es también una de mis favoritas y con el libro no pega ni junta, aunque la trama del libro es la misma que se trata en la película y tiene que ver con el conflicto que hay con las 3 leyes de la robótica, pero en muchas situaciones diferentes a la película. El libro contiene un conjunto historias cortas que hablan de la forma en que los robots interactúan con los humanos en situaciones límite exigiendo al máximo las 3 leyes de la robótica. Interesante libro!

Alguna sugerencia de lectura para este año? déjenlo en los comentarios.

Palabras al cierre

Feliz 2018 para todos! Bienvenido 2018!

y si… voy a escribir mas este año :D

TodoList Con Node.js Y MongoDB - Parte II

Lo prometido es deuda, aquí vamos con la segunda parte donde integraremos Passport para poder validar los request y contronlar sesiones de usuarios.

Primero debemos agregar los siguientes imports al archivo app.js y la estrategia (plugin de autenticación) a utilizar. Además se agregan los archivos UserModel.js y security.js extensión del modelo de User y métodos de validación del request respectivamente:

1
2
3
4
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var security = require('./config/security');
var User = require('./model/UserModel');

Ahora configuramos Express para que pueda utilizar Passport como administrador de sesiones de usuario. Es necesario que express use su propio administrador de sesiones y además debe ser inicializado antes que el de passport.

1
2
3
4
5
6
app.use(express.cookieParser('your secret here'));
app.use(express.session());

// passport initialize
app.use(passport.initialize());
app.use(passport.session());

Luego agregamos mecanismos de autenticación, serialización y deserialización a passport:

1
2
3
passport.use(new LocalStrategy(User.authenticate()));
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());

Model

Ahora extendemos el modelo de usuario para agregar nuestros atributos (si es es necesario, por ahora lo dejaremos vacío):

1
2
3
4
5
6
7
8
9
10
var mongoose = require('mongoose');
var passportLocalMongoose = require('passport-local-mongoose');

var UserSchema = new mongoose.Schema({});

UserSchema.plugin(passportLocalMongoose);

var UserModel = mongoose.model('User', UserSchema);

module.exports = UserModel;

Seguridad

Hay un archivo que se llama security.js que contiene una función que valida si el request solicitado está autenticado por passport, si está autenticado lo “deja pasar” (next), de lo contrario lo redirecciona al template de login.

1
2
3
4
5
6
exports.ensureAuthenticated = function ensureAuthenticated(req, res, next) {
if (req.isAuthenticated()) {
return next();
}
res.redirect('/login');
};

Nos queda asegurar las urls o recursos que necesitemos con la función ensureAuthenticated en el archivo app.js. La idea es que sea lo menos intrusivo posible. Hay dos formas de hacerlo, la primera es intrusiva (y no recomendada aunque puede utilizarse con fines específicos) y se debe realizar por cada request en los archivos que contienen los Controllers:

1
2
3
4
5
6
7
exports.authenticationExample = function(req, res) {
if (req.isAuthenticated()) {
// Do something
} else {
res.redirect('/login');
}
}

La segunda es interceptando los request y validando según algún patrón, que es nuestro caso. Esta configuración esta en app.js donde se declaran las rutas a los Controllers:

1
2
app.all('/api/*', security.ensureAuthenticated);
app.all('/todos', security.ensureAuthenticated);

Con esto interceptamos todos los request que van a la API y los que van a /todos y los hacemos pasar por la función de autenticación de request. Con esto evitamos poner dichas líneas de autenticación en cada controller.

Login y Logout

Finalmente nos queda la última parte respecto de la autenticación y es configurar el login y el logout. Para el login usamos la API de passport y delegamos el trabajo de autenticación de la siguiente forma (configuración):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* Funcion sin autenticación que permite desplegar el template de Login
*/
app.get('/login', function(req, res) {
res.render('login', {});
});

/**
* Función de login que permite delegar la autenticacion en la API de
* passport, se configura además cuando la autenticación es exitosa a que url
* lo debe redireccinar y lo mismo en caso de error.
*/
app.post('/login', passport.authenticate('local', {
successRedirect: '/todos',
failureRedirect: '/login'
}));

El logout es más sencillo y lo único que hay que hacer es invalidar el request, destruir la sesión del usuario y redireccionar al login.

1
2
3
4
app.get('/logout', function(req, res) {
req.logout();
res.redirect('/login');
});

Me queda pendiente en la próxima parte del tutorial explicar cómo consumir estos servicios usando angular y desplegar los templates.

TodoList con Node.js y MongoDB - Parte I

Llevo un tiempo estudiando y haciendo algunas cosas con Node.js y me he llevado una grata impresión con esta plataforma. Al principio cuesta un poco entender el cómo funciona ya que el paradigma es muy distinto del que he venido usando durante este último tiempo.

Me animé y empece a usar Node.js para hacer algo simple y así tratar de entender el cómo funciona. Para hacer algo mas sabrosa la experiencia agregué algunos ingredientes adicionales, un framework para desarrollo web llamado Express, Mongoose para modelar los documentos que van a ir a parar a MongoDB, Passport para autenticar los request (control de usuarios y permisos), uso de templates con EJS y finalmente para la vista usamos el framework AngularJS. Como podrán ver es un stack de tecnologías basadas en Javascript.

Para iniciar voy a explicar algunas partes del código que personalmente me costó entender e implementar. Algunos de esos puntos fueron la integración con passport para la autenticación de los request, control de usuario y uso de datos en sesión.

Configuración de node.js

Cuando utilizamos nodejs siempre hay un archivo principal donde se configura toda la plataforma, en este caso el archivo es llamado app.js. Voy a ir mostrando ciertas partes del código (si quieren ver el código, esta disponible en github):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var express = require('express');

var indexController = require('./routes/IndexController');
var todosController = require('./routes/TodosController');
var registerController = require('./routes/RegisterController');

var http = require('http');
var path = require('path');
var app = express();
var db = require('./config/database');


var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;

var security = require('./config/security');
var User = require('./model/UserModel');

Controllers

Lo primero es incluir el módulo de express y luego los Controllers de la aplicación. Veamos el código de IndexController

1
2
3
4
5
exports.index = function(req, res) {
res.render('todos', {
user: req.user
});
};

Este es el Controller mas simple de la aplicación, lo único que hace es exponer mediante exports la función index cuya responsabilidad es desplegar la página principal de la aplicación. Lo demás está en el controlador de la API que será llamada desde dicha página (usando ajax).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
var Todo = require('../model/TodosModel');

/**
* Busca todos los todos para el usuario registrado
* @param {Object} req request
* @param {Object} res response
*/
function findAllTodosByUser(req, res) {
var userId = req.user.id;

Todo.find()
.where('creator')
.equals(userId)
.sort('date')
.exec(function(err, todos) {
if (err) {
res.send(err);
}
res.json(todos);
});
}

exports.allTodos = findAllTodosByUser;

/**
* Crea un todo para el usuario
* @param {Object} req request
* @param {Object} res response
* @return {Object} Lista de todos
*/
exports.createTodo = function(req, res) {
var userId = req.user.id;
var textTodo = req.body.text;

Todo.create({
text: textTodo,
done: false,
creator: userId
}, function(error, todo) {
if (error) {
res.send(error);
}
findAllTodosByUser(req, res);
});
}

/**
* Elimina un Todo por su ID
* @param {Object} req request
* @param {Object} res response
* @return {Object} Lista de todos
*/
exports.deleteTodo = function(req, res) {
Todo.remove({
_id: req.params.todo_id
}, function(error, todo) {
if (error) {
res.send(error);
}
findAllTodosByUser(req, res);
});
}

Este controlador expone la API REST con la que se comunicará la pagina principal, haciendo llamadas ajax a los distintos métodos. En esta pieza se hace un require de otro script que representa el Model, en este caso es un document para MongoDB.

Model con mongoose

Aquí mostraré como se modeló el documento principal de la aplicación Todo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var mongoose = require('mongoose');

var TodoSchema = new mongoose.Schema({
text: String,
date: {
type: Date,
default: Date.now
},
done: {
type: Boolean,
default: false
},
creator: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}
});

var TodoModel = mongoose.model('Todo', TodoSchema);

module.exports = TodoModel;

Primero se incluye el módulo de mongoose que nos permitirá modelar los documentos para MongoDB. Luego se crea un schema para modelar y se le agregan los atributos que va a poseer y sus respectivas validaciones. Por ejemplo, el atributo date es de tipo Date y valor por omisión la fecha de hoy.

Además este schema contiene una referencia a otra Collection para dejar relacionado el usuario creador en sus Todos.

Route y manejo de URLs

Finalmente para que la aplicación funcioné, hay que indicarle a node.js que las urls que se soliciten hay que enviarlas a alguien que las atienda, en este caso, los controladores y sus funciones.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
app.get('/login', function(req, res) {
res.render('login', {});
});

app.post('/login', passport.authenticate('local', {
successRedirect: '/todos',
failureRedirect: '/login'
}));

app.get('/logout', function(req, res) {
req.logout();
res.redirect('/login');
});

app.get('/', function(req, res) {
res.redirect('/login')
})

app.get('/register', registerController.index);
app.post('/register', registerController.registerUser);

app.all('/api/*', security.ensureAuthenticated);
app.all('/todos', security.ensureAuthenticated);

app.get('/todos', indexController.index);
app.get('/api/todos', todosController.allTodos);
app.post('/api/todos', todosController.createTodo);
app.delete('/api/todos/:todo_id', todosController.deleteTodo);

No hay mucho que explicar, a buen programador pocas lineas de código, las urls que ahi aparecen son ruteadas a los controladores o se implementa el callback inline, como por ejemplo las urls de login y logout. Los controller que digan security o passport, serán materia del próximo post.

Quedan algunos temas sin tocar en este capítulo, falta que revisemos la integración con AngularJS, el despliegue de los templates con EJS y la integración con passport para autenticar y autorizar los request.

Si tienen preguntas, bienvenidas sean.

Instalación de Node.js con NVM

Instalar NVM

Primero instalaremos las herramientas que nos permitan administrar distintas versiones de nodejs. Una muy buena alternativa es instalar nvm (node version manager) algo siminar a rvm (ruby version manager).

1
$ curl https://raw.github.com/creationix/nvm/master/install.sh | sh

Este script instala nvm en el directorio $HOME/.nvm , luego se deben asegurar que los scripts de nvm se inicien con el terminal o shell que usen. En mi caso uso zsh y debo agregar la siguiente linea al final del archivo .zshrc (si usas bash debes agregarla en el archivo .bashrc):

1
[[ -s $HOME/.nvm/nvm.sh ]] && . $HOME/.nvm/nvm.sh # This loads NVM

Luego reiniciar la consola o cargar tu archivo de inicio:

1
$ source ~/.zshrc 

ó

1
$ source ~/.bashrc

Comprobar la instalación de nvm

Ejecutamos los siguientes comandos para comprobar que nvm funciona correctamente.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$ nvm

Node Version Manager

Usage:
nvm help Show this message
nvm install [-s] <version> Download and install a <version>
nvm uninstall <version> Uninstall a version
nvm use <version> Modify PATH to use <version>
nvm run <version> [<args>] Run <version> with <args> as arguments
nvm ls List installed versions
nvm ls <version> List versions matching a given description
nvm ls-remote List remote versions available for install
nvm deactivate Undo effects of NVM on current shell
nvm alias [<pattern>] Show all aliases beginning with <pattern>
nvm alias <name> <version> Set an alias named <name> pointing to <version>
nvm unalias <name> Deletes the alias named <name>
nvm copy-packages <version> Install global NPM packages contained in <version> to current version

Example:
nvm install v0.4.12 Install a specific version number
nvm use 0.2 Use the latest available 0.2.x release
nvm run 0.4.12 myApp.js Run myApp.js using node v0.4.12
nvm alias default 0.4 Auto use the latest installed v0.4.x version

Instalar node.js

1
2
3
4
5
6
7
8
9
10
11
12
$ nvm ls-remote

v0.11.0
v0.11.1
v0.11.2
v0.11.3
v0.11.4
v0.11.5
v0.11.6
v0.11.7
v0.11.8
v0.11.9

##Instalar la versión v0.11.8 de node.js

1
$ nvm install v0.11.8

##Comprobar la instalación de node.js

1
2
3
$ node --version

v0.11.8

Pequeño script de ubiquity

Como todos sabrán, Ubiquity es un complemento para Mozilla Firefox que trata de acercar a la web el lenguaje natural, como por ejemplo, puedes decirle, google wikipedia y ubiquity realizara la búsqueda en Google con la palabra Wikipedia. Además tiene un pequeño recuadro de preview donde mostrara los posibles resultados de búsqueda (ver imagen):

Mas información acerca de Ubiquity en Firefox Chile.

Hoy visitando algunos sitios me encontré con que muchos de ellos publican las URL’s sin que sean un link, es decir, un texto simple al cual no se le puede hacer clic y visitar dicho link.

De ahí partió la necesidad de tener algo a la mano que me permita abrir estos pseudolinks, miré rápidamente ubiquity para ver si tenía un comando para este problema y me fue mal. Entonces me decidí a hacerlo yo mismo :D, les dejo el código para que lo enchulen mejoren a su medida o hagan sus contribuciones.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/*
* Open URL selected in browser.
*/
CmdUtils.CreateCommand({
names: ["openurl"],
icon: "http://www.mozilla.com/favicon.ico",
description: "Abre una pestaña con la url seleccionada",
help: "open + selected url",
author: {
name: "pcollaog",
email: "pcollaog[at]firefox[dot]cl"
},
license: "GPL",
homepage: "http://blog.pcollaog.cl/",
arguments: [{role: 'object', nountype: noun_arb_text}],

preview: function preview(pblock, args) {
var selectedText = args.object.text;

if (Utils.isEmpty(selectedText)){
pblock.innerHTML = "Debes seleccionar una URL.";
}else{
pblock.innerHTML = "Abrirás la siguiente URL en una nueva"
+ " pestaña:
<strong>" + selectedText + "</strong>";
}

},
execute: function execute(args) {
var selectedText = args.object.text;

// TODO: validar que la URL sea valida
if (Utils.isEmpty(selectedText)) {
return;
}

Utils.openUrlInBrowser(selectedText);
displayMessage("Se abrirá la siguiente URL: "
+ selectedText, this);
}
});

El código se divide en dos grandes partes, la función preview y la función execute, que como sus nombres lo dicen, una muestra una previsualización de la acción y la otra ejecuta la acción.

Analicemos primero la función preview:

Lo primero que hace es obtener desde el argumento la URL seleccionada asignándosela a la variable selectedText y luego valida que ésta variable no este vacía. En ambos casos (vacía o no) se le envía un mensaje al usuario en el cuadro de preview (en el cuadro de dialogo abajo).

Vamos ahora por la función execute:

Hace lo mismo que la función preview al principio, es decir, asigna el valor del argumento a una variable y valida que no este vacía.
Luego viene la parte interesante, toma la URL seleccionada y haciendo uso de la función Utils.openUrlInBrowser() nos permite abrirla en una nueva pestaña. Luego de eso mandamos una notificación al usuario sobre la acción ejecutada, para eso utilizamos la función displayMessage().

Eso seria por ahora, ahora a seguir jugando con Ubiquity. Espero sus comentarios y/o aportes.

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0"?>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<head>
<title>Ubiquity OpenURL Command</title>
<link rel="commands" href="http://site.example.cl/openurl_ubiquity.js" name="OpenURL command"/>
</head>
<body>
<p>This feed contains experimental commands that might later be included as built-in Ubiquity commands.</p>
</body>
</html>

Obviamente debes hacer las adaptaciones necesarias, como por ejemplo, las urls donde quedarán alojados los html/js.

Programando como matar un dragón

Java - Llega, encuentra al dragón, desarrolla un framework para aniquilación de dragones en múltiples capas, escribe varios artículos sobre el framework… pero no mata al dragón.

.NET - Llega, ve la idea del desarrollador de Java y la copia, intenta matar al dragón, pero el bicho se lo come

C - Llega, mira al dragón con mirada de desprecio, tira de espada, degolla al dragón, encuentra a la princesa… y la ignora para ver los últimos checkins del cvs del kernel de linux

C++ - Crea un pincho básico y va juntando funcionalidades hasta tener una espada compleja que apenas consigue entender… mata al dragón pero se atasca en medio del puente por culpa de pérdidas de memoria (memory leaks)

COBOL - Llega, ve al dragón y piensa que es demasiado viejo para conseguir matar un bicho de ese tamaño y quedarse con la princesa, y entonces se va

Pascal - Se prepara durante 10 años para crear un sistema de aniquilación de dragones… cuando llega el momento descubre que el programa sólo acepta lagartijas como entrada

VB - Monta un arma de destrucción de dragones a partir de varios componentes, salta encima del lomo del dragón, y en la hora H descubre que la espada sólo funciona durante las noches de lluvia…

PL/SQL - Recoge datos de otros matadores de dragones, crea tablas con n relaciones de complejidad ternaria, datos en tres dimensiones, OLAP, tarda quince años para procesar la información… y para entonces la princesa se volvió lesbiana.

Ruby - Llega con muchísima fama, diciendo que es el mejor en hacer cualquier cosa y cuando va a enfrentarse al dragón muestra una peliculita en la que él mismo aparece matando a un dragón… el dragón se lo come de puro aburrimiento

Smalltalk - Llega, analiza al dragón y a la princesa, se da la vuelta y se pira: ellos son muy inferiores

shell - Crea un arma poderosa para matar dragones, pero en la hora H no recuerda como usarla

shell(2)- El tío se acerca al dragón con un script de dos líneas que mata, corta, destripa, empala, pica en pedacitos y empaca al bicho, pero a la hora de ejecutarlo el script aumenta, engorda, enfurece y pone alcohol en el fuego del dragón.

Ensamblador - Cree que está haciendo lo más correcto y eficiente… pero pone un A en lugar de un D y mata a la princesa para terminar follándose al dragón

Fortran - Llega y desarrolla una solución con 45 mil líneas de código, mata al dragón, va al encuentro de la princesa… pero ella le llama tirillas y se va corriendo detrás del programador de java que era elegante y además es rico

FOX PRO - Desarrolla un sistema para matar al dragón. Por fuera es precioso y funciona, pero por dentro está todo parcheado y cuando va a ejecutar el aniquilador de dragones recuerda que olvidó indexar los DBF.

ANALISTA DE PROCESOS - Se acerca al dragón con dos toneladas de documentación desarrollada sobre el proceso de matar un dragón genérico, desarrolla un DFD para liberar a la princesa y casarse con ella, convence al dragón de que es lo mejor para el y que no va a doler. Al ejecutar el proceso estima el esfuerzo y el tamaño del daño que causará con la firma del papa, de Buda y de Joan Manuel Serrat para el plano, y entonces compra dos bombas nucleares, 45 cañones, un portaaviones y contrata a 300 hombres armados hasta los dientes… cuando en realidad tan sólo necesitaría la espada que tenía en la mano desde el principio

CLIPPER: Monta una rutina que carga un array de codeblocks para insultar al dragón, cantarle a la princesa, cargar la espada a memoria, moler al dragón, limpiar la suciedad, preparar un vaso de leche condensada con moras para la princesa, follar a la princesa, darse un baño, encender el coche, ponerle gasolina y volver para casa. A la hora de ejecutar recibe un “Bound Error: Array Access” y el dragón se lo come con patatas

Lisp, donde el famoso caballero andante, tras hablar con numerosos expertos en matar dragones y modelar el conocimiento que ellos poseen programa el sistema y se da cuenta… de que se ha dejado algún paréntesis.

HTML: Monta una web sobre espadas famosas usadas para matar dragones, pero se pasa los estándares W3C por el forro. Cuando se encara con el dragón descubre que el código no es compatible con su navegador, por lo que se queda compuesto y sin espada. El dragón se lo merienda como aperitivo.

Prolog: Piensa que para matar al dragón necesita un arma. Busca en un catálogo 182014 armas. Para cuando la princesa muere de vieja ya ha logrado descubrir como fabricar todas las armas que empiezan por la A: Armas atómicas, Alabardas,Alfanges, Asesinos contratados, Armas blancas, Antiaéreos, Arcos, …

PHP: Crea una página web que al ejecutarla eliminará al $dragón tirando de una base de datos de armas en mysql y sobre un servidor apache. Sin embargo, se olvidó el Where en la query de delete y mata a la princesa, al dragon, a los campesinos, a la bruja, al hechicero y al propio programador.

JavaScript: El programador intenta matar al gran dragón verde que lanza fuego por la boca. Crean un script que borrará al dragón cuando carge una página web para unos segundos después crear unas damiselas que lancen flores y hagan soniditos de aplausos. Por desgracia no tuvo en cuenta la estructura Dom del lagarto, también conocido como Mozilla, y lo único que consigue es rellenar su consola de errores y que el libro de mozilla narre como acabó devorado.

ActiveX: Los programadores crean un tunel para entrar a la guarida del dragón desde el castillo y ejecutar un programa que matará al dragón desde una distacia segura y prudencial. El dragón descubre el tunel, se come a los trabajadores que cavaban, a los matadores de dragones y esclaviza a todos los siervos del castillo que pasan a ser sus esclavos. El castillo pasa a ser un lugar de cría de dragones lleno crías que manda en pop ups a otros castillos. Los restos poco apetitosos de los caballeros los mete en latas de Spam y manda también a otros castillos como advertencia.

Basic: Crean un arma capaz de matar a dragones de papel, pero mucho que la perfeccionan descubren que no sirve para matar a ningún dragón más grande que una cría de caniche.

Matlab: Crean un bucle que calcula las trayectorias para lanzar una flecha gigante contra el dragón. El programa funciona perfectamente. Sólo faltan los voluntarios capaces de lanzar la flecha con la fuerza y puntería necesaria.

Programador de videojuegos:Se pasa dos años programando una espada state of the art, con shaders y todo. A la hora de matar al dragón se encuentra con que la mitad de los caballeros no tienen fuerza para mover la espada. Luego alguien programa un parche que revela las escenas de sexo con la princesa y Hillary Clinton le monta un escándalo.

Perl - El caballero decide matar al dragón con una expresión regular, pero se equivoca en los carácteres de comodín y acaba incluyendo en el patrón de mortalidad a Dragones, Iguanas, lagartos, perros, gatos, osos, princesas y ratones.

HyperCard: crea en 5 minutos una pila con un catálogo de armas, con fotos, gráficas y vídeos sobre su utilización y los distintos tipos de dragones que puede matar, y que además fabrica el arma elegida utilizando las herramientas de dibujo, con una interfaz impecable y fantásticos efectos visuales, lo guarda como ejecutable, le pone un bonito icono, pero cuando va a fabricar la espada no funciona porque se dejó un XCMD en casa (Home).

Macromedia Director: crea una mágnifica interfaz destellante mata dragones, con una espada deslumbrante, fabricada a bases de Xtras de terceros fabricantes, al primer intento de matar al dragon “Script Error”, entonces a duras penas se esquiva el mordisco, y se procede al segundo intento… pero el ejecutable va tan lento que se los come a todos.

Mathematica: se crea y modela el objeto logico dragon. se modela y crea igualmente la princesa, la espada, al principe. se modela el caso especial de la articulacion manoespada, y la discontinuidad piel de dragonespada. Cuando todo esta hecho, se le pide a Matematica que lo resuelva, y el resultado es: “Hay que matar al dragon con la espada, y quedarse con la princesa”

Escuchando Primus - “Mama Didn’t Raise No Fool”