Skip to content

Custom PubSub & SSE

The module includes a default Pub/Sub system out of the box. It uses a Nitro hook (zenstack:realtime) to pass mutation events from the database hooks directly to the Server-Sent Events (SSE) handler.

While this works well for a single-server Node.js runtime, it does not scale across multiple server instances or edge workers. If you're hosting on platforms that scale out (e.g., Vercel, AWS ECS) or serverless architectures where memory is not shared, you will need a distributed Pub/Sub mechanism.

Providing a Custom Pub/Sub

You can replace the built-in system by calling provideZenstackPubsub within a Nitro plugin. You must supply a publish and a subscribe method that handles ServerRealtimeData.

The following example uses Upstash for serverless PubSub support.

ts
// server/plugins/realtime.ts
import { Redis } from '@upstash/redis'
import { Realtime } from '@upstash/realtime'
import { z } from 'zod'
import type { ServerRealtimeData } from '#build/types/nuxt-zenstack'

export default defineNitroPlugin(() => {
  const redis = new Redis({
    url: '_UPSTASH_REDIS_URL_',
    token: '_UPSTASH_REDIS_TOKEN_'
  })

  const schema = {
    data: z.object({ 
      model: z.string(), 
      action: z.string(), 
      ids: z.array(z.string().or(z.number())) 
    })
  }

  const realtime = new Realtime({ schema: schema, redis: redis })
  const channel = realtime.channel(config.upstash.realtime.channel)

  provideZenstackPubsub({
    publish: (args) => channel.emit('data', args),
    subscribe: (cb) => {
      channel.subscribe({
        events: ['data'],
        onData: ({ data }) => {
          cb(data as ServerRealtimeData)
        }
      })
    }
  })
})