A TypeScript library for loading GTFS (General Transit Feed Specification) data into a SQLite database for querying in browser, Node.js, and React Native environments. Ships with adapters for sql.js (browser / Node WASM) and better-sqlite3 (Node native); bring your own for op-sqlite, expo-sqlite, etc.
Live Demo — A fully static demo website with GTFS and GTFS-RT data running in a Web Worker, with no backend.
Théophile Helleboid / SysDevRun
This project is greatly inspired by node-gtfs, also MIT licensed. The main difference is that gtfs-sqljs aims to run on both browser and Node.js environments.
ArrayBuffer) or existing SQLite databasesArrayBuffer for persistence (sql.js / in-memory better-sqlite3)examples/cache/npm install gtfs-sqljs
Install the adapter(s) you want as peer dependencies. Install one or both depending on where the library runs:
# Browser or Node (WASM-backed, in-memory)
npm install sql.js
# Node (native, can be file-backed)
npm install better-sqlite3
Note (v0.6 breaking change): the core library no longer hard-depends on sql.js. You must pass an adapter to
fromZip/fromZipData/fromDatabase, or hand a pre-opened handle toGtfsSqlJs.attach(). All query methods are nowasyncand returnPromise<T>.
import { GtfsSqlJs } from 'gtfs-sqljs';
import { createSqlJsAdapter } from 'gtfs-sqljs/adapters/sql-js';
// Load GTFS data from a ZIP URL
const gtfs = await GtfsSqlJs.fromZip('https://example.com/gtfs.zip', {
adapter: await createSqlJsAdapter(),
});
// Query routes
const routes = await gtfs.getRoutes();
// Query stops with filters
const stops = await gtfs.getStops({ name: 'Central Station' });
// Get trips for a route on a specific date
const trips = await gtfs.getTrips({
routeId: 'ROUTE_1',
date: '20240115',
directionId: 0,
});
// Get stop times for a trip
const stopTimes = await gtfs.getStopTimes({ tripId: trips[0].trip_id });
// Clean up
await gtfs.close();
import BetterSqlite3 from 'better-sqlite3';
import { GtfsSqlJs } from 'gtfs-sqljs';
import { wrapBetterSqlite3 } from 'gtfs-sqljs/adapters/better-sqlite3';
// Open a file-backed DB yourself, then attach.
const raw = new BetterSqlite3('./gtfs.db');
const gtfs = await GtfsSqlJs.attach(wrapBetterSqlite3(raw));
const routes = await gtfs.getRoutes();
// `attach()` does not own the handle by default — you close both.
await gtfs.close();
raw.close();
See the Usage Guide for detailed examples covering fromDatabase, fromZipData, GTFS-RT, and caching.
gtfs-sqljs talks to a narrow async GtfsDatabase interface (prepare, run, export, close). Pick the adapter that matches your runtime:
| Adapter | Subpath | Typical use |
|---|---|---|
| sql.js | gtfs-sqljs/adapters/sql-js |
Browser; Node without native deps; always in-memory |
| better-sqlite3 | gtfs-sqljs/adapters/better-sqlite3 |
Node; file-backed persistence; fastest native performance |
| op-sqlite | (user-provided — see Usage Guide) | React Native (JSI) |
| expo-sqlite | (user-provided — see Usage Guide) | Expo / React Native |
Two entry points cover every scenario:
fromZip/fromZipData/fromDatabase take options.adapter: GtfsDatabaseAdapter. The library creates / opens the DB for you. Best for in-memory drivers (sql.js, in-memory better-sqlite3).GtfsSqlJs.attach(db, options?) takes a live GtfsDatabase you already built. Best for file-backed drivers where the caller owns the file path, journal mode, etc.Full API documentation: API Reference
All GtfsSqlJs instance methods return Promise<T> — use await.
GtfsSqlJs.fromZip(zipPath, options) — Create instance from a GTFS ZIP URL. options.adapter is required.GtfsSqlJs.fromZipData(zipData, options) — Create instance from pre-loaded ZIP bytes (ArrayBuffer or Uint8Array). options.adapter is required.GtfsSqlJs.fromDatabase(database, options) — Create instance from existing SQLite bytes (ArrayBuffer). options.adapter is required.GtfsSqlJs.attach(db, options?) — Attach to a pre-opened GtfsDatabase handle. No adapter needed (the handle is the adapter output). Pass skipSchema: true when the attached DB already has the GTFS schema; pass ownsDatabase: true to have close() release the underlying handle.All methods support flexible filtering with both single values and arrays:
getAgencies(filters?) - Get agencies (filters: agencyId, limit)getStops(filters?) - Get stops (filters: stopId, stopCode, name, tripId, limit)getRoutes(filters?) - Get routes (filters: routeId, agencyId, limit)getTrips(filters?) - Get trips (filters: tripId, routeId, serviceIds, directionId, agencyId, includeRealtime, limit, date)getStopTimes(filters?) - Get stop times (filters: tripId, stopId, routeId, serviceIds, directionId, agencyId, includeRealtime, limit, date)getShapes(filters?) - Get shape points (filters: shapeId, routeId, tripId, limit)getShapesToGeojson(filters?, precision?) - Get shapes as GeoJSON FeatureCollection (same filters, precision default: 6)buildOrderedStopList(tripIds) - Build an ordered list of stops from multiple trips (handles express/local variations)getActiveServiceIds(date) - Get active service IDs for a date (YYYYMMDD format)getCalendars(filters?) - Get calendars (filters: serviceId, limit)getCalendarDates(serviceId) - Get calendar date exceptions for a servicegetCalendarDatesForDate(date) - Get calendar exceptions for a specific datefetchRealtimeData(urls?) - Fetch and load RT data from protobuf feedsclearRealtimeData() - Clear all realtime data from databasesetRealtimeFeedUrls(urls) - Configure RT feed URLsgetRealtimeFeedUrls() - Get configured RT feed URLssetStalenessThreshold(seconds) - Set staleness threshold (default: 120 seconds)getStalenessThreshold() - Get current staleness thresholdgetLastRealtimeFetchTimestamp() - Get Unix timestamp (seconds) of last successful RT fetch, or null if never fetchedgetAlerts(filters?) - Get alerts (filters: alertId, routeId, stopId, tripId, activeOnly, cause, effect, limit)getVehiclePositions(filters?) - Get vehicle positions (filters: tripId, routeId, vehicleId, limit)getTripUpdates(filters?) - Get trip updates (filters: tripId, routeId, limit)getStopTimeUpdates(filters?) - Get stop time updates (filters: tripId, stopId, stopSequence, limit)export() - Export database to ArrayBuffer (includes RT data)getDatabase() - Get direct access to sql.js database for advanced queriesclose() - Close database connectiondebugExportAllAlerts() - Export all alerts without staleness filteringdebugExportAllVehiclePositions() - Export all vehicle positions without staleness filteringdebugExportAllTripUpdates() - Export all trip updates without staleness filteringdebugExportAllStopTimeUpdates() - Export all stop time updates without staleness filteringThis library is written in TypeScript and provides full type definitions for all GTFS entities, filter options, GTFS-RT types, and progress tracking:
import type {
// Adapter surface
GtfsDatabase, GtfsDatabaseAdapter, GtfsStatement, Row, SqlValue,
// Static GTFS types
Stop, Route, Trip, StopTime, Shape,
TripFilters, StopTimeFilters, ShapeFilters,
// GeoJSON types
GeoJsonFeatureCollection,
// GTFS-RT types
Alert, VehiclePosition, TripWithRealtime, StopTimeWithRealtime,
AlertFilters, VehiclePositionFilters,
// GTFS-RT enums
AlertCause, AlertEffect, ScheduleRelationship,
// Progress tracking types
ProgressInfo, ProgressCallback
} from 'gtfs-sqljs';
import { ExportNotSupportedError } from 'gtfs-sqljs';
This library implements:
MIT
Contributions are welcome! Please feel free to submit a Pull Request.
If you encounter any problems or have suggestions, please open an issue.