MooseStack

Create Project

Use with Existing ClickHouse

Use Moose with Your Existing ClickHouse

5 min setup
Zero disruption to prod
Code-first, local dev

Viewing:

What This Guide Does

This guide sets up a local ClickHouse development environment that mirrors your production database and enables code-first schema management:

  1. Introspect your remote ClickHouse tables and generate TypeScript/Python data models
  2. Create a local ClickHouse instance with your exact table schemas
  3. Seed your local database with production data (optional)
  4. Build APIs and pipelines on top of your ClickHouse data in code

How It Works

Local Development:

  • Your production ClickHouse remains untouched
  • You get a local ClickHouse instance that copies your remote table schemas
  • All development happens locally with hot-reload

Production Deployment:

  • When you deploy your code, it connects to your remote ClickHouse
  • Any new tables, materialized views, or schema changes you create in code are automatically migrated to your target database
  • Your existing data and tables remain intact

What You Get

Schema management in code

Define ClickHouse tables, materialized views, and schemas as TypeScript/Python code

Auto-generated models

TypeScript/Python classes generated from your existing ClickHouse tables

Local development environment

Local ClickHouse instance that mirrors your production schema

Type-safe APIs

Build REST APIs with full type safety and autocomplete

Automatic migrations

Schema changes are automatically applied to your target database on deployment

Prerequisites

Before you start

Existing ClickHouse Database

Any ClickHouse instance (Cloud, self-hosted, or playground)

Node.js 20+

Required for TypeScript development

Download →

Docker Desktop

For local development environment

Download →

macOS/Linux

Windows works via WSL2


Step 1: Install Moose

Install the Moose CLI globally to your system:

Terminal
bash -i <(curl -fsSL https://fiveonefour.com/install.sh) moose

What happens next

After installation, you’ll use moose init to create a new project that automatically connects to your ClickHouse and generates all the code you need.

Step 2: Create Your Project

Don't have ClickHouse?

Use the ClickHouse Playground tab to try it out!

Initialize new project
# Option 1: Provide connection string directly
moose init my-project --from-remote <YOUR_CLICKHOUSE_CONNECTION_STRING> --language typescript
 
# Option 2: Run without connection string for interactive setup
moose init my-project --from-remote --language typescript

Connection String Format:

https://username:password@host:port/?database=database_name

Interactive Setup

If you don’t provide a connection string, Moose will guide you through an interactive setup process where you’ll be prompted to enter:

  • Host and port (e.g., https://your-service-id.region.clickhouse.cloud:8443)
  • Username (usually default)
  • Password (your ClickHouse password)
  • Database name (optional, defaults to default)

This is perfect if you’re not sure about your connection details or prefer a guided experience.

What gets generated

Moose will create a complete project structure with:

  • Data models: TypeScript/Python classes for every table in your ClickHouse
  • Type definitions: Full type safety for all your data
  • Development environment: Local ClickHouse instance that mirrors your production schema
  • Build tools: Everything configured and ready to go

Install dependencies
cd my-project
npm install

You should see: Successfully generated X models from ClickHouse tables

Explore Your Generated Models

Check what Moose created from your tables in the app/index.ts file:

    • index.ts

Export Pattern

Your generated table models are exported here so Moose can detect them.
Learn more about export pattern: local development / hosted

If your database includes ClickPipes/PeerDB (CDC) tables

As noted above, when you use moose init --from-remote, Moose introspects your database. If it detects CDC‑managed tables (e.g., PeerDB/ClickPipes with fields like _peerdb_synced_at, _peerdb_is_deleted, _peerdb_version), it marks those as EXTERNALLY_MANAGED and writes them into a dedicated external models file. Your root file is updated to load these models automatically.

    • index.ts
    • externalModels.ts

This separation is a best‑effort by the CLI to keep clearly CDC‑owned tables external. For other tables you don’t want Moose to manage, set the lifecycle to external and move them into the external file. See:

  • External Tables documentation for more information on how external tables work.
  • DB Pull for keeping models in sync with the remote schema.

Step 3: Start Development

Start your development server. This spins up a local ClickHouse instance that perfectly mirrors your production schema:

Start your dev server
moose dev

What happens when you run moose dev:

  • 🏗️ Creates a local ClickHouse instance with your exact table schemas in your project code
  • 🔄 Hot-reloads migrations to your local infrastructure as you save code changes
  • 🚀 Starts a web server for building APIs
Production Safety

Your production ClickHouse remains completely untouched. This is a separate, local development environment.

Seed Your Local Database (Optional)

Copy real data from your production ClickHouse to your local development environment. This gives you realistic data to work with during development.

Why seed? Your local database starts empty. Seeding copies real data so you can:

  • Test with realistic data volumes
  • Debug with actual production data patterns
  • Develop features that work with real data structures
Terminal
moose seed clickhouse --connection-string <YOUR_CLICKHOUSE_CONNECTION_STRING> --limit 100000

Connection String Format: The connection string must use ClickHouse native protocol:

# ClickHouse native protocol (secure connection)
clickhouse://username:password@host:9440/database

Note: Data transfer uses ClickHouse’s native TCP protocol via remoteSecure(). The remote server must have the native TCP port accessible. The command automatically handles table mismatches gracefully.

Notes

  • --limit and --all are mutually exclusive
  • --all can be used to copy the entire table(s), use with caution as it can be very slow and computationally intensive.
  • Large copies are automatically batched to avoid remote limits; you’ll see per-batch progress.
  • If you stop with Ctrl+C, the current batch finishes and the command exits gracefully.

Expected Output:

 Database seeding completed
Seeded 'local_db' from 'remote_db'
 
 table1: copied from remote
⚠️  table2: skipped (not found on remote)
 table3: copied from remote

Troubleshooting:

  • Tables that don’t exist on remote are automatically skipped with warnings
  • Use --table <name> to seed a specific table that exists in both databases
  • Check moose ls table to see your local tables

Step 4: Build Your First API

Now that you have your data models, let’s build something useful! You can create APIs, materialized views, and applications with full type safety.

What you can build

  • REST APIs that expose your ClickHouse data to frontend applications
  • Materialized Views for faster queries and aggregations
  • Streaming pipelines for real-time data processing
  • Full-stack applications with your ClickHouse data as the backend

Add APIs

Build REST APIs on top of your existing tables to expose your data to your user-facing apps. This is a great way to get started with Moose without changing any of your existing pipelines.

import { TargetSchema, materializedView } from "./myMaterializedView";
 
 
export interface QueryParams {
  date: Date;
  limit?: number;
}
 
const api = new Api<QueryParams, TargetSchema>("get-aggregated-data",
  async ({ date, limit = 10 }: QueryParams, { client, sql }) => {
    const query = sql`
        SELECT * FROM ${materializedView.targetTable} 
        WHERE date = ${date} 
        LIMIT ${limit}`;
 
    const result = await client.query.execute<TargetSchema>(query); // Type-safe query execution
    return result.json();
  }
);

Building APIs with Moose

Check out the MooseAPI module for more information on building APIs with Moose.

Build Materialized Views

Build materialized views on top of your existing tables to improve query performance. If you have Materialized Views in your ClickHouse, you can use Moose to build new Materialized Views on top of your existing tables, or to migrate your existing Materialized Views to Moose.

// Data Model for your Materialized View- use type throughout your codebase
export interface TargetSchema {
  date: Date;
  count: number;
}
// Creates both the target table and the materialized view
export const materializedView = new MaterializedView<TargetSchema>({
  // Raw SQL with type-safe table references
  selectStatement: sql`SELECT count(*) as count, date FROM ${myTable} GROUP BY date`,
  selectTables: [myTable], // Automatic dependency resolution for cascading materialized views
  engine: ClickHouseEngines.MergeTree, // Can use which ever engine you want
  orderByFields: ["date"],
  tableName: "myAggregation",
  materializedViewName: "myAggregation_MV",
});

Building Materialized Views with Moose

Check out the MooseOLAP module for more information on building Materialized Views with Moose.


Known Limitations

Early Access Feature

Some advanced ClickHouse features may not be fully supported yet. Join the Moose Slack and let us know if you have any issues, feedback, or requests.

What we’re working on:

  • Selective table import (currently imports all tables)
  • Default value annotations

Troubleshooting

Error: Failed to connect to ClickHouse

This guide shows exactly where to find your host, port, username, and password, and how to construct a valid HTTPS connection string.

  1. Log into your ClickHouse Cloud console
  2. Open your service details page
  3. Click “Connect” in the sidebar
  1. Select the HTTPS tab and copy the values shown
    • Host: e.g. your-service-id.region.clickhouse.cloud
    • Port: usually 8443
    • Username: usually default
    • Password: the password you configured
  1. Build your connection string:
https://USERNAME:PASSWORD@HOST:PORT/?database=DATABASE_NAME
  1. Example (with placeholders):
https://default:your_password@your-service-id.region.clickhouse.cloud:8443/?database=default
  1. Optional: Test with curl
curl --user "USERNAME:PASSWORD" --data-binary "SELECT 1" https://HOST:PORT

moose dev fails to start

Double check Docker is running and you do not have any port conflicts.

  • ClickHouse local runs on port 18123
  • Your local webserver runs on port 4000
  • Your local management API runs on port 5001

What’s Next?