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_onat 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:
- Branch and adjust SQL in
fct_orders. dbt run -m exposure:revenue_dashboardto build all upstream nodes feeding the exposure.dbt test -m exposure:revenue_dashboardto validate tests along the path.- Share the exposure
urlwith owners for UAT in the bi tool. - Merge when owners sign off; add a note in the exposure
descriptionif 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.
Implementation tips and related resources
- Keep your domain config tidy; see dbt project structure best practices for file placement—then link exposures nearby.
- If your metrics live above models, read the dbt Semantic Layer guide to place exposures and metrics cleanly.
- For historical dimensions that feed dashboards, skim our short SCD summary and deep dive here: Slowly Changing Dimension primer.
- Explore lineage and snapshots powering dashboards: dbt snapshots (dbt developer hub).
A few gotchas to avoid
- Don’t set
depends_onto 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.
Take this concept into practice.
Reading builds context. Practice and a complete project turn the concept into a skill you can use at work.
- dbt
dbt Project Structure: Staging, Intermediate, and Marts Done Right
A proven dbt project structure you can copy today: clear folders, schema mapping in dbt_project.yml, and tight naming conventions. Includes SQL, YAML, and real-world tips.
- dbt
dbt ref() vs source(): Model-to-Table References Explained
Use ref() for dbt-built models and source() for external raw tables you declare in YAML. This guide shows exact SQL/YAML, selectors, and pitfalls so your builds run in the right order.
- dbt
dbt Semantic Layer for Analytics Engineers: MetricFlow & dbt Labs
A deep, practitioner-first guide to the dbt semantic layer: define metrics once, serve them everywhere. We cover MetricFlow, security, integrations, and real query patterns.
