← Work
02
PersonalFull-stack · 2025

Customer Support Ticketing System

The problem.

Every support tool I've used treats realtime as the source of truth. It isn't. The source of truth is the database — the API is the contract — and realtime is the layer that keeps clients in sync between requests. When those get confused, the queue starts lying to people.

I built this end-to-end: JWT-authenticated REST APIs for tickets, replies, and history. Postgres tables shaped around audit tracking so nothing gets rewritten. Socket.IO overlaid on top of REST for live status changes — but the API is always what actually persists.

The whole stack ships in Docker Compose with seed data that mimics a real queue, so the demo isn't a shell. There are customers, agents, admins, tickets in every state, and a permission model that behaves like a support desk actually behaves.

The database is the source of truth. Realtime keeps clients honest between requests.
ClientJWTREST APIPostgres + SequelizeSocket.IO
System — REST-first, realtime overlay
React + Node + Express·Postgres + Sequelize·Socket.IO·Docker Compose
Agent queue — tickets, priority, assignment
Agent queue — tickets, priority, assignment

Building the backend for this taught me how much frontend framework noise I'd been carrying — how many decisions I was making by convention instead of by need. When you own the whole stack, the app gets smaller. Fewer moving parts, clearer seams.