Build a data app
Write a data app as one HTML file, run it with anfra serve, and work within its sandbox
A data app is one HTML file in your repo's apps/ folder. It declares its queries, controls and cross-filters through the Anfra global, and renders the results with its own HTML, CSS and JavaScript. anfra supplies the data and the state. Every pixel is yours.
Build it with a coding agent
The anfra-build-data-app skill teaches a coding agent the steps on this page: learn the datasets, agree on a design, write and test the queries, then write and check the app. Install it with anfra agent setup, or npx skills add holistics/anfra-skills -a <agent-id> -g -y for agents without plugin support.
The file
- Put the file anywhere under
apps/. Subfolders group apps in the list. - The list shows each app by its
<title>. apps/sales/overview.htmlopens at/apps/sales/overview.
Most apps follow the same order, as in the example app:
- Create the app with
Anfra.createApp(). See Environment. - Declare every query and control, then connect them with control mappings and cross-filters.
app.subscribe(render), whererenderdraws each query from its state and result.app.execute().- Wire inputs and clicks to set controls or the selection, then call
app.execute()again.
Look up dataset and field names with anfra show, and run each query with anfra query before you put it in the page.
Run it
anfra status # is a server already running for this repo, and where?
anfra serve # if not, start oneOpen http://127.0.0.1:7878/apps/, or the address anfra serve prints if that port is taken. See anfra serve.
While it runs:
- The open app reloads when its file changes. When an
.amlfile changes, the datasets reload and the app remounts. - The header shows the repo's problems, such as AML that does not compile or a data source that is not configured.
- The Inspect panel has two tabs. Structure shows the parts you marked as a tree, with any markup it could not read. Data shows each query's state, executed AQL and SQL, and error. See Debug information.
- The toolbar and the Inspect panel save a PNG or PDF of the whole page, or of any block or container. See Render.
Read the console of the app's frame, not just the page's. A ValidationError from a declaring line surfaces there.
anfra serve listens on 127.0.0.1 by default, so the URL opens only on your machine. It has no authentication, so think before serving it beyond loopback with --addr.
The sandbox
The app runs in a sandboxed frame (sandbox="allow-scripts", with an opaque origin), under a content policy that limits what it may load and where it may send requests. The app has the reader's query results in hand, so the policy keeps its code from sending them anywhere unexpected. Write for it:
-
Data comes only from the SDK. The frame can't call anfra's API itself, and
fetch, XHR and WebSocket to any other server are blocked. -
The app is one HTML file. Nothing it loads may be another file in your repo, so no relative
srcorhrefsuch as./chart.jsorlogo.png. Put your own scripts and styles inline, and images inline as SVG or adata:URI. -
Load libraries, styles and fonts only from jsDelivr or cdnjs, by their
https://URL with a pinned version. Anything from another origin is blocked, including esm.sh and unpkg. Google Fonts is allowed too, but its CSS can't carry an integrity hash, so prefer the@fontsourcepackages on jsDelivr (see below). Load an npm package from jsDelivr (https://cdn.jsdelivr.net/npm/<package>@<version>/<file>), and an ES module from its+esmbuild (https://cdn.jsdelivr.net/npm/<package>@<version>/+esm). Images may come from anyhttps://server. -
Give every CDN script and stylesheet an integrity hash, so a library changed at its CDN fails to load rather than running beside your reader's data:
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/echarts.min.js" integrity="sha384-…" crossorigin="anonymous"></script>jsDelivr shows each file's hash on its page. For another file, run
curl -sL <url> | openssl dgst -sha384 -binary | openssl base64 -Aand putsha384-before the output. Load fonts from@fontsourceon jsDelivr rather than Google Fonts' CSS, which differs per browser and so can't be hashed:anfra validatewarns about a Google Fonts stylesheet for that reason. -
No storage.
localStorage,sessionStorageand cookies throw. Keep state in variables. -
No forms, popups, navigation, nested frames or
<base>. Use buttons andchangelisteners, never<form>.
anfra validate warns about a file the app references from the repo, and about a CDN script or stylesheet without an integrity hash.
Allowing another origin
To load from another origin, such as your company's own CDN, list it in your repo's .anfra/config.yml:
apps:
allow_origins:
- https://cdn.acme.comEach entry is an https:// origin with nothing after it. anfra serve adds these origins for scripts, styles, fonts and requests. They aren't published: anfra Cloud keeps its own list, which an Admin edits in the organization's settings, and anfra publish warns about any app origin the organization doesn't allow.
Make the numbers trustworthy
- Mark the parts of the page with
data-anfra-blockanddata-anfra-container, so the Inspect panel can show them and the host can render them. - Render all four states of each query: loading, error, a result, and an empty result in words. See Results.
- On each query's first success, log
result.columns,result.rows[0]andresult.debug?.executedAqlto confirm the row keys, value types and the AQL that ran. - Put the definitions, dates and rounding a reader needs on the page.
- If a number has no field or metric, add it to the semantic layer rather than computing it in JavaScript.
Related
- Add interactions: add a filter, a date drill and a cross-filter, step by step.
- Example app: a complete file to start from.
- Queries: the rules for a query's AQL.
- Errors: what each error means and what to do.
- Plugins: export, lineage and rendering, where the host installs them.