---
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<string, string> = {
  '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/)
