Local Development

Setting up local databases and development environment for Bklit Analytics

Local Development

This guide will help you set up a complete local development environment with local databases, so you can develop safely without touching production data.

Quick Start: For first-time setup, use npx @bklit/create instead of manual configuration. This guide is for advanced users who want manual control.

Optional Features: Polar billing, OAuth providers, Resend email, and Mapbox are all optional. Core analytics features work without them. See Environment Variables for details.

Prerequisites

Before you begin, ensure you have:

  • Node.js >= 22.0.0
  • pnpm >= 9.6.0
  • Docker (for ClickHouse)

Overview

Bklit uses two databases:

  • PostgreSQL - User data, projects, organizations (via Prisma Dev)
  • ClickHouse - Analytics events and session data (via Docker)

In development, you'll use local instances of both databases that are completely isolated from production.

Environment Variables

We use environment variable prefixes to separate local development from production:

  • DEV_* prefixed variables are automatically used when NODE_ENV=development
  • Regular variables (without prefix) are used in production

This design makes it impossible to accidentally connect to production databases during development.

Setting Up Your .env File

The setup wizard creates this automatically, but for manual setup:

# ===================
# REQUIRED - Auto-generated by wizard
# ===================
AUTH_SECRET="auto-generated-32-byte-secret"
DATABASE_URL="postgresql://..."
CLICKHOUSE_HOST="http://localhost:8123"

# ===================
# OPTIONAL - OAuth Providers
# ===================
# AUTH_GITHUB_ID="your-github-id"
# AUTH_GITHUB_SECRET="your-github-secret"
# AUTH_GOOGLE_ID="your-google-id"
# AUTH_GOOGLE_SECRET="your-google-secret"

# ===================
# OPTIONAL - Billing (Polar.sh)
# ===================
# POLAR_SERVER_MODE="sandbox"
# POLAR_ORGANIZATION_ID="your-org-id"
# POLAR_ACCESS_TOKEN="your-token"
# POLAR_WEBHOOK_SECRET="your-secret"

# ===================
# OPTIONAL - Email (Resend)
# ===================
# RESEND_API_KEY="your-resend-key"

# ===================
# OPTIONAL - Maps (Mapbox)
# ===================
# NEXT_PUBLIC_MAPBOX_TOKEN="your-mapbox-token"

# ===================
# OPTIONAL - Background Jobs (Trigger.dev)
# ===================
# TRIGGER_SECRET_KEY="your-trigger-secret"
# TRIGGER_API_KEY="your-trigger-api-key"

# ===================
# Development Only
# ===================
NODE_ENV="development"
NEXT_PUBLIC_APP_URL="http://localhost:3000"

In development without optional services:

  • πŸ” Login OTPs appear in your terminal
  • πŸ“§ Emails are logged to console
  • πŸ—ΊοΈ Geographic data shows as list view (no map)
  • πŸ’³ All users have free tier access

Initial Setup

Follow these steps once to set up your local development environment.

1. Install Dependencies

pnpm install

2. Start ClickHouse

Start Docker Desktop, then start ClickHouse:

pnpm dev:services

Or let it start automatically when you run pnpm dev (recommended).

3. Start Prisma Dev

In a separate terminal, start the local Prisma Postgres server:

cd apps/dashboard
npx prisma dev

When the server starts, press h to view the HTTP connection URL. You should see output like:

prisma+postgres://localhost:51213/?api_key=eyJkYXRhYmFzZVVybCI6...

Copy this URL and update the DEV_DATABASE_URL in your .env file.

Important: Keep this terminal running! Prisma Dev needs to stay active.

4. Run Database Migrations

With Prisma Dev running, open another terminal and run:

# Migrate Postgres (creates all tables)
pnpm db:migrate

You should see output confirming the connection to localhost:51213:

Datasource "db": PostgreSQL database "postgres" at "localhost:51213"

Then migrate ClickHouse:

cd packages/analytics
pnpm run migrate

5. Clean and Generate

Clear Next.js cache and regenerate Prisma client:

cd apps/dashboard
rm -rf .next
cd ../..
pnpm db:generate

6. Start Development Servers

# Start dashboard and playground (default)
pnpm dev

# Or start everything including website and trigger.dev
pnpm dev:full

Your local environment is now ready! Visit http://localhost:3000 to see the dashboard.

Daily Workflow

Once set up, your daily development workflow is simple:

Terminal 1: Start Prisma Postgres

cd apps/dashboard
npx prisma dev

Keep this running throughout your development session.

Terminal 2: Start Your App

pnpm dev

This automatically starts ClickHouse and runs the dashboard and playground apps.

Verifying Your Setup

To confirm you're connected to local databases and not production:

Check Database Connections

When you start pnpm dev, watch the terminal output. You should NOT see any references to:

  • accelerate.prisma-data.net
  • Your production ClickHouse host

Check Your Data

  • You should not be automatically logged in
  • You'll need to create a new account locally
  • No production projects or data should be visible

Use Prisma Studio

Open Prisma Studio to inspect your local database:

cd packages/db
pnpm studio

This opens at http://localhost:5555 and should show your local database with empty tables.

Available Commands

CommandDescription
pnpm devStart dashboard and playground (ClickHouse starts automatically)
pnpm dev:fullStart all apps including website and trigger.dev
pnpm dev:servicesManually start ClickHouse only
pnpm dev:services:stopStop ClickHouse
pnpm db:migrateRun Postgres migrations on local DB
pnpm db:studioOpen Prisma Studio (local DB)
pnpm db:generateRegenerate Prisma Client
pnpm dev:docsStart documentation site

How It Works

Automatic Environment Switching

The codebase automatically uses DEV_* variables when NODE_ENV=development:

In Application Code:

Before database clients initialize, packages/db/src/client.ts and packages/analytics/src/client.ts override process.env with DEV_* values in development mode. This ensures Prisma and ClickHouse clients always connect to local databases.

// In packages/db/src/client.ts
if (process.env.NODE_ENV === "development" && process.env.DEV_DATABASE_URL) {
  process.env.DATABASE_URL = process.env.DEV_DATABASE_URL;
}

In CLI Scripts:

Database migration and management commands use a with-env-dev wrapper that substitutes DEV_* variables:

  • pnpm db:migrate β†’ Uses DEV_DATABASE_URL
  • pnpm db:studio β†’ Connects to local database
  • pnpm run migrate (in analytics package) β†’ Uses DEV_CLICKHOUSE_* variables

This means you can safely run any command without accidentally touching production data.

Running Database Migrations

When updating Bklit or applying schema changes, you may need to run migrations:

PostgreSQL (Prisma) Migrations:

pnpm db:migrate

ClickHouse Migrations:

For ClickHouse schema updates (like adding new columns), use the migration scripts in apps/dashboard/scripts/:

# Cross-platform (recommended for Windows contributors)
cd apps/dashboard
pnpm tsx scripts/migrate-clickhouse-pageviews.ts

# Unix shell script (macOS/Linux only)
cd apps/dashboard
./scripts/migrate-clickhouse-pageviews.sh

See the scripts README for details on specific migrations.

Docker Compose

The docker-compose.yml at the project root defines the ClickHouse service:

  • Image: clickhouse/clickhouse-server:latest
  • Ports: 8123 (HTTP), 9000 (Native)
  • Data persistence via Docker volumes
  • Health checks to ensure service is ready

Troubleshooting

ClickHouse Won't Start

Error: Cannot connect to the Docker daemon

Solution:

  1. Open Docker Desktop
  2. Wait for it to fully start
  3. Run pnpm dev:services or pnpm dev

Error: Port conflicts or service issues

Solution:

# Stop and remove all data
docker-compose down -v

# Start fresh
pnpm dev:services

Prisma Postgres Issues

Error: Migrations fail or connection errors

Solution:

# Stop Prisma Dev (Ctrl+C)
# Remove all local instances
npx prisma dev rm '*'

# Start fresh
npx prisma dev

# Update DEV_DATABASE_URL in .env with new connection string
# Run migrations again
pnpm db:migrate

Error: Migration fails with "relation does not exist" or database state issues

This commonly happens on first-time setup when migrations can't apply cleanly. Since you're starting fresh, you can bypass migrations entirely:

Solution (Fresh Setup Only):

# Navigate to the db package
cd packages/db

# Force reset and push schema directly (skips migration history)
pnpm with-env-dev prisma db push --force-reset --schema=./prisma/schema.prisma

# Generate Prisma client
pnpm generate

# Return to project root
cd ../..

This approach:

  • Drops all tables and recreates them from your schema
  • Bypasses migration history (ideal for fresh local setup)
  • Accepts data loss (not a problem for new local environments)

Still Seeing Production Data

If you're seeing production data or accelerate.prisma-data.net in logs:

  1. Verify NODE_ENV=development is at the top of your .env
  2. Clear all caches:
    cd apps/dashboard
    rm -rf .next
    rm -rf node_modules/.cache
  3. Regenerate Prisma client:
    pnpm db:generate
  4. Restart dev server

Reset Local Databases

Reset Postgres:

cd packages/db
pnpm with-env-dev prisma migrate reset

Reset ClickHouse:

cd packages/analytics
pnpm run clear-data
pnpm run migrate

Architecture

Applications

  • Dashboard - Main SaaS application (Next.js)
  • Playground - SDK testing environment (Vite + React)
  • Docs - Documentation site (Next.js)
  • Website - Marketing site (Next.js)

Local Databases

  • Postgres - User data, projects, organizations, sessions (Prisma Dev)
  • ClickHouse - Page views, events, analytics sessions (Docker)

Development Flow

  1. User creates account locally (stored in Postgres)
  2. Creates projects and organizations (stored in Postgres)
  3. Integrates SDK in playground or test app
  4. SDK sends events to local API
  5. Events stored in ClickHouse
  6. Dashboard queries ClickHouse and displays analytics

All of this happens entirely on your machine with no connection to production services.

Testing Webhooks Locally

Why Tunnels Are Needed

Polar and Resend webhooks can't reach localhost. Use a tunnel to expose your local server for webhook testing.

1. Install ngrok:

2. Start services:

# Terminal 1: Prisma Dev
cd apps/dashboard
npx prisma dev

# Terminal 2: Your app
pnpm dev

# Terminal 3: ngrok
ngrok http 3000

3. Copy ngrok URL:

From ngrok output, copy the HTTPS forwarding URL:

Forwarding   https://abc123.ngrok.io -> http://localhost:3000

4. Configure webhooks:

Polar:

  • Dashboard β†’ Settings β†’ Webhooks
  • URL: https://abc123.ngrok.io/api/webhooks/polar
  • Secret: Your POLAR_WEBHOOK_SECRET

5. Test:

  • Create test subscription in Polar sandbox
  • Check Polar webhook logs
  • Verify your organization plan updates

Alternative: Cloudflare Tunnel

cloudflared tunnel --url http://localhost:3000

Use tunnel URL: https://abc123.trycloudflare.com/api/webhooks/polar

Important Notes

  • Free tunnel URLs change on restart - Update webhook URLs accordingly
  • Use sandbox mode: POLAR_SERVER_MODE="sandbox" in .env
  • Optional for basic dev - Webhooks only needed for testing subscriptions
  • Production uses your domain - No tunnels in production

Skip Webhooks

For basic development without webhook testing:

  1. Keep POLAR_SERVER_MODE="sandbox"
  2. Webhooks will fail gracefully (expected)
  3. Manage subscriptions in Polar UI
  4. Test webhooks in staging/production

On this page