{"id":46321739,"url":"https://github.com/lortmorris/universal-pattern","last_synced_at":"2026-03-04T15:16:29.907Z","repository":{"id":51746472,"uuid":"135940903","full_name":"lortmorris/universal-pattern","owner":"lortmorris","description":"Universal Pattern is a easy way for create API, Swagger and works with MongoDB","archived":false,"fork":false,"pushed_at":"2024-07-23T12:50:31.000Z","size":5685,"stargazers_count":7,"open_issues_count":0,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-10-03T22:57:07.935Z","etag":null,"topics":["api","mongodb","mongodb-query","node-module","nodejs","pattern","rest-api","restful-webservices","swagger","universal","universal-pattern"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lortmorris.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["lortmorris"]}},"created_at":"2018-06-03T21:10:52.000Z","updated_at":"2024-11-17T14:46:38.000Z","dependencies_parsed_at":"2024-01-26T16:44:48.133Z","dependency_job_id":"dc9e8127-e751-4f79-afb0-b371cd75fba1","html_url":"https://github.com/lortmorris/universal-pattern","commit_stats":null,"previous_names":["visiongroupnyc/universal-pattern","lortmorris/universal-pattern"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/lortmorris/universal-pattern","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lortmorris%2Funiversal-pattern","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lortmorris%2Funiversal-pattern/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lortmorris%2Funiversal-pattern/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lortmorris%2Funiversal-pattern/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lortmorris","download_url":"https://codeload.github.com/lortmorris/universal-pattern/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lortmorris%2Funiversal-pattern/sbom","scorecard":{"id":599320,"data":{"date":"2025-08-11","repo":{"name":"github.com/lortmorris/universal-pattern","commit":"3fb4a8fe09359d62abe274374a23e8761ed49e53"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Code-Review","score":0,"reason":"Found 0/9 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 27 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-20T23:58:37.982Z","repository_id":51746472,"created_at":"2025-08-20T23:58:37.983Z","updated_at":"2025-08-20T23:58:37.983Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30084686,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-04T13:22:36.021Z","status":"ssl_error","status_checked_at":"2026-03-04T13:20:45.750Z","response_time":59,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","mongodb","mongodb-query","node-module","nodejs","pattern","rest-api","restful-webservices","swagger","universal","universal-pattern"],"created_at":"2026-03-04T15:16:29.097Z","updated_at":"2026-03-04T15:16:29.896Z","avatar_url":"https://github.com/lortmorris.png","language":"JavaScript","funding_links":["https://github.com/sponsors/lortmorris"],"categories":[],"sub_categories":[],"readme":"![Universal pattern](docs/assets/logo.png)\n\nPowered by [Cesar Casas](https://www.linkedin.com/in/cesarcasas)\n\n\n# Contenido\n\n- [Contenido](#contenido)\n- [Universal Pattern 💻](#universal-pattern-)\n\t- [📚 Características Destacadas](#-características-destacadas)\n\t\t- [Definición Sencilla de Módulos y Endpoints](#definición-sencilla-de-módulos-y-endpoints)\n\t\t- [Integración Automatizada con Swagger para Documentación y Pruebas](#integración-automatizada-con-swagger-para-documentación-y-pruebas)\n\t\t- [Automatización de Validaciones y Control de Parámetros](#automatización-de-validaciones-y-control-de-parámetros)\n\t- [📋 Requerimientos](#-requerimientos)\n- [💻 Instalación](#-instalación)\n- [👨‍💼 Primer modulo](#-primer-modulo)\n\t- [Crear directorios](#crear-directorios)\n\t- [Creando module yaml](#creando-module-yaml)\n\t- [Creamos app.js](#creamos-appjs)\n\t- [Ejecutando!](#ejecutando)\n- [Preguntas frecuentes](#preguntas-frecuentes)\n\t- [👨‍💻 Arquitectura y Flujo de Trabajo](#-arquitectura-y-flujo-de-trabajo)\n\t- [👨‍💻 Personalización y Extensibilidad](#-personalización-y-extensibilidad)\n\t- [🛡️ Seguridad y Autenticación](#️-seguridad-y-autenticación)\n\t- [👨‍💻 Rendimiento y Escalabilidad](#-rendimiento-y-escalabilidad)\n\t- [👨‍💻 Soporte y Comunidad](#-soporte-y-comunidad)\n- [x-swagger-properties](#x-swagger-properties)\n\t- [x-swagger-public-fields](#x-swagger-public-fields)\n\t- [x-swagger-skip-fields](#x-swagger-skip-fields)\n\t- [x-swagger-router-controller](#x-swagger-router-controller)\n- [Routes out UP Scope.](#routes-out-up-scope)\n- [Cache](#cache)\n\t- [Midiendo la performance con cache activado:](#midiendo-la-performance-con-cache-activado)\n- [Stats.](#stats)\n- [Clustering.](#clustering)\n- [Performance testing](#performance-testing)\n- [Ejemplo](#ejemplo)\n- [Test](#test)\n- [Changelog](#changelog)\n- [License](#license)\n\n\n# Universal Pattern 💻\n![Universal pattern](docs/assets/universal-pattern-features.png)\n\nUniversal Pattern es una librería que permite de una forma muy simple crear microservicios y endpoint utilizando [Node.js](https://nodejs.org), [Swagger](https://editor.swagger.io) y [MongoDB](https://www.mongodb.com/).\n\nEl concepto principal es crear archivos `yaml` que denerán estar alojados en el directorio `swagger`.\nCada archivo `yaml` representa un módulo para Universal Pattern.\nPor ejemplo, si queremos crear un ABM de `brands`, debemos crear un archivo llamando `brands.yaml` dentro del directorio swagger.\n\nDentro de la definición del archivo swagger, estableceremos los distintos endpoints (ruta + método http).\nPodemos definir que tipo de datos de entrada necesitamos, y cual será el dato de salida.\n\nComo ya se estará dando cuenta, el propócito de Universal Pattern es poder definir módulos y que los mismos funcionen, sin necesidad de programación adicional (es decir, no tener que escribir el código de los módulos).\n\n## 📚 Características Destacadas\n\n- Alta velocidad de desarrollo\n- Documentar es darle vida a los endpoints\n- Validaciones\n- Swagger con esteroides!\n- Poder probar los endpoints en el mismo servicio! solo accediendo al directorio `/docs`\n\n\n### Definición Sencilla de Módulos y Endpoints\n\nUniversal Pattern permite a los desarrolladores definir módulos y endpoints de manera sencilla y eficiente a través de archivos YAML. Esta característica reduce significativamente la complejidad y el tiempo necesario para configurar nuevos servicios y rutas. Al no requerir programación adicional para la creación de estos módulos, facilita enormemente el proceso de desarrollo, especialmente para aquellos que no son expertos en Node.js o MongoDB.\n\n```yaml\npaths:\n  /cars:\n    get:\n      tags:\n        - cars\n      summary: cars list\n      x-swagger-router-controller: universal.search\n      parameters:\n        - $ref: '#/parameters/q'\n        - $ref: '#/parameters/page'\n        - $ref: '#/parameters/sorting'\n        - $ref: '#/parameters/limit'\n        - $ref: '#/parameters/fields'\n\n      responses:\n        '200':\n          description: cars list\n          schema:\n            $ref: '#/definitions/car'\n```\n\n\n### Integración Automatizada con Swagger para Documentación y Pruebas\n\nLa herramienta integra de manera nativa la documentación y las pruebas de endpoints a través de Swagger. Esto significa que los desarrolladores pueden generar y actualizar la documentación de su API de forma automática, así como probar los endpoints directamente desde la interfaz de Swagger. Esta integración elimina la necesidad de herramientas o procesos adicionales para la documentación y prueba de APIs, lo que simplifica considerablemente el mantenimiento y la gestión de la API.\n\n![Universal pattern](docs/assets/swagger-ui.png)\n\n### Automatización de Validaciones y Control de Parámetros\n\nUniversal Pattern maneja automáticamente las validaciones y el control de parámetros para los endpoints definidos. Esto reduce la carga de tener que escribir y mantener código adicional para la validación de datos, asegurando que los datos entrantes cumplan con los requisitos especificados en los archivos YAML. Esta automatización ayuda a prevenir errores comunes y mejora la robustez de la aplicación sin esfuerzo adicional por parte del desarrollador.\n\n```yaml\ndefinitions:\n  feedInput:\n    type: object\n    properties:\n      body:\n        type: string\n        required: true\n        minLength: 4\n      rate:\n        type: integer\n        required: true\n        max: 5\n        min: 1\n        decimals: 0\n      userId:\n        type: string\n        format: mongoId\n        x-swagger-lookup:\n          collection: users\n          populate:\n            - _id\n            - firstname\n            - lastname\n            - avatar\n      carId:\n        type: string\n        format: mongoId\n        x-swagger-lookup:\n          collection: cars\n          populate:\n            - _id\n            - name\n            - color\n            - brands.name\n```\n\n## 📋 Requerimientos\nAntes de comenzar a trabajar con Universal Pattern, debemos tener instalado previamente\n\n- Node.js (version 18 o superior)\n- MongoDB 6 o superior\n\n# 💻 Instalación\nRecomendamos utilizar Universal Pattern desde un entorno Linux, aunque funcionará sin problemas en entornos Windows y MacOS.\n\n```bash\n$ npm install universal-pattern --save\n```\n\nEs importante entender que para trabajar con Universal Pattern deberemos crear una estructura básica de archivos y directorios.\n\n```\nSuProyecto/\n\t├ swagger/\t\t\t# Directorio donde guardaremos nuestros archivos yaml (módulos)\n\t├ controllers/\t\t# Directorio donde se guardan los controladores customizados\n\t├ hooks/\t\t\t# Directorio donde se guarda los hooks customizados\n\t├ app.js\t\t\t# nuestro archivo de aplicación\n\t├ package.json\n```\n\n# 👨‍💼 Primer modulo\n🎉 Vamos a crear tu primer módulo con Universal Pattern.\nPrimero que nada crearemos un proyecto nuevo utilizando npm (el manejador de paquetes de Node.js, deberán tenerlo instalado).\nRecuerde que es necesario tener instalado Node.js version 20 o superior y MongoDB version 6 o superior.\n\n\n```bash\n$ mkdir up-example\n$ cd up-example\n$ npm init\n```\n\nEl comando npm init nos hará una seríe de preguntas.\n\n- package name: dejamos el que está por default (up-example), presionamos enter\n- version: presionamos enter.\n- description: podemos indicar o no una descripción, es opcional.\n- entry point: aquí ingresaremos \"app.js\"\n- test command: aquí ingresaremos \"node\".\n- git repository: nos está preguntando cual será el path o url de nuestro repositorio de git. Presionamos enter.\n- keywords: presionamos enter\n- author: ingresamos nuestro nombre\n- license: presionamos enter\n- Is this OK?: presionamos enter\n\n## Crear directorios\n\n```bash\n$ mkdir swagger\n$ mkdir controllers\n$ mkdir hooks\n$ npm install universal-pattern --save\n```\n\n## Creando module yaml\nAhora crearemos el archivo `models.yaml` dentro del directorio `swagger` con el siguiente contenido.\n\n```yaml\npaths:\n  /models:\n    get:\n      tags:\n        - models\n      summary: models list\n      x-swagger-router-controller: universal.search\n      parameters:\n        - $ref: '#/parameters/q'\n        - $ref: '#/parameters/page'\n        - $ref: '#/parameters/sorting'\n        - $ref: '#/parameters/limit'\n        - $ref: '#/parameters/fields'\n\n      responses:\n        '200':\n          description: reports\n          schema:\n            $ref: '#/definitions/models'\n    put:\n      tags:\n        - models\n      summary: insert new model\n      x-swagger-router-controller: universal.insert\n      parameters:\n        - name: modeldata\n          in: body\n          required: true\n          schema:\n            $ref: '#/definitions/modelInput'\n      responses:\n        '200':\n          description: model added\n          schema:\n            $ref: '#/definitions/models'\n\n    delete:\n      tags:\n        - models\n      summary: delete model\n      x-swagger-router-controller: universal.remove\n      parameters:\n        - $ref: '#/definitions/by_id'\n      responses:\n        '200':\n          description: deleted model\n          schema:\n            $ref: '#/definitions/models'\n\n    patch:\n      tags:\n        - models\n      summary: for updated model document\n      x-swagger-router-controller: universal.update\n      parameters:\n        - name: modeldata\n          in: body\n          required: true\n          schema:\n            $ref: '#/definitions/modelUpdate'\n      responses:\n        '200':\n          description: updated model\n          schema:\n            $ref: '#/definitions/models'\n\ndefinitions:\n  modelInput:\n    x-swagger-model-version: 2\n    type: object\n    properties:\n      name:\n        type: string\n        required: true\n        minLength: 4\n      description:\n        type: string\n      scoring:\n        type: integer\n        decimal: 0\n        requied: true\n        min: 1\n        max: 100\n\n  modelUpdate:\n    type: object\n    properties:\n      _id:\n        type: string\n        format: mongoId\n\n  models:\n    type: object\n    properties:\n      name:\n        type: string\n      description:\n        type: string\n      scoring:\n        type: integer\n\n```\n\n## Creamos app.js\nAhora es momento de crear el archivo `app.js` que tendrá el siguiente contenido:\n\n```javascript\nconst path = require('node:path');\nconst up = require('universal-pattern');\nconst swaggerFolder = path.join(process.cwd(), 'swagger'); // definimos el directorio swagger\nconst params = {\n\tswagger: {\n\t\tbaseDoc: process.env.BASEPATH, // es el directorio base de nuestro servicios. Ej: '/services'\n\t\thost: `${process.env.HOST}:${process.env.PORT}`, // el host, default localhost.\n\t\tfolder: swaggerFolder, // el directorio swagger\n\t\tinfo: {\n\t\t\tversion: 2.0,\n\t\t\ttitle: 'Universal Pattern Example',\n\t\t\ttermsOfService: 'www.domain.com/terms',\n\t\t\tcontact: {\n\t\t\t\temail: 'cesar@visiongroup.nyc',\n\t\t\t},\n\t\t\tlicense: {\n\t\t\t\tname: 'Apache',\n\t\t\t\turl: 'http://www.apache.org/licenses/LICENSE-2.0.html',\n\t\t\t},\n\t\t},\n\t},\n\tpreMWS: [], // array con middleware que se ejecutarán antes que Universal Pattern\n\tpostMWS: [], // array con middleware que se ejecutarán despues del flow request de UP.\n\tbodyParser: { // configuraciones para el MWS bodyParser.\n\t\tjson: { limit: '2mb' }, // seteamos el limit del tamaño de los objetos enviados por body\n\t\turlencoded: { limit: '500mb', extended: false },\n\t},\n\texpress: { // configuraciones adicionales de express\n\t\tjson: { limit: 10485760 }, // límite del json\n\t\tstatic: 'public', // directorio público.\n\t},\n\tcompress: true, // indica que el output deberá estar comprimido\n\tcors: true, // habilita cors\n\tproduction: false, // indica si está en modo producción. En modo producción no se permite el acceso a la documentación\n\trouteController: (req, res, next) =\u003e next(), // controlador a ejecutar antes que cualquier otro controlador.\n\tport: process.env.PORT, // puerto donde correrá nuestro servicio\n\tdatabase: { // la configuración de la base de datos.\n\t\turi: process.env.CONNECTION, // string de connection a la base de datos\n\t\tname: process.env.DBNAME, // nombre de la base de datos\n\t},\n\tenabledStats: true, // activa el modo stats, el cual podemos consumir en el path `/stats`\n};\n\nasync function init() {\n\ttry {\n\t\tconst upInstance = await up(params); // creamos una instancia de UP\n\t\tconsole.info(`UP InstanceId: ${upInstance.instanceId}`);\n\t} catch (err) {\n\t\tconsole.error('Error initializing ', err);\n\t}\n}\n\ninit(); // iniciamos nuestro servicio\n```\n\n\n## Ejecutando!\nAhora vamos a ejecutar nuestro ejemplo.\n\n```bash\n$ node app.js\nHOST=localhost PORT=5000 CONNECTION=mongodb://127.0.0.1:27017 DBNAME=uptesting BASEPATH=/services node app.js\n```\n\nAbrimos nuestro navegador en la siguiente url (http://localhost:5000/services/docs) y veremos la documentación de nuestro nuevo módulo (y obviamente, podremos probarlo!)\n\n# Preguntas frecuentes\n\n## 👨‍💻 Arquitectura y Flujo de Trabajo\n```\n¿Podrías explicar un poco más sobre cómo Universal Pattern interactúa con Node.js, Swagger y MongoDB en un flujo de trabajo típico?.\n```\n\nUniviersal Pattern se ingregra a un proyecto Node.js como una librería, permitiendo leer un directorio `swagger` con los archivos yaml.\nRecordemos que el objetivo es que cada archivo `yaml` represente un `module`.\n\nLo que hará Universal Patter es leer cada archivo yaml y registrar en Express (el cual gestiona internamente) las rutas, el control de parámetros de entrada a las mismas y todo aquel mecanismo que sea necesario.\n\nUniversal Pattern entiende que cada módulo es una `collection` en la base de datos, por esa razón es importante entender que la ruta está directamente relacionada a la collection.\n\nPor ejemplo:\n`http://localhost:3000/services/users`\n\nEn este caso debemos tener en cuenta:\n\n- `/services` es considerado el `basepath`, es decir, la ruta donde estará UP corriendo.\n- `/users` es el module, es decir, para Universal Pattern la collection en MongoDB se llamará `users`.\n\n\n## 👨‍💻 Personalización y Extensibilidad\n```\n¿Hay opciones para personalizar o extender la funcionalidad de los módulos generados por Universal Pattern? Por ejemplo, ¿cómo se manejarían casos en los que se necesiten lógicas de negocio específicas o integraciones con otros sistemas?\n```\n\nUniversal Pattern ofrece controladores ya pre-definidos para ahorrar tiempo y esfuerzo.\n\nCuando definimos un nuevo endpoint, debemos indicar por medio de la prop `x-swagger-router-controller` cual será el controlador (nombre del mismo).\n\n```yaml\npaths:\n  /brands:\n    get:\n      tags:\n        - brands\n      summary: brands list\n      x-swagger-router-controller: universal.search\n      parameters:\n        - $ref: '#/parameters/q'\n        - $ref: '#/parameters/page'\n        - $ref: '#/parameters/sorting'\n        - $ref: '#/parameters/limit'\n        - $ref: '#/parameters/fields'\n\n      responses:\n        '200':\n          description: return all brand from database\n          schema:\n            $ref: '#/definitions/brand'\n```\n\nUniversal pattern nos aporta controladores como:\n\n- universal.search: busca dentro de la collection, y retornará el resultado en forma paginado.\n- universal.insert: permitirá insertar información, agregando una capa de control de datos.\n- universal.update: actualiza un documento en la collection.\n- universal.remove: elimina un documento de la collection.\n- universal.count: cuenta los documentos de una collection.\n- universal.today: retorna todos los documentos del día actual.\n- universal.getLast: retorna el último documento de una collection.\n- universal.distinct: retorna todos los valores distintos de la field indicada.\n- universal.insertOrCount: intentará insertar un documento siempre y cuando el key/value indicado no exista previamente. En caso de que exista, retornará error, pero sumará en 1 la prop `_count`.\n\nAdicionalmente, Universal Pattern permite definir controladores propios, a fin de que podamos aplicar las reglas de negocio que necesitemos.\n\n\n```javascript\nupInstance.registerController('MyMoudleName.ControllerName', (req, res, next) =\u003e {\n  console.info(req.swagger);\n  res.json({ ok: true });\n});\n```\n\nRecomendamos crear los archivos de controladores propios dentro del directorio `controllers` a la misma altura que el directorio `swagger`.\n\n`upInstance` es la instancia de Universal Pattern una vez creada.\n\n```javascript\nasync function init() {\n\ttry {\n\t\tconst upInstance = await up(params);\n\t\tconsole.info(`UP InstanceId: ${upInstance.instanceId}`);\n\t} catch (err) {\n\t\tconsole.error('Error initializing ', err);\n\t}\n}\n```\n\n## 🛡️ Seguridad y Autenticación\n```\n¿Universal Pattern ofrece características integradas para manejar la seguridad y autenticación en los endpoints, o esto debe ser implementado aparte?\n```\n\nUniversal Pattern si bien no ofrece (de momento) mecanismos para seguridad, autentificación, etc. aporta una manera muy simple de lograrlo.\n\nPodemos crear un `mws` para decodificar un `jwt` y gracias a la propiedad `routeController` podemos aplicar reglas antes de que se llegue a los controladores definidos en los módulos.\n\n## 👨‍💻 Rendimiento y Escalabilidad\n```\n¿Hay alguna consideración especial en términos de rendimiento y escalabilidad cuando se utilizan microservicios generados con Universal Pattern, especialmente en aplicaciones de gran escala?\n```\nEn este sentido, tenemos varios puntos a considerar.\n\n- MongoDB: podemos escalar la base de datos con todas las opciones disponibles de MongoDB (sharding, clustering, etc).\n- Múltiples instancias: con la ayuda de un `ELB` o cualquier otra opción de balance, podemos instalar nuestros servicios (grupo de módulos) en distintos servidores y balancear la carga.\n- Múltiples core: por default, Universal Patter ya reconoce el total de cores que tiene nuestro servidor y aprovechará cada uno de ellos.\n\n## 👨‍💻 Soporte y Comunidad\n```\n¿Cómo es el soporte y la comunidad alrededor de Universal Pattern? ¿Hay una base de usuarios activa o foros donde los desarrolladores pueden buscar ayuda y compartir mejores prácticas?\n```\n- Comunidad en [Telegram](https://t.me/universalpattern).\n- Comunidad en [Linkedin](https://www.linkedin.com/groups/9580792/)\n\nAdicionalmente, pueden visitar el repositorio en [github](https://github.com/visiongroupnyc/universal-pattern/issues)\n\n\n# x-swagger-properties\n\n## x-swagger-public-fields\nIndica que propiedades se deben popular en la respuesta de un endpoint.\n\n\n## x-swagger-skip-fields\nIndica que las propiedades no deben ser populadas.\n\n\n```yaml\npaths:\n  /users:\n    get:\n      tags:\n        - users\n      summary: users list\n      x-swagger-router-controller: universal.search\n      x-swagger-public-field:\n        - firstName\n        - lastName\n      parameters:\n        - $ref: '#/parameters/q'\n        - $ref: '#/parameters/page'\n        - $ref: '#/parameters/sorting'\n        - $ref: '#/parameters/limit'\n        - $ref: '#/parameters/fields'\n```\n\n## x-swagger-router-controller\nIndica el nombre del controlador que deseamos utilizar.\nEl mismo puede ser propio de universal pattern o uno creado de forma customizada.\nA continuación, el listado de controladores soportados en Universal Pattern.\n- [insert](./docs/controllers/INSERT.md)\n- [search](./docs/controllers/SEARCH.md)\n- [remove](./docs/controllers/REMOVE.md)\n- [count](./docs/controllers/COUNT.md)\n- [today](./docs/controllers/TODAY.md)\n- [getLast](./docs/controllers/GETLAST.md)\n\n\n# Routes out UP Scope.\nPara trabajar con rutas fuera del scope de Universal Pattern podemos hacer uso de la propiedad `routes` del objeto de instancia.\n\n\n```javascript\nconst params = {\n\tswagger: {\n\t\tbaseDoc: process.env.BASEPATH,\n\t\thost: `${process.env.HOST}:${process.env.PORT}`,\n\t\tfolder: swaggerFolder,\n\t\tinfo: {\n\t\t\tversion: 2.0,\n\t\t\ttitle: 'Universal Pattern Example',\n\t\t\ttermsOfService: 'www.domain.com/terms',\n\t\t\tcontact: {\n\t\t\t\temail: 'cesar@visiongroup.nyc',\n\t\t\t},\n\t\t\tlicense: {\n\t\t\t\tname: 'Apache',\n\t\t\t\turl: 'https://www.apache.org/licenses/LICENSE-2.0.html',\n\t\t\t},\n\t\t},\n\t},\n\tpreMWS,\n\tpostMWS: [],\n\tbodyParser: {\n\t\tjson: { limit: '2mb' },\n\t\turlencoded: { limit: '500mb', extended: false },\n\t},\n\tcompress: true,\n\texpress: {\n\t\tjson: { limit: 10485760 },\n\t\tstatic: 'public',\n\t},\n\tcors: true,\n\tproduction: false,\n\trouteController: (req, res, next) =\u003e next(),\n\tport: process.env.PORT,\n\tdatabase: {\n\t\turi: process.env.CONNECTION,\n\t\tname: process.env.DBNAME,\n\t},\n\troutes: {\n\t\tget: {\n\t\t\t'/health': [\n\t\t\t\tasync (req, res, next) =\u003e {\n\t\t\t\t\treq.testing = true;\n\t\t\t\t\tnext();\n\t\t\t\t},\n\t\t\t\tasync (req, res) =\u003e res.status(200).end(`Working ${req.testing}`),\n\t\t\t],\n\t\t},\n\t},\n\tenabledStats: true, // activa el ver las estadísticas\n\tcache: true, // activa el cache\n};\n```\n\n\n# Cache\nUniversal Pattern soporta actualmente un sistema simple de cache.\nBásicamente, memorizará todos los request del method GET hacia los distintos endpoints, almacenando la respuesta enviada.\nPor el momento no soporta ttl en el sistema de cache, sin embargo el cache se elimina automáticamente cuando hay una nueva inserción de datos o una actualización (PUT/PATCH/DELETE).\n\nEjemplo:\n```javascript\nconst params = {\n\tswagger: {\n\t\tbaseDoc: process.env.BASEPATH,\n\t\thost: `${process.env.HOST}:${process.env.PORT}`,\n\t\tfolder: swaggerFolder,\n\t\tinfo: {\n\t\t\tversion: 2.0,\n\t\t\ttitle: 'Universal Pattern Example',\n\t\t\ttermsOfService: 'www.domain.com/terms',\n\t\t\tcontact: {\n\t\t\t\temail: 'cesar@visiongroup.nyc',\n\t\t\t},\n\t\t\tlicense: {\n\t\t\t\tname: 'Apache',\n\t\t\t\turl: 'https://www.apache.org/licenses/LICENSE-2.0.html',\n\t\t\t},\n\t\t},\n\t},\n\tpreMWS,\n\tpostMWS: [],\n\tbodyParser: {\n\t\tjson: { limit: '2mb' },\n\t\turlencoded: { limit: '500mb', extended: false },\n\t},\n\tcompress: true,\n\texpress: {\n\t\tjson: { limit: 10485760 },\n\t\tstatic: 'public',\n\t},\n\tcors: true,\n\tproduction: false,\n\trouteController: (req, res, next) =\u003e next(),\n\tport: process.env.PORT,\n\tdatabase: {\n\t\turi: process.env.CONNECTION,\n\t\tname: process.env.DBNAME,\n\t},\n\tenabledStats: true,\n\tcache: true,\n};\n```\n## Midiendo la performance con cache activado:\n```bash\nRunning 10s test @ http://localhost:5000/services/brands?limit=50\u0026page=1\n10 connections\n\n\n┌─────────┬──────┬──────┬───────┬──────┬─────────┬─────────┬───────┐\n│ Stat    │ 2.5% │ 50%  │ 97.5% │ 99%  │ Avg     │ Stdev   │ Max   │\n├─────────┼──────┼──────┼───────┼──────┼─────────┼─────────┼───────┤\n│ Latency │ 0 ms │ 0 ms │ 0 ms  │ 0 ms │ 0.01 ms │ 0.17 ms │ 21 ms │\n└─────────┴──────┴──────┴───────┴──────┴─────────┴─────────┴───────┘\n┌───────────┬─────────┬─────────┬─────────┬─────────┬───────────┬──────────┬─────────┐\n│ Stat      │ 1%      │ 2.5%    │ 50%     │ 97.5%   │ Avg       │ Stdev    │ Min     │\n├───────────┼─────────┼─────────┼─────────┼─────────┼───────────┼──────────┼─────────┤\n│ Req/Sec   │ 27,087  │ 27,087  │ 35,391  │ 45,503  │ 35,872.73 │ 4,176.62 │ 27,082  │\n├───────────┼─────────┼─────────┼─────────┼─────────┼───────────┼──────────┼─────────┤\n│ Bytes/Sec │ 25.6 MB │ 25.6 MB │ 33.4 MB │ 42.9 MB │ 33.8 MB   │ 3.93 MB  │ 25.5 MB │\n└───────────┴─────────┴─────────┴─────────┴─────────┴───────────┴──────────┴─────────┘\n\nReq/Bytes counts sampled once per second.\n# of samples: 11\n\n395k requests in 11.02s, 372 MB read\n\n```\n\n# Stats.\nEn Universal Pattern podemos activar el monitor de request (no se recomienda para producción).\nEl mismo permitirá ver en tiempo real cuantos request está procesando cada uno de los forks.\n\n![Stats](docs/assets/stats.png)\n\n# Clustering.\nUniversal Pattern de forma automática utilizará el módulo clustering de Node.js, creando fork por cada core disponible.\n\n# Performance testing\nUsando autocannon para medir la performance del example incluido en Universal Pattern\n\n```bash\n$ autocannon \"http://localhost:5000/services/users?page=1\u0026limit=30\"\n```\n\n```bash\nRunning 10s test @ http://localhost:5000/services/users?page=1\u0026limit=30\n10 connections\n\n\n┌─────────┬──────┬──────┬───────┬──────┬─────────┬─────────┬───────┐\n│ Stat    │ 2.5% │ 50%  │ 97.5% │ 99%  │ Avg     │ Stdev   │ Max   │\n├─────────┼──────┼──────┼───────┼──────┼─────────┼─────────┼───────┤\n│ Latency │ 1 ms │ 1 ms │ 4 ms  │ 5 ms │ 1.48 ms │ 0.94 ms │ 23 ms │\n└─────────┴──────┴──────┴───────┴──────┴─────────┴─────────┴───────┘\n┌───────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┐\n│ Stat      │ 1%      │ 2.5%    │ 50%     │ 97.5%   │ Avg     │ Stdev   │ Min     │\n├───────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤\n│ Req/Sec   │ 3,239   │ 3,239   │ 5,099   │ 6,279   │ 5,088.5 │ 876.53  │ 3,239   │\n├───────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤\n│ Bytes/Sec │ 4.36 MB │ 4.36 MB │ 6.86 MB │ 8.45 MB │ 6.84 MB │ 1.18 MB │ 4.36 MB │\n└───────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┘\n\nReq/Bytes counts sampled once per second.\n# of samples: 10\n\n51k requests in 10.02s, 68.4 MB read\n```\n\n# Ejemplo\nPodemos ver un ejemplo completo de implementación en [este link](example/README.md)\n\n# Test\n[Documentacion de test](test/README.md)\n\n# Changelog\nSee changelog [here](CHANGELOG.md)\n\n# License\n[MIT](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flortmorris%2Funiversal-pattern","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flortmorris%2Funiversal-pattern","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flortmorris%2Funiversal-pattern/lists"}