Snowflake client with multiple authentication methods and native async queries.
import { SnowflakeWarehouseClient } from '@lightdash/warehouses';
const client = new SnowflakeWarehouseClient({
type: 'snowflake',
account: 'my-account',
user: 'myuser',
password: 'mypassword',
database: 'ANALYTICS',
warehouse: 'COMPUTE_WH',
schema: 'PUBLIC',
});
await client.test();{ type: 'snowflake', user: 'myuser', password: 'mypassword', ... }{
type: 'snowflake',
user: 'myuser',
privateKey: '-----BEGIN PRIVATE KEY-----\n...',
privateKeyPass: 'passphrase', // Optional
authenticationType: 'private_key',
...
}{
type: 'snowflake',
user: 'myuser',
token: 'short-lived-access-token',
refreshToken: 'refresh-token',
authenticationType: 'sso',
...
}{
type: 'snowflake',
user: 'myuser',
authenticationType: 'external_browser',
...
}await client.streamQuery(
'SELECT DATE_TRUNC(\'week\', order_date) as week, SUM(amount) FROM orders GROUP BY week',
(data) => { /* process rows */ },
{ timezone: 'America/Los_Angeles', tags: {} }
);
// Sets TIMEZONE and WEEK_START session parameters// Snowflake has native pagination support (unlike other warehouses)
// Returns: Promise<WarehouseExecuteAsyncQuery>
const result = await client.executeAsyncQuery(
{
sql: 'SELECT * FROM large_table',
tags: {},
},
(rows, fields) => {
// Optional callback: results stream immediately if provided
console.log(`Received ${rows.length} rows`);
}
);
// Retrieve paginated results (Snowflake-only feature)
// Returns: Promise<WarehouseGetAsyncQueryResults>
const page1 = await client.getAsyncQueryResults({
sql: 'SELECT * FROM large_table',
queryId: result.queryId, // Required: from executeAsyncQuery
queryMetadata: result.queryMetadata, // Required: from executeAsyncQuery
page: 1, // 1-indexed page number
pageSize: 1000, // Rows per page
});
console.log(`Page 1: ${page1.rows.length} of ${page1.totalRows} rows`);
console.log(`Total pages: ${page1.pageCount}`);Edge Cases:
getAsyncQueryResults only works with Snowflake (other warehouses throw NotImplementedError)queryId and queryMetadata from executeAsyncQuery to retrieve pagesimport { SnowflakeSqlBuilder } from '@lightdash/warehouses';
const builder = new SnowflakeSqlBuilder();
builder.getFieldQuoteChar(); // '"'
builder.escapeString("Winter's Sale"); // "Winter''s Sale"Percentile SQL: Uses PERCENTILE_CONT (exact, slower than BigQuery's approx)
import { MetricType } from '@lightdash/common';
// Median (50th percentile)
const medianSql = builder.getMetricSql('revenue', {
type: MetricType.MEDIAN,
sql: 'revenue'
});
// Returns: "PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY revenue)"
// 99th percentile
const p99Sql = builder.getMetricSql('latency', {
type: MetricType.PERCENTILE,
sql: 'latency',
percentile: 99
});
// Returns: "PERCENTILE_CONT(0.99) WITHIN GROUP (ORDER BY latency)"Note: PERCENTILE_CONT provides exact results (unlike BigQuery's approximate). For large datasets, consider APPROX_PERCENTILE in custom SQL.
import { SnowflakeTypes } from '@lightdash/warehouses';
enum SnowflakeTypes {
NUMBER, DECIMAL, NUMERIC, INTEGER, INT, BIGINT, SMALLINT,
FLOAT, DOUBLE, REAL, STRING, TEXT, BOOLEAN,
DATE, DATETIME, TIME, TIMESTAMP, TIMESTAMP_LTZ, TIMESTAMP_NTZ, TIMESTAMP_TZ,
VARIANT, OBJECT, ARRAY, GEOGRAPHY,
}import { mapFieldType } from '@lightdash/warehouses';
mapFieldType('NUMBER'); // DimensionType.NUMBER
mapFieldType('TIMESTAMP_TZ'); // DimensionType.TIMESTAMP
mapFieldType('VARCHAR'); // DimensionType.STRINGCommon error codes:
Custom error messages via environment variable:
export SNOWFLAKE_UNAUTHORIZED_ERROR_MESSAGE="Access denied to {snowflakeSchema}.{snowflakeTable}"Forced Settings (always applied):
QUOTED_IDENTIFIERS_IGNORE_CASE = FALSEOptional Settings:
TIMEZONE: Set via query optionsWEEK_START: From credentials startOfWeek{
role: 'ANALYST', // Session role
clientSessionKeepAlive: true, // Maintain session between queries
queryTag: 'lightdash', // Query tracking tag
quotedIdentifiersIgnoreCase: false, // Case sensitivity
disableTimestampConversion: false, // Disable UTC conversion
}my-account or my-account.us-east-1