Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.bookovia.com/llms.txt

Use this file to discover all available pages before exploring further.

Bookovia TypeScript SDK

The official TypeScript SDK for the Bookovia Telematics API provides a fully type-safe, modern development experience with comprehensive IntelliSense support and compile-time error checking.

Features

  • Full Type Safety - Comprehensive TypeScript definitions
  • Auto-generated Types - Types generated from API schema
  • IntelliSense Support - Complete IDE autocomplete and validation
  • Compile-time Validation - Catch errors before runtime
  • Modern ESM/CommonJS - Support for both module systems
  • Generic Interfaces - Flexible and extensible type system
  • Strict Mode Compatible - Works with strictest TypeScript settings

Installation

npm

npm install @bookovia/typescript-sdk

yarn

yarn add @bookovia/typescript-sdk
Requirements:
  • TypeScript 4.5+
  • Node.js 16+
  • Modern build tools (Vite, Webpack 5, etc.)

Quick Start

Initialize the Client

import { BookoviaClient, ClientConfig } from '@bookovia/typescript-sdk';

// Initialize with API key
const client = new BookoviaClient('bkv_live_your_api_key');

// With typed configuration
const config: ClientConfig = {
  baseUrl: 'https://api.bookovia.com/v1',
  timeout: 30000,
  retryCount: 3,
  debug: false
};

const client = new BookoviaClient('bkv_live_your_api_key', config);

Start a Trip with Full Type Safety

import { 
  StartTripRequest, 
  Trip, 
  Location,
  TripStatus 
} from '@bookovia/typescript-sdk';

async function startTrip(): Promise<Trip> {
  const request: StartTripRequest = {
    vehicleId: 'vehicle_123',
    driverId: 'driver_456', // Optional with proper typing
    startLocation: {        // Optional with proper typing
      latitude: 40.7128,
      longitude: -74.0060,
      address: 'New York, NY' // Optional
    },
    metadata: {
      purpose: 'delivery',
      route: 'downtown',
      priority: 'high'
    }
  };
  
  try {
    const trip: Trip = await client.trips.start(request);
    console.log(`Trip started: ${trip.tripId}`);
    return trip;
  } catch (error) {
    // Error handling with typed errors
    throw error;
  }
}

Type-safe Location Upload

import { 
  LocationUploadRequest, 
  BatchUploadRequest,
  LocationPoint 
} from '@bookovia/typescript-sdk';

async function uploadLocations(tripId: string): Promise<void> {
  const locations: LocationPoint[] = [
    {
      latitude: 40.7128,
      longitude: -74.0060,
      timestamp: new Date().toISOString(),
      speed: 35,
      heading: 180,
      accuracy: 5 // Optional with type checking
    },
    {
      latitude: 40.7130,
      longitude: -74.0058,
      timestamp: new Date(Date.now() + 30000).toISOString(),
      speed: 42,
      heading: 175
    }
  ];
  
  const request: BatchUploadRequest = {
    tripId,
    locations
  };
  
  await client.locations.batchUpload(request);
}

Type Definitions

Core Types

// Trip-related types
export interface Trip {
  readonly tripId: string;
  readonly organizationId: string;
  readonly vehicleId: string;
  readonly driverId?: string;
  readonly status: TripStatus;
  readonly startTime: string; // ISO 8601
  readonly endTime?: string;
  readonly startLocation?: Location;
  readonly endLocation?: Location;
  readonly analytics: TripAnalytics;
  readonly metadata?: Record<string, unknown>;
  readonly createdAt: string;
  readonly updatedAt: string;
}

export interface Location {
  readonly latitude: number;
  readonly longitude: number;
  readonly address?: string;
}

export interface LocationPoint extends Location {
  readonly timestamp: string; // ISO 8601
  readonly speed: number;     // km/h
  readonly heading: number;   // 0-360 degrees
  readonly accuracy?: number; // meters
  readonly altitude?: number; // meters
}

export interface TripAnalytics {
  readonly distanceKm: number;
  readonly durationMinutes: number;
  readonly maxSpeedKmh: number;
  readonly avgSpeedKmh: number;
  readonly idleTimeMinutes: number;
  readonly locationsCount: number;
  readonly eventsCount: number;
  readonly safetyScore: number; // 0-100
  readonly ecoScore: number;    // 0-100
}

Enums and Union Types

// Trip status enum
export enum TripStatus {
  Active = 'active',
  Completed = 'completed',
  Paused = 'paused',
  Cancelled = 'cancelled'
}

// Event types
export enum EventType {
  HarshAcceleration = 'harsh_acceleration',
  HarshBraking = 'harsh_braking',
  HarshCornering = 'harsh_cornering',
  Speeding = 'speeding',
  IdleExcessive = 'idle_excessive',
  PhoneUsage = 'phone_usage'
}

// Severity levels
export type EventSeverity = 'low' | 'medium' | 'high' | 'critical';

// API environments
export type Environment = 'test' | 'live';

Request Types

export interface StartTripRequest {
  readonly vehicleId: string;
  readonly driverId?: string;
  readonly startLocation?: Location;
  readonly metadata?: Record<string, unknown>;
  readonly odometerReading?: number;
  readonly fuelLevelPercent?: number;
}

export interface StopTripRequest {
  readonly endLocation?: Location;
  readonly odometerReading?: number;
  readonly fuelLevelPercent?: number;
  readonly metadata?: Record<string, unknown>;
}

export interface TripFilters {
  readonly vehicleId?: string;
  readonly driverId?: string;
  readonly status?: TripStatus;
  readonly startDate?: string; // ISO 8601 date
  readonly endDate?: string;   // ISO 8601 date
  readonly limit?: number;     // 1-100
  readonly offset?: number;    // >= 0
}

export interface LocationUploadRequest {
  readonly tripId: string;
  readonly latitude: number;
  readonly longitude: number;
  readonly timestamp: string;
  readonly speed: number;
  readonly heading: number;
  readonly accuracy?: number;
  readonly altitude?: number;
}

export interface BatchUploadRequest {
  readonly tripId: string;
  readonly locations: readonly LocationPoint[];
}

Response Types

export interface TripListResponse {
  readonly trips: readonly Trip[];
  readonly totalCount: number;
  readonly hasMore: boolean;
  readonly nextOffset?: number;
}

export interface SafetyScore {
  readonly score: number;        // 0-100
  readonly grade: 'A' | 'B' | 'C' | 'D' | 'F';
  readonly factors: readonly SafetyFactor[];
  readonly recommendations: readonly string[];
  readonly period: {
    readonly start: string;
    readonly end: string;
  };
}

export interface SafetyFactor {
  readonly category: string;
  readonly score: number;
  readonly weight: number;
  readonly description: string;
}

export interface Event {
  readonly eventId: string;
  readonly tripId: string;
  readonly eventType: EventType;
  readonly severity: EventSeverity;
  readonly timestamp: string;
  readonly location: Location;
  readonly details: Record<string, unknown>;
  readonly processed: boolean;
}

Client Configuration Types

export interface ClientConfig {
  readonly baseUrl?: string;
  readonly timeout?: number;
  readonly retryCount?: number;
  readonly retryDelay?: number;
  readonly debug?: boolean;
  readonly defaultHeaders?: Record<string, string>;
  readonly requestInterceptor?: RequestInterceptor;
  readonly responseInterceptor?: ResponseInterceptor;
}

export type RequestInterceptor = (config: RequestConfig) => RequestConfig | Promise<RequestConfig>;
export type ResponseInterceptor = (response: ApiResponse) => ApiResponse | Promise<ApiResponse>;

export interface RequestConfig {
  readonly method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
  readonly url: string;
  readonly headers: Record<string, string>;
  readonly data?: unknown;
  readonly params?: Record<string, string | number>;
}

export interface ApiResponse<T = unknown> {
  readonly data: T;
  readonly status: number;
  readonly statusText: string;
  readonly headers: Record<string, string>;
  readonly requestId?: string;
  readonly responseTime?: number;
}

Service Interfaces

Trip Service

export interface TripService {
  // User Trip Queries
  getUserActiveTrips(userId: string): Promise<TelematicsResult<any>>;
  getUserTripHistory(userId: string, status?: string): Promise<TelematicsResult<any>>;
  
  // Organization Trip Queries
  getOrgActiveTrips(orgId: string): Promise<TelematicsResult<any>>;
  getOrgTripHistory(orgId: string, status?: string): Promise<TelematicsResult<any>>;
  
  // Vehicle Trip Queries
  getVehicleActiveTrips(vehicleId: string): Promise<TelematicsResult<any>>;
  getVehicleTripHistory(vehicleId: string, status?: string): Promise<TelematicsResult<any>>;
  
  // Legacy Methods
  start(request: StartTripRequest): Promise<Trip>;
  stop(tripId: string, request?: StopTripRequest): Promise<Trip>;
  get(tripId: string): Promise<Trip>;
  list(filters?: TripFilters): Promise<TripListResponse>;
  getSummary(tripId: string): Promise<TripSummary>;
  updateMetadata(tripId: string, metadata: Record<string, unknown>): Promise<Trip>;
}

Location Service

export interface LocationService {
  upload(request: LocationUploadRequest): Promise<void>;
  batchUpload(request: BatchUploadRequest): Promise<BatchUploadResponse>;
  getRoute(tripId: string): Promise<Route>;
  findNearby(request: NearbySearchRequest): Promise<NearbyLocation[]>;
}

export interface BatchUploadResponse {
  readonly accepted: number;
  readonly rejected: number;
  readonly errors: readonly ValidationError[];
}

export interface Route {
  readonly tripId: string;
  readonly coordinates: readonly [number, number][]; // [lng, lat] tuples
  readonly distance: number;
  readonly duration: number;
  readonly boundingBox: readonly [number, number, number, number]; // [minLng, minLat, maxLng, maxLat]
}

Safety Service

export interface SafetyService {
  getScore(request: SafetyScoreRequest): Promise<SafetyScore>;
  analyzeBehavior(request: BehaviorAnalysisRequest): Promise<BehaviorAnalysis>;
  getHarshEvents(filters: EventFilters): Promise<EventListResponse>;
  getCrashRisk(request: CrashRiskRequest): Promise<CrashRiskAssessment>;
}

export interface SafetyScoreRequest {
  readonly tripId?: string;
  readonly driverId?: string;
  readonly vehicleId?: string;
  readonly dateRange?: DateRange;
}

export interface DateRange {
  readonly start: string; // ISO 8601 date
  readonly end: string;   // ISO 8601 date
}

export interface BehaviorAnalysis {
  readonly driverId: string;
  readonly period: DateRange;
  readonly metrics: {
    readonly harshAccelerationRate: number;
    readonly harshBrakingRate: number;
    readonly speedingRate: number;
    readonly phoneUsageRate: number;
  };
  readonly improvements: readonly string[];
  readonly trends: readonly BehaviorTrend[];
}

export interface BehaviorTrend {
  readonly metric: string;
  readonly change: number;    // Percentage change
  readonly direction: 'improving' | 'declining' | 'stable';
}

Error Handling with Types

import { 
  BookoviaError,
  AuthenticationError,
  ValidationError,
  NotFoundError,
  RateLimitError,
  ServerError 
} from '@bookovia/typescript-sdk';

async function handleTripCreation(request: StartTripRequest): Promise<Trip | null> {
  try {
    return await client.trips.start(request);
  } catch (error) {
    if (error instanceof AuthenticationError) {
      console.error('Authentication failed:', error.message);
      // Handle auth error with proper typing
      return null;
      
    } else if (error instanceof ValidationError) {
      console.error('Validation errors:', error.fields);
      // error.fields is properly typed as ValidationField[]
      error.fields.forEach(field => {
        console.error(`${field.path}: ${field.message}`);
      });
      return null;
      
    } else if (error instanceof RateLimitError) {
      console.error(`Rate limited. Retry after: ${error.retryAfter}ms`);
      // error.retryAfter is typed as number
      return null;
      
    } else if (error instanceof NotFoundError) {
      console.error('Resource not found:', error.resourceType, error.resourceId);
      return null;
      
    } else {
      // Generic error handling
      console.error('Unexpected error:', error);
      throw error;
    }
  }
}

// Custom error types for specific domains
export interface ValidationField {
  readonly path: string;
  readonly message: string;
  readonly value: unknown;
}

Generic and Advanced Types

Generic Response Wrapper

// Generic API response wrapper
export interface ApiResult<T> {
  readonly data: T;
  readonly success: boolean;
  readonly requestId: string;
  readonly timestamp: string;
}

// Paginated response type
export interface PaginatedResponse<T> {
  readonly items: readonly T[];
  readonly totalCount: number;
  readonly page: number;
  readonly pageSize: number;
  readonly hasMore: boolean;
}

// Usage with trips
type TripPage = PaginatedResponse<Trip>;

Advanced Type Utilities

// Utility types for common operations
export type PartialUpdate<T> = Partial<Omit<T, 'id' | 'createdAt' | 'updatedAt'>>;
export type CreateRequest<T> = Omit<T, 'id' | 'createdAt' | 'updatedAt'>;
export type ReadOnlyFields<T> = Pick<T, 'id' | 'createdAt' | 'updatedAt'>;

// Example usage
type TripUpdate = PartialUpdate<Trip>; // Only allows updating mutable fields
type NewTrip = CreateRequest<Trip>;    // Excludes system-generated fields

// Conditional types for different API environments
export type ApiKey<T extends Environment> = T extends 'test' 
  ? `bkv_test_${string}` 
  : `bkv_live_${string}`;

// Usage
const testKey: ApiKey<'test'> = 'bkv_test_1234...'; // ✅ Valid
const liveKey: ApiKey<'live'> = 'bkv_live_5678...'; // ✅ Valid
// const invalid: ApiKey<'test'> = 'bkv_live_1234...'; // ❌ Type error

Advanced Usage Examples

Type-safe Fleet Management

import { 
  BookoviaClient, 
  Trip, 
  TripStatus, 
  Vehicle,
  Driver 
} from '@bookovia/typescript-sdk';

class FleetManager {
  private client: BookoviaClient;
  private activeTrips: Map<string, Trip> = new Map();

  constructor(apiKey: string) {
    this.client = new BookoviaClient(apiKey);
  }

  async startFleetOperation(
    vehicles: readonly Vehicle[], 
    drivers: readonly Driver[]
  ): Promise<Trip[]> {
    const trips: Trip[] = [];
    
    for (let i = 0; i < Math.min(vehicles.length, drivers.length); i++) {
      const vehicle = vehicles[i];
      const driver = drivers[i];
      
      try {
        const trip = await this.client.trips.start({
          vehicleId: vehicle.vehicleId,
          driverId: driver.driverId,
          metadata: {
            fleetOperation: true,
            assignedAt: new Date().toISOString(),
            priority: vehicle.priority ?? 'normal'
          }
        });
        
        this.activeTrips.set(trip.tripId, trip);
        trips.push(trip);
        
      } catch (error) {
        console.error(`Failed to start trip for vehicle ${vehicle.vehicleId}:`, error);
      }
    }
    
    return trips;
  }

  async getFleetStatus(): Promise<FleetStatus> {
    const trips = Array.from(this.activeTrips.values());
    
    return {
      totalTrips: trips.length,
      totalDistance: trips.reduce((sum, trip) => sum + trip.analytics.distanceKm, 0),
      averageSpeed: this.calculateAverageSpeed(trips),
      safetyScore: this.calculateFleetSafetyScore(trips),
      activeVehicles: new Set(trips.map(trip => trip.vehicleId)).size
    };
  }

  private calculateAverageSpeed(trips: readonly Trip[]): number {
    const validSpeeds = trips
      .map(trip => trip.analytics.avgSpeedKmh)
      .filter(speed => speed > 0);
      
    return validSpeeds.length > 0 
      ? validSpeeds.reduce((sum, speed) => sum + speed, 0) / validSpeeds.length 
      : 0;
  }

  private calculateFleetSafetyScore(trips: readonly Trip[]): number {
    const validScores = trips
      .map(trip => trip.analytics.safetyScore)
      .filter(score => score > 0);
      
    return validScores.length > 0
      ? validScores.reduce((sum, score) => sum + score, 0) / validScores.length
      : 0;
  }
}

interface Vehicle {
  readonly vehicleId: string;
  readonly model: string;
  readonly year: number;
  readonly priority?: 'low' | 'normal' | 'high';
}

interface Driver {
  readonly driverId: string;
  readonly name: string;
  readonly licenseNumber: string;
  readonly experienceYears: number;
}

interface FleetStatus {
  readonly totalTrips: number;
  readonly totalDistance: number;
  readonly averageSpeed: number;
  readonly safetyScore: number;
  readonly activeVehicles: number;
}

Real-time Type-safe Location Tracking

import { LocationPoint, BatchUploadRequest } from '@bookovia/typescript-sdk';

class LocationTracker {
  private client: BookoviaClient;
  private locationBuffer: LocationPoint[] = [];
  private intervalId: NodeJS.Timeout | null = null;

  constructor(client: BookoviaClient) {
    this.client = client;
  }

  startTracking(tripId: string, options: TrackingOptions = {}): void {
    const {
      uploadInterval = 30000,
      bufferSize = 10,
      enableHighAccuracy = true
    } = options;

    this.intervalId = setInterval(async () => {
      try {
        const location = await this.getCurrentLocation(enableHighAccuracy);
        this.locationBuffer.push(location);

        if (this.locationBuffer.length >= bufferSize) {
          await this.uploadBufferedLocations(tripId);
        }
      } catch (error) {
        console.error('Location tracking error:', error);
      }
    }, uploadInterval);
  }

  stopTracking(): void {
    if (this.intervalId) {
      clearInterval(this.intervalId);
      this.intervalId = null;
    }
  }

  private async getCurrentLocation(enableHighAccuracy: boolean): Promise<LocationPoint> {
    return new Promise((resolve, reject) => {
      navigator.geolocation.getCurrentPosition(
        (position) => {
          const location: LocationPoint = {
            latitude: position.coords.latitude,
            longitude: position.coords.longitude,
            timestamp: new Date().toISOString(),
            speed: position.coords.speed ?? 0,
            heading: position.coords.heading ?? 0,
            accuracy: position.coords.accuracy,
            altitude: position.coords.altitude ?? undefined
          };
          resolve(location);
        },
        reject,
        { enableHighAccuracy, timeout: 10000, maximumAge: 30000 }
      );
    });
  }

  private async uploadBufferedLocations(tripId: string): Promise<void> {
    if (this.locationBuffer.length === 0) return;

    const request: BatchUploadRequest = {
      tripId,
      locations: [...this.locationBuffer] // Create immutable copy
    };

    try {
      await this.client.locations.batchUpload(request);
      this.locationBuffer = []; // Clear buffer after successful upload
    } catch (error) {
      console.error('Failed to upload locations:', error);
      // Keep locations in buffer for retry
    }
  }
}

interface TrackingOptions {
  readonly uploadInterval?: number;
  readonly bufferSize?: number;
  readonly enableHighAccuracy?: boolean;
}

Testing with Types

Jest Type Testing

// __tests__/types.test.ts
import { BookoviaClient, Trip, StartTripRequest } from '@bookovia/typescript-sdk';

describe('Bookovia TypeScript SDK', () => {
  let client: BookoviaClient;

  beforeEach(() => {
    client = new BookoviaClient('bkv_test_example_key');
  });

  test('should provide correct types for trip creation', async () => {
    // Type-safe request
    const request: StartTripRequest = {
      vehicleId: 'vehicle_123',
      driverId: 'driver_456',
      startLocation: {
        latitude: 40.7128,
        longitude: -74.0060
      }
    };

    // Mock the response with correct typing
    const mockTrip: Trip = {
      tripId: 'trip_123',
      organizationId: 'org_456',
      vehicleId: 'vehicle_123',
      driverId: 'driver_456',
      status: 'active' as const,
      startTime: '2024-04-13T10:30:00Z',
      startLocation: {
        latitude: 40.7128,
        longitude: -74.0060
      },
      analytics: {
        distanceKm: 0,
        durationMinutes: 0,
        maxSpeedKmh: 0,
        avgSpeedKmh: 0,
        idleTimeMinutes: 0,
        locationsCount: 0,
        eventsCount: 0,
        safetyScore: 100,
        ecoScore: 100
      },
      createdAt: '2024-04-13T10:30:00Z',
      updatedAt: '2024-04-13T10:30:00Z'
    };

    jest.spyOn(client.trips, 'start').mockResolvedValue(mockTrip);

    const result = await client.trips.start(request);
    
    // TypeScript ensures result has correct type
    expect(result.tripId).toBe('trip_123');
    expect(result.status).toBe('active');
  });

  test('should enforce type safety at compile time', () => {
    // These would cause TypeScript compilation errors:
    
    // const invalidRequest: StartTripRequest = {
    //   vehicleId: 123, // ❌ Should be string
    //   invalidField: 'value' // ❌ Unknown field
    // };

    // const trip: Trip = {
    //   tripId: 'trip_123'
    //   // ❌ Missing required fields
    // };
  });
});

IDE Configuration

VSCode Settings

// .vscode/settings.json
{
  "typescript.preferences.strict": true,
  "typescript.preferences.noImplicitAny": true,
  "typescript.preferences.strictNullChecks": true,
  "editor.codeActionsOnSave": {
    "source.organizeImports": true,
    "source.fixAll.eslint": true
  }
}

TypeScript Configuration

// tsconfig.json
{
  "compilerOptions": {
    "target": "ES2020",
    "lib": ["ES2020", "DOM"],
    "module": "ESNext",
    "moduleResolution": "node",
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "noImplicitReturns": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "exactOptionalPropertyTypes": true
  }
}

Support


Ready for type-safe integration? Check out our quickstart guide and API reference for more examples.