Skip to main content

Overview

This guide covers local development for both the Tinybird data project and the analytics dashboard. You’ll learn how to set up your environment, make changes, and test everything locally before deploying to production.

Prerequisites

Repository Structure

web-analytics-starter-kit/
├── tinybird/              # Tinybird data project
│   ├── src/
│   │   ├── client.ts      # Tinybird client configuration
│   │   ├── datasources.ts # Data source definitions
│   │   ├── endpoints.ts   # API endpoint definitions
│   │   └── ...
│   ├── tinybird.json      # SDK configuration
│   └── package.json
├── dashboard/             # Next.js analytics dashboard
│   ├── app/
│   ├── components/
│   ├── lib/
│   └── package.json
└── middleware/            # Proxy server implementation
    └── api/
        └── tracking.js    # Vercel edge function for proxying

Tinybird Data Project Setup

The Tinybird project contains all your data sources, pipes, and endpoints defined using the Tinybird SDK.

Initial Setup

  1. Clone the repository:
git clone https://github.com/tinybirdco/web-analytics-starter-kit
cd web-analytics-starter-kit/tinybird
  1. Install dependencies:
pnpm install
  1. Authenticate with Tinybird:
pnpm login
# or
npx tinybird login
This opens a browser window for authentication and creates a .tinybird file with your credentials.
  1. Configure environment variables:
Create a .env.local file:
TINYBIRD_TOKEN=p.your_admin_token_here
TINYBIRD_HOST=https://api.tinybird.co
You can find your admin token at cloud.tinybird.co/tokens
For US East region:
TINYBIRD_HOST=https://api.us-east.tinybird.co

Development Workflow

Development Mode (Branch)

Start development mode to automatically sync changes to a Tinybird branch:
pnpm dev
This command:
  • Creates a development branch (if it doesn’t exist)
  • Watches for file changes
  • Automatically syncs changes to Tinybird
  • Provides a live preview URL
Use branches for development to avoid affecting production data and endpoints.

Manual Build

Build and push changes to a branch:
pnpm build

Deploy to Production

Deploy changes to the main workspace:
pnpm deploy
This deploys to production. Make sure you’ve tested your changes in a branch first!

Preview Changes

Preview changes without deploying:
pnpm preview

Project Configuration

The tinybird.json file configures the SDK:
{
  "include": [
    "src/client.ts",
    "src/datasources.ts",
    "src/endpoints.ts",
    "src/materializations.ts",
    "src/pipes.ts",
    "src/tokens.ts",
    "src/copies.ts",
    "src/web-vitals.ts"
  ],
  "token": "${TINYBIRD_TOKEN}",
  "baseUrl": "${TINYBIRD_HOST}",
  "devMode": "branch"
}
include
array
Files to include in the build
token
string
Admin token for authentication (from environment variable)
baseUrl
string
Tinybird API URL (from environment variable)
devMode
string
Development mode: branch creates isolated dev environments

Making Changes

Adding a New Endpoint

Create a new endpoint in src/endpoints.ts:
export const myCustomEndpoint = defineEndpoint("my_custom_endpoint", {
  description: "My custom analytics endpoint",
  tokens: [{ token: dashboardToken, scope: "READ" }],
  nodes: [
    node({
      name: "query",
      sql: `
        SELECT
          toDate(timestamp) as date,
          count() as total_events
        FROM analytics_events
        WHERE timestamp >= {{ DateTime(date_from) }}
          AND timestamp < {{ DateTime(date_to) }}
        GROUP BY date
        ORDER BY date DESC
      `,
    }),
  ],
  params: {
    date_from: p.dateTime().describe("Start date"),
    date_to: p.dateTime().describe("End date"),
  },
  output: {
    date: t.date(),
    total_events: t.uint64(),
  },
});
The changes will automatically sync if you’re running pnpm dev.

Modifying a Data Source

Edit src/datasources.ts to modify schema or engine settings:
export const analyticsEvents = defineDatasource("analytics_events", {
  schema: {
    timestamp: t.dateTime(),
    // Add a new field
    custom_field: t.string().default(""),
    // ... other fields
  },
  engine: engine.mergeTree({
    partitionKey: "toYYYYMM(timestamp)",
    sortingKey: ["tenant_id", "domain", "timestamp"],
  }),
});
Changing data source schemas requires careful migration. Test thoroughly in a branch.

Using Tinybird Local

For complete local development without cloud dependencies, use Tinybird Local:
# Start local Tinybird instance
tinybird local start

# Update environment to use local instance
export TINYBIRD_HOST=http://localhost:7181

# Deploy to local instance
pnpm deploy
Local instance runs on http://localhost:7181 and includes:
  • Local ClickHouse database
  • Tinybird API
  • Admin UI

Dashboard Development

The dashboard is a Next.js application that visualizes your analytics data.

Setup

  1. Navigate to dashboard directory:
cd ../dashboard
  1. Install dependencies:
pnpm install
  1. Configure environment:
Copy .env.example to .env or .env.local:
cp .env.example .env.local
Edit .env.local:
# Tinybird Configuration (server-only, required)
TINYBIRD_TOKEN=your-dashboard-token
TINYBIRD_HOST=https://api.tinybird.co

# Dashboard URL (public)
NEXT_PUBLIC_TINYBIRD_DASHBOARD_URL=http://localhost:3000

# Tracker token for analytics (public, optional)
NEXT_PUBLIC_TINYBIRD_TRACKER_TOKEN=

# Dashboard Basic Auth (server-only, defaults to admin/admin if not set)
DASHBOARD_USERNAME=admin
DASHBOARD_PASSWORD=changeme

# Optional: Set to "true" to disable auth during development
DISABLE_AUTH=true

# Optional: AI Chat
NEXT_PUBLIC_ASK_TINYBIRD_ENDPOINT="https://ask-tb.tinybird.live/api/chat"
Dashboard token (not tracker token) has READ access to all endpoints. Get it from cloud.tinybird.co/tokens.

Development Server

Start the development server:
pnpm dev
The dashboard runs at http://localhost:3000

Authentication

The dashboard includes built-in authentication:
  • Default credentials: admin / admin
  • Customize: Set DASHBOARD_USERNAME and DASHBOARD_PASSWORD
  • Disable for dev: Set DISABLE_AUTH=true

Tech Stack

Project Structure

dashboard/
├── app/                   # Next.js App Router
│   ├── api/              # API routes
│   │   ├── endpoints/    # Proxy to Tinybird endpoints
│   │   ├── auth/         # Authentication endpoints
│   │   └── config/       # Configuration endpoints
│   ├── page.tsx          # Main dashboard page
│   └── layout.tsx        # Root layout
├── components/            # React components
│   ├── charts/           # Chart components
│   ├── stats/            # Stat cards
│   └── ui/               # UI primitives
├── lib/                   # Utilities
│   ├── api.ts            # API client
│   ├── hooks/            # Custom React hooks
│   └── types/            # TypeScript types
└── public/               # Static assets

Making Changes

Adding a New Chart

Create a component in components/charts/:
// components/charts/CustomChart.tsx
import { useEndpoint } from '@/lib/hooks/use-endpoint';
import { LineChart, Line, XAxis, YAxis, Tooltip } from 'recharts';

export function CustomChart() {
  const { data, error, isLoading } = useEndpoint('my_custom_endpoint', {
    date_from: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString(),
    date_to: new Date().toISOString(),
  });

  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error loading data</div>;

  return (
    <LineChart width={600} height={300} data={data}>
      <XAxis dataKey="date" />
      <YAxis />
      <Tooltip />
      <Line type="monotone" dataKey="total_events" stroke="#8884d8" />
    </LineChart>
  );
}

Using Custom Endpoints

The useEndpoint hook fetches data from your Tinybird endpoints:
import { useEndpoint } from '@/lib/hooks/use-endpoint';

const { data, error, isLoading } = useEndpoint('endpoint_name', {
  param1: 'value1',
  param2: 'value2',
});
Data is automatically cached and revalidated using SWR.

Using Tinybird Local with Dashboard

To develop against a local Tinybird instance:
# In .env.local
TINYBIRD_HOST=http://localhost:7181
Then start the dashboard:
pnpm dev

Testing Locally

Test Tracking Script

Create a simple HTML page to test tracking:
<!DOCTYPE html>
<html>
<head>
  <title>Analytics Test</title>
  <script
    defer
    src="https://unpkg.com/@tinybirdco/flock.js"
    data-token="YOUR_TRACKER_TOKEN"
    data-host="http://localhost:7181"
  ></script>
</head>
<body>
  <h1>Analytics Test Page</h1>
  <button onclick="Tinybird.trackEvent('button_click', { button: 'test' })">
    Track Event
  </button>
</body>
</html>
Open in a browser and check your Tinybird workspace for events.

Query Data

Query your local or development branch:
# Using Tinybird CLI
tinybird sql "SELECT count() FROM analytics_events"

# Or via API
curl -H "Authorization: Bearer YOUR_TOKEN" \
  "https://api.tinybird.co/v0/sql?q=SELECT%20count()%20FROM%20analytics_events"

Production Build

Build Dashboard

cd dashboard
pnpm build
Test the production build locally:
pnpm start

Deploy Dashboard

Deploy to Vercel:
vercel deploy
Or use the Vercel GitHub integration for automatic deployments.

Common Development Tasks

Add a Custom Event Type

  1. Track the event from your app:
Tinybird.trackEvent('signup_completed', {
  plan: 'pro',
  trial: false,
});
  1. Create an endpoint to query it:
export const signups = defineEndpoint("signups", {
  nodes: [
    node({
      sql: `
        SELECT
          toDate(timestamp) as date,
          JSONExtractString(payload, 'plan') as plan,
          count() as signups
        FROM analytics_events
        WHERE action = 'signup_completed'
          AND timestamp >= {{ DateTime(date_from) }}
        GROUP BY date, plan
        ORDER BY date DESC
      `,
    }),
  ],
  // ... params and output
});
  1. Add a chart in the dashboard

Test Multi-Tenancy

Track events with tenant IDs:
<script
  src="https://unpkg.com/@tinybirdco/flock.js"
  data-token="YOUR_TRACKER_TOKEN"
  data-tenant-id="test-tenant-1"
  data-domain="test.local"
></script>
Query by tenant:
tinybird sql "SELECT count() FROM analytics_events WHERE tenant_id = 'test-tenant-1'"

Debug Authentication Issues

Enable authentication logs:
// middleware.ts
console.log('Auth check:', {
  pathname: request.nextUrl.pathname,
  hasAuth: !!request.headers.get('authorization'),
});

Troubleshooting

”Token not found” Error

Ensure your .env.local file has the correct token:
TINYBIRD_TOKEN=p.your_actual_token_here

“Connection refused” with Tinybird Local

Check if Tinybird Local is running:
tinybird local status

# If not running:
tinybird local start

Dashboard shows “No data”

Verify:
  1. Tracking script is sending events
  2. Dashboard token has correct permissions
  3. Date range includes data
  4. Check browser console for API errors

Changes not syncing

Restart dev mode:
# Stop (Ctrl+C)
# Start again
pnpm dev
Or manually push:
pnpm build

Best Practices

Use branches for development, deploy to production only after testing
Keep .env.local in .gitignore - never commit tokens
Use Tinybird Local for testing schema changes
Test with realistic data volumes before deploying
Document custom endpoints and their parameters

Build docs developers (and LLMs) love