Skip to content

Plan: realtime state (Soketi / Pusher protocol + TanStack Query invalidation) #97

Open

Description

@kingRayhan

Summary

Inventory and phased rollout for realtime UI updates on TechDiary: push small signals (e.g. Pusher-compatible broker such as Soketi), then invalidate / refetch TanStack Query caches. Database and server actions remain the source of truth.

Principle

  • Realtime delivers event name + optional ids only.
  • Client invalidates existing queries (same idea as today in src/app/dashboard/notifications/page.tsx and src/components/render-props/ResourceReactionable.tsx).
flowchart LR
  subgraph writers [Writers]
    Action[Server action]
    Inngest[Inngest persistNotificationFn]
  end
  subgraph broker [Broker e.g. Soketi]
    Pub[HTTP trigger]
  end
  subgraph clients [Clients]
    PusherJS[pusher-js private channel]
    QC[TanStack Query invalidate]
  end
  Action --> Pub
  Inngest --> Pub
  Pub --> PusherJS
  PusherJS --> QC
Loading

Resource inventory

Resource Primary query keys Audience Suggested channel Phase
In-app notifications my-notifications, unread-notification-count One user private-user.{userId} 1
Comments comments, resource_id, resource_type Same article/gist page private-resource.{resourceType}.{resourceId} 2
Reactions reaction, resource_id, resource_type Same as comments Same resource channel 3
Bookmarks + dashboard list bookmark-status, dashboard-articles Per user / per resource User + optional resource channel 3–4
Author dashboard articles dashboard-articles One user private-user.{userId} 3
Gists gists, gist Owner + viewers User + gist resource channel 4
Profile article feed user-article-feed Profile visitors TBD 4
Tag feed tag-articles Tag page visitors e.g. tag.{tagId} 4
Home article feed article-feed Very wide fanout Defer (or per-user if follow graph exists) Defer
Sessions mySessions One user private-user.{userId} Optional
User card user Profile viewers Low value Optional
Static widgets top-tags, etc. — Skip —

Notification hook today: comment.action.ts and reaction.actions.ts emit app/notification.requested; persistNotificationFn in src/lib/inngest.ts inserts rows. Phase 1 can publish to the recipient’s user channel after successful insert in that Inngest function.

Phased rollout

  1. Phase 1 — Notifications: Subscribe to private-user.{userId}; on persist success trigger e.g. invalidate with { scope: "notifications" }; client invalidates my-notifications and unread-notification-count.
  2. Phase 2 — Comments: Subscribe from CommentSection; publish on create/update/delete; invalidate comments query.
  3. Phase 3 — Reactions / dashboard articles: Same resource channel for reactions; user channel when the signed-in user’s articles change.
  4. Defer: Global home feed broadcast; tag/profile feeds unless product requires them.

Auth and safety

  • Next.js private channel auth endpoint: verify session (getSession / authID in src/backend/services/session.actions.ts) before signing subscription.
  • Resource channels: authorize subscribe same as “allowed to read this resource”.
  • Do not treat socket payload as authoritative.

Out of scope

  • Collaborative editor (OT/CRDT).
  • Replacing Inngest for durable delivery (add broadcast as a side effect only).
  • Pushing full comment/article bodies over the socket.

Implementation checklist

  • Phase 1: private user channel + publish after notification persist (Inngest) + client invalidate
  • Phase 2: resource channel + comment mutations + invalidate in CommentSection
  • Phase 3 (optional): reactions + dashboard-articles
  • Explicitly defer global article-feed until follow-based or other narrow design exists

Activity

  1. kingRayhan commented on Apr 4, 2026

    @kingRayhan
    MemberAuthor

    Phases 1 & 2 are done.

  2. kingRayhan commented on Apr 4, 2026

    @kingRayhan
    MemberAuthor

    This issue is open for the next phase of implementation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions