--- id: "MIGRACION-SUPABASE-EXPRESS" title: "Supabase a Express Backend" type: "Documentation" project: "trading-platform" version: "1.0.0" updated_date: "2026-01-04" --- # Plan de Migracion: Supabase a Express Backend **Ultima actualizacion:** 2025-12-05 **Proyecto origen:** stc-platform-web (Supabase) **Proyecto destino:** trading-platform (Express + pg + PostgreSQL) **Patrones base:** `shared/catalog/auth/` *(inspiración arquitectónica de patrones auth y estructura)* --- ## Resumen Ejecutivo Este documento detalla el plan de migracion del frontend existente en `stc-platform-web` que usa Supabase como backend hacia una arquitectura con Express.js como backend propio. **Estado Actual del Backend:** El backend de trading-platform (`/home/isem/workspace/projects/trading-platform/apps/backend/`) ya tiene implementacion funcional con: - Express.js + TypeScript - Autenticacion JWT completa (registro, login, refresh tokens, sesiones) - OAuth multi-proveedor (Google, Facebook, Twitter, Apple, GitHub) - 2FA con TOTP (speakeasy) - Autenticacion por telefono (Twilio SMS/WhatsApp) - PostgreSQL con pg driver nativo - Estructura modular escalable - Guards, interceptors, filters adaptados de patrones NestJS **Patrones de Gamilit Adaptados:** Se tomaron patrones del proyecto Gamilit y se adaptaron para Express: - Constantes compartidas (DB_SCHEMAS, enums) - Estructura de servicios modulares - Manejo de errores unificado - Guards de autenticacion y autorizacion --- ## 1. Arquitectura Objetivo ### Antes (Supabase) ``` Frontend (React) ──> Supabase Client ──> Supabase (Auth + DB + Storage) │ ├── Auth (Built-in) ├── PostgreSQL ├── Realtime └── Storage ``` ### Despues (Express Backend Propio) ``` Frontend (React) ──> API Client ──> Express Backend ──> PostgreSQL │ │ │ ├── JWT Auth (jsonwebtoken + Passport) │ ├── pg driver (Multi-schema) │ ├── Redis (Sessions/Cache) │ ├── S3/MinIO (Storage) │ └── Socket.io (Realtime) │ └── WebSocket ──> Realtime Updates ``` --- ## 2. Estructura Backend Actual (Express) ``` apps/backend/src/ ├── index.ts # Entry point Express server ├── config/ │ └── index.ts # Configuracion centralizada ├── core/ │ ├── decorators/ │ ├── filters/ │ │ ├── http-exception.filter.ts # Excepciones HTTP unificadas │ │ └── index.ts │ ├── guards/ │ │ ├── auth.guard.ts # Guards de autenticacion │ │ └── index.ts │ ├── interceptors/ │ │ ├── transform-response.interceptor.ts │ │ └── index.ts │ └── middleware/ │ ├── auth.middleware.ts # Middleware JWT │ ├── error-handler.ts # Manejador de errores │ ├── not-found.ts # 404 handler │ └── rate-limiter.ts # Rate limiting ├── modules/ │ ├── auth/ │ │ ├── auth.routes.ts # Rutas de auth │ │ ├── controllers/ │ │ │ └── auth.controller.ts # Controller principal │ │ ├── services/ │ │ │ ├── email.service.ts # Auth email/password │ │ │ ├── oauth.service.ts # OAuth multi-provider │ │ │ ├── phone.service.ts # SMS/WhatsApp (Twilio) │ │ │ ├── token.service.ts # JWT tokens │ │ │ └── twofa.service.ts # 2FA TOTP │ │ ├── strategies/ # Passport strategies │ │ ├── types/ │ │ │ └── auth.types.ts # Tipos de auth │ │ └── validators/ │ │ └── auth.validators.ts # Validacion Zod │ ├── users/ │ │ └── users.routes.ts │ ├── education/ │ │ └── education.routes.ts │ ├── trading/ │ │ └── trading.routes.ts │ ├── investment/ │ │ └── investment.routes.ts │ ├── payments/ │ │ └── payments.routes.ts │ └── admin/ │ └── admin.routes.ts └── shared/ ├── constants/ │ ├── database.constants.ts # DB_SCHEMAS, DB_TABLES │ ├── enums.constants.ts # UserStatusEnum, etc │ ├── routes.constants.ts # API_ROUTES │ └── index.ts ├── database/ │ └── index.ts # Pool de conexiones pg ├── dto/ ├── types/ │ ├── common.types.ts # ApiResponse, PaginatedResult │ └── index.ts └── utils/ └── logger.ts # Winston logger ``` ### Servicios de Auth Implementados | Servicio | Archivo | Funcionalidades | |----------|---------|-----------------| | **EmailService** | `email.service.ts` | Registro, login, verificacion email, reset password | | **OAuthService** | `oauth.service.ts` | Google, Facebook, Twitter, Apple, GitHub | | **PhoneService** | `phone.service.ts` | SMS OTP, WhatsApp OTP (Twilio) | | **TokenService** | `token.service.ts` | JWT access/refresh, sesiones | | **TwoFAService** | `twofa.service.ts` | TOTP setup, verify, backup codes | --- ## 3. Estructura Backend Gamilit (Referencia) ``` gamilit/apps/backend/src/ ├── main.ts # Bootstrap con Swagger, CORS, ValidationPipe ├── app.module.ts # Root module con multi-schema TypeORM ├── config/ │ ├── app.config.ts # Configuracion de aplicacion │ ├── database.config.ts # Configuracion PostgreSQL │ ├── jwt.config.ts # Configuracion JWT │ └── env.config.ts # Variables de entorno ├── modules/ │ ├── auth/ # ✅ REUTILIZAR PATRON │ │ ├── auth.module.ts # Module definition │ │ ├── controllers/ │ │ │ ├── auth.controller.ts # Login, Register, Refresh │ │ │ ├── password.controller.ts # Forgot/Reset password │ │ │ └── users.controller.ts # User CRUD │ │ ├── services/ │ │ │ ├── auth.service.ts # Core auth logic (25KB) │ │ │ ├── session-management.service.ts │ │ │ ├── security.service.ts │ │ │ ├── password-recovery.service.ts │ │ │ └── email-verification.service.ts │ │ ├── entities/ │ │ │ ├── user.entity.ts │ │ │ ├── profile.entity.ts │ │ │ ├── user-session.entity.ts │ │ │ ├── auth-attempt.entity.ts │ │ │ ├── auth-provider.entity.ts # OAuth providers │ │ │ └── ... │ │ ├── strategies/ │ │ │ └── jwt.strategy.ts # Passport JWT strategy │ │ ├── guards/ │ │ │ └── jwt-auth.guard.ts │ │ ├── dto/ │ │ │ ├── register-user.dto.ts │ │ │ ├── login.dto.ts │ │ │ └── ... │ │ └── decorators/ │ │ └── current-user.decorator.ts │ ├── educational/ # → Adaptar para courses │ ├── progress/ # → Adaptar para course progress │ ├── gamification/ # → Adaptar para trading stats │ ├── notifications/ # ✅ REUTILIZAR │ ├── websocket/ # ✅ REUTILIZAR para signals realtime │ └── health/ # ✅ REUTILIZAR └── shared/ ├── constants/ │ ├── database.constants.ts # DB_SCHEMAS, DB_TABLES │ ├── routes.constants.ts # API_PREFIX, API_VERSION │ └── enums.constants.ts # Shared enums ├── decorators/ ├── dto/ ├── filters/ │ └── http-exception.filter.ts # Global exception handler ├── guards/ ├── interceptors/ │ ├── transform-response.interceptor.ts │ └── rls.interceptor.ts # Row Level Security ├── middleware/ ├── pipes/ └── utils/ ``` ### Archivos Clave a Copiar/Adaptar | Archivo Gamilit | Destino Trading Platform | Adaptaciones | |-----------------|-------------------------|--------------| | `main.ts` | `apps/backend/src/main.ts` | Cambiar nombre app, tags Swagger | | `app.module.ts` | `apps/backend/src/app.module.ts` | Cambiar schemas y modules | | `auth/` (completo) | `modules/auth/` | Agregar OAuth providers adicionales | | `shared/` (completo) | `shared/` | Adaptar constantes y enums | | `websocket/` | `modules/websocket/` | Adaptar eventos para trading signals | | `health/` | `modules/health/` | Sin cambios | --- ## 4. Mapeo de Supabase a Express ### 4.1 Autenticacion | Supabase Method | Express Equivalent | Endpoint | |-----------------|-------------------|----------| | `supabase.auth.signInWithPassword()` | POST `/api/v1/auth/login` | JWT + Refresh Token | | `supabase.auth.signUp()` | POST `/api/v1/auth/register` | Validacion + Email verification | | `supabase.auth.signInWithOAuth()` | GET `/api/v1/auth/oauth/:provider/url` | Passport OAuth strategies | | `supabase.auth.signOut()` | POST `/api/v1/auth/logout` | Invalidar tokens | | `supabase.auth.resetPasswordForEmail()` | POST `/api/v1/auth/forgot-password` | Nodemailer | | `supabase.auth.updateUser()` | PATCH `/api/v1/auth/password` | Validacion + bcrypt | | `supabase.auth.verifyOtp()` | POST `/api/v1/auth/verify-email` | Token validation | | `supabase.auth.refreshSession()` | POST `/api/v1/auth/refresh` | JWT refresh rotation | | `supabase.auth.getUser()` | GET `/api/v1/auth/me` | JWT middleware | | `supabase.auth.getSession()` | (Client-side localStorage) | JWT decode | | `supabase.auth.onAuthStateChange()` | (Client-side event) | Estado local | ### 4.2 Queries de Base de Datos | Supabase Query | Express/TypeORM Equivalent | |----------------|---------------------------| | `.from('table').select()` | `repository.find()` | | `.from('table').select('*', { count: 'exact' })` | `repository.findAndCount()` | | `.from('table').insert()` | `repository.save()` | | `.from('table').update()` | `repository.update()` | | `.from('table').delete()` | `repository.delete()` | | `.eq('column', value)` | `{ where: { column: value } }` | | `.in('column', [values])` | `{ where: { column: In([values]) } }` | | `.order('column', { ascending: true })` | `{ order: { column: 'ASC' } }` | | `.range(0, 10)` | `{ skip: 0, take: 10 }` | | `.single()` | `repository.findOne()` | | `.rpc('function_name', params)` | Service method / Raw query | ### 4.3 Realtime | Supabase Realtime | Express Equivalent | |-------------------|-------------------| | `.channel()` | Socket.io room | | `.on('postgres_changes', ...)` | Socket.io events + DB triggers | | `.subscribe()` | `socket.join(room)` | | `supabase.removeChannel()` | `socket.leave(room)` | ### 4.4 Storage | Supabase Storage | Express Equivalent | |------------------|-------------------| | `supabase.storage.from('bucket').upload()` | Multer + S3/MinIO | | `supabase.storage.from('bucket').download()` | S3 signed URLs | | `supabase.storage.from('bucket').getPublicUrl()` | CDN URL generation | --- ## 5. Servicios a Migrar (Frontend) ### 5.1 AuthService Migration **Archivo origen:** `src/features/auth/services/authService.ts` **Archivo destino:** `apps/frontend/src/services/auth.service.ts` ```typescript // ANTES (Supabase) async signInWithEmail(email: string, password: string) { const { data, error } = await supabase.auth.signInWithPassword({ email, password }); // ... } // DESPUES (Express API) async signInWithEmail(email: string, password: string) { const response = await api.post('/auth/login', { email, password }); if (response.data.accessToken) { tokenService.setTokens( response.data.accessToken, response.data.refreshToken ); } return response.data; } ``` ### 5.2 CourseService Migration **Archivo origen:** `src/features/courses/services/courseService.ts` **Archivo destino:** `apps/frontend/src/services/course.service.ts` ```typescript // ANTES (Supabase) async getCourses(filters, sort, page, perPage) { let query = supabase .from('education.courses') .select(`*, instructor:profiles!instructor_id(...)`) // ... } // DESPUES (Express API) async getCourses(filters, sort, page, perPage) { const params = new URLSearchParams({ page: String(page), per_page: String(perPage), sort_field: sort.field, sort_dir: sort.direction, ...filters }); return api.get(`/courses?${params}`); } ``` ### 5.3 SignalService Migration **Archivo origen:** `src/features/trading/services/signalService.ts` **Cambios principales:** 1. Queries → API calls 2. Realtime subscriptions → WebSocket ```typescript // DESPUES (WebSocket) subscribeToSignals(botIds: string[], callback: (signal) => void) { socket.emit('subscribe:signals', { botIds }); socket.on('signal:new', callback); return () => socket.off('signal:new', callback); } ``` --- ## 6. Estructura de Carpetas Destino > **Nota:** La estructura del backend ya está implementada. Ver Sección 2 para el estado actual. > La siguiente es la estructura objetivo del frontend. ### 6.1 Frontend (React) ``` apps/frontend/src/ ├── services/ │ ├── api.ts # Axios instance con interceptores │ ├── auth.service.ts # Migrado de authService.ts │ ├── course.service.ts # Migrado de courseService.ts │ ├── trading.service.ts # Consolidado de trading services │ ├── payment.service.ts # Migrado de stripeService.ts │ └── socket.service.ts # NUEVO: WebSocket client ├── stores/ │ └── auth.store.ts # Migrar con cambios de API ├── hooks/ │ ├── useAuth.ts # Mantener logica, cambiar imports │ └── ... └── lib/ └── socket.ts # Socket.io client config ``` --- ## 7. Backend Endpoints a Implementar ### 7.1 Auth Module (Prioridad 1) - ✅ IMPLEMENTADO ``` POST /api/v1/auth/register POST /api/v1/auth/login POST /api/v1/auth/logout POST /api/v1/auth/refresh GET /api/v1/auth/me GET /api/v1/auth/oauth/:provider/url POST /api/v1/auth/oauth/:provider DELETE /api/v1/auth/oauth/:provider POST /api/v1/auth/phone/send POST /api/v1/auth/phone/verify POST /api/v1/auth/2fa/setup POST /api/v1/auth/2fa/enable POST /api/v1/auth/2fa/verify POST /api/v1/auth/2fa/disable POST /api/v1/auth/forgot-password POST /api/v1/auth/reset-password POST /api/v1/auth/verify-email POST /api/v1/auth/resend-verification GET /api/v1/auth/sessions DELETE /api/v1/auth/sessions/:id DELETE /api/v1/auth/sessions ``` ### 7.2 Users Module ``` GET /api/v1/users GET /api/v1/users/:id PATCH /api/v1/users/:id DELETE /api/v1/users/:id GET /api/v1/users/:id/profile PATCH /api/v1/users/:id/profile POST /api/v1/users/:id/avatar ``` ### 7.3 Courses Module (Prioridad 2) ``` GET /api/v1/courses GET /api/v1/courses/featured GET /api/v1/courses/search GET /api/v1/courses/:id GET /api/v1/courses/:slug POST /api/v1/courses PATCH /api/v1/courses/:id DELETE /api/v1/courses/:id POST /api/v1/courses/:id/publish POST /api/v1/courses/:id/archive GET /api/v1/courses/:id/modules POST /api/v1/courses/:id/modules GET /api/v1/courses/:id/modules/:mid/lessons POST /api/v1/courses/:id/enroll GET /api/v1/courses/:id/progress POST /api/v1/courses/:id/lessons/:lid/complete GET /api/v1/courses/:id/certificate ``` ### 7.4 Trading Module (Prioridad 3) ``` GET /api/v1/trading/bots GET /api/v1/trading/bots/featured GET /api/v1/trading/bots/:id POST /api/v1/trading/bots/:id/subscribe DELETE /api/v1/trading/bots/:id/subscribe GET /api/v1/trading/bots/:id/reviews POST /api/v1/trading/bots/:id/reviews GET /api/v1/trading/signals GET /api/v1/trading/signals/:id POST /api/v1/trading/signals/:id/execute GET /api/v1/trading/portfolio GET /api/v1/trading/positions GET /api/v1/trading/history ``` ### 7.5 Payments Module (Prioridad 4) ``` GET /api/v1/payments GET /api/v1/payments/:id POST /api/v1/payments/checkout GET /api/v1/payments/credits POST /api/v1/payments/credits/purchase GET /api/v1/subscriptions POST /api/v1/subscriptions DELETE /api/v1/subscriptions/:id GET /api/v1/invoices GET /api/v1/invoices/:id/download POST /api/v1/webhooks/stripe ``` --- ## 8. API Client Configuration ### 8.1 Axios Instance ```typescript // apps/frontend/src/services/api.ts import axios from 'axios'; import { tokenService } from './token.service'; const api = axios.create({ baseURL: import.meta.env.VITE_API_URL || 'http://localhost:3001/api/v1', headers: { 'Content-Type': 'application/json' } }); // Request interceptor - Add auth header api.interceptors.request.use((config) => { const token = tokenService.getAccessToken(); if (token) { config.headers.Authorization = `Bearer ${token}`; } return config; }); // Response interceptor - Handle 401, refresh token api.interceptors.response.use( (response) => response, async (error) => { const originalRequest = error.config; if (error.response?.status === 401 && !originalRequest._retry) { originalRequest._retry = true; try { const refreshToken = tokenService.getRefreshToken(); const response = await axios.post('/auth/refresh', { refreshToken }); tokenService.setTokens( response.data.accessToken, response.data.refreshToken ); originalRequest.headers.Authorization = `Bearer ${response.data.accessToken}`; return api(originalRequest); } catch (refreshError) { tokenService.clearTokens(); window.location.href = '/login'; return Promise.reject(refreshError); } } return Promise.reject(error); } ); export { api }; ``` ### 8.2 Socket.io Client ```typescript // apps/frontend/src/lib/socket.ts import { io, Socket } from 'socket.io-client'; import { tokenService } from '@/services/token.service'; let socket: Socket | null = null; export function getSocket(): Socket { if (!socket) { socket = io(import.meta.env.VITE_WS_URL || 'ws://localhost:3001', { auth: { token: tokenService.getAccessToken() }, autoConnect: false }); } return socket; } export function connectSocket(): void { const s = getSocket(); if (!s.connected) { s.connect(); } } export function disconnectSocket(): void { if (socket?.connected) { socket.disconnect(); } } ``` --- ## 9. Orden de Migracion ### Fase 1: Autenticacion (Semana 1-2) 1. Backend: Implementar auth endpoints 2. Frontend: Migrar authService.ts 3. Frontend: Migrar tokenService.ts 4. Frontend: Actualizar auth.store.ts 5. Frontend: Actualizar hooks de auth 6. Testing: Flujos completos de auth ### Fase 2: Usuarios y Perfiles (Semana 2) 1. Backend: Implementar users endpoints 2. Frontend: Crear user.service.ts 3. Frontend: Actualizar profileService.ts 4. Testing: CRUD de usuarios ### Fase 3: Cursos/Educacion (Semana 3-4) 1. Backend: Implementar courses endpoints 2. Frontend: Migrar courseService.ts 3. Frontend: Migrar enrollment/progress services 4. Frontend: Migrar quiz/certificate services 5. Testing: Flujos de cursos ### Fase 4: Trading (Semana 4-5) 1. Backend: Implementar trading endpoints 2. Backend: Implementar WebSocket para signals 3. Frontend: Migrar signalService.ts 4. Frontend: Migrar botService.ts 5. Frontend: Crear socket.service.ts 6. Testing: Trading flows + realtime ### Fase 5: Pagos (Semana 5-6) 1. Backend: Implementar payment endpoints 2. Backend: Stripe webhooks 3. Frontend: Migrar stripeService.ts 4. Frontend: Migrar webhookService.ts 5. Testing: Payment flows --- ## 10. Consideraciones Especiales ### 10.1 Manejo de Errores ```typescript // Error response standard interface ApiError { code: string; message: string; field?: string; details?: any; } // En frontend, mapear a mensajes amigables const errorMessages: Record = { 'AUTH_INVALID_CREDENTIALS': 'Email o contrasena incorrectos', 'AUTH_EMAIL_NOT_VERIFIED': 'Debes verificar tu email', 'AUTH_TOKEN_EXPIRED': 'Tu sesion ha expirado', // ... }; ``` ### 10.2 Rate Limiting El backend implementara rate limiting: - Auth endpoints: 5 requests/15min - General API: 100 requests/min - Heavy endpoints: 10 requests/min ### 10.3 Cache - Redis para sessions y tokens - Redis para cache de queries frecuentes - Frontend: TanStack Query cache ### 10.4 Variables de Entorno Frontend ```env # .env VITE_API_URL=http://localhost:3001/api/v1 VITE_WS_URL=ws://localhost:3001 VITE_STRIPE_PUBLIC_KEY=pk_test_xxx VITE_GOOGLE_CLIENT_ID=xxx VITE_FACEBOOK_APP_ID=xxx ``` --- ## 11. Testing Strategy ### 11.1 Backend Tests - Unit tests para services - Integration tests para endpoints - E2E tests para flujos criticos ### 11.2 Frontend Tests - Unit tests para services migrados - Integration tests con MSW (mocks) - E2E tests con Playwright --- ## 12. Rollback Plan En caso de problemas: 1. Mantener Supabase operativo durante migracion 2. Feature flags para cambiar entre backends 3. Logs detallados para debugging 4. Snapshots de base de datos --- ## 13. Referencias ### Documentacion Interna - [ADR-001: Stack Tecnologico](../../97-adr/ADR-001-stack-tecnologico.md) - [ADR-003: Autenticacion Multi-proveedor](../../97-adr/ADR-003-autenticacion-multiproveedor.md) - [INVENTARIO-STC-PLATFORM-WEB.md](./INVENTARIO-STC-PLATFORM-WEB.md) - [OQI-001: Fundamentos y Auth](../../01-fase-mvp/OQI-001-fundamentos-auth/_MAP.md) ### Catálogo de Patrones Reutilizables > **Nota:** Los patrones de autenticación fueron inspirados en arquitecturas previas y están > documentados en el catálogo central para reutilización. - **Catálogo Auth:** `shared/catalog/auth/` *(patrones de autenticación JWT, OAuth, 2FA)* - **Catálogo Session:** `shared/catalog/session-management/` *(gestión de sesiones)* - **Patrones Backend:** `shared/catalog/backend-patterns/` *(interceptors, filters, guards)* ### Documentacion Externa - [Express.js Documentation](https://expressjs.com/) - [node-postgres (pg) Documentation](https://node-postgres.com/) - [Passport.js Strategies](http://www.passportjs.org/) - [jsonwebtoken Documentation](https://github.com/auth0/node-jsonwebtoken) - [Zod Validation](https://zod.dev/)