Un endpoint es una URL específica donde una aplicación puede acceder a un servicio o recurso. Es como una dirección que permite comunicarse con el servidor para obtener datos o realizar acciones.
💡 Ejemplo práctico: /api/users sería un endpoint para gestionar usuarios. Cuando tu app móvil hace una petición a esta URL, el servidor responde con información de usuarios.
🔍 Explicación técnica: Los endpoints siguen el patrón REST (Representational State Transfer), donde cada URL representa un recurso específico. Por ejemplo:
GET /api/users→ Obtener lista de usuariosPOST /api/users→ Crear un nuevo usuarioGET /api/users/123→ Obtener usuario específico con ID 123
Los endpoints públicos no requieren autenticación y cualquiera puede acceder (como registro de usuarios). Los privados necesitan un token de autenticación válido para acceder.
🔒 Endpoint Público:
POST /api/v1/auth/register // Método POST para crear algo nuevo
Content-Type: application/json // Tipo de datos que enviamos
{
"email": "usuario@email.com", // Email del usuario
"password": "miPassword123" // Contraseña del usuario
}🛡️ Endpoint Privado:
GET /api/v1/users/profile // Método GET para obtener datos
Authorization: Bearer token123... // Token de seguridad requerido💡 Explicación: El token JWT (JSON Web Token) en el header Authorization verifica que el usuario está autenticado. Sin este token, el servidor devuelve error 401 (Unauthorized).
Información confidencial incluye contraseñas, números de tarjetas de crédito completos, ubicación en tiempo real de otros usuarios, y datos personales como teléfonos.
🔍 Datos SENSIBLES (nunca exponer):
- ❌ Contraseñas en texto plano
- ❌ Números completos de tarjetas de crédito
- ❌ Ubicación GPS de otros usuarios
- ❌ Números de teléfono completos
✅ Datos SEGUROS (se pueden exponer):
- ✅ Nombre y foto de perfil
- ✅ Últimos 4 dígitos de tarjeta (ej: ****1234)
- ✅ Ubicación general (ciudad, barrio)
- ✅ Rating promedio
💡 Ejemplo de respuesta segura:
{
"id": 123, // ID del usuario
"name": "Juan Pérez", // Nombre completo
"photo": "https://api.com/photos/user123.jpg", // Foto de perfil
"rating": 4.8, // Calificación promedio
"phone": "***-***-1234" // Solo últimos 4 dígitos
}Cada método tiene un propósito específico y sigue convenciones estándar que todos los desarrolladores entienden.
📋 Métodos HTTP explicados:
| Método | 🎯 Propósito | 💡 Ejemplo | 🔍 Explicación |
|---|---|---|---|
| GET | 📖 Leer datos | GET /api/users |
Solo obtiene información, no modifica nada |
| POST | ➕ Crear nuevo | POST /api/users |
Crea un nuevo recurso (usuario, viaje, etc.) |
| PUT | 🔄 Actualizar completo | PUT /api/users/123 |
Reemplaza toda la información del usuario |
| PATCH | ✏️ Actualizar parcial | PATCH /api/users/123 |
Solo actualiza campos específicos |
| DELETE | 🗑️ Eliminar | DELETE /api/users/123 |
Elimina el recurso permanentemente |
💡 Ejemplo práctico:
# Crear un nuevo viaje
POST /api/v1/rides/request // Crear viaje
{
"pickup_location": {"lat": 40.7128, "lng": -74.0060}, // Ubicación origen
"destination": {"lat": 40.7589, "lng": -73.9851} // Ubicación destino
}
# Actualizar solo el estado del viaje
PATCH /api/v1/rides/456 // Actualizar viaje específico
{
"status": "in_progress" // Nuevo estado
}Casi toda la información requiere autenticación: datos de perfil, historial de viajes, pagos, calificaciones, y ubicación en tiempo real.
🔒 Requiere autenticación:
- 👤 Datos de perfil personal
- 🚗 Historial de viajes
- 💳 Información de pagos
- ⭐ Calificaciones y comentarios
- 📍 Ubicación en tiempo real
- 🚙 Estado del conductor (disponible/ocupado)
🌐 Público (sin autenticación):
- 📝 Registro de nuevos usuarios
- 🔑 Login de usuarios existentes
- 📱 Información general de la app
💡 Explicación del token JWT:
{
"header": { // Información del tipo de token
"alg": "HS256", // Algoritmo de seguridad
"typ": "JWT" // Tipo: JSON Web Token
},
"payload": { // Datos del usuario
"user_id": 123, // ID del usuario
"role": "passenger", // Rol: pasajero o conductor
"exp": 1640995200 // Cuándo expira el token
},
"signature": "firma_digital" // Firma de seguridad del servidor
}La ubicación solo se comparte entre usuarios involucrados en un viaje activo, nunca con terceros.
🔐 Estrategias de protección:
- 🔒 Cifrado en tránsito: Todas las comunicaciones usan HTTPS
- ⏰ Almacenamiento temporal: Ubicaciones se borran después de 24 horas
- 👥 Acceso limitado: Solo pasajero y conductor del viaje activo pueden ver ubicaciones
- 🎯 Precisión reducida: Para terceros, solo mostrar ciudad/barrio
💡 Ejemplo de implementación:
// Respuesta para usuario del viaje activo
{
"driver_location": {
"latitude": 40.7128, // Ubicación exacta
"longitude": -74.0060, // Ubicación exacta
"accuracy": 5, // Precisión en metros
"timestamp": "2024-01-15T10:30:00Z" // Cuándo se actualizó
}
}
// Respuesta para otros usuarios
{
"driver_location": {
"area": "Manhattan", // Solo área general
"neighborhood": "Financial District", // Solo barrio
"timestamp": "2024-01-15T10:30:00Z" // Cuándo se actualizó
}
}La API debería devolver un mensaje claro con códigos de estado apropiados y sugerencias útiles.
📊 Códigos de estado HTTP:
| Código | 🎯 Significado | 💡 Cuándo usar |
|---|---|---|
| 404 | Not Found | No hay conductores en el área específica |
| 503 | Service Unavailable | Servicio temporalmente no disponible |
| 200 | OK | Hay conductores pero con tiempo de espera largo |
💡 Ejemplo de respuesta:
{
"error": "NO_DRIVERS_AVAILABLE", // Tipo de error
"message": "No hay conductores disponibles en tu área", // Mensaje claro
"code": 404, // Código HTTP
"timestamp": "2024-01-15T10:30:00Z", // Cuándo ocurrió
"suggestions": [ // Sugerencias útiles
"Intenta expandir el radio de búsqueda",
"Espera unos minutos y vuelve a intentar",
"Considera usar transporte público"
],
"estimated_wait_time": null // Tiempo de espera (ninguno)
}Los recursos principales organizan la API de manera lógica y facilitan el desarrollo.
🗂️ Estructura de recursos:
/api/v1/
├── 👥 users/ # Gestión de usuarios
├── 🚗 rides/ # Solicitudes y gestión de viajes
├── 💳 payments/ # Procesamiento de pagos
├── ⭐ ratings/ # Sistema de calificaciones
├── 🚙 vehicles/ # Información de vehículos
├── 📍 locations/ # Gestión de ubicaciones
└── 👨💼 admin/ # Funciones administrativas
💡 Explicación de RESTful design:
- Cada recurso tiene su propia URL base
- Los endpoints siguen convenciones estándar
- Fácil de entender y mantener
- Escalable para futuras funcionalidades
El versionado permite evolución sin romper aplicaciones existentes.
📈 Ventajas del versionado:
- 🔄 Compatibilidad: Apps antiguas siguen funcionando
- 🚀 Evolución: Nuevas funcionalidades en v2, v3, etc.
- 📱 Migración gradual: Los desarrolladores pueden actualizar cuando estén listos
- 🛡️ Estabilidad: Cambios no afectan sistemas en producción
💡 Ejemplo de versionado:
# Versión actual (estable)
GET /api/v1/users/profile
# Nueva versión con mejoras
GET /api/v2/users/profile
# Incluye nuevos campos como "preferences", "notifications"
# Versión antigua (deprecated)
GET /api/v0/users/profile
# Aún funciona pero muestra warning de deprecación{
"warning": "This endpoint is deprecated. Please migrate to v2.",
"deprecation_date": "2024-06-01",
"sunset_date": "2024-12-01"
}Documentar errores ayuda a los desarrolladores a entender qué puede salir mal y cómo manejarlo.
📋 Elementos de documentación de errores:
- 🔢 Códigos HTTP: Indican el tipo de problema
- 📝 Mensajes descriptivos: Explican qué salió mal
- 💡 Soluciones sugeridas: Cómo resolver el problema
- 🔍 Códigos de error únicos: Para debugging específico
💡 Ejemplo de documentación completa:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Los datos enviados no son válidos",
"details": [
{
"field": "email",
"message": "El formato del email es incorrecto",
"example": "usuario@ejemplo.com"
},
{
"field": "password",
"message": "La contraseña debe tener al menos 8 caracteres",
"min_length": 8
}
],
"http_status": 400,
"timestamp": "2024-01-15T10:30:00Z"
}
}🎯 Funciones principales:
- 📝 Registrarse y gestionar perfil personal
- 🚗 Solicitar viajes y ver historial
- 💳 Realizar pagos y ver recibos
- ⭐ Calificar conductores después del viaje
- ❌ Cancelar viajes (con restricciones de tiempo)
💡 Ejemplo de permisos en código:
function authorizePassenger(req, res, next) { // Función para verificar pasajero
const token = req.headers.authorization; // Obtener token del header
const decoded = jwt.verify(token, SECRET_KEY); // Verificar token
if (decoded.role !== 'passenger') { // Si no es pasajero
return res.status(403).json({ // Devolver error 403
error: 'FORBIDDEN', // Tipo de error
message: 'Solo los pasajeros pueden realizar esta acción' // Mensaje
});
}
req.user = decoded; // Guardar datos del usuario
next(); // Continuar
}🎯 Funciones principales:
- 📋 Registrarse como conductor con documentos
- 👤 Gestionar perfil y vehículo
- 📱 Recibir y aceptar solicitudes de viaje
- 📍 Actualizar ubicación en tiempo real
- ⭐ Calificar pasajeros después del viaje
- 💰 Ver ganancias y historial de viajes
💡 Ejemplo de validación de conductor:
function validateDriverAvailability(req, res, next) { // Verificar disponibilidad
const driverId = req.user.user_id; // ID del conductor
const driver = await Driver.findById(driverId); // Buscar conductor
if (driver.status !== 'available') { // Si no está disponible
return res.status(409).json({ // Devolver error 409
error: 'DRIVER_BUSY', // Tipo de error
message: 'El conductor ya tiene un viaje activo', // Mensaje
current_status: driver.status // Estado actual
});
}
next(); // Continuar
}🎯 Funciones principales:
- 👥 Gestionar usuarios y vehículos
- 📊 Ver estadísticas y reportes
- ⚖️ Resolver disputas y problemas
- ⚙️ Configurar tarifas y zonas de servicio
- 🛡️ Moderar calificaciones y comentarios
- 🔍 Acceso completo a todos los datos del sistema
💡 Ejemplo de middleware de administrador:
function authorizeAdmin(req, res, next) { // Verificar admin
const token = req.headers.authorization; // Obtener token
const decoded = jwt.verify(token, SECRET_KEY); // Verificar token
if (decoded.role !== 'admin') { // Si no es admin
return res.status(403).json({ // Devolver error
error: 'ADMIN_REQUIRED', // Tipo de error
message: 'Esta función requiere permisos de administrador' // Mensaje
});
}
req.admin = decoded; // Guardar datos admin
next(); // Continuar
}
// Endpoint solo para administradores
app.get('/api/v1/admin/users', authorizeAdmin, async (req, res) => {
const users = await User.find({}) // Buscar usuarios
.select('-password') // Excluir contraseñas
.limit(50); // Máximo 50
res.json({ // Devolver respuesta
users, // Lista de usuarios
total: users.length, // Total encontrados
admin_action: 'user_listing' // Acción realizada
});
});🗂️ Estructura completa de la API:
| Recurso | 🎯 Propósito | 📝 Descripción | 🔗 Ejemplo de URL |
|---|---|---|---|
| 👥 users | Gestión de usuarios | Pasajeros y conductores del sistema | /api/v1/users/profile |
| 🚗 rides | Solicitudes y gestión de viajes | Estados, cancelaciones, finalizaciones | /api/v1/rides/request |
| 💳 payments | Procesamiento de pagos | Métodos de pago, historial, recibos | /api/v1/payments/process |
| ⭐ ratings | Sistema de calificaciones | Evaluaciones mutuas entre usuarios | /api/v1/ratings |
| 🚙 vehicles | Información de vehículos | Datos de autos de conductores | /api/v1/vehicles/register |
| 📍 locations | Gestión de ubicaciones | GPS en tiempo real, conductores cercanos | /api/v1/locations/update |
| 👨💼 admin | Funciones administrativas | Gestión del sistema, reportes, estadísticas | /api/v1/admin/users |
💡 Explicación de la arquitectura REST:
const resources = { // Recursos de la API
users: { // Recurso de usuarios
base_url: '/api/v1/users', // URL base
operations: { // Operaciones posibles
'GET /': 'Listar usuarios', // Obtener lista
'POST /': 'Crear usuario', // Crear nuevo
'GET /:id': 'Obtener usuario específico', // Obtener uno
'PUT /:id': 'Actualizar usuario completo', // Actualizar todo
'DELETE /:id': 'Eliminar usuario' // Eliminar
}
},
rides: { // Recurso de viajes
base_url: '/api/v1/rides', // URL base
operations: { // Operaciones posibles
'POST /request': 'Solicitar viaje', // Crear viaje
'GET /available': 'Ver viajes disponibles', // Ver disponibles
'POST /:id/accept': 'Aceptar viaje' // Aceptar viaje
}
}
};🔍 Relaciones entre recursos:
👥 User (1) ──→ (N) 🚗 Ride ──→ (1) 💳 Payment
│ │
└── (N) ⭐ Rating ──→ (1) 🚗 Ride
│
└── (1) 🚙 Vehicle (solo conductores)
| Método | 🛣️ Ruta | 📝 Descripción | 📋 Parámetros | 🔒 Autenticación | 💡 Ejemplo |
|---|---|---|---|---|---|
| POST | /api/v1/auth/register |
📝 Registro de nuevo usuario | email, password, name, phone, role |
🌐 Público | {"email": "user@email.com", "password": "pass123"} |
| POST | /api/v1/auth/login |
🔑 Inicio de sesión | email, password |
🌐 Público | {"email": "user@email.com", "password": "pass123"} |
| POST | /api/v1/auth/logout |
🚪 Cerrar sesión | - | 🔑 Token | Authorization: Bearer token123 |
| GET | /api/v1/users/profile |
👤 Obtener perfil del usuario | - | 🔑 Token | Respuesta: {"name": "Juan", "email": "juan@email.com"} |
| PUT | /api/v1/users/profile |
✏️ Actualizar perfil | name, phone, photo |
🔑 Token | {"name": "Juan Pérez", "phone": "+1234567890"} |
| POST | /api/v1/users/driver/register |
🚗 Registro como conductor | license, vehicle_info, documents |
🔑 Token (pasajero) | {"license": "ABC123", "vehicle": {...}} |
| GET | /api/v1/users/driver/status |
📊 Estado de registro como conductor | - | 🔑 Token (conductor) | Respuesta: {"status": "approved", "documents": "complete"} |
| Método | 🛣️ Ruta | 📝 Descripción | 📋 Parámetros | 🔒 Autenticación | 💡 Ejemplo |
|---|---|---|---|---|---|
| POST | /api/v1/rides/request |
🚗 Solicitar un viaje | pickup_location, destination, ride_type |
🔑 Token (pasajero) | {"pickup": {"lat": 40.7128, "lng": -74.0060}} |
| GET | /api/v1/rides/available |
📱 Ver viajes disponibles | location, radius |
🔑 Token (conductor) | ?lat=40.7128&lng=-74.0060&radius=5 |
| POST | /api/v1/rides/{id}/accept |
✅ Aceptar un viaje | - | 🔑 Token (conductor) | POST /api/v1/rides/123/accept |
| POST | /api/v1/rides/{id}/start |
🚀 Iniciar viaje | - | 🔑 Token (conductor) | POST /api/v1/rides/123/start |
| POST | /api/v1/rides/{id}/complete |
🏁 Finalizar viaje | final_location, distance |
🔑 Token (conductor) | {"final_location": {...}, "distance": 5.2} |
| POST | /api/v1/rides/{id}/cancel |
❌ Cancelar viaje | reason |
🔑 Token (participante) | {"reason": "Cambio de planes"} |
| GET | /api/v1/rides/{id} |
🔍 Obtener detalles del viaje | - | 🔑 Token (participante) | Respuesta: {"id": 123, "status": "in_progress", "driver": {...}} |
| GET | /api/v1/rides/history |
📚 Historial de viajes | page, limit, status |
🔑 Token | ?page=1&limit=10&status=completed |
| Método | 🛣️ Ruta | 📝 Descripción | 📋 Parámetros | 🔒 Autenticación | 💡 Ejemplo |
|---|---|---|---|---|---|
| POST | /api/v1/locations/update |
📍 Actualizar ubicación | latitude, longitude |
🔑 Token (conductor) | {"latitude": 40.7128, "longitude": -74.0060} |
| GET | /api/v1/locations/nearby-drivers |
🔍 Conductores cercanos | latitude, longitude, radius |
🔑 Token (pasajero) | ?lat=40.7128&lng=-74.0060&radius=2 |
| Método | 🛣️ Ruta | 📝 Descripción | 📋 Parámetros | 🔒 Autenticación | 💡 Ejemplo |
|---|---|---|---|---|---|
| POST | /api/v1/payments/method |
💳 Agregar método de pago | card_token, type |
🔑 Token | {"card_token": "tok_123", "type": "visa"} |
| GET | /api/v1/payments/methods |
📋 Listar métodos de pago | - | 🔑 Token | Respuesta: [{"id": 1, "type": "visa", "last4": "1234"}] |
| POST | /api/v1/payments/process |
💰 Procesar pago de viaje | ride_id, amount |
🔑 Token (pasajero) | {"ride_id": 123, "amount": 15.50} |
| GET | /api/v1/payments/history |
📊 Historial de pagos | page, limit |
🔑 Token | ?page=1&limit=20 |
| Método | 🛣️ Ruta | 📝 Descripción | 📋 Parámetros | 🔒 Autenticación | 💡 Ejemplo |
|---|---|---|---|---|---|
| POST | /api/v1/ratings |
⭐ Calificar viaje | ride_id, rating, comment |
🔑 Token | {"ride_id": 123, "rating": 5, "comment": "Excelente servicio"} |
| GET | /api/v1/ratings/{user_id} |
👀 Ver calificaciones de usuario | - | 🔑 Token | Respuesta: {"average": 4.8, "total": 150, "reviews": [...]} |
| Método | 🛣️ Ruta | 📝 Descripción | 📋 Parámetros | 🔒 Autenticación | 💡 Ejemplo |
|---|---|---|---|---|---|
| GET | /api/v1/admin/users |
👥 Listar todos los usuarios | page, limit, role, status |
🔑 Token (admin) | ?page=1&limit=50&role=driver |
| PUT | /api/v1/admin/users/{id}/status |
⚙️ Cambiar estado de usuario | status, reason |
🔑 Token (admin) | {"status": "suspended", "reason": "Violación de términos"} |
| GET | /api/v1/admin/rides |
📊 Estadísticas de viajes | date_from, date_to |
🔑 Token (admin) | ?date_from=2024-01-01&date_to=2024-01-31 |
| GET | /api/v1/admin/reports |
📈 Reportes del sistema | type, period |
🔑 Token (admin) | ?type=revenue&period=monthly |
💡 Explicación de códigos de autenticación:
- 🌐 Público: No requiere autenticación
- 🔑 Token: Requiere JWT válido en header
Authorization: Bearer <token> - 🔑 Token (rol específico): Requiere JWT válido con rol específico (pasajero, conductor, admin)
📱 Paso a paso con ejemplos de código:
POST /api/v1/rides/request // Solicitar viaje
Authorization: Bearer token123... // Token de seguridad
Content-Type: application/json // Tipo de datos
{
"pickup_location": { // Ubicación de origen
"latitude": 40.7128, // Latitud
"longitude": -74.0060, // Longitud
"address": "Times Square, NYC" // Dirección
},
"destination": { // Ubicación de destino
"latitude": 40.7589, // Latitud
"longitude": -73.9851, // Longitud
"address": "Central Park, NYC" // Dirección
},
"ride_type": "standard" // Tipo de viaje
}💡 Respuesta del servidor:
{
"ride_id": 12345, // ID del viaje
"status": "searching_driver", // Estado actual
"estimated_wait_time": "3-5 minutes", // Tiempo estimado
"estimated_fare": "$12.50", // Precio estimado
"message": "Buscando conductor cercano..." // Mensaje para el usuario
}GET /api/v1/locations/nearby-drivers?lat=40.7128&lng=-74.0060&radius=2 // Buscar conductores cercanos
Authorization: Bearer conductor_token... // Token del conductor💡 Respuesta con conductores cercanos:
{
"available_drivers": [ // Lista de conductores disponibles
{
"driver_id": 789, // ID del conductor
"name": "Carlos", // Nombre del conductor
"rating": 4.8, // Calificación promedio
"distance": 0.8, // Distancia en km
"estimated_arrival": "2 minutes", // Tiempo estimado de llegada
"vehicle": { // Información del vehículo
"make": "Toyota", // Marca
"model": "Camry", // Modelo
"color": "White", // Color
"license_plate": "ABC-123" // Placa
}
}
]
}POST /api/v1/rides/12345/accept // Aceptar viaje específico
Authorization: Bearer conductor_token... // Token del conductor💡 Respuesta de aceptación:
{
"ride_id": 12345, // ID del viaje
"status": "driver_assigned", // Estado: conductor asignado
"driver": { // Información del conductor
"name": "Carlos", // Nombre
"phone": "+1234567890", // Teléfono
"rating": 4.8, // Calificación
"vehicle": {...} // Datos del vehículo
},
"estimated_arrival": "2 minutes" // Tiempo de llegada
}POST /api/v1/rides/12345/start // Iniciar viaje
Authorization: Bearer conductor_token... // Token del conductorPOST /api/v1/rides/12345/complete // Finalizar viaje
Authorization: Bearer conductor_token... // Token del conductor
Content-Type: application/json // Tipo de datos
{
"final_location": { // Ubicación final
"latitude": 40.7589, // Latitud
"longitude": -73.9851 // Longitud
},
"distance": 2.3, // Distancia recorrida (km)
"duration": 15 // Duración (minutos)
}POST /api/v1/payments/process // Procesar pago
Authorization: Bearer passenger_token... // Token del pasajero
Content-Type: application/json // Tipo de datos
{
"ride_id": 12345, // ID del viaje
"amount": 12.50, // Monto a pagar
"payment_method_id": 1 // Método de pago
}# Pasajero califica al conductor
POST /api/v1/ratings // Enviar calificación
Authorization: Bearer passenger_token... // Token del pasajero
Content-Type: application/json // Tipo de datos
{
"ride_id": 12345, // ID del viaje
"rated_user_id": 789, // ID del conductor
"rating": 5, // Calificación (1-5)
"comment": "Excelente conductor, muy puntual" // Comentario
}
# Conductor califica al pasajero
POST /api/v1/ratings // Enviar calificación
Authorization: Bearer conductor_token... // Token del conductor
Content-Type: application/json // Tipo de datos
{
"ride_id": 12345, // ID del viaje
"rated_user_id": 456, // ID del pasajero
"rating": 5, // Calificación (1-5)
"comment": "Pasajero muy amable" // Comentario
}📱 Flujo de cancelación con diferentes escenarios:
POST /api/v1/rides/12345/cancel // Cancelar viaje
Authorization: Bearer passenger_token... // Token del pasajero
Content-Type: application/json // Tipo de datos
{
"reason": "Cambio de planes", // Motivo de cancelación
"cancelled_by": "passenger" // Quién cancela
}💡 Respuesta:
{
"ride_id": 12345, // ID del viaje
"status": "cancelled", // Estado: cancelado
"cancellation_fee": 0, // Sin tarifa de cancelación
"refund_amount": 0, // Sin reembolso
"message": "Viaje cancelado sin penalización" // Mensaje
}POST /api/v1/rides/12345/cancel // Cancelar viaje
Authorization: Bearer passenger_token... // Token del pasajero
Content-Type: application/json // Tipo de datos
{
"reason": "Ya no necesito el viaje", // Motivo de cancelación
"cancelled_by": "passenger" // Quién cancela
}💡 Respuesta con penalización:
{
"ride_id": 12345, // ID del viaje
"status": "cancelled", // Estado: cancelado
"cancellation_fee": 5.00, // Tarifa de cancelación
"refund_amount": 7.50, // Reembolso parcial
"message": "Se aplicó tarifa de cancelación por cancelar después de 2 minutos" // Mensaje
}async function processCancellationRefund(rideId, cancellationFee) { // Función para reembolso
const ride = await Ride.findById(rideId); // Buscar viaje
const originalAmount = ride.estimated_fare; // Precio original
const refundAmount = originalAmount - cancellationFee; // Calcular reembolso
if (refundAmount > 0) { // Si hay reembolso
await processRefund(ride.passenger_id, refundAmount); // Procesar reembolso
await sendNotification(ride.passenger_id, { // Enviar notificación
type: 'refund_processed', // Tipo de notificación
amount: refundAmount, // Monto reembolsado
message: `Reembolso de $${refundAmount} procesado` // Mensaje
});
}
}📱 Flujo completo de calificaciones:
GET /api/v1/rides/12345 // Ver detalles del viaje
Authorization: Bearer passenger_token... // Token del pasajero💡 Respuesta:
{
"ride_id": 12345, // ID del viaje
"status": "completed", // Estado: completado
"can_rate": true, // Puede calificar
"rating_deadline": "2024-01-16T10:30:00Z", // Fecha límite para calificar
"driver": { // Información del conductor
"id": 789, // ID del conductor
"name": "Carlos", // Nombre
"rating": 4.8 // Calificación actual
}
}POST /api/v1/ratings // Enviar calificación
Authorization: Bearer passenger_token... // Token del pasajero
Content-Type: application/json // Tipo de datos
{
"ride_id": 12345, // ID del viaje
"rated_user_id": 789, // ID del conductor
"rating": 5, // Calificación general (1-5)
"comment": "Excelente conductor, muy puntual y amable", // Comentario
"categories": { // Calificaciones específicas
"punctuality": 5, // Puntualidad
"cleanliness": 5, // Limpieza
"friendliness": 5, // Amabilidad
"driving": 4 // Manejo
}
}POST /api/v1/ratings // Enviar calificación
Authorization: Bearer conductor_token... // Token del conductor
Content-Type: application/json // Tipo de datos
{
"ride_id": 12345, // ID del viaje
"rated_user_id": 456, // ID del pasajero
"rating": 5, // Calificación general (1-5)
"comment": "Pasajero muy respetuoso", // Comentario
"categories": { // Calificaciones específicas
"punctuality": 5, // Puntualidad
"respectfulness": 5, // Respeto
"communication": 4 // Comunicación
}
}async function updateUserRating(userId, newRating) { // Función para actualizar rating
const user = await User.findById(userId); // Buscar usuario
const currentRating = user.average_rating; // Rating actual
const totalRatings = user.total_ratings; // Total de calificaciones
const newAverage = ((currentRating * totalRatings) + newRating) / (totalRatings + 1); // Calcular nuevo promedio
await User.findByIdAndUpdate(userId, { // Actualizar usuario
average_rating: newAverage, // Nuevo promedio
total_ratings: totalRatings + 1 // Incrementar contador
});
if (newAverage < 3.0) { // Si rating muy bajo
await scheduleDriverReview(userId); // Programar revisión
}
}GET /api/v1/ratings/789 // Ver calificaciones del usuario 789
Authorization: Bearer any_valid_token... // Cualquier token válido💡 Respuesta:
{
"user_id": 789, // ID del usuario
"average_rating": 4.8, // Calificación promedio
"total_ratings": 150, // Total de calificaciones
"recent_ratings": [ // Calificaciones recientes
{
"rating": 5, // Calificación
"comment": "Excelente conductor", // Comentario
"date": "2024-01-15T10:30:00Z", // Fecha
"ride_id": 12345 // ID del viaje
}
],
"rating_breakdown": { // Desglose por estrellas
"5_stars": 120, // 5 estrellas
"4_stars": 25, // 4 estrellas
"3_stars": 3, // 3 estrellas
"2_stars": 1, // 2 estrellas
"1_star": 1 // 1 estrella
}
}🛡️ Autenticación JWT (JSON Web Token):
const token = { // Estructura del token
header: { // Información del token
"alg": "HS256", // Algoritmo de seguridad
"typ": "JWT" // Tipo: JSON Web Token
},
payload: { // Datos del usuario
"user_id": 123, // ID del usuario
"role": "passenger", // Rol: pasajero/conductor
"exp": 1640995200, // Cuándo expira
"iat": 1640908800 // Cuándo se creó
},
signature: "firma_digital_del_servidor" // Firma de seguridad
};
function authenticateToken(req, res, next) { // Función para verificar token
const authHeader = req.headers['authorization']; // Obtener header
const token = authHeader && authHeader.split(' ')[1]; // Extraer token
if (!token) { // Si no hay token
return res.status(401).json({ error: 'ACCESS_DENIED' }); // Error 401
}
jwt.verify(token, process.env.ACCESS_TOKEN_SECRET, (err, user) => { // Verificar token
if (err) return res.status(403).json({ error: 'INVALID_TOKEN' }); // Error 403
req.user = user; // Guardar datos del usuario
next(); // Continuar
});
}🔒 Cifrado de datos sensibles:
const crypto = require('crypto'); // Librería de cifrado
function encryptLocation(latitude, longitude) { // Función para cifrar ubicación
const cipher = crypto.createCipher('aes-256-cbc', process.env.LOCATION_KEY); // Crear cifrador
let encrypted = cipher.update(`${latitude},${longitude}`, 'utf8', 'hex'); // Cifrar datos
encrypted += cipher.final('hex'); // Finalizar cifrado
return encrypted; // Devolver datos cifrados
}
function getLocationForUser(rideId, userId, requesterRole) { // Función para obtener ubicación
const ride = await Ride.findById(rideId); // Buscar viaje
if (requesterRole === 'admin') { // Si es administrador
return ride.driver_location; // Ubicación completa
} else if (isParticipantInRide(userId, rideId)) { // Si es participante del viaje
return ride.driver_location; // Ubicación completa
} else { // Si es otro usuario
return { // Solo área general
area: getGeneralArea(ride.driver_location), // Área general
neighborhood: getNeighborhood(ride.driver_location) // Barrio
};
}
}⚡ Rate Limiting para prevenir abuso:
const rateLimit = require('express-rate-limit'); // Librería para límites
const generalLimiter = rateLimit({ // Límite general
windowMs: 15 * 60 * 1000, // Ventana: 15 minutos
max: 100, // Máximo 100 requests
message: { // Mensaje de error
error: 'TOO_MANY_REQUESTS', // Tipo de error
message: 'Demasiadas peticiones, intenta más tarde' // Mensaje
}
});
const strictLimiter = rateLimit({ // Límite estricto
windowMs: 15 * 60 * 1000, // Ventana: 15 minutos
max: 5, // Solo 5 intentos
message: { // Mensaje de error
error: 'LOGIN_ATTEMPTS_EXCEEDED', // Tipo de error
message: 'Demasiados intentos de login, espera 15 minutos' // Mensaje
}
});
app.use('/api/v1/auth/login', strictLimiter); // Aplicar límite estricto al login
app.use('/api/v1/', generalLimiter); // Aplicar límite general a toda la API📈 Estrategia de versionado:
const apiVersions = { // Versiones disponibles
v1: { // Versión 1
status: 'stable', // Estado: estable
endpoints: ['/auth', '/users', '/rides', '/payments'], // Endpoints disponibles
deprecation_date: null // Sin fecha de deprecación
},
v2: { // Versión 2
status: 'beta', // Estado: beta
endpoints: ['/auth', '/users', '/rides', '/payments', '/notifications'], // Más endpoints
deprecation_date: null, // Sin fecha de deprecación
new_features: ['real_time_notifications', 'advanced_ratings'] // Nuevas funcionalidades
}
};
function apiVersioning(req, res, next) { // Función para verificar versión
const version = req.path.split('/')[2]; // Extraer versión de la URL
if (!apiVersions[version]) { // Si la versión no existe
return res.status(404).json({ // Error 404
error: 'VERSION_NOT_FOUND', // Tipo de error
available_versions: Object.keys(apiVersions), // Versiones disponibles
message: 'La versión de API solicitada no existe' // Mensaje
});
}
req.apiVersion = version; // Guardar versión
next(); // Continuar
}
function deprecatedEndpoint(req, res, next) { // Función para endpoints deprecados
res.setHeader('Deprecation', 'true'); // Marcar como deprecado
res.setHeader('Sunset', '2024-12-31T23:59:59Z'); // Fecha de eliminación
res.json({ // Respuesta
warning: 'Este endpoint está deprecado', // Advertencia
message: 'Por favor migra a la nueva versión', // Mensaje
alternative: '/api/v2/users/profile', // Alternativa
sunset_date: '2024-12-31T23:59:59Z' // Fecha de eliminación
});
}📊 Códigos HTTP y su significado:
const errorCodes = { // Códigos de error
200: 'OK - Operación exitosa', // Éxito
201: 'Created - Recurso creado exitosamente', // Creado
400: 'Bad Request - Datos inválidos o faltantes', // Error del cliente
401: 'Unauthorized - Token inválido o expirado', // No autorizado
403: 'Forbidden - Sin permisos para la operación', // Prohibido
404: 'Not Found - Recurso no encontrado', // No encontrado
409: 'Conflict - Conflicto con estado actual', // Conflicto
500: 'Internal Server Error - Error interno del servidor', // Error del servidor
503: 'Service Unavailable - Servicio temporalmente no disponible' // No disponible
};
function errorHandler(err, req, res, next) { // Función para manejar errores
console.error('Error:', err); // Log para debugging
if (err.name === 'ValidationError') { // Si es error de validación
return res.status(400).json({ // Error 400
error: 'VALIDATION_ERROR', // Tipo de error
message: 'Los datos enviados no son válidos', // Mensaje
details: err.errors, // Detalles del error
timestamp: new Date().toISOString() // Timestamp
});
}
if (err.name === 'JsonWebTokenError') { // Si es error de token
return res.status(401).json({ // Error 401
error: 'INVALID_TOKEN', // Tipo de error
message: 'Token de autenticación inválido', // Mensaje
timestamp: new Date().toISOString() // Timestamp
});
}
res.status(500).json({ // Error genérico 500
error: 'INTERNAL_SERVER_ERROR', // Tipo de error
message: 'Error interno del servidor', // Mensaje
timestamp: new Date().toISOString(), // Timestamp
request_id: req.id // ID de la petición
});
}🔄 Graceful Degradation:
async function getRideWithFallback(rideId) { // Función con respaldo
try { // Intentar obtener datos completos
const ride = await Ride.findById(rideId).populate('driver passenger'); // Buscar con relaciones
return ride; // Devolver datos completos
} catch (error) { // Si hay error
console.error('Error getting ride:', error); // Log del error
try { // Intentar datos básicos
const basicRide = await Ride.findById(rideId).select('status pickup destination'); // Solo campos básicos
return { // Devolver datos básicos
...basicRide, // Datos del viaje
degraded_mode: true, // Modo degradado activado
message: 'Algunos datos pueden no estar disponibles' // Mensaje de advertencia
};
} catch (fallbackError) { // Si también falla el respaldo
throw new Error('Servicio temporalmente no disponible'); // Error final
}
}
}| Código | 🎯 Significado | 💡 Cuándo usar | 🔍 Ejemplo de contexto |
|---|---|---|---|
| 200 | ✅ OK | Operación exitosa | GET exitoso, actualización completada |
| 201 | ➕ Created | Recurso creado exitosamente | Usuario registrado, viaje solicitado |
| 400 | ❌ Bad Request | Datos inválidos o faltantes | Email mal formateado, campos requeridos faltantes |
| 401 | 🔐 Unauthorized | Token inválido o expirado | Token expirado, credenciales incorrectas |
| 403 | 🚫 Forbidden | Sin permisos para la operación | Pasajero intenta aceptar viaje |
| 404 | 🔍 Not Found | Recurso no encontrado | Usuario inexistente, viaje no encontrado |
| 409 | Conflicto con estado actual | Usuario ya existe, viaje ya cancelado | |
| 422 | 📝 Unprocessable Entity | Datos válidos pero lógicamente incorrectos | Pago con tarjeta vencida |
| 500 | 🔥 Internal Server Error | Error interno del servidor | Error de base de datos, servicio externo caído |
| 503 | 🚧 Service Unavailable | Servicio temporalmente no disponible | Mantenimiento programado, sobrecarga |
{
"error": "USER_NOT_FOUND",
"message": "El usuario con ID 123 no existe",
"code": 404,
"timestamp": "2024-01-15T10:30:00Z"
}💡 Qué significa: Intentaste acceder a un usuario que no existe. Verifica que el ID sea correcto.
{
"error": "TOKEN_EXPIRED",
"message": "El token de autenticación ha expirado",
"code": 401,
"timestamp": "2024-01-15T10:30:00Z"
}💡 Qué significa: Tu sesión expiró. Necesitas hacer login nuevamente.
{
"error": "NO_DRIVERS_AVAILABLE",
"message": "No hay conductores disponibles en tu área",
"code": 404,
"timestamp": "2024-01-15T10:30:00Z",
"suggestion": "Intenta expandir el radio de búsqueda"
}💡 Qué significa: No hay conductores cerca. Puedes esperar un poco o buscar en un área más amplia.
{
"error": "PAYMENT_FAILED",
"message": "El pago no pudo ser procesado",
"code": 422,
"timestamp": "2024-01-15T10:30:00Z",
"details": "Fondos insuficientes en la tarjeta"
}💡 Qué significa: Tu tarjeta no tiene suficiente dinero. Verifica tu saldo o usa otra tarjeta.
{
"error": "RIDE_ALREADY_CANCELLED",
"message": "Este viaje ya ha sido cancelado",
"code": 409,
"timestamp": "2024-01-15T10:30:00Z"
}💡 Qué significa: Este viaje ya fue cancelado antes. Si necesitas transporte, solicita un nuevo viaje.
Implementar notificaciones en tiempo real para informar sobre cambios de estado de viajes, mensajes de conductores, y promociones. Esto mejoraría significativamente la experiencia del usuario al mantenerlos informados sin necesidad de refrescar la aplicación.
💡 Ejemplo simple: Cuando un conductor acepta tu viaje, recibes una notificación inmediata en tu teléfono.
Agregar funcionalidades como navegación integrada, estimación de tráfico en tiempo real, y rutas optimizadas. Esto ayudaría a conductores a tomar mejores decisiones de ruta y mejorar la eficiencia del servicio.
💡 Ejemplo simple: El conductor ve la mejor ruta considerando el tráfico actual, no solo la distancia más corta.
Permitir comunicación directa entre pasajero y conductor durante el viaje activo. Esto facilitaría la coordinación de encuentros y resolvería dudas sin necesidad de llamadas telefónicas.
💡 Ejemplo simple: El pasajero puede escribir "Estoy en la puerta azul" y el conductor lo ve inmediatamente.
Implementar un sistema de puntos que los usuarios puedan acumular por viajes frecuentes y canjear por descuentos o beneficios. Esto aumentaría la retención de usuarios y fomentaría el uso regular de la plataforma.
💡 Ejemplo simple: Por cada viaje ganas puntos, y con 100 puntos puedes obtener un viaje gratis.
Usar datos históricos para predecir demanda, optimizar precios dinámicos, y sugerir mejores ubicaciones para conductores. Esto mejoraría la eficiencia operativa y la satisfacción tanto de conductores como pasajeros.
💡 Ejemplo simple: El sistema aprende que en el centro a las 5 PM siempre hay mucha demanda, entonces sugiere a los conductores ir ahí.
Developed with ❤️
🔥 Visit my GitHub 🚀