Configuración de entorno de la API
Overview
Este módulo, en services/api/src/config/env.js, centraliza la lectura de variables de entorno de la API. Construye un único objeto env con valores normalizados, defaults y validaciones estrictas para secretos y conexiones críticas. También define la URI local de MongoDB usada fuera de producción.
[!NOTE]
dotenv.config()se ejecuta al importar el archivo. Eso permite leer variables desde.envantes de resolverenv.
How it works
flowchart TB
Dotenv[dotenv]
ProcessEnv[process env]
RequireEnv[requireEnv]
RequireSecret[requireSecret]
RequireHexKey[requireHexKey]
ResolveMongoUri[resolveMongoUri]
ResolveAltchaRedisUrl[resolveAltchaRedisUrl]
EnvObj[env]
Dotenv -->|configura| ProcessEnv
ProcessEnv --> RequireEnv
ProcessEnv --> RequireSecret
ProcessEnv --> RequireHexKey
ProcessEnv --> ResolveMongoUri
ProcessEnv --> ResolveAltchaRedisUrl
RequireEnv --> EnvObj
RequireSecret --> EnvObj
RequireHexKey --> EnvObj
ResolveMongoUri --> EnvObj
ResolveAltchaRedisUrl --> EnvObj
El módulo carga dotenv, luego define ayudantes de validación, y al final exporta env. Cada valor sensible se resuelve en tiempo de importación. Si falta una variable obligatoria, el proceso falla rápido. Eso evita arrancar con configuración parcial o insegura.
Key components
requireEnv
{
"title": "requireEnv",
"content": "Lee una variable de entorno o un fallback y lanza un Error cuando el valor final queda vac\u00edo o no est\u00e1 definido.",
"type": "function",
"filePath": "services/api/src/config/env.js",
"badges": []
}
Esta función es la base de la validación. Usa process.env[name] y cae al fallback solo si el valor es nulo o indefinido. Si el resultado sigue vacío, lanza Falta variable de entorno requerida: ${name}.
requireSecret
{
"title": "requireSecret",
"content": "Valida un secreto obligatorio y exige al menos 32 caracteres.",
"type": "function",
"filePath": "services/api/src/config/env.js",
"badges": []
}
Esta función delega en requireEnv. Después comprueba la longitud del valor. Si mide menos de 32 caracteres, lanza un error.
requireHexKey
{
"title": "requireHexKey",
"content": "Valida una clave hexadecimal con longitud exacta en bytes convertidos a caracteres hex.",
"type": "function",
"filePath": "services/api/src/config/env.js",
"badges": []
}
Esta función también delega en requireEnv. Calcula la longitud esperada como byteLength * 2. Además exige que el valor contenga solo caracteres hexadecimales.
resolveMongoUri
{
"title": "resolveMongoUri",
"content": "Devuelve MONGODB_URI cuando existe, usa la URI local fuera de producci\u00f3n y falla en producci\u00f3n si falta.",
"type": "function",
"filePath": "services/api/src/config/env.js",
"badges": []
}
Esta función resuelve la conexión principal de MongoDB. Si existe MONGODB_URI, la devuelve. En producción, la ausencia de MONGODB_URI detiene el arranque. Fuera de producción, usa mongodb://127.0.0.1:27017/plano_backoffice.
resolveAltchaRedisUrl
{
"title": "resolveAltchaRedisUrl",
"content": "Resuelve la URL de Redis para replay protection de ALTCHA y exige configuraci\u00f3n en producci\u00f3n.",
"type": "function",
"filePath": "services/api/src/config/env.js",
"badges": []
}
Esta función busca ALTCHA_REDIS_URL y luego REDIS_URL. Si ninguno existe, devuelve cadena vacía. En producción, una URL vacía provoca un error. El mensaje indica que esta conexión es necesaria para replay protection.
env [badge:public]
El objeto env es la salida principal del archivo. Agrupa la configuración ya resuelta y validada. El resto del backend consume este objeto en lugar de leer process.env directamente.
Data & fields
Este bloque describe las propiedades exportadas por env. Los campos combinan valores obligatorios, defaults locales y credenciales opcionales. Las agrupaciones reflejan la estructura funcional del archivo.
[
{
"name": "port",
"type": "number",
"required": true,
"description": "Puerto HTTP de la API. Usa PORT o 3000."
},
{
"name": "mongoUri",
"type": "string",
"required": true,
"description": "URI de MongoDB. Usa MONGODB_URI o la URI local mongodb://127.0.0.1:27017/plano_backoffice fuera de producción."
},
{
"name": "jwtSecret",
"type": "string",
"required": true,
"description": "Secreto obligatorio para firmar JWT."
},
{
"name": "jwtExpiresIn",
"type": "string",
"description": "Duración de los JWT. Usa JWT_EXPIRES_IN o 7d."
},
{
"name": "clientOrigin",
"type": "string",
"description": "Origen permitido del cliente web. Usa CLIENT_ORIGIN o http://localhost:5174."
},
{
"name": "appUrl",
"type": "string",
"description": "URL base de la app web. Usa APP_URL o http://localhost:5174."
},
{
"name": "apiUrl",
"type": "string",
"description": "URL base de la API. Usa API_URL o http://localhost:3000."
}
]
[
{
"name": "altchaHmacSecret",
"type": "string",
"required": true,
"description": "Secreto HMAC de ALTCHA. Debe tener al menos 32 caracteres."
},
{
"name": "altchaChallengeTtlMs",
"type": "number",
"description": "Tiempo de vida del challenge ALTCHA en milisegundos. Usa ALTCHA_CHALLENGE_TTL_MS o 300000."
},
{
"name": "altchaRedisUrl",
"type": "string",
"description": "URL de Redis usada para replay protection. Usa ALTCHA_REDIS_URL o REDIS_URL."
},
{
"name": "retentionEncryptionKey",
"type": "string",
"required": true,
"description": "Clave hexadecimal de 32 bytes para cifrado del canal de contacto de retención."
}
]
[
{
"name": "emailAssetsUrl",
"type": "string",
"description": "Origen de assets estáticos embebidos en emails transaccionales. Usa EMAIL_ASSETS_URL o cadena vacía."
},
{
"name": "googleClientId",
"type": "string",
"description": "Client ID de Google Identity Services. Usa GOOGLE_CLIENT_ID o cadena vacía."
},
{
"name": "twilioAccountSid",
"type": "string",
"description": "Account SID de Twilio. Usa TWILIO_ACCOUNT_SID o cadena vacía."
},
{
"name": "twilioApiKeySid",
"type": "string",
"description": "API Key SID de Twilio. Usa TWILIO_API_KEY_SID o cadena vacía."
},
{
"name": "twilioApiKeySecret",
"type": "string",
"description": "API Key Secret de Twilio. Usa TWILIO_API_KEY_SECRET o cadena vacía."
},
{
"name": "twilioVerifyServiceSid",
"type": "string",
"description": "Verify Service SID de Twilio. Usa TWILIO_VERIFY_SERVICE_SID o cadena vacía."
},
{
"name": "resendApiKey",
"type": "string",
"description": "API key de Resend. Usa RESEND_API_KEY o cadena vacía."
},
{
"name": "resendFrom",
"type": "string",
"description": "Remitente por defecto para emails. Usa RESEND_FROM o cadena vacía."
},
{
"name": "resendReplyTo",
"type": "string",
"description": "Dirección reply-to para emails. Usa RESEND_REPLY_TO o cadena vacía."
}
]
[
{
"name": "imagekitPublicKey",
"type": "string",
"description": "Clave pública de ImageKit. Usa IMAGEKIT_PUBLIC_KEY o cadena vacía."
},
{
"name": "imagekitPrivateKey",
"type": "string",
"description": "Clave privada de ImageKit. Usa IMAGEKIT_PRIVATE_KEY o cadena vacía."
},
{
"name": "imagekitUrlEndpoint",
"type": "string",
"description": "Endpoint de URL de ImageKit. Usa IMAGEKIT_URL_ENDPOINT o cadena vacía."
},
{
"name": "imagekitMaxImagesPerListing",
"type": "number",
"description": "Máximo de imágenes por listing. Usa IMAGEKIT_MAX_IMAGES_PER_LISTING o 6."
},
{
"name": "imagekitMaxImageSizeMB",
"type": "number",
"description": "Tamaño máximo por imagen en MB. Usa IMAGEKIT_MAX_IMAGE_SIZE_MB o 10."
},
{
"name": "imagekitMaxDocumentsPerListing",
"type": "number",
"description": "Máximo de documentos fuente por listing. Usa IMAGEKIT_MAX_DOCUMENTS_PER_LISTING o 1."
},
{
"name": "imagekitMaxDocumentSizeMB",
"type": "number",
"description": "Tamaño máximo por documento en MB. Usa IMAGEKIT_MAX_DOCUMENT_SIZE_MB o 15."
}
]
[
{
"name": "stripeSecretKey",
"type": "string",
"description": "Clave secreta de Stripe. Usa STRIPE_SECRET_KEY o cadena vacía."
},
{
"name": "stripeWebhookSecret",
"type": "string",
"description": "Secreto del webhook de Stripe. Usa STRIPE_WEBHOOK_SECRET o cadena vacía."
},
{
"name": "stripePriceProfessionalMonthly",
"type": "string",
"description": "Price ID mensual del plan Professional. Usa STRIPE_PRICE_PROFESSIONAL_MONTHLY o cadena vacía."
},
{
"name": "stripePriceCreatorMonthly",
"type": "string",
"description": "Price ID mensual del plan Creator. Usa STRIPE_PRICE_CREATOR_MONTHLY o cadena vacía."
},
{
"name": "stripeBillingPortalReturnUrl",
"type": "string",
"description": "URL de retorno para el Billing Portal. Usa STRIPE_BILLING_PORTAL_RETURN_URL o cadena vacía."
}
]
Behaviors
requireEnv— Leeprocess.env[name]ofallback, y lanza un error si el resultado queda vacío.requireSecret— ReusarequireEnvy exige un secreto con al menos 32 caracteres.requireHexKey— ReusarequireEnvy valida una cadena hexadecimal con longitud exacta.resolveMongoUri— DevuelveMONGODB_URIsi existe, o la URI local fuera de producción.resolveAltchaRedisUrl— Devuelve la URL de Redis de ALTCHA y exige valor en producción.env— Exporta la configuración final usada por el resto del backend.
Gotchas & edge cases
[!WARNING]
resolveMongoUri()lanza un error siMONGODB_URIfalta en producción.resolveAltchaRedisUrl()hace lo mismo siALTCHA_REDIS_URLyREDIS_URLestán vacías en producción.
[!CAUTION]
requireEnv()considera vacío un string''. Además, los campos numéricos usanNumber(...) || default, así que valores no numéricos o cero pueden caer al valor por defecto.
[!IMPORTANT]
emailAssetsUrlestá separado deappUrl. El archivo lo trata como origen canónico para assets estáticos en emails transaccionales.
Related
services/api/src/config/db.js consume env.mongoUri para conectar Mongoose con MongoDB. services/api/src/app.js importa env para el wiring de arranque del servidor. Ambos vínculos aparecen en los fragmentos de código proporcionados.