Quarry
API Reference

DB Typing Helpers

Type-level helpers for distinguishing tables from views and for expressing different read and write shapes.

Quarry's default DB shape is still plain TypeScript:

interface DB {
  users: {
    id: number;
    email: string;
  };
}

Plain object sources are treated as regular tables.

Use the helpers on this page when you want more precision than a plain object can express.

TypedView

TypedView<Row> marks a source as selectable but not insertable.

import type { TypedView } from "@oorestisime/quarry";

interface DB {
  daily_users: TypedView<{
    signup_date: string;
    total_users: string;
  }>;
}

This means:

  • db.selectFrom("daily_users") is allowed
  • db.insertInto("daily_users") is rejected by the type system
  • db.table("daily_users") is rejected by the type system

TypedTable

TypedTable<Row> is the table counterpart.

import type { TypedTable } from "@oorestisime/quarry";

interface DB {
  users: TypedTable<{
    id: number;
    email: string;
  }>;
}

You do not need TypedTable for ordinary handwritten schemas because a plain object already behaves like a table. It is mainly useful for symmetry with generated introspection output.

TypedDictionary

TypedDictionary<Row> marks a source as a ClickHouse dictionary.

import type { TypedDictionary } from "@oorestisime/quarry";

interface DB {
  partner_rates: TypedDictionary<{
    rate_cents: number;
    currency: string;
  }>;
}

Dictionaries are not queryable tables. You cannot selectFrom or insertInto a dictionary. Instead, you use the typed dictGet, dictGetOrDefault, and dictHas functions from the expression builder.

db.selectFrom("events as e").selectExpr((eb) => [
  eb.fn.dictGet("partner_rates", "rate_cents", "e.partner_id").as("rate"),
  eb.fn.dictHas("partner_rates", "e.partner_id").as("has_rate"),
]);

What the Row interface contains

The Row type in TypedDictionary<Row> contains only attribute columns — the columns you can retrieve with dictGet. It does not include:

  • Primary key columns — these are the lookup input, not a retrievable value. This applies to all layouts: HASHED, COMPLEX_KEY_HASHED, RANGE_HASHED, CACHE, DIRECT, IP_TRIE, and every other layout.
  • Range columns (for RANGE_HASHED layouts) — these are internal engine mechanics used to select the correct row, not queryable attributes.

This matches ClickHouse runtime behavior: dictGet('dict', 'key_col', key) fails at runtime when 'key_col' is a primary key, not an attribute.

For example, with a RANGE_HASHED dictionary:

CREATE DICTIONARY partner_revenue_dict (
    partner_id Int64,          -- PRIMARY KEY (not in TypedDictionary)
    start_date Date,           -- RANGE MIN (not in TypedDictionary)
    end_date Date,             -- RANGE MAX (not in TypedDictionary)
    revenue_per_user Decimal(10, 4),  -- attribute
    partner_name String        -- attribute
)
PRIMARY KEY partner_id
LAYOUT(RANGE_HASHED())
RANGE(MIN start_date MAX end_date)

The generated interface will only contain the attributes:

interface DB {
  partner_revenue_dict: TypedDictionary<{
    revenue_per_user: number;
    partner_name: string;
  }>;
}

ColumnType

ColumnType<Select, Insert, Where> lets a column read as one type while accepting a broader or different type for inserts and predicates.

import type { ColumnType } from "@oorestisime/quarry";

type MetricText = ColumnType<string, number, number>;

interface DB {
  metrics: {
    display_value: MetricText;
  };
}

That means:

  • selected rows expose display_value as string
  • inserts accept number
  • where("display_value", "=", ...) accepts number

ClickHouse aliases

Quarry exports a small set of ClickHouse-specific aliases built on top of ColumnType.

Date and time

  • ClickHouseDate: reads as string, inserts as string
  • ClickHouseDate32: reads as string, inserts as string
  • ClickHouseDateTime: reads as string, inserts as string | Date
  • ClickHouseDateTime64: reads as string, inserts as string | Date

Large numeric types

  • ClickHouseUInt64: reads as string, inserts as string | number | bigint
  • ClickHouseInt64: reads as string, inserts as string | number | bigint
  • ClickHouseDecimal: reads as number, inserts as number | string

These aliases match Quarry's current runtime semantics under JSONEachRow and the ClickHouse JS client.

Generated output

The CLI introspection flow uses these helpers automatically. When ClickHouse metadata is broader than your application type, such as a JSON payload column that should be UserPayload, configure column type overrides in the introspection guide.

import type {
  ClickHouseDate,
  ClickHouseDateTime64,
  ClickHouseUInt64,
  TypedTable,
  TypedView,
} from "@oorestisime/quarry";

export interface Users {
  id: number;
  created_at: ClickHouseDateTime64;
}

export interface DailyUsers {
  signup_date: ClickHouseDate;
  total_users: ClickHouseUInt64;
}

export interface Tables {
  users: TypedTable<Users>;
}

export interface Views {
  daily_users: TypedView<DailyUsers>;
}

export interface DB extends Tables, Views {}

On this page