LiveBoard
A real-time collaborative Kanban board with presence, optimistic UI, version-based conflict handling and Redis for scaling across instances.
- Type
- Side project
- Tech stack
- NestJS
- Socket.IO
- React
- Redis
The problem
A shared board is only useful if everyone sees the same thing. That's easy in a demo and hard in practice: two people edit the same card, a connection drops, or users end up on different server instances. None of that should make the UI flicker or quietly lose a change.
What it does
LiveBoard is a Kanban board where changes show up for everyone on the board straight away. You can create, edit, move and delete cards, see who else is there with avatars, live cursors and typing indicators, and follow every change in an activity log. Each board is its own WebSocket room with access control.
Changes apply in the UI immediately and roll back if the server rejects them. After a lost connection, the client fetches the events it missed and catches up.
Moving a card
- The client moves the card, updates the UI optimistically and emits
card:movewith the card'sversion. - The gateway checks the JWT, board membership and the
version. - The change is saved in PostgreSQL in a transaction and the
versiongoes up by one. - The server broadcasts
card:movedto the board room, and through Redis to the other instances. - If the version was out of date, the sender gets
card:rejectedand resyncs.
- BrowsersSocket.IO clients
- NestJS gatewaysmultiple instances
- Redispub/sub adapter
- PostgreSQLPrisma
Decisions
Every card carries a version number. Instead of last write wins, the server rejects updates based on an old version, so nobody silently overwrites a teammate's change.
Users log in over REST, and the JWT is checked when the socket connects. Each connection is then limited to the boards that user belongs to.
The Redis adapter was there from the start. Running docker compose up --scale api=2 shows that clients on different instances stay in sync. Cursor events are throttled, so presence stays smooth without flooding the connection.