Functions
apps/functions/README.md
Firebase Cloud Functions
apps/functions contiene el backend ejecutado por Firebase Cloud Functions v2. Es una app
independiente dentro del monorepo: sus funciones se compilan con tsup, se agrupan desde
src/index.ts y Firebase despliega el resultado usando la configuración de firebase.json.
Este documento explica cómo trabajar en el runtime, sin enumerar las funciones públicas. La API expuesta se documentará por separado.
Documentación interna disponible en el showcase:
Responsabilidades y límites
apps/functions: entry point, handlers, composición por dominio, configuración runtime y lógica específica de esta app de Functions.packages/app-function-services: casos de uso server-side reutilizables.packages/firebase-admin-adapter: acceso encapsulado a Firebase Admin.packages/schemas: contratos Zod compartidos para inputs, outputs y datos de dominio.packages/constants: paths, permisos y constantes transversales.
No importar el SDK de Firebase directamente desde componentes de apps. Dentro de Functions, preferir los adapters y services compartidos antes de crear wrappers locales.
Estructura
apps/functions/
├── src/index.ts # Entry point; agrupa exports por dominio
├── src/domains/ # Barrels de cada dominio desplegable
│ ├── auth/index.ts
│ ├── email/index.ts
│ └── users/index.ts
├── src/http-functions/ # Handlers, servicios y soporte HTTP/Callable
│ ├── auth/
│ ├── email/
│ └── users/
├── src/config/ # Secrets y carga de entorno local
├── tsup.config.ts # Bundle ESM para Node 22
├── vitest.config.ts # Tests Node
└── package.json # Scripts y dependencias runtime
Flujo de exports
- Un dominio agrega o reexporta sus módulos desde
src/domains/<domain>/index.ts. src/index.tsimporta los barrels de dominio y los exporta agrupados.firebase.jsonusaapps/functionscomosource.- Firebase resuelve el
maindel package,lib/index.js, después del build.
Al agregar una función, actualizar el barrel del área y el barrel del dominio. No crear un segundo entry point ni exportar directamente desde una app cliente.
Tipos de handler
Callable
Usar onCall para operaciones invocadas mediante el SDK de Firebase Functions. El handler recibe un
objeto request, valida request.data, puede inspeccionar request.auth y devuelve un payload
serializable validado.
HTTP/Express
Usar onRequest cuando se necesita una API HTTP tradicional, middleware Express, rutas versionadas,
headers o integración con herramientas externas. La app Express debe separar middleware, rutas,
servicios y mapeo de errores.
Validación obligatoria
Cada handler debe validar el input antes de ejecutar servicios o acceder a Firebase:
const parsed = inputSchema.safeParse(request.data);
if (!parsed.success) {
return outputSchema.parse({
success: false,
message: 'Invalid payload',
});
}
const result = await service(parsed.data);
return outputSchema.parse(result);
- Los schemas viven en
@repo/schemascuando el contrato se comparte. - Los tipos se derivan de los schemas; no duplicar interfaces equivalentes.
- Validar también las respuestas antes de devolverlas.
- Ejecutar autorización antes de la operación de negocio cuando el dominio lo requiera.
- Los errores internos deben mapearse a códigos y mensajes controlados, sin filtrar secretos ni detalles del Admin SDK.
Configuración y secretos
La configuración de runtime está en src/config/README.md. Los secretos
SMTP se declaran con defineSecret y se vinculan a cada función mediante la opción secrets.
Para desarrollo local, el proveedor SMTP carga variables desde apps/functions/.env.local cuando
NODE_ENV=development. No commitear valores reales ni completar .env.example con credenciales.
Variables SMTP esperadas:
SMTP_USER, SMTP_PASS, SMTP_HOST, SMTP_PORT, SMTP_FROM_EMAIL, SMTP_FROM_NAME.
Desarrollo local
Desde la raíz:
pnpm dev:emulators
Esto construye los packages necesarios y levanta Auth, Firestore y Functions. Para persistir datos:
pnpm dev:emulators:persist
Desde apps/functions:
pnpm run build
pnpm run serve
pnpm run shell
Los emuladores definidos en firebase.json usan Auth 9099, Firestore 8080, Functions 5001 y
la UI de Firebase 4000.
Build y bundle
tsup.config.ts compila src/index.ts a lib/ como ESM para Node 22, genera sourcemaps y mantiene
firebase-functions y firebase-admin como dependencias externas. Los packages internos listados
en noExternal se incluyen en el bundle.
No editar lib/ manualmente. Es salida generada y se regenera con pnpm run build.
Deploy
Los comandos del package seleccionan el proyecto Firebase y despliegan solo Functions:
pnpm --dir apps/functions run deploy:dev
pnpm --dir apps/functions run deploy:prod
firebase.json ejecuta antes del deploy:
pnpm --dir apps/functions build.node scripts/functions-predeploy.js, que adapta temporalmentepackage.jsonpara eliminar dependenciasworkspace:*.
Después ejecuta node scripts/functions-postdeploy.js, que restaura el package.json original.
No interrumpir este proceso ni commitear el package.json temporal.
Tests y validaciones
Vitest usa entorno Node, busca **/*.test.ts y permite el alias @/* hacia src/*.
pnpm --dir apps/functions test
pnpm --dir apps/functions test:run
pnpm --dir apps/functions lint
pnpm --dir apps/functions build
Los tests deben cubrir validaciones rechazadas, autorización, mapeo de errores, servicios externos y casos de éxito. Mockear adapters, secrets y proveedores externos; no depender de credenciales reales.
Flujo para agregar código
- Leer
packages/README.mdy.opencode/rules/packages.md. - Buscar schemas, services, adapters y constantes existentes.
- Elegir dominio y capa: handler, service, helper, error, config o template.
- Agregar schema de input/output antes del handler si el contrato es nuevo.
- Validar input, autorización y output.
- Registrar el módulo en los barrels correspondientes.
- Agregar tests junto al módulo.
- Ejecutar lint, tests y build antes de desplegar.