express-boilerplate
Código fuente base para servidores basados en Node.js + Express + SQL.
Índice
-
express-boilerplate
- Índice
- Prestaciones
- Instalación
- Ejecución
- Filosofía
-
Uso
- Crear un fichero estático
- Crear una plantilla HTML con EJS
- Crear una plantilla CSS con EJS
- Crear un controlador
- Crear una utilidad
- Crear un modelo de dato
- Crear un modelo de dato programático
- Crear datos de migración inicial
- Crear una consulta
- Crear un comando
- Crear una configuración
- Crear librería de interfaz
- Crear componente de interfaz
- Habilitar o deshabilitar controladores
- ¿Qué más ofrece el boilerplate?
Prestaciones
- ✔ Documentación en profundidad con REFERENCE.md.
- ✔ Herramienta de línea de comandos de serie.
- ✔ Comandos de serie fácilmente ampliables.
- ✔ Variables de entorno.
- ✔ Drivers de base de datos ampliables
- ✔ Scripts de creación y migración de bases de datos mediante sistema de plantillas EJS.
- ✔ Estructuras JSON del modelo de la base de datos automáticamente extraído del SQL.
- ✔ Drivers ampliables de bases de datos para:
- ✔ SQLite
- ✔ MySQL
- ✔ Decoradores de serie y fácilmente ampliables para:
- ✔ Condicionales
- ✔ Consecuenciales
- ✔ Interceptores
- ✔ Formateadores
- ✔ Documentación autogenerada mediante javadoc.
- ✔ Subida de ficheros incluída como funcionalidad nativa.
- ✔ Interfaces desde servidor a 3 bandas:
- ✔ Caché: sistema de cacheo incorporado para páginas optimizadas.
- ✔ Plantillas EJS: sistema de plantillas para páginas que requieren lógica fresca en cada llamada.
- ✔ Ficheros estáticos: sistema de ficheros estáticos para ficheros inmutables.
- ✔ Middlewares de serie y fácilmente ampliables, basados en clases.
- ✔ Controladores de serie y fácilmente ampliables, basados en clases. Incluyen:
- ✔ ControllerForCache.js: controlador para el sistema de caché de interfaces de servidor.
- ✔ ControllerForFiles.js: controlador para ficheros estáticos.
- ✔ ControllerForIndex.js: controlador específico para el fichero index.js
- ✔ ControllerForTemplates.js: controlador para el sistema de plantillas de interfaces de servidor.
- ✔ ControllerForUploads.js: controlador para ficheros estáticos de las subidas.
- ✔ Debug.js: controlador para propósitos de debugging.
- ✔ Delete.js: controlador para eliminar instancia del sistema automático CRUD.
- ✔ Environment.js: controlador para propósitos de debugging.
- ✔ Insert.js: controlador para insertar instancia del sistema automático CRUD.
- ✔ Login.js: controlador para entrar al sistema.
- ✔ Logout.js: controlador para salir del sistema.
- ✔ Schema.js: controlador para averiguar el esquema de datos.
- ✔ Select.js: controlador para seleccionar instancias del sistema automático CRUD.
- ✔ SetFile.js: controlador para subir fichero del sistema automático CRUD.
- ✔ Update.js: controlador para actualizar instancia del sistema automático CRUD.
- ✔ Modelos de serie basados en clase y en Sequelize, autogenerados a partir del SQL.
- ✔ Sistema de queries para separar las consultas puramente del resto de lógica.
- ✔ Carpeta de recursos, con librerías que puede interesar ampliar fácilmente. Incluye:
- ✔ Librería de comprobaciones
check-that
. - ✔ Librería de parsing del SQL
hql.pegjs
y suhql.js
.
- ✔ Librería de comprobaciones
- ✔ Utilidades globales disponibles en toda la aplicación, basadas en clases, fácilmente ampliables.
- ✔ Destacablemente, ofrece un sistema REST automático propio que resuelve las 4 operaciones CRUD mediante HTTP.
- ✔ También autogenera modelos para el ORM de Sequelize.
- ✔ Los decoradores son la prestación más avanzada, con la cual puedes reusar mucha lógica, además de ahorrar mucho código.
Instalación
Primero, instalas la herramienta globalmente:
npm install -g express-boilerplate
Después, ya puedes crear proyectos desde 0, colocándote en el directorio en el que quieres empezar el proyecto, y luego simplemente con:
ebo create .
Alternativamente, también puedes descargarte el proyecto git o clonarlo directamente en el directorio que desees, que al final sería lo mismo.
Ejecución
Para ejecutarlo simplemente
npm start
Filosofía
Este proyecto es completamente minimalista.
La ejecución se basa prácticamente en 2 ficheros:
-
src/load.js
: carga toda la api. Pero no arranca el servidor. -
src/main.js
: carga toda la api y luego arranca el servidor.
Los datos se basan en 2 ficheros más:
Además, en el código original, la base de datos es SQLite, por lo cual es un proyecto compacto, que no depende de URLs/servidores externos.
Uso
En esta sección se desarrollan una serie de procesos que el usuario probablemente quiera hacer.
Crear un fichero estático
Simplemente añadiendo un fichero cualquiera bajo la carpeta src/Interface/www
.
Crear una plantilla HTML con EJS
Simplemente añadiendo un fichero *.html
bajo la carpeta src/Interface/ejs
.
Crear una plantilla CSS con EJS
Simplemente añadiendo un fichero *.css
bajo la carpeta src/Interface/ejs
.
Crear un controlador
En src/Controllers
puedes crear un fichero como éste:
module.exports = class {
method = "use";
route = "/";
priority = 5000;
getMiddleware() {
return [];
}
async dispatch(request, response, next) {
this.api.Utilities.Trace("api.Controllers.ControllerForIndex");
try {
const errorParameter = this.api.Utilities.GetRequestParameter(request, "error", false);
if(typeof errorParameter === "string") {
throw new Error(errorParameter);
}
const db = this.api.Utilities.GetDatabaseConnection();
const [{ Result: result }] = await db.Execute("SELECT 100 as 'Result';");
const [{ Result: result2 }] = await this.api.Utilities.QueryDatabase("SELECT 200 as 'Result';");
return this.api.Utilities.DispatchSuccess(response, {
message: "The API is working",
result: result + result2
});
} catch (error) {
return this.api.Utilities.DispatchError(response, error);
}
}
};
La api
(con Utilities
) se inyecta en todas las clases de controlador, una vez instanciado. También se recibe como parámetro en el constructor
.
Por cierto, el auténtico ControllerForIndex
, en cambio, redirecciona automáticamente la petición a /index.html
. En este ejemplo se quería mostrar el acceso a las utilidades desde la API inyectada en todos los controladores al igual que utilidades.
Crear una utilidad
En src/Utilities
puedes crear un fichero como éste:
module.exports = class {
action() {
this.api.Utilities.Trace("api.Utilities.GetDatabaseConnection");
return this.api.Database.Connection;
}
}
Esta utilidad es una action
, pero también puedes crear una utilidad factory
simplemente escribiendo el método factory
(y no action
) a modo de factory propiamente, o fábrica del valor final de la utilidad.
La api
(con Utilities
) se inyecta en todas las clases de utilidad, una vez instanciada. También se recibe como parámetro en el constructor
.
Crear un modelo de dato
En src/Database/Scripts/creation.sql
añades la tabla de datos que deseas.
Crear un modelo de dato programático
En src/Models
añades la clase de modelo que deseas. No tienen una interfaz obligatoria, el consenso se delega a la lógica del proyecto particular. Eso sí, se inyectará api
y se pasará al constructor.
Crear datos de migración inicial
En src/Database/Scripts/migration.sql
añades la tabla de datos que deseas.
Crear una consulta
En src/Queries/
añades una nueva clase con el método query
o factory
.
Crear un comando
Los comandos se ejecutarían mediante ebo
. Deberías ampliar los scripts del npm
también. Pero ebo {Comando}
es el que hará la función para llamar a los comandos. La carpeta de src/Commands
está pretendidamente puesta para almacenar el código de casos como estos.
Crear una configuración
En src/load.js
tienes la función setupConfigurations
, donde se establecen los valores para las variables de entorno de process.env
. Puedes añadrila ahí.
Alternativamente puedes usar el fichero src/Configurations/.env
para establecer las variables globales directamente, sin intervenir código.
Crear librería de interfaz
En src/Interface/www/lib
añades una nueva carpeta para la nueva librería.
Crear componente de interfaz
En src/Interface/www/lib/components
añades una nueva carpeta para el nuevo componente. Luego típicamente crearías un fichero con el mismo nombre pero terminado en js
para usar como componente, otro con .css
y otro con .md
para guía de uso.
Habilitar o deshabilitar controladores
Para habilitar controladores solo tienes que mover los controladores, que deben seguir la especificación de clase, en la carpeta src/Controllers
.
Para deshabilitar controladores solo tienes que mover los controladores de la carpeta src/Controllers
a la carpeta src/Controllers/Off
.
¿Qué más ofrece el boilerplate?
Hay algunas otras ventajas, como clases utilitarias, o herramientas para análisis de software.
Clases utilitarias
Unas pocas clases utilitarias:
-
AuthenticateRequest
para autentificar peticiones. Le pone elrequest.$$authentication
. -
CheckThat
para hacer comprobaciones con reporte de errores uniforme. -
CloneExceptProperties
para clonar un objeto, sin ciertas propiedades. -
CloneOnlyProperties
para clonar un objeto, solo ciertas propiedades. -
CloseDeployment
para cerrar el despliegue: conexiones, sockets, etc. -
Die
para interrumpir el proceso del sistema. Para debugging. -
DispatchError
para despachar errores en JSON. -
DispatchErrorAsHtml
para despachar errores en HTML. -
DispatchSuccess
para despachar éxitos en JSON. -
DispatchSuccessAsHtml
para despachar ficheros HTML. -
FormatRowsByGroups
para agrupar los datos resultantes de una consulta con JOINs en el SQL. -
GetDatabaseConnection
para obtener conexión de base de datos. -
GetDateFromString
para obtener Date de String. -
GetDateToString
para obtener String de Date. -
GetRandomString
para obtener String aleatorio. -
GetRequestParameter
para obtener parametro de petición. -
GetStringLeftPadded
para obtener String con espaciado por la izquierda. -
InitializeDatabase
para inicializar la base de datos. -
QueryDatabase
para ejecutar una consulta SQL. -
Trace
para tracear un método o función.
Generador de documentación
Con express-boilerplate
también tienes un generador de documentación basado en comentarios javadoc
. Solo tienes que hacer:
npm run build-documentation
Así puedes generar documentación en src/Documentation/REFERENCE.md
.
Decoradores de base de datos
Los decoradores de base de datos son una de las features estrella de «express-boilerplate». Con ellos, podemos decorar partes de la base de datos (tabla o columna, básicamente) con referencias que podemos recuperar luego en el código, y usarlas para que tenga comportamientos específicos en según qué partes del código.
Los adaptadores que actualmente están disponibles son:
- Conditionals
- Consequencials
- Formateadores
-
Interceptors/Columns
- $interceptors.Columns.es_actualizable_si_id_usuario_coincide_con
- $interceptors.Columns.fijar_fecha_actual_al_actualizar
- $interceptors.Columns.fijar_fecha_actual_al_insertar
- $interceptors.Columns.fijar_id_de_usuario_al_actualizar
- $interceptors.Columns.fijar_id_de_usuario_al_insertar
- $interceptors.Columns.fijar_valor_inicial
- $interceptors.Columns.solo_html_seguro
- Interceptors/Tables
Las sintaxis del lenguaje de metabases de datos HQL
El lenguaje HQL o Hyper-Query-Language está por detrás parseando el fichero de creación de base de datos. Con el fin de extender las funcionalidades del servidor, a la hora de capacitarlo para decisiones de seguridad, se pueden usar una serie de expresiones incrustadas como comentarios multilínea del SQL.
Estas expresiones o sintaxis son las que siguen:
- Permissions
- Restrictions
- Interceptors
A continuación se explicarán uno por uno.
La sintaxis de permissions
La sintaxis de permissions se usa como sigue:
CREATE TABLE Tabla_determinada /*
@comprobar_permiso:
al { OPERACIONES CRUD: select | insert | update | delete }
si { CONDICION }
entonces { CONSECUENCIA }
*/ ( ... );
Un ejemplo típico es cuando aplicas (un consecuencial) una prohibición cuando (sucede un condicional) no tiene un permiso.
CREATE TABLE Tabla_determinada /*
@comprobar_permiso:
al select | insert | update | delete
si $conditionals.no_tiene_permiso(data, "permiso de administración") && $conditionals.no_tiene_permiso(data, "supervisión del almacén")
entonces $consequencials.prohibir(data, "Esta operación requiere de privilegios específicos")
*/ ( ... );
Ten en cuenta que:
- Las permissions se aplican por tabla.
- Las permissions se aplican por operación CRUD.
- Las permissions se aplican según un condicional.
- Las permissions se aplican mediante un consecuencial, un interceptor, o un método predeterminado cualquiera al fin y al cabo.
La sintaxis de restrictions
La sintaxis de restrictions se usa como sigue:
CREATE TABLE Tabla_determinada (
nombre VARCHAR(500) /*
@comprobar_restriccion:
{ SUBOPERACION CRUD }
si { CONDICION }
*/
);
CREATE TABLE Tabla_determinada (
nombre VARCHAR(500) /*
@comprobar_restriccion:
no es seleccionable
si $conditionals.no_tiene_permido(data, "permiso de administración")
*/
);
Ten en cuenta que:
- Las restrictions se aplican por columna.
- Las restrictions se aplican por suboperaciones CRUD, éstas son:
- no es seleccionable
- no es filtrable
- no es ordenable
- no es insertable
- no es actualizable
- Las restrictions se aplican según un condicional.
- Las restrictions se aplican mediante una expresión de suboperación CRUD.
La sintaxis de interceptors
La sintaxis de interceptors es mucho más sencilla y permite aplicar decoradores completamente personalizados. Se usa como sigue:
CREATE TABLE Tabla_determinada /*
@interceptar: $interceptors.Tables.registrar_cambios_en(data, this, "Tabla_determinada_historial")
*/
También puede tener aplicaciones en las columnas:
CREATE TABLE Tabla_determinada (
creado_por VARCHAR(500) /*
@interceptar: $interceptors.Columns.fijar_id_de_usuario_al_insertar(data, "creado_por", this)
*/
);
- Los interceptors se aplican por tabla y/o por columna.
- Los interceptors se aplican independientemente de la operación CRUD concreta. Eso se discrimina dentro del interceptor en sí.
- Los interceptors son mucho más sencillos y personalizables que las otras categorías de decoradores de base de datos.