TypeScript SDKDatasets

Dimensions

Define typed fields for selecting, grouping, and filtering dataset queries.

Dimensions are the non-aggregated fields in a dataset. They are the fields callers can select, group by, filter on, and order by.

The object key is the semantic API name. The dimension helper tells hypequery the field type, and the options tell hypequery how that semantic field maps to ClickHouse.

dimensions: {
  id: dimension.string(),
  status: dimension.string({ label: 'Status' }),
  isTrial: dimension.boolean({ column: 'is_trial' }),
  createdAt: dimension.timestamp({ column: 'created_at' }),
}

With that definition, callers use the semantic names:

await analytics.execute(Orders, {
  dimensions: ['status', 'createdAt'],
  measures: ['revenue'],
});

The generated SQL still uses the mapped ClickHouse columns, such as created_at.

Dimension helpers

  • dimension.string(opts?)
  • dimension.number(opts?)
  • dimension.boolean(opts?)
  • dimension.timestamp(opts?)

The helper should match the value type of the field. That type is used for query validation, generated contracts, and integration surfaces.

These are semantic dimension types, not a full mirror of every ClickHouse physical type. Use the closest analytical type:

ClickHouse typesDimension type
String, LowCardinality(String), Enum, UUIDdimension.string()
UInt64, Int32, Float64, Decimaldimension.number()
Bool or boolean-style UInt8 flagsdimension.boolean()
Date, DateTime, DateTime64dimension.timestamp()

The query builder can still use precise generated ClickHouse schema types. Datasets expose a smaller semantic model for analytics, validation, docs, and integrations.

Dimension values in result rows

Dimension values come back as ClickHouse returns them, so result row types are wider than the helper type:

Dimension typeResult row value
dimension.string()string | null
dimension.number()number | string | null
dimension.boolean()boolean | null
dimension.timestamp()string | null

Numeric dimensions include string because the built-in adapter returns Int64, UInt64 and wider integers as JSON strings to keep their precision. Every dimension includes null because Nullable columns return SQL NULL. The period column of a grained query is string | null for the same reason. Convert with Number(value) or BigInt(value) where you need a number. The DimensionResultValue<typeof dimension> type describes these values.

Options

Every dimension helper accepts the same options object:

type DimensionOptions = {
  label?: string;
  description?: string;
  column?: string;
  sql?: string;
  filterable?: boolean;
  groupable?: boolean;
};
OptionUse it for
columnMapping a semantic field name to a physical ClickHouse column
sqlBacking a dimension with a SQL expression instead of a simple column
labelHuman-readable display text for docs, UIs, and agents
descriptionLonger metadata for generated docs, UIs, and agents
filterableWhether callers should be allowed to filter on the dimension. Defaults to true
groupableWhether callers should be allowed to group/select the dimension. Defaults to true in the semantic model

Column mapping

Use opts.column when the semantic field name differs from the physical column name.

dimensions: {
  userId: dimension.string({ column: 'user_id' }),
  isActive: dimension.boolean({ column: 'is_active' }),
  createdAt: dimension.timestamp({ column: 'created_at' }),
}

This keeps your public dataset API idiomatic while still targeting the real table columns:

await analytics.execute(Users, {
  dimensions: ['userId', 'createdAt'],
});

Labels and descriptions

Use label and description for human-readable metadata. These do not change SQL generation, but they are exposed through generated docs and MCP schema introspection so agents and UIs can understand what a field means.

dimensions: {
  status: dimension.string({
    label: 'Order Status',
    description: 'Current lifecycle state for the order.',
  }),
}

For MCP, this metadata becomes part of get_dataset_schema, which helps agents choose the right fields without guessing from column names alone.

SQL-backed dimensions

Use sql when the dimension is computed rather than stored as a single column.

dimensions: {
  countryCode: dimension.string({ column: 'country_code' }),
  countryUpper: dimension.string({ sql: 'upper(country_code)' }),
}

Use SQL-backed dimensions sparingly. Schema compatibility checks can inspect simple column references, but complex SQL expressions can only produce warnings because they cannot be fully verified from the table schema.

Trusted model code

The sql option is authored in your dataset definition. Runtime callers should only reference the semantic dimension name, not provide SQL. See Trust Boundaries.

Filterable and groupable

Use filterable and groupable when a field exists in the dataset contract but should not be available for every query operation.

dimensions: {
  email: dimension.string({
    filterable: true,
    groupable: false,
    description: 'Allowed in filters, but not useful as a grouping key.',
  }),
}

Most dimensions can omit these options. By default, dimensions are available for filtering and grouping. Set filterable: false to remove a dimension from automatic filtering. Declared filters can still expose that dimension, so remove those filters too if callers must not filter on it. Declaring filters adds to the filterable dimensions; it does not replace them.

groupable records the intended grouping behavior on the dimension definition. Use it to communicate stricter query intent to generated docs, UIs, or integration layers.

On this page

Get early access to hypequery cloud.

Cloud hosts the same model you run in your backend, with a managed API and MCP server, versions, and rollback. We're letting teams in a few at a time. Join the waitlist and we'll reach out when your project can go live.

How do you want to build your analytics layer? (Optional)
What features do you most need? (Optional)

Pick as many as you like.

Only your email is required. All other questions are optional.