2026 · Backend and full-stack
Guild-Hub
Real-time messaging with channels, roles and uploads.
- Role
- Backend and full-stack
- Year
- 2026
- Discipline
- Full-stack · Backend
01 — The problem
Chat looks simple until permissions, history and delivery all have to be correct at once. A message is only useful if the right people can see it, it arrives without a refresh, and it still makes sense when you scroll back a week later.
02 — Approach
- 01
Model the domain before writing transport code: users, channels, memberships and messages as explicit tables with real foreign keys.
- 02
Enforce role checks at the query layer, so channel membership is the unit of authorisation rather than a client-side flag.
- 03
Keep socket events small and namespaced — the socket carries a signal, the database remains the source of truth.
- 04
Reuse one message pipeline for direct messages and channels so the two views cannot drift apart.
03 — What I built
- One-to-one and channel-based messaging with persisted history.
- Socket.io services for low-latency delivery across connected clients.
- File uploads and emoji reactions inside the message flow.
- Role-based access control backed by a Prisma schema on PostgreSQL.
- A modular service structure that keeps transport, data access and authorisation separate.
04 — Outcome
- A single message model serves both private and channel conversations.
- Permission logic lives in one place, so a new surface cannot accidentally bypass it.
05 — What I learned
Real-time features fail in ways REST does not: duplicate events, reconnects, and messages arriving out of order. Treating the socket as a notification channel and the database as the truth removed most of that class of bug before it existed.
Stack
- Next.js
- TypeScript
- Node.js
- PostgreSQL
- Prisma
- Socket.io