@existo/middlewares
v1.2.0
Published
Express middlewares
Readme
@existo/middlewares
Colección de middlewares de Express diseñados para usarse con Astro en modo SSR.
Índice
Descripción general
Este repositorio contiene una colección de middlewares de Express diseñados para usarse con Astro en modo SSR. Y con el adaptador de node en modo middleware.
Para empesar a usarlos, se debe importar el middleware deseado y registrarlo en la aplicación de Express.
Instalación
Estos middlewares vienen todos en un solo paquete @existo/middlewares y se pueden instalar con npm install @existo/middlewares.
Para usarlos en express se debe importar el middleware deseado y registrarlo en la aplicación de Express.
import { routeIgnorer } from '@existo/middlewares';
app.use(routeIgnorer({ ... }));Ejemplo de configuración
Ahora veremos un ejemplo de configuración de los middlewares de @existo/middlewares.
En nuestro archivo server.js se debe importar el middleware deseado y registrarlo en la aplicación de Express.
El orden en que se registran los middlewares es importante. Este puede depender de la configuración de la aplicación y de las necesidades de la misma.
El orden recomendado es:
import express from 'express';
import { cors, cookieParser } from 'express';
import { geolocation, routeIgnorer, trailingSlash, redirect, cacheControl, notFound, routeAnouncer } from '@existo/middlewares';
import { astroHandler } from 'dist/server/entry.mjs';
const app = express(); // Instancia de Express
/**
* Middlewares de Express necesarios para el funcionamiento de los middlewares de @existo/middlewares
*/
app.set('trust proxy', true);
app.use(cookieParser());
/**
* Middlewares de @existo/middlewares
*/
app.use(geolocation({ ... })); // Middleware de geolocalización
app.use(routeAnouncer({ ... })); // Middleware para anunciar las rutas a nivel de Express globalmente
app.use(routeIgnorer({ ... })); // Ignorador de rutas a nivel de Express globalmente
app.use(trailingSlash({ ... })); // Middleware para redirigir las rutas con y sin trailing slash
app.use(redirect({ ... })); // Middleware para redireccionar las rutas
app.use(cacheControl({ ... })); // Middleware para establecer el cache control
/**
* Middlewares de Astro
*/
app.use() // Servidor de estaticos de Astro
app.use(astroHandler); // Handler de Astro
/**
* Middleware 404 de@existo/middlewares
*/
app.use(notFound(astroHandler, { ... })); // Middleware para manejar el 404
// Arrancar el servidor
app.listen(3000, () => {
console.log('Server is running on port 3000 (http://localhost:3000)');
});Para el uso del middleware redirect se debe crear un archivo _redirects en la raíz del proyecto, de lo contrario se arrojara un error.
Requisitos
Para usar los middlewares de @existo/middlewares se deben tener instalados los siguientes paquetes:
expressmicromatchcookie-parserdotenv
Middlewares
routeIgnorer
Este middleware se encarga de ignorar rutas a nivel de Express globalmente. Sirve para ignorar rutas a nivel de Express globalmente (Devuelve un 404 forzado a nivel de Express). La idea es no ensuciar los logs y ahorrar procesamiento en los middlewares posteriores.
Opciones
| Opción | Tipo | Por defecto | Descripción |
|--------|------|-------------|-------------|
| ignoredPaths | string[] | [] | Patrones de rutas a ignorar (soporta glob con micromatch) |
Ejemplo
import { routeIgnorer } from '@existo/middlewares';
app.use(routeIgnorer({
ignoredPaths: ['/favicon.ico', '/*.txt', '/api/**'],
}));
/**
* En este caso por nuestro servidor no pasaran la ruta /favicon.ico, la ruta /archivo.txt y todas las rutas que coincidan con el patrón /api/**
* Esto aplica para todos los métodos HTTP (GET, POST, PUT, DELETE, etc.)
*/geolocation
Geolocation es un middleware que se encarga de detectar la geolocalización del usuario y asignar las cookies de país y idioma. La idea es que este middleware se ejecute antes de los middlewares como redirects ya que necesita las cookies de país y idioma.
Su funcionamiento es el siguiente:
- Si ya tiene las cookies de país y idioma, pasa al siguiente middleware
- Si no tiene la cookie de país, intenta obtener el país del IP del usuario usando la API de geolocalización de https://geoip.dnsproxymanager.com/ con la IP del usuario
- Si no tiene la cookie de idioma, intenta obtener el idioma del usuario usando el header
accept-language - En caso de que no se pueda obtener el país o el idioma, se usa el valor por defecto
- También asigna las cookies a req.cookies para que esté disponible en middlewares posteriores
Variables de entorno requeridas
| Variable | Descripción |
|----------|-------------|
| GEOLOCATION_BEARER | Token de autenticación para la API de geolocalización |
Opciones
| Opción | Tipo | Por defecto | Descripción |
|--------|------|-------------|-------------|
| defaultCountry | string | 'unknown' | País por defecto si no se puede detectar |
| defaultLocale | string | 'es' | Idioma por defecto si no se puede detectar |
Cookies que genera
| Cookie | Descripción |
|--------|-------------|
| country | Código de país ISO 3166-1 alpha-2 (ej: es, us) |
| locale | Código de idioma ISO 639-1 (ej: es, en) |
Ejemplo
import { geolocation } from '@existo/middlewares';
app.use(geolocation());
/**
* En este caso se usará el valor por defecto decidido por existo para el país y el idioma
*/trailingSlash
Este middleware se encarga de redirigir las rutas con y sin trailing slash.
Opciones
| Opción | Tipo | Por defecto | Descripción |
|--------|------|-------------|-------------|
| trailingMode | 'always' \| 'never' \| 'ignore' | 'always' | Comportamiento del trailing slash (Ver documentacion de Astro (https://docs.astro.build/en/reference/configuration-reference/#trailingslash)) |
| excludedPaths | string[] | [] | Patrones de rutas a excluir en las que no queremos que actue el middleware (soporta glob con micromatch) |
| internalRewrite | boolean | false | Si activar el rewrite interno (la URL con / se mantiene en el navegador pero internamente Express trabaja sin ella creando un proxy interno) |
| redirectStatus | number | 301 | Status de la redirección |
Ejemplo
import { trailingSlash } from '@existo/middlewares';
app.use(trailingSlash({
trailingMode: 'always',
excludedPaths: ['/api/**'],
redirectStatus: 301,
}));
/**
* En este caso se usará el modo 'always' para redirigir las rutas con trailing slash ("/ al final de la ruta")
* Se excluirán las rutas que coincidan con el patrón /api/** ya que provoara errores si hemos declarado los fetchs sin trailing slash
* No se activará el rewrite interno
* Se usará el status code 301 por defecto para las redirecciones
*/redirect
Este middleware se encarga de manejar las redirecciones de las rutas. Su funcionamiento es el siguiente:
- Si encuentra una redirección, se redirige a la URL de destino
- Si no encuentra una redirección, pasa al siguiente middleware
- También maneja los query parameters de las redirecciones
- También maneja las condiciones de país y idioma de las redirecciones
- También maneja las redirecciones externas
- También maneja las redirecciones internas
- También maneja las redirecciones con y sin trailing slash
IMPORTANTE: Para que este middleware funcione correctamente se debe crear un archivo _redirects en la raíz del proyecto, de lo contrario se arrojara un error.
NOTA: En caso de error de parseo del archivo _redirects se arrojara un error con la linea y el mensaje de error correspondiente.
NOTA2: Ten encuenta los trailing slash en la ruta de origen y destino.
Opciones
| Opción | Tipo | Por defecto | Descripción |
|--------|------|-------------|-------------|
| redirectsFilePath | string | '_redirects' | Ruta al archivo de redirecciones (Ruta relativa a la raíz del proyecto) |
Formato del archivo _redirects
# Sintaxis: <from> [queryParams] <to> <status> [Country=xx,yy | Locale=xx,yy]
/old-path /new-path 301
/productos/** /products/:slug 302
/page /other 301 Country=us,gb
/page /otra 301 Locale=es,ca
# Con query parameters
/search q=:query /resultados/:query 302
/legacy ref=promo /nueva-promo 301
/test q=1 /test/:query 302Ejemplo
import { redirect } from '@existo/middlewares';
app.use(redirect({
redirectsFilePath: '_redirects',
}));
/**
* En este caso se usará el archivo _redirects en la raíz del proyecto
*/Ejemplo de redirecciónes válidas:
- Redirección clásica:
/sitio-viejo /sitio-nuevo 301- Redirecion externa:
/sitio-viejo https://www.sitio-nuevo.com 301- Redirección de con patron de wildcard:
/sitio-viejo/** /sitio-nuevo/:slug 301En este caso se redirigira a la ruta /sitio-nuevo/slug-del-sitio-viejo
- Mediante redirecciones internas:
/sitio-viejo /sitio-nuevo/:slug 200
/sitio-viejo /sitio-nuevo/:slug 404
/sitio-viejo /sitio-nuevo/:slug 500Al poner un status code diferente crearas un proxy interno (rewrite) a la ruta /sitio-nuevo/slug-del-sitio-viejo por lo que la url del navegador no cambiara pero internamente Express/Astro trabajara con la ruta /sitio-nuevo/slug-del-sitio-viejo
- Mediante parametros de geolocalización:
/sitio-viejo /sitio-nuevo/:slug 301 Country=us,gb
/sitio-viejo /sitio-nuevo/:slug 301 Locale=es,caEn este caso se redirigira a la ruta /sitio-nuevo/slug-del-sitio-viejo solo si el país es US o GB y el idioma es ES o CA
- Mediante query parameters:
/sitio-viejo q=:query /sitio-nuevo/:query 302En este caso se redirigira a la ruta /sitio-nuevo/query-del-sitio-viejo solo si el query es igual a query-del-sitio-viejo
- Usan query parameters como placeholder:
/sitio-viejo q=:query /sitio-nuevo/:query 302En este caso se capturará el valor del query y se usará como placeholder en la ruta de destino
- Combinación de todas las opciones:
/sitio-viejo/** q=:query /sitio-nuevo/:slug 200 Country=us,gb En este caso se creará un proxy interno (rewrite) a la ruta /sitio-nuevo/slug-del-sitio-viejo solo si el país es US o GB y el query es igual a query-del-sitio-viejo y si la ruta coincide con el patrón /sitio-viejo/**
cacheControl
Este middleware se encarga de establecer el cache control en las respuestas de Astro. La idea es tener dos tipos de cache:
- Cache-Control: max-age=0 por defecto; por defecto (Paginas que no son estáticas como las de blog, productos, etc.)
- Cache-Control: max-age=31536000 y immutable para rutas en options (Paginas estáticas como las de landing, etc. y/o archivos estáticos)
Opciones
| Opción | Tipo | Por defecto | Descripción |
|--------|------|-------------|-------------|
| longCacheRoutes | string[] | [] | Patrones de rutas que recibirán cache de larga duración (max-age=31536000, immutable) (soporta glob con micromatch) |
El resto de rutas reciben public, max-age=0, must-revalidate por defecto.
Ejemplo
import { cacheControl } from '@existo/middlewares';
app.use(cacheControl({
longCacheRoutes: ['/_astro/**', '/fonts/**'],
}));
/**
* En este caso las rutas que coincidan con el patrón /_astro/** y /fonts/** recibirán cache de larga duración
* El resto de rutas reciben `public, max-age=0, must-revalidate` por defecto.
*/notFound
Este middleware se encarga de manejar los errores 404 de Astro.
Parámetros
| Parámetro | Tipo | Por defecto | Descripción |
|-----------|------|-------------|-------------|
| astroHandler | Function | - | El handler de Astro (primer argumento posicional) (Necesario) |
| options.redirectOn404 | boolean | false | Si es true, hace un redirect a /404; si es false (por defecto), hace un rewrite interno sirviendo la página /404 de Astro |
Ejemplo
import { notFound } from '@existo/middlewares';
app.use(notFound(astroHandler));
/**
* En este caso se usará el rewrite interno para servir la página /404 de Astro por defecto
* Si se desea hacer un redirect a `/404` se debe pasar el parametro `redirectOn404: true`
*/routeAnouncer
Este middleware se encarga de anunciar las rutas a nivel de Express globalmente. Sirve para anunciar las rutas a nivel de Express globalmente (Imprime un log en consola).
Opciones
| Opción | Tipo | Por defecto | Descripción |
|--------|------|-------------|-------------|
| announcedPaths | string[] | [] | Patrones de rutas a anunciar (soporta glob con micromatch) |
Ejemplo
import { routeAnouncer } from '@existo/middlewares';
app.use(routeAnouncer({
announcedPaths: ['/api/**'],
}));
/**
* En este caso se anunciarán las rutas que coincidan con el patrón /api/**
* Esto aplica para todos los métodos HTTP (GET, POST, PUT, DELETE, etc.)
*/