Integrando H2 en una app con Node.Js
Introducción
H2 es un servidor de base de datos ligero escrito en Java. Puede integrarse en aplicaciones Java o ejecutarse como un servidor independiente.
En este tutorial, revisaremos por qué H2 puede ser una buena opción para sus proyectos. También aprenderemos cómo integrar H2 con Node.js mediante la construcción de una API Express simple.
Las características de H2
H2 fue construido pensando en el rendimiento.
“H2 es una combinación de: rápido, estable, fácil de usar y funciones”.
Aunque H2 se destaca principalmente porque puede integrarse en aplicaciones Java, tiene algunas características interesantes que también se aplican a su versión de servidor. Veamos algunos de ellos a continuación.
Tamaño y rendimiento
El archivo .jar utilizado para la versión del servidor es de alrededor de 2 MB. Podemos descargarlo del sitio H2, incluido con scripts y documentación adicionales. Sin embargo, si buscamos en Maven Central, podemos descargar el archivo .jar por sí solo.
El rendimiento H2 brilla en su versión integrada. Aun así, el benchmark oficial muestra que su versión cliente-servidor también es impresionante.
Bases de datos en memoria y cifrado
Las bases de datos en memoria no son persistentes. Todos los datos se almacenan en la memoria, por lo que la velocidad aumenta considerablemente.
El sitio H2 explica que las bases de datos en memoria son particularmente útiles cuando se crean prototipos o cuando se utilizan bases de datos de solo lectura.
El cifrado es otra característica útil para proteger los datos en reposo. Las bases de datos se pueden cifrar con el algoritmo AES-128.
Otras funciones útiles
H2 también proporciona un modo de clúster, la capacidad de ejecutar varios servidores y conectarlos. Las escrituras se realizan en todos los servidores al mismo tiempo, mientras que las lecturas se realizan desde el primer servidor del clúster.
H2 sorprende por su sencillez. Proporciona varias funciones útiles y es fácil de configurar.
Iniciemos un servidor H2 como preparación para las siguientes secciones:
Los argumentos que comienzan con tcp habilitan la comunicación con el servidor. El argumento ifNotExists permite que se cree la base de datos al acceder a ella por primera vez.
Descripción de la API y diagrama general
Supongamos que estamos escribiendo una API para registrar todos los exoplanetas encontrados hasta la fecha. Los exoplanetas son planetas que se encuentran fuera de nuestro Sistema Solar, orbitando otras estrellas.
Esta es nuestra definición de API simple, un CRUD para un recurso:
Esta definición, junto con el resto del código que veremos a continuación, está disponible en este repositorio de GitHub.
Así es como se verá nuestra aplicación al final de este tutorial:
A la izquierda del diagrama vemos el cliente API. Ese cliente puede ser la función “Pruébelo” del Swagger Editor, o cualquier otro cliente, como Postman o cURL.
En el otro extremo encontramos el servidor de base de datos H2, que se ejecuta en el puerto TCP 5234 como se explicó anteriormente.
Finalmente, nuestra aplicación en el medio se compone de dos archivos. El primero tendrá la aplicación Express que responderá a todas las solicitudes de API REST. Todos los puntos finales que describimos en la definición anterior se agregarán a este archivo.
El segundo archivo tendrá las funciones de persistencia para acceder a la base de datos para ejecutar las operaciones CRUD, utilizando el paquete JDBC.
Esquema de base de datos
Para almacenar el recurso Exoplanet en una base de datos H2, primero debemos escribir las funciones CRUD básicas. Comencemos con la creación de la base de datos.
Usamos el paquete JDBC para acceder a bases de datos a través de JDBC:
La función initialize () es bastante simple debido a las funciones auxiliares escritas de antemano. Crea la tabla de exoplanetas si aún no existe. Esta función debe ejecutarse antes de que nuestra API comience a recibir solicitudes. Veremos más adelante dónde hacer eso con Express.
El objeto h2 se configura con la cadena de conexión y las credenciales para acceder al servidor de la base de datos. Es más simple para este ejemplo, pero hay margen de mejora con respecto a la seguridad. Podríamos guardar nuestras credenciales en otro lugar, como variables de entorno, por ejemplo.
Además, necesitábamos agregar la ruta al archivo jar H2 en el método jinst.setupClasspath (). Esto se debe a que el paquete JDBC necesita un controlador para conectarse a H2, org.h2.Driver.
La cadena de conexión JDBC termina en / exoplanets; database_to_lower = true. Esto significa que al conectarse por primera vez se creará una base de datos llamada exoplanets. Además, los nombres de las tablas y columnas se guardarán en minúsculas. Esto simplificará la API, por lo que no será necesaria la conversión de nombres de propiedad.
La función queryDB () utiliza los métodos de la biblioteca JDBC para acceder a la base de datos. Primero, necesita reserve() una conexión a la base de datos. Los siguientes pasos son createStatement () y luego executeQuery () si se espera un resultado, o executeUpdate () de lo contrario. La conexión siempre se libera.
Todas las funciones anteriores pueden devolver un error. Para simplificar este ejemplo, todos los errores se dejan sin marcar, pero en un proyecto real deberíamos comprobarlos.
La función getH2 () devuelve un objeto que representa la base de datos. Creará ese objeto solo una vez, utilizando el mismo mecanismo que usan las clases Singleton para devolver solo una instancia siempre.
Validemos ahora los datos del usuario y permitamos que realicen operaciones CRUD.
Funciones de la base de datos CRUD
Hagamos las funciones necesarias para permitir que esta aplicación realice operaciones CRUD en exoplanets. Los agregaremos a module.exports para que podamos hacer referencia a ellos desde otros archivos fácilmente y crear un módulo auxiliar persistence.js que podamos usar:
Ambas funciones get () y getAll () consultan la base de datos para devolver uno o más exoplanetas. La API los devolverá directamente al cliente de la API.
Todas las funciones son principalmente consultas SQL, pero create () y update () merecen más explicación.
La instrucción INSERT SQL puede recibir columnas y valores separados, en la forma INSERT INTO table (column1Name) VALUES (‘column1Value’). Podemos usar el método join () para generar una cadena de columnas separadas por comas y hacer algo similar para unir todos los valores que queramos en la función create ().
La instrucción UPDATE SQL es un poco más compleja. Su forma es UPDATE table SET column1Name = ‘column1Value’. Así que necesitamos crear una nueva matriz en la función update () para almacenar los valores en este formato y join() más tarde.
Guardemos todas las funciones de la base de datos en su propio archivo, persistence.js, para que podamos agregar algo de contexto cuando llamemos a las funciones en el archivo API, así:
Esquema Joi
Como regla general, siempre debemos validar lo que envía un usuario antes de usarlo, por ejemplo, cuando el usuario intenta crear un recurso.
Algunos paquetes facilitan esta tarea. Usaremos Joi para lograr la validación.
Primero, necesitamos definir un esquema de nuestro recurso, una definición de propiedades y sus tipos. Nos recuerda la declaración SQL CREATE que definimos antes:
Cada tipo impondrá alguna validación. Por ejemplo, la propiedad del link debe parecerse a un URI y el name es required().
Posteriormente podemos validar un recurso mediante el método exoplanetSchema.validate (theObject). Este método devolverá un objeto con una propiedad de error con errores de validación si los hubiera, y una propiedad de value con el objeto procesado. Usaremos esta validación al crear y actualizar un objeto.
Para agregar robustez a nuestra API, sería bueno ignorar y descartar cualquier propiedad adicional que no esté incluida en nuestro esquema. Esto se logra en la definición anterior estableciendo la opción stripUnknown en true.
API REST con Express
Usaremos el paquete Express para crear nuestra API REST. Y como acabamos de ver, también usaremos Joi para validar recursos.
Configuremos un servidor Express normal:
La variable de la aplicación es nuestra API, vacía por ahora. Express permite extender su funcionalidad mediante el uso de middleware, funciones que pueden modificar las solicitudes y respuestas de nuestra API. En este caso, estamos usando dos middlewares.
Primero, cors () permitirá que otras aplicaciones de navegador llamen a nuestra API. Esto incluye el Swagger Editor que podemos usar para probar nuestra API más adelante. Si desea leer más sobre el manejo de CORS con Node.js y Express, lo tenemos cubierto.
En segundo lugar, agregamos el middleware express.json () para permitir el análisis de objetos JSON en el cuerpo de las solicitudes.
Agreguemos ahora algunos puntos finales a la API. Comenzaremos con post () y put (), ya que usan la validación Joi explicada en la última sección:
Express admite una función por verbo HTTP, por lo que en este caso, tenemos post () y put () como dos funciones.
En ambas funciones, el recurso se valida primero y cualquier error se devuelve al cliente API. Para mantener este código simple, solo se devuelve el primer error de validación en ese caso.
put () también comprueba si el recurso existe intentando obtenerlo de la base de datos. Actualizará el recurso solo si existe.
Con las funciones post () y put () que requieren validación fuera del camino, manejemos los métodos get () cuando los usuarios quieran echar un vistazo a los exoplanetas, así como la función delete () utilizada para eliminar un exoplaneta de la base de datos:
Una vez definidos todos los puntos finales, configuremos el puerto en el que la aplicación escuchará las solicitudes en:
La devolución de llamada anterior se llamará solo una vez al iniciar el servidor, por lo que es el lugar perfecto para initialize () la base de datos.
Conclusión
H2 es un servidor de base de datos útil, eficaz y fácil de usar. Aunque es un paquete de Java, también se ejecuta como un servidor independiente, por lo que podemos usarlo en Node.js con el paquete JDBC.
En este tutorial, definimos primero un CRUD simple para ilustrar cómo acceder a la base de datos y qué funciones están disponibles. Después de eso, definimos una API REST con Express. Esto nos ayudó a tener una idea más completa de cómo recibir recursos y guardarlos en H2.
Aunque se omitieron varios conceptos en aras de la brevedad, como autenticación y paginación, este tutorial es una buena referencia para comenzar a usar H2 en nuestros proyectos Express.