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

text
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

  1. Un dominio agrega o reexporta sus módulos desde src/domains/<domain>/index.ts.
  2. src/index.ts importa los barrels de dominio y los exporta agrupados.
  3. firebase.json usa apps/functions como source.
  4. Firebase resuelve el main del 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:

ts
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/schemas cuando 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:

bash
pnpm dev:emulators

Esto construye los packages necesarios y levanta Auth, Firestore y Functions. Para persistir datos:

bash
pnpm dev:emulators:persist

Desde apps/functions:

bash
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:

bash
pnpm --dir apps/functions run deploy:dev
pnpm --dir apps/functions run deploy:prod

firebase.json ejecuta antes del deploy:

  1. pnpm --dir apps/functions build.
  2. node scripts/functions-predeploy.js, que adapta temporalmente package.json para eliminar dependencias workspace:*.

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/*.

bash
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

  1. Leer packages/README.md y .opencode/rules/packages.md.
  2. Buscar schemas, services, adapters y constantes existentes.
  3. Elegir dominio y capa: handler, service, helper, error, config o template.
  4. Agregar schema de input/output antes del handler si el contrato es nuevo.
  5. Validar input, autorización y output.
  6. Registrar el módulo en los barrels correspondientes.
  7. Agregar tests junto al módulo.
  8. Ejecutar lint, tests y build antes de desplegar.