Skip to content

dbt Exposures: Exposure Lineage, Selectors — dbt Developer Hub

dbt exposures connect dashboards, notebooks, and apps to the dbt DAG. Learn how to define, automate, and use them for safe changes and clear lineage.

dbt exposures document the downstream dashboards, ML notebooks, and apps that rely on your transformed tables. You declare each exposure in YAML, point it at the dbt models it depends_on, give it a url, owner, and description, and it becomes a first-class node in the DAG. The immediate payoff: clear lineage in dbt docs, faster impact analysis, targeted dbt run/dbt test with selectors, and better visibility for data consumers. Below you’ll see concrete patterns for defining exposures, practical examples (Looker/Tableau), automation ideas, and answers to the questions engineers ask most.

What is an exposure in dbt?

An exposure is a lightweight record that tells dbt, “this downstream asset is powered by these upstream resources.” Exposures show up as nodes in the DAG and on the docs site. They’re ideal for any downstream use of your dbt models: a BI dashboard, a forecasting notebook in data science, an internal service endpoint, or a stakeholder report. In short, exposures make downstream use explicit and auditable.

# models/marts/finance/_exposures.yml
version: 2

exposures:
  - name: revenue_dashboard
    type: dashboard
    url: https://bi.company.com/dashboards/revenue
    maturity: high
    owner:
      name: Finance Analytics
      email: finance-analytics@company.com
    description: >-
      Executive revenue dashboard with weekly and daily breakdowns.
    depends_on:
      - ref('fct_orders')
      - ref('dim_customers')
    tags: ['finance', 'kpi', 'executive']

Notes:

  • type accepts dashboard, notebook, analysis, or application.
  • url points to the asset (bi tool link, doc, or internal app).
  • depends_on uses ref()/source() to capture the true dependency graph.
  • Config lives alongside your dbt project, versioned like code.

Once committed, exposures will appear in your documentation site when you generate dbt docs. In dbt Cloud or any hosted docs, you’ll see the exposure node, its upstream lineage, owners, and description.

Why exposures matter (and when they pay off immediately)

Imagine your orders model serves a revenue dashboard and has 40M rows. You need to change a join to order_items. With an exposure that targets the dashboard, you can do targeted runs/tests and tight impact checks before a deploy:

# List what would be affected
$ dbt ls -s exposure:revenue_dashboard

# Build all upstream nodes required for the exposure
$ dbt run -m exposure:revenue_dashboard

# Run tests that touch the exposure's upstream graph
$ dbt test -m exposure:revenue_dashboard

That one exposure gives you a safe path to validate the change without rebuilding the entire DAG. It also tells on-call engineers exactly which downstream assets could be at risk if an upstream change goes sideways.

Exposures in dbt: patterns that scale

For most teams, defining exposures at the mart layer is the sweet spot. Let the DAG carry transitive dependencies; you don’t need to list every staging model. A clear exposure per dashboard (or per major section) usually maps best to how stakeholders think.

# models/marts/marketing/_exposures.yml
version: 2

exposures:
  - name: looker_customer_ltv
    type: dashboard
    url: https://looker.company.com/dashboards/123
    owner:
      name: Growth BI
      email: growth-bi@company.com
    depends_on:
      - ref('fct_customer_ltv')

  - name: tableau_campaign_roas
    type: dashboard
    url: https://tableau.company.com/views/ROAS/Overview
    owner:
      name: Marketing Ops
      email: mops@company.com
    depends_on:
      - ref('fct_campaign_performance')

Those two lines of YAML per exposure buy you searchable documentation, a stable selector for CI, and accountable ownership details. Exposures in the dbt docs also help new teammates understand how dbt models are used downstream during onboarding.

Automating exposures

You can create exposures manually, but for larger orgs, automation keeps the list fresh. Two common approaches:

  • BI API sync. Pull dashboard metadata from Looker/Tableau APIs (titles, ids, owners, URLs), map them to mart models, and emit YAML. Commit via a bot PR.
  • Code generation from lineage. If you maintain a mapping of model-to-dataset-to-dashboard, generate exposure blocks nightly to capture new downstream assets.
# pseudo-script outline (Python)
bi_dashboards = fetch_bi_dashboards()  # includes url, owner, title
model_map = read_model_to_dataset_map()

exposures = []
for d in bi_dashboards:
    depends = infer_depends_on(d, model_map)  # returns ["ref('fct_...')"]
    exposures.append({
        'name': slugify(d['title']),
        'type': 'dashboard',
        'url': d['url'],
        'owner': {'name': d['owner_name'], 'email': d['owner_email']},
        'depends_on': depends
    })

write_yaml('models/_exposures.generated.yml', {'version': 2, 'exposures': exposures})

Start small: automate a single domain (e.g., Finance) and expand once you trust the mapping. If you’re using dbt Cloud, hosting the docs site alongside the repo makes this workflow smooth for your data team.

Selectors, CI, and partial builds

Selectors unlock precise builds around an exposure. Wire them into CI to prove safety before merging:

# Example GitHub Actions step
- name: Build the graph for exec revenue exposure
  run: |
    dbt deps
    dbt seed --select state:modified+  # optional
    dbt run -m exposure:revenue_dashboard
    dbt test -m exposure:revenue_dashboard

This optimizes the pipeline and keeps validation focused on what the dashboard actually queries. It’s also friendly for UAT: share the exposure url with owners, then rebuild just the upstream graph after fixes.

Best practices for exposures

  • Define one exposure per meaningful dashboard, ML notebook, or app page; avoid catch-all buckets—this is the core best practices pattern.
  • Point depends_on at marts; the DAG will capture upstream staging/intermediate lineage.
  • Always set url, owner name/email, and a one-sentence description for quick triage.
  • Use tags for domain routing (e.g., finance, marketing, ml).
  • Keep the exposure file close to the domain models it references.
  • Gate merges by running dbt test -m exposure:* in CI for high-maturity domains.
  • For semantic metrics, pair exposures with the dbt’s Semantic Layer but don’t duplicate ownership details.
  • Use ref() vs source() correctly so lineage is accurate.

Exposures vs. other documentation tools

Option Strength Weakness When to choose
Exposure (dbt) Tight DAG integration, selectors, owners Needs basic YAML upkeep or automation Link dashboards/apps to models and enable targeted runs/tests
BI folder docs Close to data consumers No build-time lineage to dbt models Stakeholder how-tos and visuals
Data catalog Broad enterprise metadata May not drive dbt selectors Cross-platform governance
Runbooks/tickets Operational context Not connected to DAG Procedures and on-call steps

Use exposures to wire the DAG to downstream assets; keep catalogs and BI docs for policy and UX details.

End-to-end example: shipping a change safely

Scenario: You’re updating fct_orders window logic; two executive dashboards depend on it. You’ve declared one exposure per dashboard and added owners. Before merging:

  1. Branch and adjust SQL in fct_orders.
  2. dbt run -m exposure:revenue_dashboard to build all upstream nodes feeding the exposure.
  3. dbt test -m exposure:revenue_dashboard to validate tests along the path.
  4. Share the exposure url with owners for UAT in the bi tool.
  5. Merge when owners sign off; add a note in the exposure description if helpful.

This keeps the pipeline focused on what matters and avoids rebuilding unrelated parts of the DAG.

FAQ: exposures and adjacent questions

How and why use exposures?

Define exposures to connect downstream assets to upstream models. Benefits: clear lineage, targeted selectors for dbt run/dbt test, owner accountability, and faster impact analysis. Put the YAML next to the domain models, and keep depends_on pointed at marts.

Is it testing the model or the exposure?

When you run dbt test -m exposure:name, dbt selects the upstream nodes referenced by the exposure and runs tests on those resources. You’re not testing the exposure itself; you’re testing the models and other nodes in its selected graph.

Q1 - Should I state the whole downstream of tables or just the direct tables that it depends on?

In an exposure, declare the direct dbt models your asset relies on (usually marts). Do not list every staging table; the DAG infers transitive lineage. This keeps the exposure minimal while preserving complete dependency context.

Q3 - What’s the purpose of dbt run -m exposure:name and dbt test -m exposure:name?

These selectors compile the exposure’s upstream graph so you can build and validate exactly what powers that asset—great for impact checks, UAT, and domain-scoped CI.

What are some common use cases for dbt?

  • Transform warehouse tables for analytics marts
  • Codify business logic for BI dashboards and reports
  • Prepare features for data science notebooks and jobs
  • Standardize and test data quality

What does dbt stand for?

dbt stands for Data Build Tool.

Is dbt better than Databricks?

They’re different categories: dbt focuses on SQL-first transformation and testing; Databricks is a Spark-based lakehouse platform. Many teams use them together. Pick based on your primary workloads and team skills.

Is dbt an ETL tool?

dbt is not an ETL tool; it runs the “T” in ELT inside your warehouse/lakehouse. Use ingestion tools for extract/load, then model data using dbt.

Ready to Crush the dbt Certified Developer Exam?

Review foundational topics on the dbt topic hub, practice selectors and DAG reasoning with exposures, and drill into refs/sources here: model-to-table references explained.

Want to learn more about how to leverage dbt Cloud’s features?

Host your docs site in dbt Cloud, wire exposures into CI, and standardize owners. For reference material, the official docs cover exposures well: dbt docs: exposures.

A few gotchas to avoid

  • Don’t set depends_on to raw warehouse tables; reference your modeled layer so the graph is accurate.
  • Don’t over-specify. One exposure per dashboard page or ML job is enough. Let the DAG expand upstream.
  • Don’t skip owners. When something breaks, the right inbox matters more than any tag.
  • Include a short documentation note for context if owners change or the dashboard moves urls.

Where exposures fit in the bigger picture

Exposures are the thin connective tissue from the dbt DAG to downstream assets. They don’t replace a data catalog or BI documentation, but they uniquely power selectors and connect changes to impact at build time. With a small amount of YAML, you get auditable lineage, safer deploys, and higher visibility into downstream use. That’s leverage for any data team using dbt.

Extended notes: defining exposures is covered in the dbt developer hub, and exposures in the dbt ecosystem are designed to be simple so they don’t slow you down. Add just enough metadata for owners, keep URLs fresh, and let automation help as your footprint grows. With this, the downstream use of your dbt becomes first-class, and your DAG tells the full story—from upstream sources to the last dashboard tile (bi).

Before you leave: try the free graded practice exercises at /practice.

Next steps

Take this concept into practice.

Reading builds context. Practice and a complete project turn the concept into a skill you can use at work.