Sources and Freshness

Sources and the Source Freshness Check

Sources declare the raw tables dbt 37,942 reads but does not build. Models use {{ source('shop', 'orders') }} instead of a hard-coded name, so lineage starts at the real inputs and one YAML entry can move them. The Jinja 15,439 expressions pick DuckDB 61,228 's attached database or PostgreSQL 1,289 's public schema from the active target:

models/staging/_sources.yml: BookNest's shop tables and a freshness ruleYAML
version: 2
sources:
  - name: shop
    description: BookNest's order database (generated sample data, Section 4.1).
    database: "{{ 'src' if target.type == 'duckdb' else target.database }}"
    schema: "{{ 'main' if target.type == 'duckdb' else 'public' }}"
    tables:
      - name: books
      - name: customers
      - name: order_items
      - name: orders
        config:
          loaded_at_field: order_ts
          freshness:
            warn_after: {count: 1, period: day}
            error_after: {count: 7, period: day}

A freshness rule turns "is the loader still running?" into a check that compares max(order_ts) with now:

Checking how recent the newest order is
dbt source freshness
Output
17:22:00  1 of 1 ERROR STALE freshness of shop.orders ............ [ERROR STALE in 0.12s]
17:22:01  [ERROR]: in source orders (models/staging/_sources.yml)

The check is right: the sample orders end on 30 June 2026, far past the seven-day limit. In a pipeline this stops the run before stale data is reported as current. A business timestamp such as order_ts also goes stale when the shop has no orders; an ingestion timestamp measures only the loader.