@drfrost/bods-js
v1.0.1
Published
JavaScript client for the UK's Bus Open Data Service (BODS) API
Maintainers
Readme
bods-js
BODS JavaScript Client
A comprehensive TypeScript/JavaScript client for the UK's Bus Open Data Service (BODS) API. This library provides a simple, type-safe interface to access bus timetables, fares, real-time vehicle locations, and service disruptions.
🚀 Features
- 🎯 Full TypeScript Support - Complete type definitions for all API responses
- 📅 Timetables API - Access to bus schedules and route information
- 🎟️ Fares API - Bus fare information by operator and area
- 🚌 Real-time Vehicle Tracking - Live bus locations in SIRI-VM and GTFS-RT formats
- ⚠️ Service Disruptions - Current and planned service disruptions
- 🗺️ Geographic Queries - Search by bounding box with built-in utilities
- 📄 Comprehensive Documentation - JSDoc comments throughout
- 🛡️ Error Handling - Robust error handling with detailed error types
- ⚡ Performance - Built with modern fetch API and optimized for speed
📦 Installation
# Using npm
npm install @drfrost/bods-js
# Using yarn
yarn add @drfrost/bods-js
# Using pnpm
pnpm add @drfrost/bods-js
# Using bun
bun add @drfrost/bods-js🔑 Getting Started
1. Get Your API Key
First, register for a free API key at data.bus-data.dft.gov.uk.
2. Basic Usage
// Named import (recommended)
import { BODSClient } from '@drfrost/bods-js';
// Default import (also supported)
import BODSClient from '@drfrost/bods-js';
// Import specific clients
import { TimetablesClient, AVLClient } from '@drfrost/bods-js';
const client = new BODSClient({
apiKey: 'your-api-key-here'
});
// Search for timetables
const timetables = await client.timetables.search({
noc: ['SCMN'], // Stagecoach Manchester
status: 'published'
});
console.log(`Found ${timetables.count} timetables`);📚 API Reference
🏗️ Client Initialization
const client = new BODSClient({
apiKey: 'your-api-key',
baseUrl: 'https://data.bus-data.dft.gov.uk', // optional
timeout: 30000 // optional, in milliseconds
});📅 Timetables API
Access bus schedules and route information. Data updated daily at 06:00 GMT.
// Search timetables
const timetables = await client.timetables.search({
noc: ['SCMN', 'SCGH'], // Operator codes
adminArea: ['060'], // Area codes
status: 'published', // published | inactive
dqRag: 'green', // red | amber | green
bodsCompliance: true, // BODS compliant only
limit: 50 // Max results
});
// Get specific timetable
const timetable = await client.timetables.getById(123);
// Get by operator
const operatorTimetables = await client.timetables.getByOperator('SCMN');
// Get high-quality timetables
const quality = await client.timetables.getHighQuality();
// Get recently modified
const recent = await client.timetables.getRecentlyModified(
new Date('2023-01-01')
);🎟️ Fares API
Access bus fare information. Data updated daily at 06:00 GMT.
// Search fares
const fares = await client.fares.search({
noc: ['SCMN'],
status: 'published',
boundingBox: [-2.930, 53.374, -3.085, 53.453] // Liverpool area
});
// Get specific fares dataset
const fareData = await client.fares.getById(456);
// Get by operator
const operatorFares = await client.fares.getByOperator(['SCMN']);
// Get by geographic area
const areaFares = await client.fares.getByArea(
[-2.930, 53.374, -3.085, 53.453]
);
// Get published fares only
const published = await client.fares.getPublished();🚌 Real-time Vehicle Locations (AVL)
Access live bus locations. Data updated every 10 seconds.
// Get vehicles in SIRI-VM format (XML)
const vehicles = await client.avl.getSIRIVM({
operatorRef: ['SCMN'],
boundingBox: [-2.930, 53.374, -3.085, 53.453],
lineRef: '85A'
});
// Get vehicles in GTFS-RT format (Protocol Buffers)
const gtfsVehicles = await client.avl.getGTFSRT({
boundingBox: [-2.930, 53.374, -3.085, 53.453]
});
// Get by operator
const operatorVehicles = await client.avl.getByOperator(['SCMN']);
// Get by line
const lineVehicles = await client.avl.getByLine('85A');
// Get by vehicle
const vehicle = await client.avl.getByVehicle('BUSC-001');
// Get by area in different formats
const siriVehicles = await client.avl.getByArea(boundingBox, 'siri-vm');
const gtfsVehicles2 = await client.avl.getByArea(boundingBox, 'gtfs-rt');⚠️ Service Disruptions
Access current and planned service disruptions in SIRI-SX format.
// Get all current disruptions (raw XML)
const disruptions = await client.disruptions.getCurrent();
// Get parsed disruptions
const parsed = await client.disruptions.getCurrentParsed();
parsed.forEach(disruption => {
console.log(`${disruption.summary}: ${disruption.description}`);
console.log(`Planned: ${disruption.planned}`);
console.log(`Severity: ${disruption.severity}`);
});
// Filter disruptions
const unplanned = client.disruptions.filterDisruptions(parsed, {
planned: false
});
const tfgmDisruptions = client.disruptions.filterDisruptions(parsed, {
participantRef: 'TfGM'
});�️ Utilities
The library includes helpful utility functions:
import {
createBoundingBox,
calculateDistance,
isValidNOC,
UK_CITIES
} from '@drfrost/bods-js';
// Create bounding box from center point and radius
const bbox = createBoundingBox(53.4808, -2.2426, 10); // Manchester, 10km
// Use predefined city bounding boxes
const manchesterVehicles = await client.avl.getByArea(UK_CITIES.MANCHESTER);
const londonFares = await client.fares.getByArea(UK_CITIES.LONDON);
// Calculate distance between points
const distance = calculateDistance(53.4808, -2.2426, 51.5074, -0.1278);
// Validate NOC codes
const isValid = isValidNOC('SCMN'); // trueAvailable City Bounding Boxes
UK_CITIES.LONDON // Greater London (30km radius)
UK_CITIES.MANCHESTER // Greater Manchester (15km radius)
UK_CITIES.BIRMINGHAM // Birmingham (15km radius)
UK_CITIES.LEEDS // Leeds (15km radius)
UK_CITIES.LIVERPOOL // Liverpool (15km radius)
UK_CITIES.BRISTOL // Bristol (15km radius)
UK_CITIES.SHEFFIELD // Sheffield (15km radius)
UK_CITIES.LEICESTER // Leicester (10km radius)
UK_CITIES.COVENTRY // Coventry (10km radius)
UK_CITIES.BRADFORD // Bradford (10km radius)🔧 Error Handling
The client provides detailed error handling:
import { HttpClientError } from '@drfrost/bods-js';
try {
const timetables = await client.timetables.search({ noc: ['INVALID'] });
} catch (error) {
if (error instanceof HttpClientError) {
switch (error.status) {
case 401:
console.error('Invalid API key');
break;
case 403:
console.error('Access forbidden');
break;
case 429:
console.error('Rate limit exceeded');
break;
default:
console.error(`HTTP ${error.status}: ${error.message}`);
}
}
}🌐 Environment Support
- Node.js 18+ (with fetch support)
- Next.js 13+ (App Router and Pages Router)
- React (Client and Server Components)
- Browsers (modern browsers with fetch support)
- Deno (with compatibility layer)
- Bun (native support)
Using with Next.js
The library is fully compatible with Next.js applications:
// app/page.tsx (App Router)
import { BODSClient } from '@drfrost/bods-js';
export default async function HomePage() {
const client = new BODSClient({
apiKey: process.env.BODS_API_KEY!
});
const timetables = await client.timetables.search({
noc: ['SCMN'],
limit: 10
});
return (
<div>
<h1>Bus Timetables</h1>
{/* Render timetables */}
</div>
);
}// pages/api/buses.ts (API Routes)
import { BODSClient } from '@drfrost/bods-js';
import type { NextApiRequest, NextApiResponse } from 'next';
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const client = new BODSClient({
apiKey: process.env.BODS_API_KEY!
});
try {
const vehicles = await client.avl.getSIRIVM({
operatorRef: ['SCMN']
});
res.status(200).json(vehicles);
} catch (error) {
res.status(500).json({ error: 'Failed to fetch vehicles' });
}
}📖 Advanced Examples
Monitoring Live Buses
// Monitor buses in real-time
async function monitorBuses() {
const interval = setInterval(async () => {
try {
const vehicles = await client.avl.getSIRIVM({
operatorRef: ['SCMN'],
boundingBox: UK_CITIES.MANCHESTER
});
// Parse and process vehicle locations
console.log('Updated vehicle positions');
} catch (error) {
console.error('Failed to fetch vehicles:', error);
}
}, 10000); // Every 10 seconds
// Clean up
setTimeout(() => clearInterval(interval), 60000);
}Building a Route Planner
async function getRouteInfo(operatorCode: string, routeNumber: string) {
// Get timetable data
const timetables = await client.timetables.search({
noc: [operatorCode],
search: routeNumber,
status: 'published'
});
// Get fare information
const fares = await client.fares.getByOperator(operatorCode);
// Get live vehicle positions
const vehicles = await client.avl.getSIRIVM({
operatorRef: [operatorCode],
lineRef: routeNumber
});
// Check for disruptions
const disruptions = await client.disruptions.getCurrentParsed();
return {
timetables: timetables.results,
fares: fares.results,
liveVehicles: vehicles.xmlData,
disruptions: disruptions.filter(d =>
d.description?.includes(routeNumber)
)
};
}🧪 Testing
bun test📄 License
MIT License - see LICENSE for details.
🤝 Contributing
Contributions are welcome! Please read our contributing guidelines and submit pull requests.
📞 Support
- Documentation: GitHub Wiki
- Issues: GitHub Issues
- BODS API Docs: data.bus-data.dft.gov.uk
Bus Open Data Service (BODS) API Overview
The Bus Open Data Service provides comprehensive data about UK bus services through four main APIs:
1. Timetables Data API 📅
Detailed information about bus schedules and routes, updated daily at 06:00 GMT.
2. Fares Data API 🎟️
Bus fare information by operator and geographic area, updated daily at 06:00 GMT.
3. Automatic Vehicle Location (AVL) API 🚌
Real-time bus location data in SIRI-VM (XML) and GTFS-RT (Protocol Buffers) formats, updated every 10 seconds.
4. Disruptions Data API ⚠️
Current and planned service disruptions in SIRI-SX format, updated as information becomes available.
Made with ❤️ for the UK transport community
