dbt Core
dbt migration support is in private alpha. Contact us to request preview access.
If your transformations live in dbt Core today, you can bring them to Bauplan with only a few minor changes.
Bauplan does not run dbt projects natively, but the gap is small as they are built on the same primitives--SQL models chained into a DAG, with contracts and tests on their outputs.
Converting a project is mostly mechanical, and we provide agent skills that do it for you. You can then build, test, and publish the result with a single bauplan run command on an isolated data branch.
This page shows what a dbt project looks like after the conversion, what changes, and how to migrate yours.
Side by side
The example below has two models: titanic_passengers selects from a raw table, and survival_by_class reads from titanic_passengers.
- dbt
- Bauplan
select
passengerid as passenger_id,
pclass as passenger_class,
survived
from raw.titanic
select
passenger_class,
count(*) as passenger_count,
avg(cast(survived as float)) as survival_rate
from {{ ref('titanic_passengers') }}
group by passenger_class
models:
- name: titanic_passengers
columns:
- name: passenger_id
data_tests:
- unique
- name: passenger_class
data_tests:
- not_null
- accepted_values:
values: [1, 2, 3]
dbt run
dbt test
-- bauplan: output_schema=PassengerSchema
select
passengerid as passenger_id,
pclass as passenger_class,
survived
from raw.titanic
-- bauplan: output_schema=SurvivalSchema
select
passenger_class,
count(*) as passenger_count,
avg(cast(survived as float)) as survival_rate
from titanic_passengers
group by passenger_class
from typing import Annotated
import bauplan
from bauplan import TableField
class PassengerSchema(bauplan.TableSchema):
"""One row per Titanic passenger."""
passenger_id: bauplan.Int64
# No `| None`: the column is required and may not contain nulls
passenger_class: Annotated[
bauplan.Int64,
TableField(doc="Class of the Titanic passenger"),
]
survived: bauplan.Int64 | None
class SurvivalSchema(bauplan.TableSchema):
"""Survival rate aggregated by passenger class."""
passenger_class: bauplan.Int64
passenger_count: bauplan.Int64
survival_rate: bauplan.Float64 | None
from typing import Annotated
import pyarrow
import bauplan
from bauplan.standard_expectations import (
expect_column_accepted_values,
expect_column_all_unique,
)
class PassengerKeys(bauplan.TableSchema):
"""Columns checked by the passenger expectations."""
passenger_id: bauplan.Int64
passenger_class: bauplan.Int64
@bauplan.expectation()
@bauplan.python("3.11")
def test_titanic_passengers(
data: Annotated[
pyarrow.Table,
bauplan.Model("titanic_passengers", projection_schema=PassengerKeys),
],
) -> bool:
"""Passenger ids are unique and passenger classes are 1, 2 or 3."""
return expect_column_all_unique(data, "passenger_id") and expect_column_accepted_values(
data, "passenger_class", accepted_values=[1, 2, 3]
)
bauplan run
What changes
| dbt | Bauplan | |
|---|---|---|
| Model code | SQL with Jinja ref() macros | Plain SQL, upstream models referenced by name |
| Contracts and tests | YAML, run by dbt test as separate queries | Typed Python schemas and expectations, checked by bauplan run |
| Failure handling | Tests run after data is materialized | A failing run stays on its data branch and never reaches main |