GPS Vehicle Tracking System
Sistema fullstack de rastreo de flota en tiempo real con mapas interactivos, comandos remotos vía AWS IoT y almacenamiento en DynamoDB.
Arquitecturas AWS utilizadas
Problema & Solución
Problema
Una empresa de logística necesita monitorear su flota de vehículos en tiempo real: ver la posición actual en un mapa, consultar el historial de coordenadas, y enviar comandos remotos (bloquear motor, activar alarma) directamente al dispositivo GPS instalado en el vehículo.
Solución
Dashboard React + MapLibre GL que muestra posiciones en tiempo real. Backend Node.js + Express que gestiona autenticación JWT, CRUD de vehículos y dispositivos GPS en DynamoDB, y envío de comandos remotos via AWS IoT (MQTT). Los dispositivos GPS publican coordenadas al IoT endpoint; el backend las persiste en DynamoDB con TTL.
Diagrama de Arquitectura
┌────────────────────────────────────────────────────────────────┐
│ Dashboard React (Vite + MapLibre GL + TailwindCSS) │
│ • Mapa interactivo con posición de vehículos │
│ • CommandPanel: bloquear motor, alarma, flash de luces │
│ • Historial de coordenadas por vehículo │
└──────────────────────────────┬─────────────────────────────────┘
│ JWT Auth + REST
▼
┌──────────────────────────────────────────────────────────────┐
│ Backend (Node.js + Express 4) │
│ • Auth: bcryptjs + jsonwebtoken │
│ • Rutas: /auth, /gps, /vehicles, /commands │
│ • AWS IoT: publica comandos MQTT al dispositivo │
└────────────────────┬──────────────────────┬──────────────────┘
│ │
▼ ▼
┌─────────────────────────┐ ┌─────────────────────────────┐
│ DynamoDB │ │ AWS IoT Core │
│ • Usuarios │ │ MQTT endpoint │
│ • GPS (dispositivos) │ │ Comandos → dispositivo │
│ • Vehículos │ │ (block_engine, alarm, etc) │
│ • Coordenadas (TTL) │ └─────────────────────────────┘
│ • Comandos │ │
│ • Eventos (TTL) │ ▼ MQTT
└─────────────────────────┘ ┌─────────────────────────────┐
│ Dispositivo GPS (hardware) │
│ Publica coordenadas │
│ Recibe comandos MQTT │
└─────────────────────────────┘Implementación
Frontend — Mapa interactivo con MapLibre GL
El dashboard usa MapLibre GL (open-source, sin costo de API key) para renderizar mapas. Los vehículos se muestran como marcadores con su posición actual. El componente Map.jsx actualiza las posiciones consultando GET /api/gps periódicamente. React Router gestiona las páginas: Dashboard, Login, detalle de vehículo.
Backend — Autenticación JWT + RBAC
El backend usa bcryptjs para hashear contraseñas y jsonwebtoken para emitir tokens JWT. El middleware auth.middleware.js verifica el token en cada request protegido. Los usuarios se almacenan en DynamoDB (tabla Usuarios) con un GSI en email para búsqueda eficiente.
Envío de comandos remotos vía AWS IoT
El servicio iot-commands.service.js publica mensajes MQTT al IoT endpoint de AWS usando el AWS SDK. Cada comando tiene un payload específico (ej: block_engine → {CMD: 'setdigout 100'}). El estado del comando (pending → sent → executed/failed/timeout) se persiste en DynamoDB (tabla Comandos).
Persistencia de coordenadas con TTL en DynamoDB
Las coordenadas GPS se almacenan en DynamoDB con TTL habilitado — expiran automáticamente después del período configurado, controlando el costo de storage. Un GSI en gpsId + timestamp permite consultar el historial de posiciones de un dispositivo ordenado cronológicamente.
Tech Stack
Frontend
Backend
Infraestructura AWS
Decisiones Técnicas
MapLibre GL vs Google Maps vs Leaflet
Elegido
MapLibre GL
Alternativas
- —Google Maps — mejor UX y cobertura, costo por uso ($7/1000 requests)
- —Leaflet — open-source, simple, raster tiles, menos performance con muchos marcadores
- —Mapbox GL — similar a MapLibre (es el fork), requiere API key y tiene costos
Razón
MapLibre GL es open-source y no requiere API key ni cargos por uso. Soporta mapas vectoriales con WebGL — mejor performance que Leaflet (raster tiles). Leaflet es más simple pero limitado para mapas con muchos marcadores en tiempo real. Google Maps tiene mejor UX pero cobra por request.
DynamoDB vs RDS para persistir coordenadas GPS
Elegido
DynamoDB con TTL
Alternativas
- —RDS PostgreSQL con PostGIS — queries geoespaciales avanzadas, requiere gestionar instancias
- —TimescaleDB — optimizado para series de tiempo, más complejo de operar
Razón
Las coordenadas GPS son writes frecuentes (cada 30s por vehículo) con lecturas por rango de tiempo. DynamoDB On-Demand escala automáticamente sin capacity planning. El TTL elimina coordenadas antiguas automáticamente sin queries de limpieza. Para 100 vehículos → 2,880 writes/hora → DynamoDB maneja esto sin configuración.
Snippets de Código
import AWS from 'aws-sdk';
const iotData = new AWS.IotData({
endpoint: process.env.IOT_ENDPOINT,
region: process.env.AWS_REGION,
});
const COMMAND_PAYLOADS = {
block_engine: { CMD: 'setdigout 100' },
unblock_engine: { CMD: 'setdigout 000' },
activate_alarm: { CMD: 'setdigout 010' },
deactivate_alarm: { CMD: 'setdigout 000' },
flash_lights: { CMD: 'setdigout 001' },
request_status: { CMD: 'getinfo' },
};
export async function sendIoTCommand(deviceId, command, parameters = {}) {
const payload = COMMAND_PAYLOADS[command] ?? { CMD: command, ...parameters };
await iotData.publish({
topic: `gps/${deviceId}/commands`,
qos: 1,
payload: JSON.stringify(payload),
}).promise();
}import axios from 'axios';
const api = axios.create({
baseURL: import.meta.env.VITE_API_URL,
headers: { 'Content-Type': 'application/json' },
});
// Adjunta el JWT automáticamente a cada request
api.interceptors.request.use((config) => {
const token = localStorage.getItem('token');
if (token) config.headers.Authorization = `Bearer ${token}`;
return config;
});
export const sendCommand = async (gpsId, command, parameters = {}) => {
const { data } = await api.post(`/commands/${gpsId}/send`, {
command,
parameters,
});
return data;
};
export const getCommandHistory = async (gpsId, limit = 50) => {
const { data } = await api.get(`/commands/${gpsId}/history`, {
params: { limit },
});
return data;
};