A lightweight Node.js + Express app that demonstrates live sports match management and real-time commentary using WebSockets, Drizzle ORM (MySQL), Zod validation, and simple observability integration.
- The app code is back to the earlier state, with the MySQL/Docker setup changes removed.
- The main blocker is database connectivity: Drizzle migrations still need a reachable MySQL server that accepts the credentials in
.env. - The repo is otherwise ready to run once the database is available, and the next practical step is to get MySQL online and rerun
npm run db:migrate.
- Express REST API for matches and commentary (JSON middleware)
- WebSocket server (via
ws) for real-time broadcasts (/ws) - Drizzle ORM with
mysql2adapter for type-safe DB access - Migrations using
drizzle-kit - Zod validation schemas for requests
- APM (apminsight) integration via environment variable (
APM_LICENSE_KEY) (optional) - Arcjet middleware used for request protection/rate limits
src/index.js— Express server + WebSocket attachws/server.js— WebSocket server + subscription managementroutes/matches.js— endpoints for creating/listing matchescommentary.js— nested routes:/matches/:id/commentary
db/db.js— Drizzle +mysql2/promiseconnection poolschema.js— DB schema (matches,commentary,match_statusenum)
validation/matches.js— Zod schemas for match endpointscommentary.js— Zod schemas for commentary
seed/seed.js— API-based seeder that populates matches & commentary
data/data.json— Seed data for matches and commentary entries
ws/— WebSocket handlers and broadcasting functionsarcjet.js— Arcjet protection middleware
drizzle.config.js— Drizzle Kit config for migrationstest-client.html— Browser-based WebSocket test client.env— environment variables (ignored by git)
Prerequisites: Node.js (LTS), npm, a MySQL database
- Install dependencies
npm install- Add environment variables (create
.envat project root)
# MySQL connection
DB_HOST=localhost
DB_PORT=3306
DB_USER=root
DB_PASSWORD=yourpassword
DB_NAME=sportrealtime
# Server
PORT=8000
HOST=0.0.0.0
# Optional APM
APM_LICENSE_KEY="your_apm_license_key"
# Arcjet
ARCJET_KEY="your_arcjet_key"
ARCJET_ENV="development"Note: Do not commit
.envwith secrets.
- Generate / apply migrations
npm run db:generate
npm run db:migrate- Run the server
npm run dev # development with watcher
npm start # production-mode- Seed the database (requires running server)
npm run seed- GET
/— health/status - GET
/matches— list matches (query:limit) - POST
/matches— create a match (payload validated with Zod) - GET
/matches/:id/commentary— list commentary for a match (validated params and query) - POST
/matches/:id/commentary— create commentary (validated payload)
WebSocket endpoint: ws://<host>/ws
- Messages:
{ type: 'subscribe', matchId },{ type: 'unsubscribe', matchId } - Server broadcasts:
match_created,commentary
- APM key rotation: If a secret is accidentally committed, rotate it immediately, remove it from repo, and scrub git history (use
git filter-repoor BFG). .gitignoreincludes.envandapminsight.jsonto avoid committing secrets.- Arcjet middleware is used for rate-limiting / bot protection (enabled only if
ARCJET_KEYset).
- If
drizzle-kitisn't found duringnpm run db:migrate, ensuredrizzle-kitis installed as a dev dependency. - If migrations fail with
Access denied, the local MySQL server is rejecting the credentials in.env; fix the DB user/password or point the app at a database that already exists. - If imports fail (module not found), run
npm installto updatenode_modulesand commitpackage.json/package-lock.json.
- Add unit/integration tests for route handlers and validation
- Add CI (GitHub Actions) to run lint/tests and migrations against a test DB
- Add pagination & cursors to commentary listing
- Add DB indexes for performance (e.g.,
commentary(match_id, created_at desc))
PRs welcome. For security-sensitive changes (rotating keys, scrubbing secrets), coordinate changes with the team and do not push secrets to the repo.