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 .env antes de resolver env.

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 — Lee process.env[name] o fallback, y lanza un error si el resultado queda vacío.
  • requireSecret — Reusa requireEnv y exige un secreto con al menos 32 caracteres.
  • requireHexKey — Reusa requireEnv y valida una cadena hexadecimal con longitud exacta.
  • resolveMongoUri — Devuelve MONGODB_URI si 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 si MONGODB_URI falta en producción. resolveAltchaRedisUrl() hace lo mismo si ALTCHA_REDIS_URL y REDIS_URL están vacías en producción.

[!CAUTION] requireEnv() considera vacío un string ''. Además, los campos numéricos usan Number(...) || default, así que valores no numéricos o cero pueden caer al valor por defecto.

[!IMPORTANT] emailAssetsUrl está separado de appUrl. El archivo lo trata como origen canónico para assets estáticos en emails transaccionales.

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.