uvx create-dagster@latest project my_proj # scaffold (GA)★GACreate a project withcreate-dagster(alsopip install create-dagster). It lays out adefs/folder (auto-loaded definitions) +pyproject.toml. The bare library ispip install dagster dagster-webserver.import dagster as dg @dg.asset def raw_users() -> list[dict]: return fetch_users()★An asset is a function that produces a persistent object (a table/file/model). The function name is the asset key; its return value is the data.@dg.asset(group_name="ingest", kinds={"python", "s3"}, description="raw user records")Organize the graph withgroup_name, tag technologies withkinds(icons in the UI), and document withdescription.dg dev # or: dagster dev — UI at http://127.0.0.1:3000★Launches the webserver + daemon (and schema-checks yourdefs.yamlfirst). Click an asset → Materialize to run it and see lineage, runs, and metadata.
@dg.asset def clean_users(raw_users): # arg name = upstream asset return [normalize(u) for u in raw_users]★Depend on another asset by naming it as a parameter — Dagster passes the upstream's loaded value and draws the edge automatically. This is the core idea.@dg.asset(deps=[raw_events]) # dependency without loading the value def report(): ...Usedeps=for an ordering dependency when you don't need the upstream's Python value (e.g. it wrote to a DB the SQL reads).@dg.multi_asset(specs=[dg.AssetSpec("a"), dg.AssetSpec("b")]) def both(): ...Produce several assets from one computation with@multi_asset.dg.AssetSpecalso declares external/source assets you don't compute.dg.load_assets_from_modules([my_pkg.assets])Auto-collect every asset in a module/package instead of listing them — or let thedefs/folder auto-load them (card 11).
return dg.MaterializeResult(metadata={ "rows": len(df), "preview": dg.MetadataValue.md(df.head().to_markdown())})★Attach run metadata (row counts, previews, plots, links) to a materialization — it shows on the asset in the UI over time.@dg.asset_check(asset=clean_users) def no_nulls(clean_users): return dg.AssetCheckResult(passed=all(u["id"] for u in clean_users))★Asset checks are data-quality tests bound to an asset (nulls, ranges, freshness, row counts) — they run with the asset and can block downstreams.@dg.asset_check(asset=events, partitions_def=daily) # per-partition★1.13Partitioned asset checks:@asset_checkandAssetCheckSpecnow takepartitions_def, so a check runs against a specific partition of its upstream rather than the whole asset.@dg.asset(check_specs=[dg.AssetCheckSpec("rowcount", asset="t")])Emit checks from inside the asset function too (yieldAssetCheckResults alongside the data).
@dg.asset(owners=["team:data", "jane@co.com"], tags={"tier":"gold"})★Owners & tags power the catalog: filter, search, and route alerts by team.kindsadds tech icons.@dg.asset(code_version="v2") # stale-data detection when the code changes★code_versionlets Dagster mark downstreams stale when the logic (not just data) changes — pairs with declarative automation.@dg.asset(retry_policy=dg.RetryPolicy(max_retries=3, delay=10), backfill_policy=dg.BackfillPolicy.single_run())Per-asset retries;backfill_policylets a partitioned asset backfill many partitions in one run instead of one-run-per-partition.@dg.asset(is_virtual=True) # a view / derived table — not materializedpreviewVirtual assets (preview) model things like DB views or derived tables that appear in the graph & lineage but don't need materializing. Also onAssetSpec(is_virtual=True). ⚠️ Preview API — confirm before relying on it.