vTilt
By roleFoundersKnow what to fix firstMarketersSee what happens after the clickCMOsSee what your budget really buys
By businessSaaSTurn signups into active usersEcommerceSee why shoppers abandon cartsAgenciesProve your work to clientsEnterpriseOne customer record for every team
MeasureWeb AnalyticsFind where buyers leaveSession ReplaySee why they left, not just that they didAdsKnow what paid visitors do nextIntegrationsOne snippet feeds your stack
ThinkPeopleOne profile per customerCustomer MemoryvTilt remembers every visitorSite KnowledgeAnswers and audits from your own pages
ActAsk AIAnswers from real behaviorAI ChatChat that knows the visitorEmail CampaignsEmail from real visits
PricingWhy vTiltDocs
Docs / Hono
Getting Started
OverviewInstallInitializeIdentify usersTrack eventsLogout & resetVerify eventsCommon mistakes
Guides
Event forwardingReverse proxyRealtime dashboardSite knowledge
Frontend frameworks
Next.jsNuxt.jsVue.jsReactReact RouterRemixGatsbySvelte / SvelteKitAstroAngularTanStack StartDocusaurus
Backend frameworks
NestJSHonoCloudflare WorkersDjangoFlaskLaravelPhoenixRuby on Rails
Backend languages
PythonPHPRubyElixirGoJava.NET / C#Rust
Stack guides
Vue + PHP
API Reference
Browser SDK
Script bundlesAutocaptureWeb VitalsSession recordingChat widgetFeature readinessRemote configurationDebug logging
Node SDK
Install & setupCapture, identify & aliasContext & shutdownGlobal properties & opt-outError tracking
MCP server
Guides
OverviewAuthenticationOAuthAgent skills (prompts)AI intelligenceSite knowledgeGoogle AdsMeta Ads
Client setup
CursorClaude DesktopVS CodeCodex

Getting Started

OverviewInstallInitializeIdentify usersTrack eventsLogout & resetVerify eventsCommon mistakes

Guides

Event forwardingReverse proxyRealtime dashboardSite knowledge
NestJSHonoCloudflare WorkersDjangoFlaskLaravelPhoenixRuby on Rails

API Reference

MCP server

On this page

On this page

  • 1. Install
  • 2. Middleware
  • 3. Mount and use
  • 4. Identify users on every request
  • 5. Graceful shutdown (long-running runtimes)
  • Next steps
DocsGuidesBackend frameworksHono

Hono

Last updated July 22, 2026

Integrate vTilt with Hono — Node SDK in a request-scoped middleware, identify, capture, and shutdown across runtimes (Node, Bun, Cloudflare Workers).

Hono runs on Node, Bun, Deno, and Cloudflare Workers. The @v-tilt/node SDK works in all four — it's a thin HTTP client with no Node-only APIs. The pattern below uses Hono middleware to expose a c.get('vtilt') helper to every handler.

Tip

On Cloudflare Workers the same code applies, but you must call await ctx.executionContext.waitUntil(client.shutdown()) so events flush before the worker is frozen — see Cloudflare Workers for a worker-specific example.

#1. Install

npm install hono @v-tilt/node
bash

#2. Middleware

// src/middleware/vtilt.ts
import { createMiddleware } from 'hono/factory'
import { VTiltNode } from '@v-tilt/node'

type Env = {
  Variables: { vtilt: VTiltNode }
  Bindings: { VTILT_TRACKER_TOKEN: string; VTILT_HOST: string }
}

let _client: VTiltNode | null = null

export const vtilt = createMiddleware<Env>(async (c, next) => {
  if (!_client) {
    _client = new VTiltNode(c.env.VTILT_TRACKER_TOKEN, {
      host: c.env.VTILT_HOST,
    })
  }
  c.set('vtilt', _client)
  await next()
  // On serverless: ctx.executionContext.waitUntil(_client.shutdown())
})
typescript

#3. Mount and use

// src/index.ts
import { Hono } from 'hono'
import { vtilt } from './middleware/vtilt'

const app = new Hono<typeof vtilt extends infer M ? M : never>()
app.use('*', vtilt)

app.post('/checkout', async c => {
  const { userId, amount } = await c.req.json<{
    userId: string
    amount: number
  }>()

  c.var.vtilt.capture({
    distinctId: userId,
    event: 'purchase_completed',
    properties: { amount },
  })

  return c.json({ ok: true })
})

export default app
typescript
# .env (Node / Bun)
VTILT_TRACKER_TOKEN=YOUR_PROJECT_TOKEN
VTILT_HOST=https://www.vtilt.com
text

#4. Identify users on every request

If your auth middleware sets c.var.user, identify on every request so the server-side person record stays in sync with browser activity.

import { createMiddleware } from 'hono/factory'

export const vtiltIdentify = createMiddleware(async (c, next) => {
  await next()
  const user = c.get('user')
  if (user) {
    c.var.vtilt.identify({
      distinctId: user.id,
      anonymousId: c.req.header('cookie')?.match(/vt_anon=([^;]+)/)?.[1],
      properties: { email: user.email },
    })
  }
})

app.use('*', vtilt, vtiltIdentify)
typescript

#5. Graceful shutdown (long-running runtimes)

For Node/Bun servers, flush on SIGINT/SIGTERM:

import { serve } from '@hono/node-server'

const server = serve({ fetch: app.fetch, port: 3000 })

const shutdown = async () => {
  await _client?.shutdown()
  server.close()
  process.exit(0)
}

process.on('SIGINT', shutdown)
process.on('SIGTERM', shutdown)
typescript

#Next steps

  • Cloudflare Workers — workers-specific waitUntil pattern.
  • Node SDK / Context & shutdown — request-scoped identity.
  • Node SDK / Capture, identify & alias — full event shapes.
  • Common mistakes — integration pitfalls to avoid before you ship.
PreviousNestJSIntegration guidesNextCloudflare WorkersIntegration guides