dbt Integration
dfmt has a native Jinja evaluator and dbt compiler -- it does not shell out to dbt compile. This makes it fast and dependency-free.
How It Works
When dfmt detects a dbt project (by finding dbt_project.yml), it:
- Loads the project: reads
dbt_project.yml, discovers models and macros, resolves packages - For each model template:
- Parses Jinja into a template AST
- Evaluates the template with a dbt-compatible context (
ref(),source(),config(),var(), macros) - Parses the compiled SQL into a full SQL AST
- Restores Jinja constructs by matching source positions --
{{ ref('users') }}appears in the output exactly as written - Formats the SQL while preserving all Jinja
What Gets Preserved
All Jinja constructs are preserved verbatim in the formatted output:
| Construct | Example | Preserved? |
|---|---|---|
| Expressions | {{ ref('model') }} | Yes |
| Filters | {{ col | upper }} | Yes |
| Config blocks | {{ config(materialized='table') }} | Yes |
| If blocks | {% if is_incremental() %}...{% endif %} | Yes |
| For loops | {% for col in columns %}...{% endfor %} | Yes |
| Set blocks | {% set query %}...{% endset %} | Yes |
| Macro calls | {{ dbt_utils.star(ref('model')) }} | Yes |
| Cross-token | {{ schema }}.{{ table }} | Yes |
dbt Features Supported
ref()andsource()resolutionconfig()with all standard parametersvar()with defaultsis_incremental()detection- Custom macros from project and packages
- Adapter-specific macros (dispatch)
{% materialization %}blocks- Package macros (dbt_utils, dbt_expectations, etc. -- requires
dbt deps)
Configuration
.dfmt.json in a dbt project
Place .dfmt.json at your dbt project root (next to dbt_project.yml):
{
"dialect": "snowflake",
"keywordCase": "lower",
"maxLineWidth": 100,
"include": ["models/**/*.sql"],
"exclude": ["models/staging/legacy/**"]
}
The dialect can be omitted -- dfmt infers it from the dbt adapter type in profiles.yml or dbt_project.yml.
Formatting a single model
dfmt --model dim_customers --write
Formatting only changed models
dfmt --changed-since main --write
Skipping dbt Detection
If you want to format .sql files in a dbt project as plain SQL (ignoring Jinja):
dfmt --no-jinja --write
Package Dependencies
If your dbt project uses packages (packages.yml), run dbt deps first so the package macros are available. dfmt reads macros from the dbt_packages/ directory.
Limitations
Some Jinja patterns are not fully supported:
- Runtime-dependent loops:
{% for %}loops where the iteration count depends on runtime data may not format optimally - Complex nested conditionals: Deeply nested
{% if %}blocks (3+ levels) may fall back to verbatim output - Custom materializations: Supported but may produce warnings
- env_var(): References to environment variables use dummy values during formatting (the formatted SQL is the same regardless)
When dfmt encounters a pattern it can't handle, it returns the template verbatim (unmodified) and reports a warning. It never produces incorrect output.