Server-Sent Events (SSE) for push notifications without polling.
Teacher saves → Server → EventBus → SSE → Students
type AppEvent =
| { type: 'class-invitation'; classId: string; className: string }
| { type: 'teacher-annotation'; classId: string; pageId: string; annotation: Annotation }
| { type: 'quiz-submission'; quizId: string; studentPseudonym: string }
| { type: 'collaboration-request'; fromUserId: string; fromName: string }
| Pattern | Use |
|---|
user:${userId} | User-specific notifications |
class:${classId} | Class-wide broadcasts |
class:${classId}:students | Students only |
quiz:${quizId} | Quiz state changes |
import { eventBus } from '@/lib/events'
await eventBus.publish(`class:${classId}`, {
type: 'teacher-annotation',
classId,
pageId,
annotation: data
})
import { useRealtimeEvents } from '@/hooks/use-realtime-events'
useRealtimeEvents(
['class-invitation'],
(event) => handleInvitation(event),
enabled
)
| Implementation | Use Case |
|---|
memory-bus.ts | Single server (default) |
postgres-bus.ts | Multi-instance (PostgreSQL LISTEN/NOTIFY) |
Switch via EVENT_BUS=postgres environment variable.
Teachers can broadcast annotations to students in real-time:
- Teacher enables Broadcast Mode for a class
- Annotation saves trigger broadcast (piggybacks on 2s debounce)
- Students receive same data format as DB storage
- Late-joining students load from DB
| Metric | Value |
|---|
| Latency | <100ms |
| Per connection | ~1KB memory |
| Browser limit | 6 SSE connections/domain |
| File | Purpose |
|---|
src/lib/events/types.ts | Event definitions |
src/lib/events/memory-bus.ts | In-memory implementation |
src/lib/events/index.ts | Factory export |
src/app/api/events/stream/route.ts | SSE endpoint |
src/hooks/use-realtime-events.ts | Client hook |