@@ -396,15 +396,159 @@ const { data } = trpc.users.list.useQuery({ page: 1 });
396396- [ ] Monitoring alerts configured
397397```
398398
399+ ## WebSocket & Real-Time Patterns
400+
401+ ### Server-Sent Events (Simple Real-Time)
402+ ``` typescript
403+ // Server — SSE endpoint (lightweight alternative to WebSocket)
404+ app .get (' /api/events' , (req , res ) => {
405+ res .writeHead (200 , {
406+ ' Content-Type' : ' text/event-stream' ,
407+ ' Cache-Control' : ' no-cache' ,
408+ ' Connection' : ' keep-alive' ,
409+ });
410+
411+ const sendEvent = (event : string , data : unknown ) => {
412+ res .write (` event: ${event }\n data: ${JSON .stringify (data )}\n\n ` );
413+ };
414+
415+ // Subscribe to updates
416+ const unsubscribe = eventBus .subscribe (' task:updated' , (task ) => {
417+ sendEvent (' task-update' , task );
418+ });
419+
420+ req .on (' close' , () => {
421+ unsubscribe ();
422+ res .end ();
423+ });
424+ });
425+
426+ // Client — EventSource API
427+ const events = new EventSource (' /api/events' );
428+ events .addEventListener (' task-update' , (e ) => {
429+ const task = JSON .parse (e .data );
430+ updateTaskInUI (task );
431+ });
432+ ```
433+
434+ ### WebSocket with Socket.io
435+ ``` typescript
436+ // Server
437+ import { Server } from ' socket.io' ;
438+
439+ const io = new Server (server , {
440+ cors: { origin: process .env .CLIENT_URL },
441+ connectionStateRecovery: {
442+ maxDisconnectionDuration: 2 * 60 * 1000 , // 2 minutes
443+ },
444+ });
445+
446+ // Authentication middleware
447+ io .use (async (socket , next ) => {
448+ const token = socket .handshake .auth .token ;
449+ try {
450+ const user = await verifyToken (token );
451+ socket .data .user = user ;
452+ next ();
453+ } catch (err ) {
454+ next (new Error (' Authentication failed' ));
455+ }
456+ });
457+
458+ io .on (' connection' , (socket ) => {
459+ // Join user-specific room
460+ socket .join (` user:${socket .data .user .id } ` );
461+
462+ // Join project rooms
463+ socket .on (' join:project' , async (projectId ) => {
464+ const hasAccess = await checkProjectAccess (socket .data .user .id , projectId );
465+ if (hasAccess ) socket .join (` project:${projectId } ` );
466+ });
467+
468+ // Broadcast updates to project members
469+ socket .on (' task:update' , async (data ) => {
470+ const task = await updateTask (data );
471+ io .to (` project:${task .projectId } ` ).emit (' task:updated' , task );
472+ });
473+ });
474+
475+ // Client (React hook)
476+ function useSocket(projectId : string ) {
477+ const [socket, setSocket] = useState <Socket | null >(null );
478+
479+ useEffect (() => {
480+ const s = io (process .env .NEXT_PUBLIC_WS_URL ! , {
481+ auth: { token: getAuthToken () },
482+ });
483+ s .emit (' join:project' , projectId );
484+ setSocket (s );
485+ return () => { s .disconnect (); };
486+ }, [projectId ]);
487+
488+ return socket ;
489+ }
490+ ```
491+
492+ ## Background Jobs & Task Queues
493+
494+ ### BullMQ (Node.js)
495+ ``` typescript
496+ import { Queue , Worker } from ' bullmq' ;
497+
498+ // Producer — Add jobs to queue
499+ const emailQueue = new Queue (' emails' , {
500+ connection: { host: ' redis' , port: 6379 },
501+ defaultJobOptions: {
502+ attempts: 3 ,
503+ backoff: { type: ' exponential' , delay: 2000 },
504+ removeOnComplete: { count: 1000 },
505+ removeOnFail: { count: 5000 },
506+ },
507+ });
508+
509+ await emailQueue .add (' welcome-email' , {
510+ to: user .email ,
511+ template: ' welcome' ,
512+ data: { name: user .name },
513+ });
514+
515+ // Scheduled/recurring jobs
516+ await emailQueue .add (' daily-digest' , { type: ' digest' }, {
517+ repeat: { pattern: ' 0 9 * * *' }, // Every day at 9 AM
518+ });
519+
520+ // Consumer — Process jobs
521+ const worker = new Worker (' emails' , async (job ) => {
522+ switch (job .name ) {
523+ case ' welcome-email' :
524+ await sendEmail (job .data );
525+ break ;
526+ case ' daily-digest' :
527+ await generateAndSendDigest ();
528+ break ;
529+ }
530+ }, {
531+ connection: { host: ' redis' , port: 6379 },
532+ concurrency: 5 ,
533+ limiter: { max: 10 , duration: 1000 }, // 10 jobs/second
534+ });
535+
536+ worker .on (' failed' , (job , error ) => {
537+ logger .error (` Job ${job ?.id } failed: ` , error );
538+ });
539+ ```
540+
399541---
400542
401543## Remember
402544
403- > ** Full-stack development is about reducing the distance between idea and working product.**
404-
405- Priorities:
406- 1 . ** Type safety end-to-end** : Catch errors at compile time
407- 2 . ** Convention over configuration** : Use framework defaults
408- 3 . ** Ship fast, iterate** : Get feedback early
409- 4 . ** Shared validation** : Same rules on client and server
410- 5 . ** Progressive enhancement** : Works without JS, better with it
545+ ```
546+ ✦ TYPE SAFETY END-TO-END: Catch errors at compile time — tRPC, Zod, Prisma
547+ ✦ REAL-TIME: SSE for simple updates, WebSocket for bidirectional communication
548+ ✦ BACKGROUND JOBS: Never process long tasks in request handlers — use queues
549+ ✦ CONVENTION OVER CONFIG: Use framework defaults — Next.js, Nuxt, SvelteKit
550+ ✦ SHARED VALIDATION: Same Zod schemas on client and server — single source of truth
551+ ✦ PROGRESSIVE ENHANCEMENT: Works without JS, better with it
552+ ✦ SHIP FAST: Get feedback early — iterate based on real usage
553+ ✦ PRODUCTION READY: Health checks, monitoring, graceful shutdown, auto-scaling
554+ ```
0 commit comments