Skip to main content

Serve Vizly Charts from FastAPI

FastAPI is a common home for chart endpoints. Vizly ships FastAPI helpers that return ready-to-serve responses: html_response() for a full page, json_response() for API consumers, and dashboard_response() for multi-chart pages.

Install the extra once:

pip install "vizly[fastapi]"

Single chart as HTML

Return a self-contained HTML page from an endpoint. The browser gets local, vendored ECharts, with no CDN dependency.

from fastapi import FastAPI
import vizly as vz
from vizly.integrations.fastapi import html_response

app = FastAPI()

@app.get("/chart/revenue")
def revenue_chart():
data = [
{"date": "2026-01-01", "revenue": 42},
{"date": "2026-01-02", "revenue": 48},
{"date": "2026-01-03", "revenue": 45},
]
chart = vz.line(data, x="date", y="revenue", title="Revenue")
return html_response(chart)

Chart as JSON

For SPAs and API consumers that render ECharts themselves, return the option dict as JSON. This is the same to_option() payload, wrapped in a FastAPI JSONResponse.

from vizly.integrations.fastapi import json_response

@app.get("/api/chart/revenue")
def revenue_json():
data = [{"date": "2026-01-01", "revenue": 42}, {"date": "2026-01-02", "revenue": 48}]
chart = vz.line(data, x="date", y="revenue", title="Revenue")
return json_response(chart)

Multi-chart dashboard

dashboard_response() renders several charts with one ECharts load. Pass any number of charts and a title.

from vizly.integrations.fastapi import dashboard_response

@app.get("/dashboard/ops")
def ops_dashboard():
cpu = [{"time": "00:00", "value": 34}, {"time": "01:00", "value": 38}]
mem = [{"time": "00:00", "value": 14}, {"time": "01:00", "value": 15}]
chart = vz.page(
charts=[
vz.line(cpu, x="time", y="value", title="CPU %"),
vz.area(mem, x="time", y="value", title="Memory GB"),
],
title="Ops dashboard",
)
return dashboard_response(chart.charts, title="Ops dashboard")

Fragments inside a shell page

When your chart lives inside an existing template, serve assets once in the shell and use fragments so ECharts is not loaded per chart. The endpoint returns only the chart markup.

from vizly.integrations import assets_html, chart_html
from vizly.integrations.fastapi import html_response

@app.get("/chart/revenue")
def revenue_fragment():
data = [{"date": "2026-01-01", "revenue": 42}]
chart = vz.line(data, x="date", y="revenue", title="Revenue")
return html_response(chart, fragment=True, include_assets=False)

In your shell template, load assets once:

shell_assets = assets_html(charts=[c1, c2])  # place in <head>

Then the fragments can assume ECharts is already on the page.

From SQL to endpoint

Vizly pairs naturally with a database-backed API. Query with from_sql and return the chart.

from sqlalchemy import create_engine
from vizly.integrations.fastapi import html_response

engine = create_engine("postgresql+psycopg://user:[email protected]:5432/sales")

@app.get("/chart/region/{region}")
def region_chart(region: str):
table = vz.from_sql(
"SELECT product, SUM(sales) AS sales FROM orders WHERE region = :r GROUP BY product",
bind=engine,
params={"r": region},
)
chart = vz.bar(table, x="product", y="sales", title=f"{region} sales")
return html_response(chart)

Tips

TipDetail
Pick your responsehtml_response for humans, json_response for apps, dashboard_response for multi-chart pages.
Cache chart constructionBuild expensive charts once and cache them, or add FastAPI response caching, if the underlying data is static.
Fragments reuse EChartsUse fragment=True, include_assets=False when the shell already loaded assets.
Query paramsBind user input through from_sql(params=...), never by formatting SQL strings.