A privacy-first analytics toolkit for web applications, powered by DynamoDB single-table design.
- Privacy-First: No cookies, no personal data collection, GDPR-compliant by design
- DynamoDB Single-Table Design: Efficient, scalable, and cost-effective storage
- Real-time Analytics: Live visitor tracking and dashboard updates
- Goal Tracking: Define and track conversion goals
- Vue Dashboard Components: Ready-to-use dashboard UI components
- Framework Agnostic: Works with Bun, Express, Hono, AWS Lambda, and more
- Stacks Integration: First-class support for the Stacks framework
bun add @stacksjs/analyticsimport { AnalyticsStore, createAnalyticsTable } from '@stacksjs/analytics'
import { DynamoDBClient } from '@aws-sdk/client-dynamodb'
import { CreateTableCommand, DescribeTableCommand } from '@aws-sdk/client-dynamodb'
const client = new DynamoDBClient({ region: 'us-east-1' })
// Create the analytics table (one-time setup)
await createAnalyticsTable(client, {
tableName: 'my-analytics',
billingMode: 'PAY_PER_REQUEST',
}, { CreateTableCommand, DescribeTableCommand })
// Initialize the store
const store = new AnalyticsStore({
tableName: 'my-analytics',
})import { generateTrackingScript } from '@stacksjs/analytics'
const script = generateTrackingScript({
siteId: 'my-site-id',
endpoint: 'https://api.example.com/collect',
// Optional settings
trackPageviews: true,
trackOutboundLinks: true,
respectDoNotTrack: true,
})
// Inject into your HTML
const html = `<html><head>${script}</head>...</html>`import { AnalyticsAPI, createBunRouter } from '@stacksjs/analytics'
const api = new AnalyticsAPI({
tableName: 'my-analytics',
siteId: 'my-site-id',
})
// For Bun
const router = createBunRouter(api, executeCommand)
// For AWS Lambda
import { createLambdaHandler } from '@stacksjs/analytics'
export const handler = createLambdaHandler(api, executeCommand)The AnalyticsStore provides DynamoDB operations for all analytics entities:
const store = new AnalyticsStore({
tableName: 'analytics',
useTtl: true,
rawEventTtl: 30 * 24 * 60 * 60, // 30 days
})
// Create a site
const siteCommand = store.createSiteCommand({
id: 'site-123',
name: 'My Website',
domains: ['example.com'],
ownerId: 'user-456',
})
// Record a page view
const pvCommand = store.recordPageViewCommand({
id: 'pv-789',
siteId: 'site-123',
path: '/blog/hello-world',
visitorId: 'visitor-hash',
sessionId: 'session-abc',
timestamp: new Date(),
})Pre-compute statistics for fast dashboard queries:
import { AggregationPipeline, AnalyticsAggregator } from '@stacksjs/analytics'
const pipeline = new AggregationPipeline({
tableName: 'analytics',
})
// Run hourly aggregation
const aggregator = new AnalyticsAggregator({
tableName: 'analytics',
})
await aggregator.aggregateHourly('site-123', new Date())Fetch analytics data for dashboards:
import { AnalyticsQueryAPI } from '@stacksjs/analytics'
const queryApi = new AnalyticsQueryAPI({
tableName: 'analytics',
})
// Generate dashboard queries
const queries = queryApi.generateDashboardQueries({
siteId: 'site-123',
dateRange: { start: new Date('2024-01-01'), end: new Date() },
})Define and track conversion goals:
import { GoalMatcher } from '@stacksjs/analytics'
const goals = [
{ id: 'signup', type: 'pageview', pattern: '/signup/complete', matchType: 'exact' },
{ id: 'purchase', type: 'event', pattern: 'purchase', matchType: 'exact' },
]
const matcher = new GoalMatcher(goals)
// Check if a page view matches any goals
const matches = matcher.matchPageView('/signup/complete')
// => [{ goalId: 'signup', value: undefined }]Vue 3 components for building analytics dashboards:
<script setup>
import {
AnalyticsDashboard,
StatCard,
TimeSeriesChart,
TopList,
DeviceBreakdown,
RealtimeCounter,
DateRangePicker,
} from '@stacksjs/analytics'
</script>
<template>
<AnalyticsDashboard
:config="{ baseUrl: '/api/analytics', siteId: 'my-site' }"
/>
</template>import { createAnalyticsComposable, fetchDashboardData } from '@stacksjs/analytics'
// Create a composable for Vue
const analytics = createAnalyticsComposable({
baseUrl: '/api/analytics',
siteId: 'my-site',
})
// Or fetch data directly
const data = await fetchDashboardData(
{ baseUrl: '/api/analytics', siteId: 'my-site' },
{ startDate: new Date('2024-01-01'), endDate: new Date() }
)First-class integration with the Stacks framework:
import { createAnalyticsDriver, createAnalyticsMiddleware } from '@stacksjs/analytics'
// Create the driver
const driver = await createAnalyticsDriver({
tableName: 'analytics',
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)
const stats = await actions.getDashboardStats({ startDate: '2024-01-01' })Generate infrastructure code for deployment:
import {
generateCloudFormationTemplate,
generateCdkCode,
generateSamTemplate,
generateAwsCliCommands,
} from '@stacksjs/analytics'
// CloudFormation
const cfn = generateCloudFormationTemplate({ tableName: 'analytics' })
// AWS CDK
const cdk = generateCdkCode({ tableName: 'analytics' })
// SAM
const sam = generateSamTemplate({ tableName: 'analytics' })
// AWS CLI commands
const cli = generateAwsCliCommands({ tableName: 'analytics' })Stacks-compatible model definitions for all analytics entities:
import {
SiteModel,
PageViewModel,
SessionModel,
CustomEventModel,
GoalModel,
AggregatedStatsModel,
// ... and more
} from '@stacksjs/analytics'bun testPlease see our releases page for more information on what has changed recently.
Please see CONTRIBUTING for details.
For help, discussion about best practices, or any other conversation that would benefit from being searchable:
For casual chit-chat with others using this package:
Join the Stacks Discord Server
Stacks OSS will always stay open-sourced, and we will always love to receive postcards from wherever Stacks is used! And we also determine the OSS dependencies we utilize.
Our address: Stacks.js, 12665 Village Ln #2306, Playa Vista, CA 90094, USA
We would like to extend our thanks to the following sponsors for funding Stacks development. If you are interested in becoming a sponsor, please reach out to us.
The MIT License (MIT). Please see LICENSE for more information.
Made with love by Chris Breuer and contributors.
