Live data
import { Aside } from ‘@astrojs/starlight/components’;
Cómo consultar datos en vivo desde un artifact HTML publicado. Leé esto antes de
escribir cualquier fetch('/v1/data/…') o new ShareOut() — hacerlo mal produce
Authentication required, gráficos vacíos o números silenciosamente incorrectos.
Modelo de dos orígenes
Sección titulada «Modelo de dos orígenes»Los artifacts publicados corren en un iframe en sandbox en un host de contenido separado:
| Capa | Host | Rol |
|---|---|---|
| Shell de confianza | shareout.site (o subdominio del workspace) | Chrome de la página, cookies de sesión, bridge padre↔iframe |
| Contenido no confiable | <hex>.shareoutcdn.site | Tu HTML/JS del artifact — origen opaco |
El shell padre genera un Bearer sessionToken de corta duración y se lo pasa al SDK
vía postMessage. Las cookies no se envían desde el iframe.
| Enfoque | ¿Funciona en sandbox? |
|---|---|
await ShareOut.create() + métodos del SDK | Sí — el SDK envía Authorization: Bearer … |
fetch(…/v1/data/…, { credentials: 'include' }) | No — las cookies no se envían desde el iframe |
new ShareOut() a nivel superior (sin await) | Con race condition — el token puede llegar después del init |
Inicialización requerida
Sección titulada «Inicialización requerida»<script src="https://shareout.site/sdk/shareout.js"></script><script>(async () => { const sdk = await ShareOut.create(); // espera el sessionToken del shell padre
// cargá datos, luego renderizá})();</script>Usá un IIFE async (o await a nivel superior en un module script). Nunca llamés a
.get(), .query() ni ningún otro método del SDK antes de que ShareOut.create() resuelva.
Nunca llamés a /v1/data/* con fetch directo
Sección titulada «Nunca llamés a /v1/data/* con fetch directo»// MAL — falla en sandboxconst res = await fetch(`${base}/v1/data/${aid}/connections/mixpanel/query`, { method: 'POST', credentials: 'include', body: JSON.stringify({ query, options }),});
// BIEN — conexión de workspace REST genérica (Mixpanel, Meta Graph, cualquier 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,});Todas las llamadas a /v1/data/{artifactId}/* desde el JS del artifact deben ir por el SDK.
Conexiones REST genéricas (sdk.connection)
Sección titulada «Conexiones REST genéricas (sdk.connection)»Conexiones de workspace con kind: generic y provider: rest_api:
const sdk = await ShareOut.create();
// .fetch() desenvuelve el envelope — devuelve el body del proveedor directamenteconst 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,});
// Los proveedores suelen anidar datos — desenvolvé con cuidadoconst inner = mpBody?.data ?? mpBody;
// .query() mantiene { 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;Ver connections para materialize() y refresh programado.
Proveedores de plataforma (BigQuery, Snowflake, GA, Shopify)
Sección titulada «Proveedores de plataforma (BigQuery, Snowflake, GA, Shopify)»Las conexiones de plataforma (kind: platform) usan sdk._internalFetch. No existe
un store sdk.platform en el bundle del browser — no lo inventés.
const sdk = await ShareOut.create();
// 1. Resolver el id de conexión (solo owner)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. Ejecutar el endpoint del proveedorconst 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. Parsear las filas de BigQueryif (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;});Patrón de endpoint del proveedor: /platform/{providerId}/{endpointId}/execute.
Consultas paralelas
Sección titulada «Consultas paralelas»El SDK deduplica POSTs en vuelo por path + hash del body. Las llamadas paralelas al mismo endpoint con bodies distintos son seguras en las versiones actuales del SDK. Para SDKs más antiguos, ejecutá las consultas al warehouse de forma secuencial.
¿Quién puede consultar en vivo?
Sección titulada «¿Quién puede consultar en vivo?»| API | Viewer con contraseña | Owner | Miembro del workspace (conector per_user) |
|---|---|---|---|
sdk.json / sdk.table() | Sí (según access policy) | Sí | Sí |
sdk.connection().query/fetch (compartido) | No | Sí | No |
sdk.connection().query/fetch (per_user) | No | Sí (propio token) | Sí (propio token) |
sdk._internalFetch('/platform/…') (compartido) | No | Sí | Sí |
Los conectores por usuario devuelven 403 CREDENTIALS_REQUIRED hasta que el miembro del
workspace guarde su token vía PUT /v1/workspaces/{id}/connections/{connectionId}/my-credentials.
Errores comunes
Sección titulada «Errores comunes»| Error | Síntoma |
|---|---|
fetch directo + credentials: 'include' | Authentication required |
new ShareOut() sin create() | Fallos de auth intermitentes |
Desenvolvimiento incorrecto del proveedor (r.data vs r.data.data) | Gráficos renderizan, números todos en cero |
| POST paralelo mismo path en SDK antiguo | Shape de la primera consulta reutilizada — datos vacíos/incorrectos |
| Esperar que viewers con contraseña ejecuten consultas en vivo | Forbidden o datos vacíos |
Checklist
Sección titulada «Checklist»const sdk = await ShareOut.create()dentro de un IIFE async- Sin
fetchdirecto a/v1/data/{artifactId}/… - Fuentes REST →
sdk.connection('name').fetch(…) - BigQuery / plataforma →
sdk._internalFetch('/platform/…') - Desenvolvimiento defensivo del
.dataanidado del proveedor - Consultas en vivo solo para owners documentadas en el copy de la UI, o usá materialize para audiencia pública