npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@jysperu/load-express

v2.0.9

Published

Configura Express con CORS, compresión, minificación, timeout, cookie-parser, Pug y utilidades para APIs REST con respuestas estandarizadas

Readme

@jysperu/load-express

Paquete para inicializar Express con middlewares listos para producción, helpers para APIs/Pug e internacionalización con i18n.

Instalación

npm install @jysperu/load-express

Inicio rápido

import { init } from "@jysperu/load-express";

await init({
  addRoutes(app) {
    app.get("/api/v1/ping", (req, res) => {
      res.success("pong");
    });
  },
});

API pública

Export principal

  • default -> initExpress
  • init(options?)
  • init(addRoutesFn)
  • initExpress -> alias de init
  • close()
  • closeExpress -> alias de close
  • ExpressApp() -> función que retorna la instancia Express actual
  • ExpressServerInstance() -> función que retorna la instancia del servidor HTTP
  • port(force?)
  • url(force?)
  • addPugContextCallback(callback)

Aliases exportados

  • ExpressJsonResult -> alias de jsonResult interno
  • AntiAbuseReasons — objeto con los mensajes de bloqueo de antiAbuse

Estado/objetos exportados

  • ExpressApp() (función getter que retorna la instancia Express actual o null)
  • ExpressServerInstance() (función getter que retorna la instancia del servidor HTTP o null)
  • express (librería Express)
  • Router (Router de Express)

Tipos exportados (principales)

  • InitOptions, callableAddRoutes
  • Express, Request, Response, NextFunction, Middleware, ExpressServer
  • HelpersOptions, HelpersDefaults, CallablePugContext, EnvVals
  • SecureHeadersOptions, SecureHeadersDefaults
  • RateLimitOptions, RateLimitDefaults
  • AntiAbuseOptions, AntiAbuseDefaults
  • TimeOutTime, TimeOutTimeValue, TimeOutTimeCallback, TimeoutOptions
  • CorsOptions, JsonOptions, UrlEncodedOptions
  • CookieParserOptions, CookieParserSecret, CookieParserOptionsParameter
  • SanitizeHtmlOptions, SanitizeHtmlTarget
  • CompressionOptions
  • MinifyOptions, MinifyCacheOption, MinifyCacheCallback
  • I18nOptions, UrlData
  • ResultHookPayload, ResultHook

Funciones exportadas (avanzado)

Todas las funciones de middleware están disponibles individualmente:

import {
  setHelpers,
  secureHeaders,
  rateLimit,
  antiAbuse,
  timeout,
  cors,
  json,
  urlencoded,
  cookieParser,
  sanitizeHtml,
  compression,
  minify,
  minifyCacheOption,
  i18n,
  parseUrl,
  publicJS,
  getClientIp,
  getClientUserAgent,
  getPath,
  getHost,
  getReferer,
  defaultKeyGenerator,
  helpersDefaults,
  secureHeadersDefaults,
  rateLimitDefaults,
  antiAbuseDefaults,
  timeoutDefaults,
  envVals,
  ExpressApp,
  ExpressServerInstance,
} from "@jysperu/load-express";

Variables de entorno

  • PORT o EXPRESS_PORT: puerto del servidor (default 3000)
  • URL o EXPRESS_URL: URL base (default http://localhost:{PORT})
  • EXPRESS_APIPATH: regex de rutas API (default ^/api(?:/v[0-9]+(\.[0-9]+)?)?/)
  • EXPRESS_AUTHPATH: regex de rutas auth (default ^(?:/api(?:/v[0-9]+(\.[0-9]+)?)?)?/auth/)
  • EXPRESS_PUGPATH: carpeta de vistas Pug (default ./pug/pages)

InitOptions

init acepta dos formas:

  • init(options?)async, devuelve Promise<Express | false>
  • init(addRoutesFn)async, devuelve Promise<Express | false>

InitOptions (forma init(options)) acepta:

  • addRoutes?: (app) => void
  • addPugContextCallback?: CallablePugContext
  • prerequests?: Middleware[] — middlewares adicionales que se registran tras setHelpers
  • apiPattern?: string | RegExp
  • authPattern?: string | RegExp
  • pugPath?: string
  • secureHeaders?: SecureHeadersOptions | boolean
  • rateLimit?: RateLimitOptions | boolean
  • antiAbuse?: AntiAbuseOptions | boolean
  • timeout?: TimeOutTime | TimeoutOptions | boolean
  • cors?: CorsOptions | boolean
  • i18n?: I18nOptions
  • json?: JsonOptions | boolean
  • urlencoded?: UrlEncodedOptions | boolean
  • cookieParser?: CookieParserOptions | boolean
  • sanitizeHtml?: SanitizeHtmlOptions | boolean
  • compression?: CompressionOptions | boolean
  • minify?: MinifyOptions | boolean
  • i18nPublicRoute?: string | false (default "/i18n.js")
  • onResultSuccess?: ResultHook — se ejecuta cuando result.success === true
  • onResultFailure?: ResultHook — se ejecuta cuando result.success === false
  • onResultRedirect?: ResultHook — se ejecuta cuando el HTTP code es 301 o 302
  • onResultComplete?: ResultHook — se ejecuta siempre al finalizar un result() o redirectResult()

Todos los middlewares (salvo i18n y setHelpers) pueden desactivarse con false.

Middlewares incluidos en init

Orden real de ejecución en init():

  1. cookieParser (si no es false)
  2. json (si no es false)
  3. urlencoded (si no es false)
  4. i18n
  5. setHelpers
  6. prerequests (si se proporcionan)
  7. secureHeaders (si no es false)
  8. rateLimit (si no es false)
  9. antiAbuse (si no es false)
  10. sanitizeHtml (si no es false)
  11. timeout (si no es false)
  12. cors (si no es false)
  13. compression (si no es false)
  14. minify (si no es false)
  15. Ruta i18nPublicRoute (si no es false)
  16. addRoutes (rutas de la aplicación)
  17. Catch-all Pug (busca archivo .pug en pugPath; si no existe llama a req.result('error404', ...) con código 404)
  18. Error handler (llama a req.result('error500', ...) con código 500; renderiza Pug o devuelve JSON según el tipo de ruta)

sanitizeHtml sanea recursivamente req.body, req.query y req.params. Por defecto no permite etiquetas ni atributos y usa disallowedTagsMode: "recursiveEscape", por lo que el HTML rechazado se conserva como texto escapado. Puede desactivarse con false o configurarse con las opciones de sanitize-html. Se ejecuta después de antiAbuse para que la capa de bloqueo inspeccione primero el payload original.

Helpers en Request/Response

Nota de migración (2.0.4): el helper extendido redirect(...) fue renombrado a redirectResult(...). El método res.redirect(...) mantiene el comportamiento nativo de Express.

Request

  • req.success(message, next?, options?) — establece notificación de tipo success y llama a next
  • req.error(message, next?, options?) — establece notificación de tipo error
  • req.warning(message, next?, options?) — establece notificación de tipo warning
  • req.info(message, next?, options?) — establece notificación de tipo info
  • req.addPugContext(key, value) — agrega una clave al contexto Pug de la solicitud
  • req.getPugContext(extra?) — retorna el contexto Pug completo
  • req.response(uri4pug?, json?, httpCode?) — renderiza vista Pug o devuelve JSON según isApiUri; httpCode default 200
  • req.result(uri4pug, success, message?, json?, httpCode?) — combina jsonResult con response
  • req.redirectResult(uri4redirect, success, message?, json?, httpStatus?) — redirige (o devuelve JSON en rutas API)
  • req.isApiUritrue si la URL coincide con apiPattern
  • req.isAuthUritrue si la URL coincide con authPattern
  • req.urlData — objeto UrlData con la URL normalizada (ver sección UrlData)
  • req.notification — última notificación establecida con req.success/error/warning/info
  • req.pugContext — objeto de contexto Pug adicional
  • req.locale — locale activo (inyectado por i18n)
  • req.locales — lista de locales disponibles
  • req.translations — catálogo completo de todos los locales ({ [locale]: { [key]: value } })
  • req.i18n — instancia I18n
  • req.__ — función de traducción (req.__(key, ...args)) inyectada por i18n vía registro global
  • req.getClientIp() — obtiene la dirección IP real del cliente considerando proxies
  • req.getClientUserAgent() — obtiene el User-Agent del cliente
  • req.getPath() — obtiene la ruta (path) limpia sin query params
  • req.getHost() — obtiene el valor del header Host
  • req.getReferer() — obtiene el valor del header Referer

Response

  • res.success(message?, json?): Promise<void> — responde { success: true, message? } con status 200
  • res.error(error?, json?): Promise<void> — responde { success: false, error? } con status 200
  • res.addPugContext(key, value)
  • res.getPugContext(extra?)
  • res.response(uri4pug?, json?, httpCode?)
  • res.result(uri4pug, success, message?, json?, httpCode?)
  • res.redirect(url) y res.redirect(status, url) — comportamiento nativo de Express
  • res.redirectResult(uri4redirect, success, message?, json?, httpStatus?)
  • res.getClientIp() — obtiene la dirección IP real del cliente considerando proxies
  • res.getClientUserAgent() — obtiene el User-Agent del cliente
  • res.getPath() — obtiene la ruta (path) limpia sin query params
  • res.getHost() — obtiene el valor del header Host
  • res.getReferer() — obtiene el valor del header Referer

Contexto Pug base (getPugContext)

Incluye:

  • url, url_base, urlBase
  • url_full, urlFull
  • url_full_query, urlFullQuery
  • get (req.query)
  • post (req.body)
  • notification
  • auth (inyectado por middleware externo, p.ej. JWT)
  • locale
  • locales
  • más lo agregado con addPugContext y los callbacks de addPugContextCallback

Orden de prioridad al mezclar: contexto base → callbacks globales → req.pugContext → parámetro extra.

addPugContextCallback

addPugContextCallback((ctx, req, res) => {
  ctx.siteName = "Mi App";
  ctx.user = (req as any).auth ?? null;
});

Los callbacks se almacenan globalmente y se ejecutan en cada solicitud antes de renderizar. El objeto ctx se modifica in-place.

Result Hooks

Los hooks permiten interceptar y reaccionar a las respuestas generadas por result() y redirectResult(). Se registran en InitOptions:

await init({
  onResultSuccess: (payload) => {
    console.log("✓ Respuesta exitosa", payload);
  },
  onResultFailure: (payload) => {
    console.log("✗ Respuesta fallida", payload);
  },
  onResultRedirect: (payload) => {
    console.log("→ Redirección:", payload.uri4redirect);
  },
  onResultComplete: (payload) => {
    console.log("◉ Respuesta completada (siempre se ejecuta)");
  },
});

Payload del hook

type ResultHookPayload = {
  uri4pug?: string; // vista Pug a renderizar (si aplica)
  uri4redirect?: string; // URL de redirección (si aplica)
  httpCode: number; // código HTTP de la respuesta
  result?: {
    success: boolean; // true/false del resultado
    message?: string; // mensaje (si success=true)
    error?: string; // error (si success=false)
    [key: string]: any; // datos adicionales
  };
  req: Request; // objeto Request de Express
  res: Response; // objeto Response de Express
};

Ejecución de hooks

  • onResultSuccess se ejecuta si result.success === true
  • onResultFailure se ejecuta si result.success === false
  • onResultRedirect se ejecuta si httpCode es 301 o 302
  • onResultComplete se ejecuta siempre, al finalizar cualquier result() o redirectResult()

Los hooks se disparan antes de enviar la respuesta al cliente, permitiendo logging, telemetría, o modificaciones finales en req/res.

UrlData

req.urlData contiene la URL normalizada con las siguientes propiedades:

  • Estándar: href, origin, protocol, host, hostname, port, pathname, search, searchParams, hash
  • Derivadas: scheme (protocolo sin :), https (boolean), full (origen + pathname), fullWithQuery (full + search), base (origen), path (pathname), qs (query string sin ?), fragment (hash sin #)

i18n

i18n(options) usa defaults:

  • locales: ["en", "es"]
  • defaultLocale: "es"
  • cookie: "lang"
  • queryParameter: "lang"
  • header: "accept-language"
  • directory: "./locales" (si no se pasa)
  • updateFiles: false
  • syncFiles: false
  • autoReload: true
  • objectNotation: false

Ejemplo con catálogo estático

import { init } from "@jysperu/load-express";

await init({
  i18n: {
    staticCatalog: {
      es: { hello: "hola" },
      en: { hello: "hello" },
    },
    defaultLocale: "es",
    locales: ["es", "en"],
  },
});

Ruta pública opcional de catálogo i18n

Por defecto init publica una ruta JS en "/i18n.js".

import { init } from "@jysperu/load-express";

await init({
  i18nPublicRoute: "/assets/i18n.js",
  i18n: {
    defaultLocale: "es",
    locales: ["es", "en"],
    staticCatalog: {
      es: { hello: "hola" },
      en: { hello: "hello" },
    },
  },
});

Ejemplo de JS resultante de GET /i18n.js:

window.locale = "en";
window.i18n = { hello: "hello", welcome_user: "welcome {{name}}" };
window.__ = function __(key, ...args) {
  const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");

  const table = window?.i18n ?? {};
  const source = table[key];
  let str = typeof source === "string" ? source : key;

  if (args.length === 1 && args[0] && typeof args[0] === "object" && !Array.isArray(args[0])) {
    const replacements = args[0];
    for (const [k, value] of Object.entries(replacements)) {
      const token = new RegExp(`{{${escapeRegExp(k)}}}`, "g");
      str = str.replace(token, String(value));
    }
  } else if (args.length > 0) {
    for (const arg of args) {
      str = str.replace(/%[sd]/, String(arg));
    }
  }

  return str;
};

La suite incluye una prueba de ejecución real del script devuelto por /i18n.js:

  • Evalúa el JS en un sandbox.
  • Verifica window.locale, window.i18n y window.__.
  • Comprueba traducción de claves existentes, reemplazos {{name}} y fallback de clave faltante.

Puedes ejecutarla de forma aislada con:

npm test -- tests/i18nPublicRoute.test.ts

Seguridad y control

secureHeaders(options?)

Defaults principales:

  • X-XSS-Protection: 1; mode=block
  • X-Content-Type-Options: nosniff
  • X-Frame-Options: SAMEORIGIN
  • Referrer-Policy: no-referrer
  • Strict-Transport-Security: max-age=15552000; includeSubDomains
  • Cross-Origin-Opener-Policy: same-origin
  • Cross-Origin-Resource-Policy: same-site

rateLimit(options?)

Defaults:

  • windowMs: 60000
  • max: 100
  • setHeaders: true
  • denyStatusCode: 429
  • denyMessage: "Too many requests"

antiAbuse(options?)

Bloquea por método/IP/UA/path, patrones sospechosos, longitud de URL y tamaño de body.

Defaults clave:

  • allowedMethods: ["GET","POST","PUT","DELETE","PATCH","HEAD","OPTIONS"]
  • maxUrlLength: 2048
  • maxContentLength: 10MB
  • denyStatusCode: 403

Los patrones se evaluan sobre la URL (raw y decodificada) y el body, cubriendo ataques con y sin URL-encoding. Los RegExp se compilan una sola vez al crear el middleware para minimizar el impacto en rendimiento.

Patrones incluidos por defecto:

  • SQL Injection: UNION SELECT, SELECT FROM, INSERT INTO, UPDATE SET, DELETE FROM, DROP TABLE, INTO OUTFILE/DUMPFILE, LOAD_FILE(), base64_encode/decode(), benchmark(), exec()/xp_cmdshell, concat(), sleep(), WAITFOR DELAY (MSSQL), pg_sleep() (PostgreSQL)
  • XSS: <script>, <iframe>, <object>, <embed>, javascript:, event handlers inline (onX=)
  • Path traversal: ../ y variantes encoded (%2e%2e%2f, %2e%2e\)
  • Null byte: \x00 y %00
  • CRLF injection: %0a, %0d
  • Command injection: operadores shell (`; | & ``) seguidos de comandos comunes

Cuando una solicitud es bloqueada, el código de bloqueo se devuelve en json.code y el mensaje en json.reason. Los códigos posibles están en AntiAbuseReasons:

  • BLOCKED_USER_AGENT, BLOCKED_IP, BLOCKED_METHOD, BLOCKED_PATH
  • URL_TOO_LONG, CONTENT_TOO_LARGE, SUSPICIOUS_PATTERN

Opción skip

Todos los middlewares (secureHeaders, rateLimit, antiAbuse, timeout, cors, json, urlencoded, cookieParser, compression, minify) aceptan una función skip para omitir el middleware condicionalmente:

init({
  rateLimit: { skip: (req) => req.path === "/health" },
  antiAbuse: { skip: (req) => req.ip === "127.0.0.1" },
});

En antiAbuse, skip también puede reemplazar arrays de patrones por request (solo el array sustituido se recompila; el resto usa los patrones pre-compilados):

antiAbuse({
  suspiciousPatterns: [],
  skip: (req, opts) => {
    if (req.path === "/strict") opts.suspiciousPatterns = [/union\s+select/i];
    return false;
  },
});

Wrappers adicionales

  • timeout(...) (default 30s)
  • cors(...)
  • json(...) (default limit: "50mb")
  • urlencoded(...) (default limit: "50mb", extended: true)
  • cookieParser(...)
  • compression(...)
  • minify(...) (default cache: true, jsonMatch: false)

El parámetro cache de minify acepta también una función para determinar la carpeta de caché por solicitud:

init({
  minify: { cache: (req) => (req.isApiUri ? false : "./cache") },
});

Ejemplo completo

import { init, addPugContextCallback } from "@jysperu/load-express";

addPugContextCallback((ctx, req) => {
  ctx.siteName = "Mi App";
  ctx.user = (req as any).auth ?? null;
});

await init({
  i18n: {
    defaultLocale: "es",
    locales: ["es", "en"],
    staticCatalog: {
      es: { hello: "hola" },
      en: { hello: "hello" },
    },
  },
  secureHeaders: true,
  rateLimit: { max: 120, windowMs: 60_000 },
  antiAbuse: true,
  timeout: "5s",
  addRoutes(app) {
    app.get("/api/v1/hello", (req, res) => {
      res.success(req.__?.("hello"));
    });
  },
});

Acceso al estado interno

Para casos avanzados donde necesites acceso directo a las instancias de Express:

import { ExpressApp, ExpressServerInstance } from "@jysperu/load-express";

// Obtener la instancia Express actual (puede ser null si no está inicializada)
const app = ExpressApp();
if (app) {
  // Trabajar con la instancia Express directamente
  app.use("/custom", myMiddleware);
}

// Obtener la instancia del servidor HTTP (puede ser null)
const server = ExpressServerInstance();
if (server) {
  // Trabajar con el servidor directamente
  console.log("Servidor corriendo en:", server.address());
}

Nota de seguridad: Las funciones getter ExpressApp() y ExpressServerInstance() proporcionan acceso controlado y seguro al estado interno, reemplazando el acceso directo a propiedades app y server de versiones anteriores.

Observaciones de compatibilidad

  • init() es asíncrona (Promise<Express | false>): siempre usa await init(...).
  • init() es singleton: llamadas posteriores reutilizan la misma instancia app.
  • Para reiniciar la instancia en tests/procesos controlados, usar close().
  • urlData de request normaliza URL (full, fullWithQuery, qs, scheme, etc.).

Scripts

npm run test
npm run test:watch
npm run build

Licencia

MIT © JYS Perú