Skip to Content
Despliegue

Despliegue

Guía detallada para llevar Nexlo a producción. Backend (API) en Railway, frontend (web) y wiki en Vercel, base de datos PostgreSQL gestionada por Railway.

Topología

AppPlataformaQué desplegarBase de datos
apps/apiRailwayServicio Node (Fastify) sobre la raíz del repoPlugin PostgreSQL de Railway
apps/webVercelProyecto Next.js con root apps/web
apps/wikiVercelProyecto Next.js (Nextra) con root apps/wiki

Orden recomendado: 1) Railway (API + DB) → 2) Vercel (web) → 3) Vercel (wiki). La web necesita la URL del API, y la wiki comparte secreto con el API.


1. API + PostgreSQL en Railway

1.1 Crear el servicio

  1. railway.app New ProjectDeploy from GitHub repo.

  2. Selecciona el repo y deja el Root Directory en la raíz (/). Es obligatorio: los workspaces de pnpm necesitan instalarse desde la raíz.

  3. Railway detecta railway.toml, que ya define todo:

    [build] builder = "NIXPACKS" buildCommand = "pnpm install --frozen-lockfile" [deploy] startCommand = "pnpm start:api" # prisma db push + arranque del servidor healthcheckPath = "/health" healthcheckTimeout = 120 restartPolicyType = "ON_FAILURE"
    • pnpm start:apipnpm --filter @nexlo/api deploy:startprisma db push --skip-generate && tsx src/index.ts.
    • El postinstall de la raíz ejecuta prisma generate automáticamente.

1.2 Añadir PostgreSQL

  1. Dentro del proyecto: New → Database → Add PostgreSQL.
  2. Railway inyecta DATABASE_URL en el servicio del API automáticamente. Si no se enlaza solo, en el servicio del API añade la referencia: DATABASE_URL = ${{Postgres.DATABASE_URL}}.

1.3 Variables de entorno

En el servicio del API → Variables:

# Secretos (genera valores fuertes, p. ej. `openssl rand -hex 32`) JWT_SECRET=<aleatorio-fuerte> WIKI_JWT_SECRET=<aleatorio-fuerte> # EXACTAMENTE el mismo en la wiki # URLs públicas (rellénalas tras desplegar web/wiki; controlan CORS y los links) WEB_URL=https://tu-web.vercel.app WIKI_URL=https://tu-wiki.vercel.app # Cashela CASHELA_WEBHOOK_SECRET=<secreto-compartido-con-cashela> # Cuando tengas credenciales reales (sin ellas se usa el provider mock): # CASHELA_API_URL=https://api.cashela.com # CASHELA_API_KEY=ck_live_... # Producción: desactiva el modo demo (endpoints /dev y simulador de webhooks) DEMO_MODE=false

Sin DATABASE_URL el API arranca en modo memoria con datos demo. En Railway, con el plugin de Postgres, siempre habrá DATABASE_URL, así que corre contra la base real. Define DEMO_MODE=false explícitamente en prod.

1.4 Verificar

  • Logs: debe aparecer [nexlo-api] escuchando en :4000 y el db push sin errores.
  • Healthcheck: https://<api>.up.railway.app/health200.
  • (Opcional) Sembrar datos demo una vez: en Settings → Deploy → Custom Start Command temporal, o desde la consola del servicio: pnpm --filter @nexlo/api db:seed.

2. Web en Vercel

2.1 Crear el proyecto

  1. vercel.com Add New → Project → importa el repo.

  2. Root Directory: apps/web. Vercel detecta el monorepo pnpm y Next.js automáticamente (Framework Preset: Next.js).

  3. Build & Output (normalmente autodetectado, no hace falta tocar):

    • Install Command: pnpm install (Vercel lo ejecuta desde la raíz del workspace).
    • Build Command: next build (o pnpm build).
    • Output Directory: .next.

    Si el build no encuentra @nexlo/shared, fuerza el Install Command a pnpm install --frozen-lockfile y activa Include source files outside of the Root Directory (Settings → Build & Development).

2.2 Variables de entorno

NEXT_PUBLIC_API_URL=https://<api>.up.railway.app # URL del API en Railway NEXT_PUBLIC_WHATSAPP_NUMBER=34600000000 # tu número de WhatsApp Business

NEXT_PUBLIC_* se inyecta en build: si la cambias, hay que redeplegar.

2.3 Multidioma (i18n) — importante

La web usa next-intl sin routing por URL: el idioma se guarda en la cookie NEXT_LOCALE y el idioma por defecto es inglés. Implicaciones en deploy:

  • No hay rutas /en, /es… — todas las URLs son únicas y el contenido se renderiza según la cookie. Por eso todas las páginas son dinámicas (SSR), no estáticas (lo verás como ƒ en el output del build). Es lo esperado.
  • No requiere ninguna variable de entorno adicional.
  • El selector de idioma (🌐 en la navbar) cubre 10 idiomas: en, es, zh, hi, fr, pt, ru, de, ja, id. Las cadenas sin traducir caen a inglés automáticamente.

3. Wiki en Vercel

  1. Add New → Project → mismo repo, Root Directory: apps/wiki.

  2. Framework: Next.js (autodetectado). El postbuild indexa la búsqueda con Pagefind automáticamente.

  3. Variable de entorno:

    WIKI_JWT_SECRET=<el-MISMO-valor-que-en-el-API>
  4. La wiki está cerrada por proxy.ts: sin token JWT válido no se entra. La única puerta es el botón «Wiki interna» del backoffice de la web, que pide un token de 15 min al API y lo cambia por una cookie de sesión de 8 h.

Para que el botón del backoffice funcione, WEB_URL/WIKI_URL en el API y WIKI_JWT_SECRET (API ↔ wiki) deben ser coherentes entre los tres servicios.


4. Webhooks de Cashela

En el panel de Cashela, configura el endpoint de webhooks:

https://<api>.up.railway.app/webhooks/cashela

con el secreto de CASHELA_WEBHOOK_SECRET. En local, el modo demo permite simular cada paso (payin/payout) desde la UI sin Cashela real.


5. Checklist post-despliegue

  • GET https://<api>/health responde 200.
  • La web carga y la calculadora muestra una cotización (usa @nexlo/shared).
  • El selector de idioma cambia el idioma y persiste al recargar (cookie).
  • Registro + login funcionan (la web habla con el API → CORS con WEB_URL ok).
  • Crear un envío demo recorre los estados (o, en prod, llega el webhook de Cashela).
  • El botón «Wiki interna» del backoffice abre la wiki (secreto compartido ok).
  • DEMO_MODE=false en el API de producción.
  • Dominios propios configurados en Vercel y WEB_URL/WIKI_URL actualizados en Railway.

6. Dominios propios (opcional)

  1. Vercel (web y wiki): Settings → Domains → añade nexlo.app y wiki.nexlo.app, configura los DNS que indique Vercel.
  2. Railway (API): Settings → Networking → Custom Domain → api.nexlo.app.
  3. Actualiza en Railway: WEB_URL=https://nexlo.app, WIKI_URL=https://wiki.nexlo.app; y en Vercel (web): NEXT_PUBLIC_API_URL=https://api.nexlo.app. Redespliega web y API.

7. Desarrollo local

pnpm install pnpm dev:api # :4000 (sin DATABASE_URL → modo memoria + datos demo) pnpm dev:web # :3000 pnpm dev:wiki # :3100 pnpm test # shared + api + web

Todo-en-uno con Docker (hot-reload)

pnpm docker:dev # PostgreSQL + API + web + wiki con recarga en caliente pnpm docker:dev:build # reconstruye la imagen tras cambiar dependencias pnpm docker:stop # vuelca la DB a ./backups y detiene el stack

Copias de seguridad de la base (fuera de Docker, en ./backups):

pnpm db:dump # backups/nexlo-<fecha>.sql.gz (+ nexlo-latest.sql.gz) pnpm db:restore # restaura el último dump (en caliente) pnpm db:restore backups/nexlo-XXXX.sql.gz

Detalle del flujo de desarrollo y backups en README.md.

Last updated on