I tried creating a dashboard from Athena dbt models using dbt Charts

I tried creating a dashboard from Athena dbt models using dbt Charts

I tried creating an order status dashboard from an Amazon Athena + Iceberg dbt project using the dbt Charts CLI. I will introduce the implementation method and operation results.
2026.10.12

This page has been translated by machine translation. View original

Introduction

I'm Kasama from the Data Business Division.
dbt Charts is a tool that allows you to write dashboards in YAML within a dbt project. It connects to Athena via a dbt profile. In this article, I'd like to use the dbt Charts CLI (dct) to create an order status dashboard from an Amazon Athena + Iceberg dbt project.

Prerequisites

What is dbt Charts?

dbt Charts is a tool for writing dashboards in YAML within a dbt project. A single YAML file is called a board, and you define queries, charts, and layouts in a board. dct reads the board, executes SQL, and dct serve displays it in a browser.

https://dbtcharts.com/
https://dev.classmethod.jp/articles/dbt-charts-snowflake-dbt-model-yaml-dashboard/

Requirements for using Athena with dct (CLI)

There are two ways for dct to connect to a database.

Method Where to write connection settings Example supported databases
Direct Write connection settings such as type: postgres in dbt_charts.yml Postgres, Snowflake, BigQuery, Redshift, DuckDB
dbt_profile dbt's profiles.yml. Write only type: dbt_profile and the profile name in dbt_charts.yml Athena, SQL Server, ClickHouse, Spark

With direct connection, the connection settings are fully contained in dbt_charts.yml. With dbt_profile, you can use the connection settings from the profiles.yml already used by dbt. On the Sources page of the web documentation, the connection method for Athena is listed as type: dbt_profile, and the installation is listed as dbt-charts[athena].

https://docs.dbtcharts.com/sources/

The requirements for using Athena with dct are as follows.

Item Details
Connection Write type: dbt_profile in dbt_charts.yml
Installation Install with dbt-charts[athena]. The extra athena adds dbt-athena>=1.10,<2.0.

Test Environment

We use dbt-charts (dct) version 0.9.1, which is the latest release as of October 12, 2026. The list of releases can be found on GitHub.

https://github.com/dbt-labs/dbt-charts/releases

Item Value
dbt-charts (dct) 0.9.1
dbt-core 1.12.5
dbt-athena 1.11.1
Region ap-northeast-1

Architecture

Both dbt and dct are run locally, with roles divided as follows.

  • dbt (dbt build)
    • Runs seeds and models on Athena, creating tables and views
    • Table definitions are saved in Glue Data Catalog, and data is saved in S3
    • Generates target/manifest.json
  • dct (dbt Charts)
    • Connects to Athena using dbt's profiles.yml
    • Reads target/manifest.json and replaces ref() in board SQL with table names
    • Executes SELECT statements written in the board on Athena and turns the results retrieved from tables created by dbt into charts
    • Displays the board in a browser with dct serve

The sample data is based on jaffle_shop, published by dbt Labs for dbt demos. jaffle_shop contains customer and order data for a fictional e-commerce site. For this article, we kept the same column structure but reduced the number of rows, and included it as seeds (CSV) in the dbt project.

https://github.com/dbt-labs/jaffle-shop-classic

The dbt models create staging views from seeds, and then create Iceberg tables in the mart from staging.

mart Description Rows
order_status_summary Number of orders by status 5
customer_orders Number of orders, first order date, last order date per customer 10

dct displays one board to check order status on a single screen. The board lists total order count, daily order count, count by status, and an order list by customer. Table joins are handled in the dbt mart, so the board SQL only reads from order_status_summary, customer_orders, and stg_orders.

Implementation

The implementation code is stored on GitHub.

https://github.com/cm-yoshikikasama/blog_code/tree/main/73_dbt_charts_athena

73_dbt_charts_athena/
├── cfn/
│   └── datastore.yaml              # S3 bucket and Glue Database
├── docs/
│   └── architecture.drawio         # Architecture diagram
└── dbt/
    ├── pyproject.toml              # dbt-core and dbt-athena version pinning
    ├── dbt_project.yml
    ├── profiles.yml                # Profile shared by dbt and dct
    ├── dbt_charts.yml              # dct source definition
    ├── seeds/
    │   ├── raw_customers.csv
    │   └── raw_orders.csv
    ├── models/
    │   ├── staging/
    │   │   ├── stg_customers.sql
    │   │   └── stg_orders.sql
    │   └── marts/
    │       ├── order_status_summary.sql
    │       └── customer_orders.sql
    └── charts/
        └── order_overview.yml      # dct board

CloudFormation

https://github.com/cm-yoshikikasama/blog_code/blob/main/73_dbt_charts_athena/cfn/datastore.yaml

datastore.yaml defines the S3 bucket and Glue Database. The S3 bucket stores seed and mart data, as well as Athena query results. The Glue Database is used as the dbt schema.

dbt

https://github.com/cm-yoshikikasama/blog_code/blob/main/73_dbt_charts_athena/dbt/profiles.yml

profiles.yml is read by both dbt and dct. The bucket name is received from the environment variable DBT_S3_BUCKET. s3_staging_dir is where Athena query results are saved, and s3_data_dir is where dbt saves table data. They are separated within the same bucket as athena-results/ and data/. Since dbt creates tables and views in the Glue Database specified in schema, it should match the Glue Database name created in CloudFormation.

https://github.com/cm-yoshikikasama/blog_code/blob/main/73_dbt_charts_athena/dbt/dbt_project.yml

In dbt_project.yml, the mart is configured as an Iceberg table (materialized: table). Since dbt-athena uses RENAME when creating Iceberg tables, the S3 path for the table needs to be different each time. Therefore, s3_data_naming in profiles.yml is set to schema_table_unique, making the path unique per execution like data/<schema_name>/<table_name>/<UUID>/.

dbt Charts

https://github.com/cm-yoshikikasama/blog_code/blob/main/73_dbt_charts_athena/dbt/dbt_charts.yml

dbt_charts.yml is placed at the root of the dbt project and defines the source that dct connects to. When type: dbt_profile is specified, dct uses the profile and target from profiles.yml to connect to Athena. profiles_dir is a relative path from the dbt project. Since profiles.yml is placed directly under the dbt project in this case, . is specified.

https://github.com/cm-yoshikikasama/blog_code/blob/main/73_dbt_charts_athena/dbt/charts/order_overview.yml

For ref() such as {{ ref('order_status_summary') }} in SQL, dct reads target/manifest.json and replaces them with relation names. dct reads ref() calls as strings and looks up the table name from the manifest.

The manifest is generated by dbt parse or dbt build. If you use ref() without a manifest, dct validate will output a warning (WARN-DBT-MANIFEST-MISSING) and succeed. dct render cannot replace ref() with table names and will fail with an error (ERR-DBT-MANIFEST-MISSING). You need to generate the manifest with dbt before running dct.

Deployment

Clone the repository

Clone the sample repository and navigate to the 73_dbt_charts_athena directory. All subsequent commands are run from this directory as the base.

git clone https://github.com/cm-yoshikikasama/blog_code.git
cd blog_code/73_dbt_charts_athena

Create the CloudFormation stack

Create the S3 bucket and Glue Database, then retrieve the bucket name from the Outputs.

aws cloudformation deploy \
  --profile <your-profile> \
  --region ap-northeast-1 \
  --stack-name cm-kasama-dbt-charts-athena \
  --template-file cfn/datastore.yaml

aws cloudformation describe-stacks \
  --profile <your-profile> \
  --region ap-northeast-1 \
  --stack-name cm-kasama-dbt-charts-athena \
  --query 'Stacks[0].Outputs'

Install dct and dbt

Install dct with the Athena extra as dbt-charts[athena]. Adding the extra also installs dbt-athena. Pin the dbt version in dbt/pyproject.toml and install it into a virtual environment for the dbt project using uv sync.

uv tool install 'dbt-charts[athena]'
cd dbt
uv sync

Set credentials and bucket name as environment variables

Set the bucket name retrieved from the stack Outputs to DBT_S3_BUCKET, which is referenced by profiles.yml. Specify the AWS profile used by dbt and dct with AWS_PROFILE.

export AWS_PROFILE=<your-profile>
export DBT_S3_BUCKET=<your-bucket>

Trying it out

dbt build

Build everything from seed to mart.

uv run dbt build --profiles-dir .
1 of 6 OK loaded seed file cm_kasama_jaffle_shop_dev.raw_customers ............. [INSERT 10 in 7.10s]
2 of 6 OK loaded seed file cm_kasama_jaffle_shop_dev.raw_orders ................ [INSERT 20 in 8.18s]
3 of 6 OK created sql view model cm_kasama_jaffle_shop_dev.stg_customers ....... [OK -1 in 1.95s]
4 of 6 OK created sql view model cm_kasama_jaffle_shop_dev.stg_orders .......... [OK -1 in 1.92s]
5 of 6 OK created sql table model cm_kasama_jaffle_shop_dev.customer_orders .... [OK 10 in 8.47s]
6 of 6 OK created sql table model cm_kasama_jaffle_shop_dev.order_status_summary  [OK 5 in 8.59s]
Done. PASS=6 WARN=0 ERROR=0 SKIP=0 NO-OP=0 REUSED=0 TOTAL=6

All 6 items from seed to mart were created successfully.

Verify the connection and board

Use dct query to verify the connection to Athena, and dct validate to validate the board YAML. Both are run from the dbt directory.

dct query athena 'SELECT 1'
dct validate charts/
_col0
1

1 rows
✓ charts/order_overview.yml OK

Check with text format while editing

While editing the board, use --format text to check aggregation results. --format text outputs the values, row counts, and value ranges for each chart.

dct render charts/order_overview.yml --format text
# Order Status

## Order Status_row0

### kpi_orders (kpi)
- value: 20

### kpi_customers (kpi)
- value: 10

## Order Status_row1

### Daily Order Count (line)
- x: order_date, y: order_count
- 18 rows | order_count: 1–2

### Count by Status (bar)
- x: status, y: order_count
- 5 rows | status: completed, placed, return_pending, returned, shipped | order_count: 1–9

## Orders by Customer (table)
- columns: Customer Name, Order Count, First Order Date, Last Order Date
- showing first 3 of 10 rows
| customer_name | order_count | first_order_date | last_order_date |
| --- | --- | --- | --- |
| Frank R. | 2 | 2018-01-11 | 2018-01-19 |
| Henry W. | 2 | 2018-01-12 | 2018-01-20 |
| Jennifer F. | 2 | 2018-01-04 | 2018-01-17 |
- 10 rows

The total order count of 20 and customer count of 10 matched the number of rows in the seeds.

With --format terminal, which draws charts in the terminal, the x-axis labels of the bar chart for count by status were misaligned. Only 4 labels appeared for 5 bars, and "completed" was displayed under the bar with height 9 (placed). Use --format text for verifying values.

   ┌───────────────────────────────────────────────────────────────┐
9.0┤███████████                                                    │
7.5┤███████████  ███████████                                       │
6.0┤███████████  ███████████                                       │
3.0┤███████████  ███████████                                       │
1.5┤███████████  ███████████  ███████████  ███████████  ███████████│
0.0┤███████████  ███████████  ███████████  ███████████  ███████████│
   └─────┬────────────┬────────────┬────────────┬──────────────────┘
    completed    returned   return_pending   shipped

Display in browser with dct serve

dbt Charts is designed to be viewed via the dct serve URL or the hosted dbt Charts Cloud URL. In this case, we serve it locally with dct serve.

dct serve --port 8080

The board file path corresponds to the URL, so charts/order_overview.yml is displayed at http://localhost:8080/order_overview/. Opening it in a browser shows the following screen.

The board for checking order status on a single screen was displayed in the browser. The first row shows total orders and customer count, the second row shows a line chart of daily orders and a horizontal bar chart of count by status, and the third row shows an order list by customer.

Export HTML for sharing

To share the board as a file, export it with --format html.

dct render charts/order_overview.yml --format html

renders/order_overview.html is output. This HTML embeds the data at the time of rendering, and the displayed data remains fixed at that point. Other output formats include svg, png, pdf, terminal, json, text, text-data, yaml, and data.

Conclusion

We connected dbt Charts' dct to an Athena + Iceberg dbt project via dbt_profile and displayed an order status board using dct serve. Since the board is placed as YAML within the dbt project and references marts via ref(), you can manage models and dashboards in the same repository and review them via pull requests. This can be useful in projects building a data platform with dbt and Athena, such as when you want to verify aggregation results as a chart during model development, or when you need to confirm numbers with stakeholders before introducing a BI tool. If you want to update data regularly, one approach would be to set up a job in CI that runs dbt build followed by dct render --format html, then shares the exported HTML. I hope this is helpful for someone.


dbtの導入支援はクラスメソッドにお任せください!

クラスメソッドでは dbt の導入を支援しております。
製品の詳細や支援の内容についてお気軽にお問い合わせください。

dbtの詳細を見る

Share this article

Related articles