Skip to content
← 02 / Projects

03 / Description

Canadian Political Donations Platform

Explore donation data by donor, party and region, with filtering and export for researchers. I built the backend and API.

  • React
  • MapLibre GL
  • Express
  • PostgreSQL
  • Supabase
  • Jest
backend tests (Jest)
52
person team
3

Runs in your browser

Filter by year and party, click a province, then open Analytics from the bar inside the demo.

Map of donation totals by province, shaded by amount
Fig. 1 Donation totals by province

Problem

Elections Canada publishes contribution records, but they are hard for a researcher to explore. Totals by region, party and year need aggregation, and individual records need controlled access.

Approach

A React and MapLibre front end shows totals by federal riding for 2019 and 2021, with party filters, per-party breakdowns, per-year trends and a comparison view. An Express API aggregates in SQL on Supabase Postgres. A pandas pipeline cleans the raw data. I owned the backend and API layer on a team of three (Weavers).

Key technical decision

Enforce access on the server, not in the UI. The individual-record endpoint validates the caller's token on the backend and limits it to university-affiliated accounts, public map endpoints serve aggregates only, and every access, including denied ones, is written to an audit log. Sort columns are whitelisted and search text is escaped, so user input never becomes SQL.

Result

Researchers sign in, then filter, sort, paginate and export individual records. The public map stays open. The backend has 52 passing Jest tests, and the data pipeline has pytest coverage.

Run it

The demo on this page is the project front end. While the database answers, the map, analytics, researcher records, and export use live aggregates. If it does not, the same UI shows a preview of public province, year, and party totals.

cd backend && npm install && npm start    # http://localhost:3001/api/health
cd frontend && npm install && npm start   # http://localhost:3000

The backend needs SUPABASE_DB_URL, SUPABASE_URL and SUPABASE_ANON_KEY (see backend/.env.example). Load the data with the scripts in data/Database Creation.