This is the repository for the TransitMatters Data Dashboard. Client code is written in Typescript with React and Next.js, and the backend is written in Python with Chalice.
- node 24.x and npm 11.x+ required
- verify with
node -v
- verify with
- Python 3.12 with recent uv (0.9.8 or later)
- Verify with
python --version && uv --version uv self updateto update uv
- Verify with
- With mise installed,
mise trust && mise installsets up Node and Python (versions are pinned in.nvmrc/.python-version)
- Add
MBTA_V3_API_KEYto your shell environment:export MBTA_V3_API_KEY='KEY'in ~/.bashrc or ~/.zshrc
- In the root directory, run
npm installto install all frontend and backend dependencies - Run
npm startto start both the JavaScript development server and the Python backend at the same time. - Navigate to http://localhost:3000 (or the url provided after running
npm start)
The backend supports three data source modes, controlled by the TM_BACKEND_SOURCE environment variable:
| Mode | Description |
|---|---|
aws |
Use local AWS credentials to query DynamoDB/S3 directly (default if AWS credentials are detected) |
prod |
Proxy requests to the production API (live data, no AWS credentials needed) |
static |
Use cached sample data (offline development, default if no AWS credentials) |
For external contributors (no AWS access): No configuration needed! The backend auto-detects missing AWS credentials and falls back to cached sample data. For live data without AWS credentials:
export TM_BACKEND_SOURCE=prodFor TransitMatters members (with AWS access):
export TM_BACKEND_SOURCE=awsIf you have access to AWS credentials, add them to your local setup for a better development experience. If you don't yet have TransitMatters AWS credentials, reach out in slack.
- Add your AWS credentials (AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY) to your shell environment, OR add them to a .boto config file with awscli command
aws configure. - Ensure that AWS is set to read from/to
us-east-1viaexport AWS_DEFAULT_REGION=us-east-1or with an aws config
AWS access is not strictly required for development - see "Backend Data Source" above for alternatives
- Configure AWS CLI 2.x with your AWS access key ID and secret under the profile name
transitmatters. - Configure shell environment variables for AWS ACM domain certificates.
TM_FRONTEND_CERT_ARNTM_LABS_WILDCARD_CERT_ARN- (You may also need to set
AWS_DEFAULT_REGIONin your shell tous-east-1. Maybe not! We're not sure.)
- Execute
./deploy.sh(for beta) or./deploy.sh -p(for production). If deploying from a CI platform (such as GitHub Actions) you may also want to include the-cflag.
Additional notes:
- If you're running this locally, your local MBTA-performance API key (which might be your own) will get uploaded to AWS!
- If you're on a platform with a non-GNU
sed, deploy.sh might fail. On macOS, this is fixed bybrew install gnu-sedand adding it to your PATH. - If you get an unexplained error, check the CloudFormation stack status in AWS Console. Good luck!
pages/ # Next.js pages (routing); pages compose layouts via a `Layout` property
modules/ # Self-contained feature modules (alerts, delays, headways, slowzones, etc.)
common/
api/ # API client and React Query hooks (common/api/hooks/)
components/ # Shared UI components
constants/ # Date ranges, line configurations, colors
state/ # Zustand stores (date and station selection)
types/ # TypeScript types (lines.ts has line and route definitions)
utils/ # Utility functions
server/
app.py # Chalice API routes
chalicelib/ # Backend modules; data sources are abstracted in data_funcs.py, s3.py, dynamo.py
chalicelib/models.py # Pydantic response models
chalicelib/config.py # Backend configuration
openapi.json # Generated API spec
The frontend uses Next.js, Zustand, TanStack React Query, Tailwind CSS, and Chart.js. The backend is Python on AWS Chalice, reading from DynamoDB and S3.
Other useful commands:
npm run get-datafetches static data for local development (runs automatically withnpm start)npm run storybookstarts Storybook on port 6006npm run generate-openapiregeneratesserver/openapi.json. Run it after changing API routes or models; CI fails if the committed spec is out of date.
To lint frontend and backend code, run npm run lint in the root directory
To lint just frontend code, run npm run lint-frontend (or npm run lint-frontend-fix to auto-fix)
To lint just backend code, run npm run lint-backend (or npm run lint-backend-fix to auto-fix)
To run backend tests, run cd server && uv run pytest
Frontend code uses ESLint with Prettier, enforced import ordering, and type imports (import type { Foo }). Backend code uses Ruff with a 120 character line length.
If you're using VSCode, .vscode contains a based default workspace setup. It also includes recommended extentions that will improve the dev experience. This config is meant to be as small as possible to enable an "out of the box" easy experience for handling eslint.
The MBTA changes bus routes every few months, usually as part of Better Bus Network. Each dashboard bus route is a station manifest in common/constants/bus_constants/. To update them:
- From
server/bus, generate the manifest from the GTFS schedule in effect after the change:uv run python manifest.py ../../common/constants/bus_constants/<route>.json -r <route ids...> --gtfs-date <YYYY-MM-DD> --service-start <YYYY-MM-DD>. For combined pages, pass every route and keep the existing key with--name. Runcheck_latest_manifests.sh <YYYY-MM-DD>to diff every current manifest against the new schedule. - If a route's stops changed, keep the old manifest as
<route>-legacy.jsonwith aservice_endand give the new one aservice_start, so the old data stays reachable. - Register new keys in
common/constants/stations.tsandcommon/types/lines.ts(BusRoute,BusRouteId,BUS_ROUTES), and add aPEAK_RIDERSHIPentry incommon/constants/baselines.ts(server/scripts/update_max_ridership.pycan compute it). Combined routes also need an entry ingetStationForInvalidFromSelection(common/utils/stations.ts). - For Better Bus Network changes, add the phase to
common/constants/bnrd.tsand banner text tocommon/components/notices/BnrdBanner.tsx.
GTFS archives used by the bus scripts are downloaded to data/gtfs_archives/ by default. Set GTFS_DATA_DIR to share them with other TransitMatters repos (they'll go in $GTFS_DATA_DIR/gtfs_archives).
If you've found this app helpful or interesting, please consider donating to TransitMatters to help support our mission to provide data-driven advocacy for a more reliable, sustainable, and equitable transit system in Metropolitan Boston.