@gpe-sistemas/tripero-node
v0.6.0
Published
Node.js/TypeScript SDK for Tripero GPS trip detection service
Maintainers
Readme
📋 Overview
tripero-node is the official Node.js/TypeScript SDK for Tripero, an intelligent GPS trip detection and stop analysis microservice. This SDK simplifies integration with Tripero by providing:
- Type-safe API - Full TypeScript support with complete type definitions
- Redis PubSub abstraction - Simple methods for publishing positions and subscribing to events
- HTTP client - Access Tripero's REST API for queries and configuration
- NestJS integration - Dedicated module with dependency injection support
- Fire-and-forget - Non-blocking async operations by default
✨ Features
| Feature | Description | |---------|-------------| | 🔌 Easy Integration | Simple API to publish GPS positions and receive trip events | | 📡 Real-time Events | Subscribe to trip starts, completions, stops, and state changes | | 🔒 Type Safety | Full TypeScript definitions for all events and configurations | | ⚡ High Performance | Fire-and-forget publishing with Redis pipelines for batch operations | | 🏗️ NestJS Ready | First-class NestJS support with module, service, and decorators | | 🔧 Configurable | Flexible configuration with sensible defaults | | 📊 HTTP API Access | Query tracker status, trips, stops, and configure odometers | | 🏷️ Metadata Support | Multi-tenancy support with custom metadata propagation |
📦 Installation
# npm
npm install @gpe-sistemas/tripero-node
# yarn
yarn add @gpe-sistemas/tripero-node
# pnpm
pnpm add @gpe-sistemas/tripero-nodePeer Dependencies
For NestJS integration (optional):
npm install @nestjs/common @nestjs/core🚀 Quick Start
Basic Usage (Node.js)
import { TriperoClient } from '@gpe-sistemas/tripero-node';
// Create client
const tripero = new TriperoClient({
redis: {
host: 'localhost',
port: 6379,
},
});
// Connect to Redis
await tripero.connect();
// Publish a GPS position
await tripero.publishPosition({
deviceId: 'VEHICLE-001',
timestamp: Date.now(),
latitude: -34.6037,
longitude: -58.3816,
speed: 45,
ignition: true,
});
// Subscribe to trip events
tripero.on('trip:started', (event) => {
console.log(`🚗 Trip started: ${event.tripId}`);
});
tripero.on('trip:completed', (event) => {
console.log(`🏁 Trip completed: ${event.tripId}`);
console.log(` Distance: ${event.distance}m`);
console.log(` Duration: ${event.duration}s`);
});
await tripero.subscribe();
// Cleanup on shutdown
process.on('SIGINT', async () => {
await tripero.disconnect();
process.exit(0);
});With HTTP API
const tripero = new TriperoClient({
redis: {
host: 'redis-tripero-service',
port: 6379,
},
http: {
baseUrl: 'http://tripero-service:3001',
},
});
await tripero.connect();
// Get tracker status
const status = await tripero.getTrackerStatus('VEHICLE-001');
console.log(`State: ${status.data.currentState.state}`);
console.log(`Odometer: ${status.data.odometer.totalKm} km`);
// Get trip history
const trips = await tripero.getTrips({
deviceId: 'VEHICLE-001',
from: new Date('2024-01-01'),
to: new Date(),
});
// Set odometer (sync with vehicle's real odometer)
await tripero.setOdometer('VEHICLE-001', 125000000, 'vehicle_sync');📚 API Reference
TriperoClient
The main class for interacting with Tripero.
Constructor
new TriperoClient(options: TriperoClientOptions)Configuration Options
interface TriperoClientOptions {
redis: {
host?: string; // Default: 'localhost'
port?: number; // Default: 6379
db?: number; // Default: 0
password?: string; // Optional
username?: string; // Optional (Redis 6+ ACL)
keyPrefix?: string; // Default: 'tripero:'
};
http?: {
baseUrl: string; // Tripero HTTP API URL
timeout?: number; // Default: 10000ms
headers?: Record<string, string>;
};
options?: {
enableRetry?: boolean; // Default: false
enableOfflineQueue?: boolean; // Default: false
throwOnError?: boolean; // Default: false
logLevel?: 'debug' | 'info' | 'warn' | 'error' | 'silent';
};
}Connection Methods
| Method | Description |
|--------|-------------|
| connect() | Connect to Redis server |
| disconnect() | Disconnect from Redis server |
| health() | Check Redis connection health |
| healthHttp() | Check Tripero HTTP API health (requires http config) |
Publishing Methods
publishPosition(position: PositionEvent): Promise<void>
Publish a GPS position to Tripero.
await tripero.publishPosition({
deviceId: 'VEHICLE-001', // Required: unique device identifier
timestamp: Date.now(), // Required: Unix timestamp in ms
latitude: -34.6037, // Required: -90 to 90
longitude: -58.3816, // Required: -180 to 180
speed: 45, // Required: km/h
ignition: true, // Optional: engine state
heading: 180, // Optional: degrees 0-360
altitude: 25, // Optional: meters
accuracy: 5, // Optional: GPS accuracy in meters
satellites: 12, // Optional: number of satellites
metadata: { // Optional: custom data
tenant_id: 'acme-corp',
fleet_id: 'delivery',
driver_id: 'driver-123',
},
});publishPositions(positions: PositionEvent[]): Promise<void>
Publish multiple positions in a single batch (uses Redis pipeline for performance).
await tripero.publishPositions([
{ deviceId: 'V-001', timestamp: Date.now(), latitude: -34.60, longitude: -58.38, speed: 45 },
{ deviceId: 'V-002', timestamp: Date.now(), latitude: -34.61, longitude: -58.39, speed: 30 },
{ deviceId: 'V-003', timestamp: Date.now(), latitude: -34.62, longitude: -58.40, speed: 55 },
]);publishIgnitionEvent(event: IgnitionEvent): Promise<void>
Publish an ignition state change.
await tripero.publishIgnitionEvent({
deviceId: 'VEHICLE-001',
timestamp: Date.now(),
ignition: true, // true = ON, false = OFF
latitude: -34.6037,
longitude: -58.3816,
});Subscription Methods
on(eventType, handler): void
Register an event handler.
tripero.on('trip:started', (event: TripStartedEvent) => {
console.log(`Trip ${event.tripId} started at ${event.startTime}`);
});
tripero.on('trip:completed', (event: TripCompletedEvent) => {
console.log(`Trip ${event.tripId}: ${event.distance}m in ${event.duration}s`);
});
tripero.on('stop:started', (event: StopStartedEvent) => {
console.log(`Stop detected: ${event.reason}`);
});
tripero.on('tracker:state:changed', (event: TrackerStateChangedEvent) => {
console.log(`${event.previousState} → ${event.currentState}`);
});off(eventType, handler): void
Remove an event handler.
subscribe(): Promise<void>
Start listening to all registered events.
unsubscribe(): Promise<void>
Stop listening to events.
HTTP API Methods
Note: These methods require the
httpconfiguration option.
getTrackerStatus(trackerId: string): Promise<TrackerStatusResponse>
Get real-time status of a tracker including state, odometer, position, and statistics.
setOdometer(trackerId: string, meters: number, reason?: string): Promise<OdometerSetResponse>
Set the odometer offset to sync with the vehicle's real odometer.
getTrips(options: ReportQueryOptions): Promise<TripReport[]>
Query historical trips.
const trips = await tripero.getTrips({
deviceId: 'VEHICLE-001', // or ['V-001', 'V-002'] or 'all'
from: new Date('2024-01-01'),
to: new Date(),
tenantId: 'acme-corp', // Optional: filter by tenant
fleetId: 'delivery', // Optional: filter by fleet
});getStops(options: ReportQueryOptions): Promise<StopReport[]>
Query historical stops with the same options as getTrips().
🏗️ NestJS Integration
Module Setup
// app.module.ts
import { Module } from '@nestjs/common';
import { TriperoModule } from '@gpe-sistemas/tripero-node/nestjs';
@Module({
imports: [
// Static configuration
TriperoModule.forRoot({
redis: {
host: 'redis-tripero-service',
port: 6379,
},
http: {
baseUrl: 'http://tripero-service:3001',
},
}),
],
})
export class AppModule {}Async Configuration
// app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { TriperoModule } from '@gpe-sistemas/tripero-node/nestjs';
@Module({
imports: [
ConfigModule.forRoot(),
TriperoModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
redis: {
host: config.get('TRIPERO_REDIS_HOST', 'localhost'),
port: config.get('TRIPERO_REDIS_PORT', 6379),
keyPrefix: config.get('TRIPERO_KEY_PREFIX', 'tripero:'),
},
http: {
baseUrl: config.get('TRIPERO_HTTP_URL'),
},
}),
}),
],
})
export class AppModule {}Using the Service
// position.service.ts
import { Injectable } from '@nestjs/common';
import { TriperoService } from '@gpe-sistemas/tripero-node/nestjs';
@Injectable()
export class PositionService {
constructor(private readonly tripero: TriperoService) {}
async handleGpsData(data: GpsData) {
await this.tripero.publishPosition({
deviceId: data.imei,
timestamp: Date.now(),
latitude: data.lat,
longitude: data.lon,
speed: data.speed,
ignition: data.acc,
metadata: {
tenant_id: data.tenantId,
},
});
}
async getVehicleStatus(deviceId: string) {
return this.tripero.getTrackerStatus(deviceId);
}
}Event Decorators
// trip-listener.service.ts
import { Injectable, OnModuleInit } from '@nestjs/common';
import { TriperoService } from '@gpe-sistemas/tripero-node/nestjs';
import type { TripStartedEvent, TripCompletedEvent } from '@gpe-sistemas/tripero-node';
@Injectable()
export class TripListenerService implements OnModuleInit {
constructor(private readonly tripero: TriperoService) {}
async onModuleInit() {
// Register event handlers
this.tripero.on('trip:started', this.handleTripStarted.bind(this));
this.tripero.on('trip:completed', this.handleTripCompleted.bind(this));
// Start listening
await this.tripero.subscribe();
}
private handleTripStarted(event: TripStartedEvent) {
console.log(`🚗 Trip started: ${event.tripId}`);
// Send notification, update dashboard, etc.
}
private handleTripCompleted(event: TripCompletedEvent) {
console.log(`🏁 Trip completed: ${event.tripId}`);
console.log(` Distance: ${event.distance}m`);
console.log(` Duration: ${event.duration}s`);
// Generate report, calculate fuel, etc.
}
}📡 Events
Input Events (publish to Tripero)
| Channel | Method | Description |
|---------|--------|-------------|
| position:new | publishPosition() | GPS position data |
| ignition:changed | publishIgnitionEvent() | Ignition state changes |
Output Events (receive from Tripero)
| Event | Description | Key Fields |
|-------|-------------|------------|
| tracker:state:changed | Tracker state transition | previousState, currentState, odometer |
| trip:started | Trip has started | tripId, deviceId, startTime, startLocation |
| trip:completed | Trip has ended | tripId, distance, duration, avgSpeed, maxSpeed |
| stop:started | Stop detected during trip | stopId, tripId, location, reason |
| stop:completed | Stop has ended | stopId, duration |
| position:rejected | Position failed validation | deviceId, reason |
Tracker States
STOPPED ←→ IDLE ←→ MOVING| State | Description |
|-------|-------------|
| STOPPED | Vehicle completely stopped (ignition OFF) |
| IDLE | Engine running but not moving (ignition ON, speed ≈ 0) |
| MOVING | Vehicle in motion (ignition ON, speed > threshold) |
🏷️ Metadata Support
Tripero supports custom metadata that propagates automatically from positions to trips and stops:
await tripero.publishPosition({
deviceId: 'VEHICLE-001',
timestamp: Date.now(),
latitude: -34.6037,
longitude: -58.3816,
speed: 45,
metadata: {
// Optimized fields (B-tree index, ~1-2ms queries)
tenant_id: 'acme-corp',
client_id: 'client-123',
fleet_id: 'delivery-trucks',
// Custom fields (GIN index, ~5-10ms queries)
driver_id: 'driver-456',
route_number: 'R42',
delivery_id: 'DEL-789',
},
});Query trips/stops by metadata:
const trips = await tripero.getTrips({
deviceId: 'all',
from: new Date('2024-01-01'),
to: new Date(),
tenantId: 'acme-corp', // Fast query (~1-2ms)
fleetId: 'delivery-trucks', // Fast query (~1-2ms)
});⚙️ Configuration
Environment Variables
When using with NestJS ConfigService:
| Variable | Description | Default |
|----------|-------------|---------|
| TRIPERO_REDIS_HOST | Redis server host | localhost |
| TRIPERO_REDIS_PORT | Redis server port | 6379 |
| TRIPERO_REDIS_DB | Redis database number | 0 |
| TRIPERO_REDIS_PASSWORD | Redis password | - |
| TRIPERO_KEY_PREFIX | Redis key/channel prefix | tripero: |
| TRIPERO_HTTP_URL | Tripero HTTP API URL | - |
| TRIPERO_LOG_LEVEL | Log level | info |
Redis Key Prefix
The SDK uses tripero: as the default key prefix to match Tripero server defaults. This allows sharing a Redis instance with other applications:
const tripero = new TriperoClient({
redis: {
host: 'shared-redis',
keyPrefix: 'myapp:tripero:', // Custom prefix
},
});Important: The
keyPrefixmust match theREDIS_KEY_PREFIXconfigured in Tripero server.
🔧 Advanced Usage
Custom Logger
import { TriperoClient, TriperoLogger } from '@gpe-sistemas/tripero-node';
const customLogger: TriperoLogger = {
debug: (msg, ...args) => myLogger.debug(msg, ...args),
info: (msg, ...args) => myLogger.info(msg, ...args),
warn: (msg, ...args) => myLogger.warn(msg, ...args),
error: (msg, ...args) => myLogger.error(msg, ...args),
};
const tripero = new TriperoClient({
redis: { host: 'localhost' },
options: {
logger: customLogger,
},
});Error Handling
By default, the SDK logs errors but doesn't throw (fire-and-forget). To enable exceptions:
const tripero = new TriperoClient({
redis: { host: 'localhost' },
options: {
throwOnError: true,
},
});
try {
await tripero.publishPosition(position);
} catch (error) {
console.error('Failed to publish:', error);
}Connection Retry
const tripero = new TriperoClient({
redis: {
host: 'localhost',
redisOptions: {
retryStrategy: (times) => Math.min(times * 1000, 30000),
maxRetriesPerRequest: 3,
},
},
options: {
enableRetry: true,
enableOfflineQueue: true,
},
});🤝 Contributing
Contributions are welcome! Here's how you can help:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Development Setup
# Clone the repository
git clone https://github.com/GPE-Sistemas/tripero-node.git
cd tripero-node
# Install dependencies
npm install
# Build
npm run build
# Run tests
npm test
# Lint
npm run lintCode Style
- Follow TypeScript best practices
- Use meaningful variable and function names
- Add JSDoc comments for public APIs
- Write tests for new features
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
MIT License
Copyright (c) 2024 GPE Sistemas
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.🔗 Related Projects
📞 Support
- Issues: GitHub Issues
- Discussions: GitHub Discussions
