dbt Exposures

Exposures are YAML declarations that tell dbt about the downstream consumers of your data — dashboards, reports, ML models, applications, and reverse ETL jobs. Defining exposures lets your team see in the lineage graph exactly what depends on each model, who owns each consumer, and what breaks when a model changes.

Why Exposures Exist

Without exposures:
  dbt lineage graph ends at your mart models.
  Nobody knows which Tableau dashboard uses fct_orders.
  You refactor fct_orders, rename a column.
  Three dashboards break. You find out when the VP complains.

With exposures:
  The lineage graph shows: fct_orders → Revenue Dashboard (owned by Finance)
  Before you rename a column, you see which dashboards are affected.
  You notify the Finance team and update the dashboard together.

Defining an Exposure

Create a YAML file (often named exposures.yml) in your models/ folder:

# models/exposures.yml
version: 2

exposures:
  - name: revenue_dashboard
    label: "Revenue Dashboard"
    type: dashboard
    maturity: high
    url: https://tableau.company.com/views/RevenueDashboard
    description: >
      The main revenue dashboard used by the Finance and Sales teams.
      Updated daily at 7 AM. Used in the Monday morning executive meeting.
    depends_on:
      - ref('fct_orders')
      - ref('dim_customers')
      - ref('fct_revenue_monthly')
    owner:
      name: Finance Data Team
      email: finance-data@company.com

Exposure Types

Type         Used For
---------    --------
dashboard    Tableau, Looker, Power BI, Metabase dashboards
notebook     Jupyter, Hex, Observable notebooks
analysis     Ad-hoc analysis or one-off reports
ml model     Machine learning models trained on dbt outputs
application  Customer-facing apps or internal tools
source       (rare) another data source consuming your models

Maturity Levels

Maturity    Meaning
--------    -------
low         Experimental, not widely used, may change
medium      Stable but not mission-critical
high        Mission-critical, used in executive reporting

Multiple Exposures in One File

version: 2

exposures:
  - name: revenue_dashboard
    type: dashboard
    maturity: high
    depends_on:
      - ref('fct_orders')
      - ref('dim_customers')
    owner:
      name: Finance Team
      email: finance@company.com

  - name: churn_prediction_model
    type: ml model
    maturity: medium
    url: https://mlflow.company.com/experiments/42
    description: "Weekly churn prediction model trained on customer behavior data."
    depends_on:
      - ref('fct_sessions')
      - ref('dim_customers')
      - ref('fct_orders')
    owner:
      name: Data Science Team
      email: datascience@company.com

  - name: customer_portal
    type: application
    maturity: high
    description: "Customer-facing portal showing order history."
    depends_on:
      - ref('fct_orders')
      - ref('dim_customers')
    owner:
      name: Engineering Team
      email: eng@company.com

Exposures in the Lineage Graph

Exposures appear in the dbt docs lineage graph as orange nodes at the far right of the DAG, downstream of your mart models:

[source: shopify.orders]
        |
        v
[stg_orders]
        |
        v
[fct_orders] ──────────────────── [Revenue Dashboard] (orange node)
        |                                  owner: Finance Team
        v
[fct_revenue_monthly] ─────────── [Executive Report]  (orange node)

Click an exposure node in the docs site to see its full description, URL, owner, and all the models it depends on.

Selecting by Exposure

Run all models that feed a specific exposure:

# Run all models upstream of the revenue dashboard
dbt run --select +exposure:revenue_dashboard

# Test all models that the churn model depends on
dbt test --select +exposure:churn_prediction_model

# List all nodes used by all exposures
dbt ls --select +exposure:*

This selection is particularly useful before a release: run all models that feed production dashboards and ensure their tests pass before deploying.

Source References in Exposures

Exposures can also reference source tables directly if a consumer reads raw data:

depends_on:
  - ref('fct_orders')
  - source('ecommerce', 'raw_products')   ← references a source directly

Best Practices

  • Define an exposure for every dashboard or report used in leadership meetings
  • Set maturity: high for anything used in financial reporting or executive decisions
  • Always include an owner email so there is a clear point of contact when something breaks
  • Add the URL to the dashboard so teammates can click directly from the docs site
  • Run dbt run --select +exposure:* in your CI job to validate all models feeding exposures

Leave a Comment

Your email address will not be published. Required fields are marked *