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
| Tip | Detail |
|---|---|
| Pick your response | html_response for humans, json_response for apps, dashboard_response for multi-chart pages. |
| Cache chart construction | Build expensive charts once and cache them, or add FastAPI response caching, if the underlying data is static. |
| Fragments reuse ECharts | Use fragment=True, include_assets=False when the shell already loaded assets. |
| Query params | Bind user input through from_sql(params=...), never by formatting SQL strings. |