Static single-page site listing tweets from the @ReformExposed Twitter account, filterable by area and keyword. Tweet data is pulled at runtime from a PostgreSQL database via a small HTTP API.
The site is backed by a Postgres database. A small Node server (server.js) serves the
static files and the /api/tweets endpoint that reads from the database.
- Node.js
- A reachable PostgreSQL database with the
tweetstable loaded (see "Loading the data" below)
cd reformexposed-site
npm install # installs the `pg` driver
cp .env.example .env # then edit DATABASE_URL to point at your Postgres
npm start # starts server.js on http://localhost:8088Then open http://localhost:8088.
server.js automatically reads .env from the project root. Alternatively export
DATABASE_URL in your shell.
The tweet data lives in Updated_For_REXPO.csv (columns: URL, Area, all_persons,
Tweet). To (re)load it into Postgres:
createdb reformexposed
psql reformexposed -c "
CREATE TABLE tweets (
id SERIAL PRIMARY KEY,
url TEXT,
area TEXT,
all_persons TEXT,
tweet TEXT
);"
psql reformexposed -c "\copy tweets(url, area, all_persons, tweet) FROM 'Updated_For_REXPO.csv' WITH (FORMAT csv, HEADER true)"npm i -g vercel
cd reformexposed-site
vercel # preview deploy
vercel --prod # production deployThe /api/tweets.js serverless function serves the JSON API on Vercel. Set the
DATABASE_URL environment variable in the Vercel project settings (do not commit
.env).
vercel.json sets clean URLs and basic caching/security headers.
index.html— the app (markup, styles, data loading + filtering logic)api/tweets.js— Vercel serverless function returning tweet JSON from Postgresserver.js— local dev server (static files +/api/tweets) backed by Postgrespackage.json/package-lock.json— Node dependencies (pg)assets/hero.jpeg— vendored hero image (originally hotlinked from samkriss.com)vercel.json— deployment configUpdated_For_REXPO.csv— source data used to populate the database
Tweets are read from the tweets table in Postgres. The API (/api/tweets) returns rows
with area, tweet, and url fields; the frontend filters them by area and keyword.