Tracking User Pages
Track which pages users are viewing. Use route data to send invalidation messages to users on specific pages.
Route tracking requires
connectionTracking: true and only works with sse or ws update strategies.Route data is only available to authorized connections for privacy. Regular users cannot see which pages other users are viewing.
Setup
Enable route tracking in your Nuxt config:
nuxt.config.ts
export default defineNuxtConfig({
skewProtection: {
connectionTracking: true,
routeTracking: true
}
})Authorization hook. The server only sends stats to connections that pass authorization. Implement the skew:authorize-stats hook:
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()
}
})
})Real-time Stats via useActiveConnections
For live updates, use the useActiveConnections composable on authorized pages:
pages/admin/dashboard.vue
<script setup lang="ts">
const { total, versions, routes, authorized } = useActiveConnections()
const sortedRoutes = computed(() => {
return Object.entries(routes.value)
.sort(([, a], [, b]) => b - a)
.slice(0, 10)
})
</script>
<template>
<div v-if="authorized" class="live-pages">
<h3>Most Active Pages ({{ total }} users)</h3>
<table>
<tr v-for="[route, count] in sortedRoutes" :key="route">
<td>{{ route }}</td>
<td>{{ count }} viewers</td>
</tr>
</table>
</div>
<div v-else>
Not authorized for live stats
</div>
</template>API Endpoint (Alternative)
For non-WebSocket access (CI/CD, external tools), expose stats via an API endpoint:
server/api/admin/stats.get.ts
export default defineEventHandler(async (event) => {
const authHeader = getHeader(event, 'authorization')
if (authHeader !== `Bearer ${process.env.ADMIN_SECRET}`) {
throw createError({ statusCode: 401 })
}
return new Promise((resolve) => {
const nitroApp = useNitroApp()
// @ts-expect-error custom hook
nitroApp.hooks.callHook('skew:stats', (stats) => {
resolve(stats)
})
})
})The stats object contains:
interface Stats {
total: number // Total active connections
versions: Record<string, number> // Users per build version
routes: Record<string, number> // Users per route path
}Invalidating Users on Specific Routes
Force users on a specific page to refresh when you deploy critical changes to that page.
Server Plugin for Route Invalidation
server/plugins/route-invalidation.ts
import { defineNitroPlugin } from 'nitropack/runtime'
export default defineNitroPlugin((nitroApp) => {
const connections = new Map<string, { route: string, send: (data: unknown) => void }>()
nitroApp.hooks.hook('skew:connection:open', ({ id, route, send }) => {
connections.set(id, { route: route || '/', send })
})
nitroApp.hooks.hook('skew:connection:route-update', ({ id, route }) => {
const conn = connections.get(id)
if (conn)
conn.route = route
})
nitroApp.hooks.hook('skew:connection:close', ({ id }) => {
connections.delete(id)
})
// Expose function to invalidate specific routes
// @ts-expect-error extending global
globalThis.invalidateRoute = (routePattern: string | RegExp) => {
const version = useRuntimeConfig().app.buildId
for (const [, conn] of connections) {
const matches = typeof routePattern === 'string'
? conn.route === routePattern || conn.route.startsWith(routePattern)
: routePattern.test(conn.route)
if (matches) {
conn.send({ type: 'version', version, force: true })
}
}
}
})API Endpoint for Invalidation
server/api/admin/invalidate-route.post.ts
export default defineEventHandler(async (event) => {
const { route, secret } = await readBody(event)
if (secret !== process.env.ADMIN_SECRET) {
throw createError({ statusCode: 401 })
}
// @ts-expect-error global function from plugin
globalThis.invalidateRoute?.(route)
return { ok: true, route }
})Usage from CI/CD
After deploying changes to a specific page:
# Invalidate all users on /checkout
curl -X POST https://yoursite.com/api/admin/invalidate-route \
-H "Content-Type: application/json" \
-d '{"route": "/checkout", "secret": "your-admin-secret"}'
# Invalidate all users on /blog/* routes
curl -X POST https://yoursite.com/api/admin/invalidate-route \
-H "Content-Type: application/json" \
-d '{"route": "/blog/", "secret": "your-admin-secret"}'Performance Considerations
Route tracking sends a message to the server on every client-side navigation. For high-traffic sites:
- The module tracks routes per-connection, not per-pageview
- Messages are small (~50 bytes per navigation)
- Consider disabling for sites with rapid navigation patterns
Limitations
- Same limitations as connection tracking
- Route paths only (the module does not track query strings or hashes)
- Client-side navigations only (full page loads reconnect with new route)
- Route data is server-side only (not exposed to clients for privacy)
Was this page helpful?