Skip to content

Live data

import { Aside } from ‘@astrojs/starlight/components’;

How to query live data from a published HTML artifact. Read this before writing any fetch('/v1/data/…') or new ShareOut() call — getting it wrong causes Authentication required, empty charts, or silently wrong numbers.

Published artifacts run in a sandboxed iframe on a separate content host:

LayerHostRole
Trusted shellshareout.site (or workspace subdomain)Page chrome, session cookies, parent↔iframe bridge
Untrusted content<hex>.shareoutcdn.siteYour artifact HTML/JS — opaque origin

The parent shell mints a short-lived Bearer sessionToken and delivers it to the SDK via postMessage. Cookies are not sent from the iframe.

ApproachWorks in sandbox?
await ShareOut.create() + SDK methodsYes — SDK sends Authorization: Bearer …
fetch(…/v1/data/…, { credentials: 'include' })No — cookies not sent from iframe
new ShareOut() at top-level (no await)Racey — token may arrive after init
<script src="https://shareout.site/sdk/shareout.js"></script>
<script>
(async () => {
const sdk = await ShareOut.create(); // waits for the sessionToken from the parent shell
// load data, then render
})();
</script>

Use an async IIFE (or top-level await in a module script). Never call .get(), .query(), or any other SDK method before ShareOut.create() resolves.

// WRONG — fails in sandbox
const res = await fetch(`${base}/v1/data/${aid}/connections/mixpanel/query`, {
method: 'POST',
credentials: 'include',
body: JSON.stringify({ query, options }),
});
// RIGHT — REST workspace connection (Mixpanel, Meta Graph, any rest_api)
const body = await sdk.connection('mixpanel').fetch('/query/events', {
params: { project_id: '123', event: '["login_success"]', type: 'general', unit: 'day', from_date, to_date },
ttl: 300,
});

All /v1/data/{artifactId}/* calls from artifact JS must go through the SDK.

Workspace connections with kind: generic and provider: rest_api:

const sdk = await ShareOut.create();
// .fetch() unwraps the envelope — returns the provider body directly
const mpBody = await sdk.connection('mixpanel').fetch('/query/events', {
params: {
project_id: '3212168',
event: JSON.stringify(['$ae_session', 'login_success']),
type: 'general',
unit: 'day',
from_date: '2026-05-01',
to_date: '2026-05-07',
},
ttl: 300,
});
// Providers often nest data — unwrap defensively
const inner = mpBody?.data ?? mpBody;
// .query() keeps { data, cached, executionTimeMs }
const r = await sdk.connection('meta').query(
{ endpoint: '/act_123/insights', method: 'GET' },
{ params: { fields: 'impressions,clicks' }, ttl: 120 }
);
const payload = r.data;

See connections for materialize() and scheduled refresh.

Platform providers (BigQuery, Snowflake, GA, Shopify)

Section titled “Platform providers (BigQuery, Snowflake, GA, Shopify)”

Platform connections (kind: platform) use sdk._internalFetch. There is no sdk.platform store in the browser bundle — do not invent it.

const sdk = await ShareOut.create();
// 1. Resolve the connection id (owner-only)
const { connections } = await sdk._internalFetch('/platform/connections');
const bq = connections.find(c => c.name === 'bigquery');
if (!bq) throw new Error("No 'bigquery' connection in this workspace.");
// 2. Execute the provider endpoint
const result = await sdk._internalFetch('/platform/bigquery/jobs.query/execute', {
method: 'POST',
body: JSON.stringify({
connectionId: bq.id,
params: {
pathParams: { projectId: 'my-gcp-project' },
body: {
query: 'SELECT CAST(MAX(date) AS STRING) AS maxd FROM `proj.dataset.table`',
useLegacySql: false,
maxResults: 5000,
},
},
}),
});
// 3. Parse BigQuery rows
if (result.success === false || result.error) {
throw new Error(result.error?.message || 'Query failed');
}
const bqResp = result.data || result;
const fields = (bqResp.schema?.fields || []).map(f => f.name);
const rows = (bqResp.rows || []).map(row => {
const o = {};
(row.f || []).forEach((cell, i) => { o[fields[i]] = cell.v; });
return o;
});

Provider endpoint pattern: /platform/{providerId}/{endpointId}/execute.

The SDK deduplicates in-flight POSTs by path + body hash. Parallel calls to the same endpoint with different bodies are safe on current SDK builds. For older SDKs, run warehouse queries sequentially.

APIPassword viewerOwnerWorkspace member (per_user connector)
sdk.json / sdk.table()Yes (scoped by access policy)YesYes
sdk.connection().query/fetch (shared)NoYesNo
sdk.connection().query/fetch (per_user)NoYes (own token)Yes (own token)
sdk._internalFetch('/platform/…') (shared)NoYesYes

Per-user connectors return 403 CREDENTIALS_REQUIRED until the workspace member saves their token via PUT /v1/workspaces/{id}/connections/{connectionId}/my-credentials.

MistakeSymptom
Raw fetch + credentials: 'include'Authentication required
new ShareOut() without create()Intermittent auth failures
Wrong provider unwrap (r.data vs r.data.data)Charts render, numbers all zero
Parallel POST same path on old SDKFirst query shape reused — empty/wrong data
Expecting password viewers to run live queriesForbidden or empty data
  • const sdk = await ShareOut.create() inside async IIFE
  • No raw fetch to /v1/data/{artifactId}/…
  • REST sources → sdk.connection('name').fetch(…)
  • BigQuery / platform → sdk._internalFetch('/platform/…')
  • Defensive unwrap of nested .data from providers
  • Owner-only live queries documented in UI copy, or use materialize for public audiences