Skip to content
Analytik
Esc
↑↓navigate↵open⌘Jpreview
On this page

Getting started

Install Analytik, run the server locally with SQLite, create a site, add the script and see the first pageview.

This page runs everything on your machine with Bun or Node and a SQLite file. The deploy guides cover Cloudflare, Vercel, Railway and a VPS.

Install

npm install analytik hono
pnpm add analytik hono
yarn add analytik hono
bun add analytik hono
nub add analytik hono
aube add analytik hono

Analytik is ESM only and needs Node 22.19 or newer, or Bun 1.3 or newer. hono is a peer dependency of the server. react and react-dom (version 19) are needed only for the React bindings and the dashboard. Postgres needs postgres or pg, and you pass the connected client to postgresStorage(); see Storage. The tracker script and the browser client have no dependencies.

Write the server

import { createAnalytik } from 'analytik/server'
import { sqliteStorage } from 'analytik/storage/sqlite'
import { Hono } from 'hono'
import { serveStatic } from 'hono/bun'

const storage = sqliteStorage('./analytik.db')
await storage.migrate()

const analytik = createAnalytik({
  storage,
  admin: { password: process.env.ADMIN_PASSWORD!, secret: process.env.SESSION_SECRET! },
})

const app = new Hono()
app.route('/', analytik.app)
// The server does not serve the tracker script; see "Add the script" below.
app.use('/js/analytik.js', serveStatic({ path: './public/js/analytik.js' }))

export default {
  port: 8787,
  fetch: app.fetch,
}

Run it with ADMIN_PASSWORD=change-me SESSION_SECRET=$(openssl rand -hex 32) bun server.ts. createAnalytik() throws at startup when ADMIN_PASSWORD is empty or SESSION_SECRET is shorter than 32 characters, so a weak password or a forgeable session cookie never goes live. On Node, serve the app with @hono/node-server instead; see Bun, Node and SQLite.

migrate() applies pending migrations and is safe to call on every start. The same call exists on every storage driver.

Create a site

A site is one website you track. Log in to the admin API with your password, then create it:

curl -i -c cookies.txt -X POST http://localhost:8787/api/auth/login \
  -H 'Content-Type: application/json' \
  -d '{"password":"change-me"}'

curl -b cookies.txt -X POST http://localhost:8787/api/sites \
  -H 'Content-Type: application/json' \
  -d '{"name":"My site","domain":"example.com","timezone":"Europe/Bratislava","currency":"EUR","allowLocalhost":true}'

The domain must look like example.com: the server rejects localhost and other names without a dot and a top-level domain. For local work, use a domain you own or example.com, and set allowLocalhost so events from localhost are accepted.

The response is { "site": { ... }, "apiKey": "ak_..." }. Take the site id from site.id. The apiKey is for server-to-server calls such as payments; it is shown only this once, and you can issue a new one with POST /sites/:id/rotate-key. You can also create sites from the dashboard (the Websites view, Add website) once it is embedded; see Embedding the dashboard.

Add the script

<script defer src="http://localhost:8787/js/analytik.js" data-site="SITE_ID" data-allow-localhost></script>

The Analytik server does not serve the tracker script. The file is dist/analytik.js in the package (exported as analytik/analytik.js). Copy it into the folder your server or host serves as static files, and copy it again after you upgrade the package:

mkdir -p public/js && cp node_modules/analytik/dist/analytik.js public/js/analytik.js

The server above serves that file at /js/analytik.js. Without data-api the tracker sends events to the same origin as the script, under /api, which matches this setup.

data-allow-localhost is only for local work; without it the tracker does nothing on localhost, and the server rejects events from hosts that are not the site’s domain unless the site has allowLocalhost set. All attributes are listed in the tracker attributes reference.

By default the first pageview is sent after the first interaction or three seconds of visibility, so open the page, wait, and scroll.

See the data

curl -b cookies.txt "http://localhost:8787/api/sites/SITE_ID/realtime"

The result is a Realtime object: visitors in the last five minutes and the latest events. The same data drives the dashboard, which you can mount in a React app:

import { Dashboard } from 'analytik/dashboard'
import 'analytik/dashboard/styles.css'

export function Admin() {
  return <Dashboard api="http://localhost:8787/api" />
}

Next

Was this page helpful?