Framework Integrations
ts-analytics works with any JavaScript/TypeScript runtime and framework.
Bun (Native)
Built-in support for Bun's HTTP server:
import { AnalyticsAPI, createBunRouter } from '@stacksjs/ts-analytics'
const api = new AnalyticsAPI({ tableName: 'AnalyticsTable' })
const router = createBunRouter(api, executeCommand)
Bun.serve({
port: 3000,
fetch: router.fetch,
})
Hono
Use with the Hono web framework:
import { Hono } from 'hono'
import { createAnalyticsRoutes, mountAnalyticsRoutes } from '@stacksjs/ts-analytics'
const app = new Hono()
// Option 1: Mount routes
mountAnalyticsRoutes(app, {
tableName: 'AnalyticsTable',
basePath: '/api/analytics',
executeCommand,
})
// Option 2: Create routes manually
const analyticsRoutes = createAnalyticsRoutes({
tableName: 'AnalyticsTable',
executeCommand,
})
app.route('/api/analytics', analyticsRoutes)
export default app
Hono Middleware
import { analyticsMiddleware } from '@stacksjs/ts-analytics'
app.use('*', analyticsMiddleware({
siteId: 'my-site',
tableName: 'AnalyticsTable',
executeCommand,
excludePaths: ['/api', '/admin'],
}))
AWS Lambda
Deploy as a Lambda function:
import { AnalyticsAPI, createLambdaHandler } from '@stacksjs/ts-analytics'
import { DynamoDBClient } from '@aws-sdk/client-dynamodb'
const client = new DynamoDBClient({ region: process.env.AWS_REGION })
const api = new AnalyticsAPI({
tableName: process.env.TABLE_NAME!,
})
async function executeCommand(cmd: { command: string; input: Record<string, unknown> }) {
const Command = await import('@aws-sdk/client-dynamodb').then(m => m[cmd.command])
return client.send(new Command(cmd.input))
}
export const handler = createLambdaHandler(api, executeCommand)
SAM Template
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
AnalyticsFunction:
Type: AWS::Serverless::Function
Properties:
Handler: index.handler
Runtime: nodejs20.x
MemorySize: 256
Timeout: 30
Environment:
Variables:
TABLE_NAME: !Ref AnalyticsTable
Events:
Api:
Type: Api
Properties:
Path: /{proxy+}
Method: ANY
Cloudflare Workers
Deploy to the edge:
import {
createAnalyticsHandler,
createD1Adapter,
type CloudflareEnv,
} from '@stacksjs/ts-analytics'
export default {
async fetch(request: Request, env: CloudflareEnv): Promise<Response> {
// Use D1 as storage (Cloudflare's SQL database)
const adapter = createD1Adapter(env.DB)
const handler = createAnalyticsHandler({
siteId: 'my-site',
storage: adapter,
})
return handler(request)
},
}
wrangler.toml
name = "analytics"
main = "src/index.ts"
[[d1_databases]]
binding = "DB"
database_name = "analytics"
database_id = "xxx"
Express
Use with Express:
import express from 'express'
import { AnalyticsAPI } from '@stacksjs/ts-analytics'
const app = express()
app.use(express.json())
const api = new AnalyticsAPI({ tableName: 'AnalyticsTable' })
const ctx = api.createContext(executeCommand)
// Mount routes
app.post('/api/analytics/collect', async (req, res) => {
const response = await api.handleCollect({
method: 'POST',
path: '/collect',
params: {},
query: req.query as Record<string, string>,
body: req.body,
headers: req.headers as Record<string, string>,
ip: req.ip,
userAgent: req.headers['user-agent'],
}, ctx)
res.status(response.status).json(response.body)
})
app.get('/api/analytics/sites/:siteId/stats', async (req, res) => {
const response = await api.handleGetStats({
method: 'GET',
path: `/sites/${req.params.siteId}/stats`,
params: req.params,
query: req.query as Record<string, string>,
body: {},
headers: req.headers as Record<string, string>,
}, ctx)
res.status(response.status).json(response.body)
})
app.listen(3000)
Fastify
Use with Fastify:
import Fastify from 'fastify'
import { AnalyticsAPI } from '@stacksjs/ts-analytics'
const fastify = Fastify()
const api = new AnalyticsAPI({ tableName: 'AnalyticsTable' })
const ctx = api.createContext(executeCommand)
fastify.post('/api/analytics/collect', async (request, reply) => {
const response = await api.handleCollect({
method: 'POST',
path: '/collect',
params: {},
query: request.query as Record<string, string>,
body: request.body,
headers: request.headers as Record<string, string>,
ip: request.ip,
userAgent: request.headers['user-agent'],
}, ctx)
reply.status(response.status).send(response.body)
})
fastify.listen({ port: 3000 })
Stacks Framework
First-class integration with Stacks:
import {
createAnalyticsDriver,
createAnalyticsMiddleware,
createDashboardActions,
createServerTrackingMiddleware,
} from '@stacksjs/ts-analytics'
// Create the driver
const driver = await createAnalyticsDriver({
tableName: 'AnalyticsTable',
siteId: 'my-site',
region: 'us-east-1',
})
// Add tracking middleware
app.use(createAnalyticsMiddleware(driver))
// Server-side tracking
app.use(createServerTrackingMiddleware(driver, {
excludedPaths: [/^\/api/, /^\/admin/],
}))
// Dashboard actions
const actions = createDashboardActions(driver)
app.get('/dashboard/stats', async () => {
return actions.getDashboardStats({ startDate: '2024-01-01' })
})
Stacks Models
Use the provided model definitions:
import {
SiteModel,
PageViewModel,
SessionModel,
GoalModel,
} from '@stacksjs/ts-analytics'
// Register models with Stacks
export default {
models: [
SiteModel,
PageViewModel,
SessionModel,
GoalModel,
],
}
Next.js
API Routes (App Router)
// app/api/analytics/collect/route.ts
import { AnalyticsAPI } from '@stacksjs/ts-analytics'
import { NextRequest, NextResponse } from 'next/server'
const api = new AnalyticsAPI({ tableName: 'AnalyticsTable' })
const ctx = api.createContext(executeCommand)
export async function POST(request: NextRequest) {
const body = await request.json()
const response = await api.handleCollect({
method: 'POST',
path: '/collect',
params: {},
query: Object.fromEntries(request.nextUrl.searchParams),
body,
headers: Object.fromEntries(request.headers),
ip: request.ip ?? request.headers.get('x-forwarded-for') ?? undefined,
userAgent: request.headers.get('user-agent') ?? undefined,
}, ctx)
return NextResponse.json(response.body, { status: response.status })
}
Middleware
// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
// Track server-side page views
if (request.nextUrl.pathname.startsWith('/blog')) {
// Send to analytics API
fetch('http://localhost:3000/api/analytics/collect', {
method: 'POST',
body: JSON.stringify({
s: 'my-site',
e: 'pageview',
u: request.url,
r: request.headers.get('referer'),
}),
})
}
return NextResponse.next()
}
Nuxt
Server API Route
// server/api/analytics/collect.post.ts
import { AnalyticsAPI } from '@stacksjs/ts-analytics'
const api = new AnalyticsAPI({ tableName: 'AnalyticsTable' })
const ctx = api.createContext(executeCommand)
export default defineEventHandler(async (event) => {
const body = await readBody(event)
const response = await api.handleCollect({
method: 'POST',
path: '/collect',
params: {},
query: getQuery(event),
body,
headers: getHeaders(event),
ip: getRequestIP(event),
userAgent: getHeader(event, 'user-agent'),
}, ctx)
setResponseStatus(event, response.status)
return response.body
})
Generic Integration
For any framework, implement the request/response mapping:
import { AnalyticsAPI, AnalyticsRequest, AnalyticsResponse } from '@stacksjs/ts-analytics'
const api = new AnalyticsAPI({ tableName: 'AnalyticsTable' })
const ctx = api.createContext(executeCommand)
// Convert your framework's request to AnalyticsRequest
function toAnalyticsRequest(frameworkRequest: any): AnalyticsRequest {
return {
method: frameworkRequest.method,
path: frameworkRequest.path,
params: frameworkRequest.params || {},
query: frameworkRequest.query || {},
body: frameworkRequest.body || {},
headers: frameworkRequest.headers || {},
ip: frameworkRequest.ip,
userAgent: frameworkRequest.headers?.['user-agent'],
}
}
// Convert AnalyticsResponse to your framework's response
function fromAnalyticsResponse(response: AnalyticsResponse): any {
return {
status: response.status,
headers: response.headers,
body: response.body,
}
}
Next Steps
- AWS Deployment - Deploy to production
- Local Development - Set up development environment
- API Reference - Full API documentation