Nuxt API

useActiveConnections()

Last updated by Harlan Wilton in chore: lint.

A composable for accessing real-time statistics about active SSE/WebSocket connections and their version distribution.

This composable requires connectionTracking: true in your module config and only works with sse or ws update strategies. It does not support Pusher/Ably adapters.
Stats are not exposed to all users by default. You must implement the skew:authorize-stats hook to authorize specific connections.

Setup

Enable connection tracking in your Nuxt config:

nuxt.config.ts
export default defineNuxtConfig({
  skewProtection: {
    connectionTracking: true,
    routeTracking: true, // optional: track current routes
    ipTracking: true, // optional: track IP addresses
  }
})

Authorization Hook

Stats are only sent to authorized connections. Implement the skew:authorize-stats hook in a server plugin:

server/plugins/skew-auth.ts
export default defineNitroPlugin((nitroApp) => {
  nitroApp.hooks.hook('skew:authorize-stats', async ({ event, authorize }) => {
    // With nuxt-auth-utils
    const session = await getUserSession(event)
    if (session.user?.role === 'admin') {
      authorize()
    }
  })
})

Usage

const { total, versions, routes, connections, yourId, authorized } = useActiveConnections()

Returns

total

  • Type: ComputedRef<number>

The total number of active connections across all versions.

const { total } = useActiveConnections()

console.log(total.value) // 42

versions

  • Type: ComputedRef<Record<string, number>>

A map of build IDs to connection counts, showing how many users are on each version.

const { versions } = useActiveConnections()

console.log(versions.value)
// { "abc123": 35, "def456": 7 }

routes

  • Type: ComputedRef<Record<string, number>>

A map of route paths to connection counts. Requires routeTracking: true in config.

const { routes } = useActiveConnections()

console.log(routes.value)
// { "/": 20, "/products": 15, "/checkout": 7 }

connections

  • Type: ComputedRef<ConnectionInfo[]>

Individual connection details including ID, version, route, and IP (when ipTracking: true).

const { connections } = useActiveConnections()

console.log(connections.value)
// [{ id: "abc123", version: "def456", route: "/", ip: "192.168.1.1" }]

yourId

  • Type: ComputedRef<string | undefined>

Your connection ID to identify yourself in the connections list.

const { connections, yourId } = useActiveConnections()

const isMe = conn => conn.id === yourId.value

authorized

  • Type: ComputedRef<boolean | null>

Whether the current connection is authorized to receive stats. Returns null while pending, false if denied, true if authorized.

const { authorized, total } = useActiveConnections()

// total.value will be 0 until authorized becomes true

Type Definitions

interface ConnectionInfo {
  id: string
  version: string
  route: string
  ip?: string // requires ipTracking: true
}

interface ConnectionStats {
  total: number
  versions: Record<string, number>
  routes: Record<string, number>
  connections: ConnectionInfo[]
  yourId?: string
}

Examples

Admin Dashboard Widget

<script setup>
const { total, versions, routes, authorized } = useActiveConnections()

const versionList = computed(() => {
  return Object.entries(versions.value)
    .sort(([, a], [, b]) => b - a)
    .map(([id, count]) => ({
      id: id.slice(0, 8),
      count,
      percentage: Math.round((count / total.value) * 100)
    }))
})

const topRoutes = computed(() => {
  return Object.entries(routes.value)
    .sort(([, a], [, b]) => b - a)
    .slice(0, 5)
})
</script>

<template>
  <div v-if="authorized" class="connections-widget">
    <h3>Active Users: {{ total }}</h3>
    <div class="versions">
      <h4>By Version</h4>
      <ul>
        <li v-for="v in versionList" :key="v.id">
          {{ v.id }}: {{ v.count }} ({{ v.percentage }}%)
        </li>
      </ul>
    </div>
    <div class="routes">
      <h4>Top Pages</h4>
      <ul>
        <li v-for="[route, count] in topRoutes" :key="route">
          {{ route }}: {{ count }}
        </li>
      </ul>
    </div>
  </div>
  <div v-else>
    Not authorized for live stats
  </div>
</template>

Deployment Safety Check

const { total, versions } = useActiveConnections()

const usersOnOldVersions = computed(() => {
  const currentBuildId = useRuntimeConfig().app.buildId
  return Object.entries(versions.value)
    .filter(([id]) => id !== currentBuildId)
    .reduce((sum, [, count]) => sum + count, 0)
})

watch(usersOnOldVersions, (count) => {
  if (count === 0) {
    console.log('All users migrated to latest version!')
  }
})

Real-time Stats Display

<script setup>
const { total, versions } = useActiveConnections()
const buildId = useRuntimeConfig().app.buildId

const stats = computed(() => {
  const current = versions.value[buildId] || 0
  const outdated = total.value - current
  return {
    current,
    outdated,
    percentCurrent: total.value ? Math.round((current / total.value) * 100) : 0
  }
})
</script>

<template>
  <div class="stats">
    <span>{{ stats.current }} current</span>
    <span>{{ stats.outdated }} outdated</span>
    <span>{{ stats.percentCurrent }}% up to date</span>
  </div>
</template>

Limitations

  • Only works with updateStrategy: 'sse' or updateStrategy: 'ws'
  • Does not support Pusher or Ably adapters (connections are managed by third-party)
  • Stats are per-server instance (not aggregated across horizontal scaling)
  • Requires server with persistent connections (not compatible with serverless/edge)
Did this page help you?