Variables de Entorno
Wabot ofrece dos mecanismos para leer variables de entorno según el contexto:
Env— clase inyectable para leer env vars dentro de servicios y constructores.- Config helpers (
str,num,bool, …) — funciones para pasar env vars directamente en argumentos de decoradores, donde no es posible inyectar dependencias.
Conceptos clave
Env: singleton que encapsula el acceso aprocess.env.requireString(name, options?): obtiene una variable como string (lanza error si no existe y no tiene default).requireNumber(name, options?): obtiene una variable como number.isDevelopment(),isProduction(),isTesting(): verifican el entorno actual basándose enWABOT_ENV.
Lectura de variables
import { Env, singleton } from '@wabot-dev/framework'
@singleton()export class AppConfig { readonly databaseUrl: string readonly port: number readonly apiKey: string readonly maxRetries: number
constructor(env: Env) { // Requerida — lanza error si no existe this.databaseUrl = env.requireString('DATABASE_URL')
// Con valor por defecto this.port = env.requireNumber('PORT', { default: 3000 }) this.apiKey = env.requireString('API_KEY', { default: 'dev-key' }) this.maxRetries = env.requireNumber('MAX_RETRIES', { default: 3 }) }}Entornos de ejecución
La variable WABOT_ENV controla el entorno. Los valores válidos son:
| Valor | Método | Descripción |
|---|---|---|
development | isDevelopment() | Desarrollo local (default si no se define) |
staging | — | Pre-producción |
testing | isTesting() | Ejecución de tests |
production | isProduction() | Producción |
import { Env, injectable } from '@wabot-dev/framework'
@injectable()export class LoggingService { constructor(private env: Env) {}
log(message: string) { if (this.env.isDevelopment()) { console.log(`[DEV] ${message}`) }
if (this.env.isProduction()) { // Enviar a servicio de logging externo this.sendToCloudLogging(message) } }}Variables del framework
Wabot usa estas variables internamente:
| Variable | Tipo | Default | Descripción |
|---|---|---|---|
WABOT_ENV | string | development | Entorno de ejecución |
PORT | number | 3000 | Puerto del servidor HTTP |
JWT_SECRET | string | — | Clave secreta para JWT (requerida si usas auth) |
JWT_ALGORITHM | string | HS256 | Algoritmo de firma JWT |
JWT_ACCESS_EXPIRATION_SECONDS | number | 600 | Expiración del access token |
JWT_REFRESH_EXPIRATION_SECONDS | number | 31536000 | Expiración del refresh token |
SOCKET_CORS_ORIGIN | string | — (CORS deshabilitado) | Orígenes CORS para WebSocket: * o lista separada por comas |
DATABASE_URL | string | — | URL de conexión a PostgreSQL |
Ejemplo completo
Configurar un servicio que se comporta diferente según el entorno:
import { Env, singleton, Logger } from '@wabot-dev/framework'
@singleton()export class PaymentGateway { private readonly apiUrl: string private readonly apiKey: string private readonly sandbox: boolean private logger = new Logger('wabot:payments')
constructor(env: Env) { this.sandbox = !env.isProduction()
if (env.isProduction()) { this.apiUrl = env.requireString('PAYMENT_API_URL') this.apiKey = env.requireString('PAYMENT_API_KEY') } else { this.apiUrl = env.requireString('PAYMENT_API_URL', { default: 'https://sandbox.payments.example.com', }) this.apiKey = env.requireString('PAYMENT_API_KEY', { default: 'sandbox-test-key', }) }
this.logger.info('PaymentGateway inicializado', { sandbox: this.sandbox, url: this.apiUrl, }) }
async charge(amount: number, currency: string) { if (this.sandbox) { this.logger.debug('Pago simulado (sandbox)', { amount, currency }) return { success: true, transactionId: 'sandbox-' + Date.now() } }
// Lógica real de cobro const response = await fetch(`${this.apiUrl}/charge`, { method: 'POST', headers: { 'Authorization': `Bearer ${this.apiKey}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ amount, currency }), })
return await response.json() }}Config helpers para decoradores
Los decoradores en TypeScript se evalúan cuando el módulo se carga, antes de que el contenedor DI exista. Esto significa que no puedes inyectar Env dentro de un argumento de decorador. Para ese caso existen los config helpers.
Los config helpers son funciones de template tag que crean una referencia diferida a una variable de entorno. El framework la resuelve automáticamente cuando el decorador se registra.
Sintaxis
import { str, num, bool, strArr } from '@wabot-dev/framework'
str`ruta.al.valor` // string requeridostr`ruta.al.valor:default` // string con valor por defectonum`ruta.al.valor:3000` // número con valor por defectobool`ruta.al.valor:false` // boolean con valor por defectostrArr`ruta.al.valor` // array de strings (separados por comas o JSON)Conversión de ruta a nombre de variable
El framework convierte la ruta a mayúsculas reemplazando puntos por guiones bajos:
| Config helper | Variable de entorno |
|---|---|
str\whatsapp.number“ | WHATSAPP_NUMBER |
str\telegram.bot_token“ | TELEGRAM_BOT_TOKEN |
str\socket.namespace“ | SOCKET_NAMESPACE |
num\server.port:3000“ | SERVER_PORT |
bool\feature.enabled:false“ | FEATURE_ENABLED |
Uso en decoradores de canal
Los decoradores @whatsApp() y @socket() aceptan config helpers directamente en sus argumentos. Esto evita escribir process.env.X! y centraliza toda la configuración en variables de entorno:
import { chatController, chatBot, ChatBot, cmd, whatsApp, socket, str, type IReceivedMessage } from '@wabot-dev/framework'import { MyMindset } from '@/mindsets/MyMindset'
@chatController()export class MyChatController { constructor(@chatBot(MyMindset) private bot: ChatBot) {}
// Sin config helper — funciona, pero es frágil @whatsApp({ number: process.env.WHATSAPP_NUMBER! }) onWhatsAppMessage(ctx: IReceivedMessage) { this.bot.sendMessage(ctx.message, (reply) => ctx.reply(reply)) }}import { chatController, chatBot, ChatBot, whatsApp, telegram, socket, str, type IReceivedMessage } from '@wabot-dev/framework'import { MyMindset } from '@/mindsets/MyMindset'
@chatController()export class MyChatController { constructor(@chatBot(MyMindset) private bot: ChatBot) {}
// Con config helper — tipado, con mensajes de error claros si falta la variable @whatsApp({ number: str`whatsapp.number`, // WHATSAPP_NUMBER accessToken: str`whatsapp.access_token`, // WHATSAPP_ACCESS_TOKEN businessNumberId: str`whatsapp.business_number_id`, // WHATSAPP_BUSINESS_NUMBER_ID }) onWhatsAppMessage(ctx: IReceivedMessage) { this.bot.sendMessage(ctx.message, (reply) => ctx.reply(reply)) }
@socket({ namespace: str`socket.namespace:chat`, // SOCKET_NAMESPACE, default: 'chat' }) onSocketMessage(ctx: IReceivedMessage) { this.bot.sendMessage(ctx.message, (reply) => ctx.reply(reply)) }}Nota:
@telegram()también admite config helpers:@telegram({ botToken: str\telegram.bot_token` })resuelveTELEGRAM_BOT_TOKEN`.
Helpers disponibles
| Helper | Tipo resuelto | Ejemplo |
|---|---|---|
str | string | str\api.key“ |
num | number | num\server.port:3000“ |
bool | boolean | bool\feature.enabled:false“ |
obj | object (JSON) | obj\service.config“ |
strArr | string[] | strArr\allowed.origins“ |
numArr | number[] | numArr\retry.delays“ |
boolArr | boolean[] | boolArr\feature.flags“ |
Para arrays, el valor en la variable de entorno puede ser JSON (["a","b"]) o valores separados por comas (a,b).
Siguiente paso
Agrega logging estructurado a tu aplicación: Logger.