Ir al contenido

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.

Los artifacts publicados corren en un iframe en sandbox en un host de contenido separado:

CapaHostRol
Shell de confianzashareout.site (o subdominio del workspace)Chrome de la página, cookies de sesión, bridge padre↔iframe
Contenido no confiable<hex>.shareoutcdn.siteTu 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 SDKSí — 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
<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 sandbox
const 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 de workspace con kind: generic y provider: rest_api:

const sdk = await ShareOut.create();
// .fetch() desenvuelve el envelope — devuelve el body del proveedor directamente
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,
});
// Los proveedores suelen anidar datos — desenvolvé con cuidado
const 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 proveedor
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. Parsear las filas de BigQuery
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;
});

Patrón de endpoint del proveedor: /platform/{providerId}/{endpointId}/execute.

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.

APIViewer con contraseñaOwnerMiembro del workspace (conector per_user)
sdk.json / sdk.table()Sí (según access policy)
sdk.connection().query/fetch (compartido)NoNo
sdk.connection().query/fetch (per_user)NoSí (propio token)Sí (propio token)
sdk._internalFetch('/platform/…') (compartido)No

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.

ErrorSí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 antiguoShape de la primera consulta reutilizada — datos vacíos/incorrectos
Esperar que viewers con contraseña ejecuten consultas en vivoForbidden o datos vacíos
  • const sdk = await ShareOut.create() dentro de un IIFE async
  • Sin fetch directo a /v1/data/{artifactId}/…
  • Fuentes REST → sdk.connection('name').fetch(…)
  • BigQuery / plataforma → sdk._internalFetch('/platform/…')
  • Desenvolvimiento defensivo del .data anidado del proveedor
  • Consultas en vivo solo para owners documentadas en el copy de la UI, o usá materialize para audiencia pública