Add a Warehouse Adapter
Step-by-step checklist for adding a new warehouse connection to Lightdash. Follow the Athena adapter as the canonical example (PRs #19751/#19752). The MotherDuck/DuckDB adapter is the most recent implementation.
Layer 1: Common Types (packages/common/)
- Add entry to
WarehouseTypesenum insrc/types/projects.ts - Define
CreateXxxCredentialstype withtype: WarehouseTypes.XXXdiscriminant- Sensitive fields (tokens, passwords, keys) should be optional (
token?: string) — not required with empty-string sentinel
- Sensitive fields (tokens, passwords, keys) should be optional (
- Define
XxxCredentials = Omit<CreateXxxCredentials, SensitiveCredentialsFieldNames> - Add sensitive fields to
sensitiveCredentialsFieldNamesarray if not already present (tokens, passwords, keys) - Add
CreateXxxCredentialstoCreateWarehouseCredentialsunion - Add
XxxCredentialstoWarehouseCredentialsunion - Update
UserWarehouseCredentialsunion insrc/types/userWarehouseCredentials.ts - Update
UserWarehouseCredentialsWithSecretsunion - Add case to
getFieldQuoteChar()insrc/utils/warehouse.ts - Verify
getAggregatedField()insrc/utils/warehouse.tshandles the new adapter (may fall into existing case) - Check
src/utils/timeFrames.tsadapter config map has the new adapter - Check
src/compiler/translator.tsconvertTimezone()has the new adapter - Run:
pnpm -F common typecheck && pnpm -F common lint
Layer 2: Backend (packages/backend/)
Database Migration
- Create migration:
pnpm -F backend create-migration add_xxx_warehouse_type -
up: Insert{ warehouse_type: 'xxx' }intowarehouse_types -
down: Delete xxx credentials and type
Entity & Profile
- Add
'xxx'towarehouseTypesarray insrc/database/entities/warehouseCredentials.ts - Add case to
credentialsTarget()insrc/dbt/profiles.ts(beforedefault)- Pass secrets via
envVarReference()/envVar()pattern (not raw env var names or inline values) - Follow ClickHouse pattern:
password: envVarReference('password')in target,[envVar('password')]: credentials.passwordin environment
- Pass secrets via
- Verify
quoteCharsinsrc/dbt/DbtMetadataApiClient.tshas the adapter
Service Updates (exhaustive switch statements)
-
src/services/ProjectService/ProjectService.ts-clearSecretsFromCredentials()- Blank sensitive fields (
token: undefined) instead of destructuring + unsafeascast - Follow the same pattern as other warehouses (
{ ...credentials, password: '' })
- Blank sensitive fields (
-
src/services/ProjectService/ProjectService.ts- warehouse client creation switch -
src/services/ProjectService/ProjectService.ts- user credentials creation switch -
src/services/ProjectService/ProjectService.ts-getDatabaseFromWarehouseCredentials() -
src/models/UserWarehouseCredentials/UserWarehouseCredentialsModel.ts - Run:
pnpm -F backend typecheck && pnpm -F backend lint
Layer 3: Warehouse Client (packages/warehouses/)
- Create
src/warehouseClients/XxxWarehouseClient.ts- Extend
WarehouseBaseClient<CreateXxxCredentials> - Implement
XxxSqlBuilder extends WarehouseBaseSqlBuilder - Implement:
streamQuery(),getCatalog(),getAllTables(),getFields()(inheritedtest(),runQuery(),executeAsyncQuery()work via base class — don't add no-op overrides) - Pass credentials directly to client library (like ClickHouse/Databricks), never via
process.env - Use constructor
overridesparam for alternate construction (e.g. pre-aggregate) instead ofAnyTypecasts on readonly fields
- Extend
- Add case in
src/warehouseClientFromCredentials.tsfactory - Add case in
src/ssh/sshTunnel.ts(typically a no-opbreakfor cloud warehouses) - Export from
src/index.ts - Write tests in
src/warehouseClients/XxxWarehouseClient.test.ts - Run:
pnpm -F @lightdash/warehouses typecheck && pnpm -F @lightdash/warehouses test
Layer 4: Frontend (packages/frontend/)
Warehouse Form
- Create
src/components/ProjectConnection/WarehouseForms/XxxForm.tsx- Follow
AthenaForm.tsxpattern with Mantine v8 components - Export
XxxSchemaInputforDbtSettingsForm
- Follow
- Add SVG logo to
src/components/ProjectConnection/ProjectConnectFlow/Assets/
Registration
- Add defaults to
WarehouseForms/defaultValues.ts(XxxDefaultValues+ add towarehouseDefaultValues) - Add validators to
WarehouseForms/validators.ts - Register in
WarehouseSettingsForm.tsx(labels + forms maps) - Register in
DbtSettingsForm.tsx(schema input switch) - Register in
ProjectConnectFlow/utils.tsx(WarehouseTypeLabels array) - Register in
UserSettings/MyWarehouseConnectionsPanel/CreateCredentialsModal.tsx(defaultCredentials) - Register in
UserSettings/MyWarehouseConnectionsPanel/EditCredentialsModal.tsx(getCredentialsWithPlaceholders) - Register in
UserSettings/MyWarehouseConnectionsPanel/WarehouseFormInputs.tsx - Run:
pnpm -F frontend typecheck && pnpm -F frontend lint
Layer 5: CLI (packages/cli/)
- Create
src/dbt/targets/xxx.ts- Define
XxxTargettype matching dbt profile structure - Define
xxxSchema: JSONSchemaType<XxxTarget>for ajv validation - Export
convertXxxSchema()function
- Define
- Add
case 'xxx':insrc/dbt/profile.tswarehouseCredentialsFromDbtTarget() - Add mock credentials in
src/handlers/dbt/getWarehouseClient.tsgetMockCredentials()
Layer 6: Docker (Dockerfile)
- Add the dbt adapter pip package (
dbt-xxx) to dbt venvs 1.8+ in theDockerfile- The adapter package name is typically
dbt-xxx(e.g.,dbt-duckdb,dbt-athena) - Use unversioned
"dbt-xxx"if the adapter has its own version scheme, or"dbt-xxx~=X.Y.0"if it tracks dbt-core versions - Check PyPI for the package name and version compatibility
- Only add to dbt versions that support the adapter (1.8+, not older 1.4-1.7)
- Reference:
dbt-athenawas added from 1.9+,dbt-duckdbfrom 1.8+
- The adapter package name is typically
Layer 7: Demo Project Compatibility (examples/full-jaffle-shop-demo/)
- Update
dbt/data/seeds.ymlcolumn types for the new adapter (DuckDB doesn't supportjsonb,TIME, etc.) - Update SQL macros in
dbt/macros/that have warehouse-specific branches (e.g.quarter_end_date.sql,casts.sql) - Add a profile target in
profiles/profiles.ymlfor testing - Test:
dbt seed --target xxx && dbt run --target xxx
Post-Implementation
-
pnpm generate-api(regenerates routes.ts and swagger.json) -
pnpm generate:chart-as-code-schema -
pnpm check:chart-as-code-schema - Run full typecheck:
pnpm -F common typecheck && pnpm -F backend typecheck && pnpm -F frontend typecheck
Reference Files
| Layer | Key File | Purpose |
|---|---|---|
| Common | packages/common/src/types/projects.ts |
Types, sensitive fields, unions |
| Backend | packages/backend/src/dbt/profiles.ts |
dbt profile generation |
| Backend | packages/backend/src/database/entities/warehouseCredentials.ts |
DB entity |
| Warehouse | packages/warehouses/src/warehouseClientFromCredentials.ts |
Factory |
| Frontend | packages/frontend/src/components/ProjectConnection/WarehouseSettingsForm.tsx |
Form registration |
| CLI | packages/cli/src/dbt/profile.ts |
CLI target conversion |
| Docker | Dockerfile |
dbt adapter pip packages per version |
Canonical Examples
- Athena: PRs #19751/#19752 (first adapter added with this pattern)
- MotherDuck/DuckDB: Branch
02-23-feat_add_duckdb_warehouse_client_with_s3_support