Hono
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.
#1. Install
npm install hono @v-tilt/nodebash
#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 apptypescript
# .env (Node / Bun)
VTILT_TRACKER_TOKEN=YOUR_PROJECT_TOKEN
VTILT_HOST=https://www.vtilt.comtext
#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
waitUntilpattern. - Node SDK / Context & shutdown — request-scoped identity.
- Node SDK / Capture, identify & alias — full event shapes.