Skip to main content

Real-time Updates Guide

The Tracker GraphQL API provides real-time updates through Socket.IO, enabling live tracking and instant notifications.

Getting Started

Socket.IO Client Setup

import { io } from "socket.io-client";

const socket = io("https://your-api-url", {
auth: {
token: "your.jwt.token", // JWT token required
},
transports: ["websocket"],
reconnection: true,
reconnectionDelay: 1000,
reconnectionDelayMax: 5000,
});

Connection Handling

socket.on("connect", () => {
console.log("Connected with ID:", socket.id);
});

socket.on("connect_error", (error) => {
console.error("Connection error:", error.message);
});

socket.on("disconnect", (reason) => {
console.log("Disconnected:", reason);
});

Subscribing to Updates

Location Updates

// Subscribe to tracker updates
socket.emit(
"subscribe",
{
type: "location_updates",
filters: {
tracker_ids: ["tracker_1", "tracker_2"],
},
},
(response) => {
if (response.status === "subscribed") {
console.log("Successfully subscribed to location updates");
}
},
);

// Handle location updates
socket.on("location_update", (data) => {
console.log("New location:", data);
// {
// tracker_id: "tracker_1",
// latitude: 37.7749,
// longitude: -122.4194,
// timestamp: 1635789600,
// speed: 30,
// heading: 180
// }
});

Status Changes

// Subscribe to status changes
socket.emit("subscribe", {
type: "status_updates",
filters: {
status_types: ["online", "offline", "error"],
},
});

// Handle status updates
socket.on("status_change", (data) => {
console.log("Status changed:", data);
// {
// tracker_id: "tracker_1",
// status: "offline",
// timestamp: 1635789600,
// reason: "connection_lost"
// }
});

Event Types

Available Events

interface EventTypes {
// Location Events
location_update: LocationUpdate;
geocoding_complete: GeocodingResult;

// Status Events
status_change: StatusChange;
battery_low: BatteryStatus;

// Alert Events
geofence_breach: GeofenceAlert;
speed_limit: SpeedAlert;

// System Events
maintenance: MaintenanceNotice;
error: ErrorNotification;
}

Event Schemas

interface LocationUpdate {
tracker_id: string;
latitude: number;
longitude: number;
timestamp: number;
speed?: number;
heading?: number;
accuracy?: number;
}

interface StatusChange {
tracker_id: string;
status: "online" | "offline" | "error";
timestamp: number;
reason?: string;
details?: Record<string, any>;
}

Advanced Usage

Batch Subscriptions

// Subscribe to multiple event types
socket.emit(
"subscribe_batch",
{
subscriptions: [
{
type: "location_updates",
filters: { tracker_ids: ["tracker_1"] },
},
{
type: "status_updates",
filters: { status_types: ["error"] },
},
],
},
(response) => {
console.log("Batch subscription result:", response);
},
);

Custom Event Filters

// Subscribe with complex filters
socket.emit("subscribe", {
type: "location_updates",
filters: {
tracker_ids: ["tracker_1"],
min_speed: 30,
regions: ["US-CA", "US-NY"],
update_interval: 60, // seconds
},
});

Error Handling

Reconnection Strategy

const socket = io("https://your-api-url", {
reconnection: true,
reconnectionAttempts: 5,
reconnectionDelay: 1000,
reconnectionDelayMax: 5000,
randomizationFactor: 0.5,
});

socket.io.on("reconnect_attempt", (attempt) => {
console.log(`Reconnection attempt ${attempt}`);
});

socket.io.on("reconnect_failed", () => {
console.error("Failed to reconnect after all attempts");
});

Event Error Handling

socket.on("error", (error) => {
switch (error.code) {
case "AUTHENTICATION_FAILED":
// Handle authentication errors
refreshToken().then((newToken) => {
socket.auth.token = newToken;
socket.connect();
});
break;

case "SUBSCRIPTION_FAILED":
// Handle subscription errors
console.error("Failed to subscribe:", error.message);
break;

default:
console.error("Socket error:", error);
}
});

Best Practices

1. Connection Management

class SocketManager {
constructor(url, options) {
this.socket = io(url, options);
this.setupListeners();
this.subscriptions = new Map();
}

setupListeners() {
this.socket.on("connect", this.handleConnect.bind(this));
this.socket.on("disconnect", this.handleDisconnect.bind(this));
this.socket.on("error", this.handleError.bind(this));
}

async handleConnect() {
// Resubscribe to previous subscriptions
for (const [type, filters] of this.subscriptions) {
await this.subscribe(type, filters);
}
}
}

2. Event Buffering

class EventBuffer {
constructor(flushInterval = 1000) {
this.buffer = [];
this.flushInterval = flushInterval;
}

add(event) {
this.buffer.push(event);
this.scheduleFlush();
}

async flush() {
if (this.buffer.length === 0) return;

const events = [...this.buffer];
this.buffer = [];

await this.processEvents(events);
}
}

3. Performance Optimization

// Implement throttling for high-frequency events
function throttleEvents(socket, eventName, delay) {
let lastEvent = null;
let timeoutId = null;

socket.on(eventName, (data) => {
lastEvent = data;

if (!timeoutId) {
timeoutId = setTimeout(() => {
if (lastEvent) {
handleEvent(lastEvent);
lastEvent = null;
}
timeoutId = null;
}, delay);
}
});
}

Monitoring

Client-side Metrics

const metrics = {
messageCount: 0,
reconnections: 0,
errors: 0,
latency: [],

track(event) {
this.messageCount++;
this.latency.push(Date.now() - event.timestamp);

// Report metrics every minute
if (this.messageCount % 100 === 0) {
this.reportMetrics();
}
},
};

Health Checks

function checkConnectionHealth() {
return {
connected: socket.connected,
lastMessageTime: lastMessageTime,
reconnectionAttempts: socket.io.reconnectionAttempts,
bufferedMessages: socket.io.engine.writeBuffer.length,
};
}

Security Considerations

  1. Always use secure WebSocket connections (WSS)
  2. Implement proper authentication
  3. Validate all incoming data
  4. Use rate limiting
  5. Monitor for suspicious activity