@golive/data

Acede ao Postgres do teu projecto directamente do browser (ou da edge), com um query builder encadeado. No plano Free é a forma de teres dados sem backend.

Instalar

$ npm i https://golive.co.ao/sdk/golive-data-0.1.0.tgz

Ou por URL, sem passo de build:

import { GoLiveData } from "https://golive.co.ao/sdk/data.js";

Cria a base de dados primeiro com golive db create.

Início rápido

No browser, o acesso é feito em nome do utilizador autenticado — passa o token do @golive/auth:

import { GoLiveData } from "@golive/data";
import { GoLiveAuth } from "@golive/auth";

const auth = new GoLiveAuth({ apiKey, tenant, controlPlane });
const db = new GoLiveData({
  projectId: "a1b2c3d4",
  apiKey,
  getToken: () => auth.getIdToken(),   // JWT do utilizador
});

const { data, error } = await db.from("tarefas").select().eq("feita", false);
if (error) console.error(error.message);
else console.log(data);

Este SDK nunca lança — devolve sempre { data, error }. Verifica o error. (O @golive/auth é o oposto: lança.)

Configuração

OpçãoTipoO que faz
projectIdstringId do projecto GoLive. Obrigatório.
apiKeystringChave pública (a mesma do @golive/auth). Obrigatória.
getTokenfunçãoDevolve o JWT do utilizador final (sync ou async). Obrigatório no browser — ex.: () => auth.getIdToken().
serviceKeystringChave de serviço (edge/backend): permite sql(), rpc() privilegiado e ignora o scope por utilizador. Nunca no browser.
endpointstringURL da Data API. Omissão: control plane. No Dev Pack: http://localhost:18321/__golive/data.

O resultado

Toda a consulta devolve o mesmo objecto:

interface DataResult<T> {
  data: T[];                    // as linhas
  count: number | null;         // total, se pediste .count()
  error: GoLiveDataError | null;
  status: number;               // código HTTP
  row?: T | null;               // só com .single() / .maybeSingle()
}

O builder é thenable: basta await na cadeia, sem chamar nada no fim.

select(...colunas)

Sem argumentos devolve todas as colunas.

await db.from("clientes").select();
await db.from("clientes").select("id", "nome", "email");

Filtros

MétodoSQLExemplo
eq(col, v)=.eq("estado", "activo")
neq(col, v)<>.neq("estado", "apagado")
gt / gte> / >=.gte("idade", 18)
lt / lte< / <=.lt("preco", 5000)
like(col, v)LIKE.like("nome", "Ana%")
ilike(col, v)ILIKE.ilike("nome", "%silva%")
in(col, [..])IN.in("id", [1, 2, 3])
is(col, null|bool)IS.is("apagado_em", null)
contains@>arrays / JSONB
containedBy<@arrays / JSONB
overlaps&&arrays
textSearch(col, q)@@pesquisa full-text

Combinar, negar e agrupar:

// vários filtros = AND
await db.from("encomendas").select().eq("estado", "pago").gte("total", 10000);

// atalho para vários AND de igualdade
await db.from("encomendas").select().match({ estado: "pago", moeda: "AOA" });

// negar um filtro
await db.from("clientes").select().not("estado", "eq", "apagado");

// OR
await db.from("clientes").select().or([
  { column: "cidade", op: "eq", value: "Luanda" },
  { column: "cidade", op: "eq", value: "Benguela" },
]);

Ordenar & paginar

await db.from("posts").select()
  .order("criado_em", { ascending: false })
  .limit(20);

// paginação por intervalo (inclusivo)
await db.from("posts").select().range(0, 19);   // primeiros 20
await db.from("posts").select().range(20, 39);  // seguintes 20

// ou por offset
await db.from("posts").select().limit(20).offset(40);

order aceita { ascending?: boolean, nullsFirst?: boolean }.

Linha única & contagem

// .single() → erro se não houver exactamente 1 linha
const { row, error } = await db.from("clientes").select().eq("id", 7).single();

// .maybeSingle() → row = null se não existir (sem erro)
const { row } = await db.from("clientes").select().eq("email", e).maybeSingle();

// .count() → total além das linhas devolvidas
const { data, count } = await db.from("posts").select().limit(10).count();

insert(dados)

Aceita um objecto ou um array (inserção em lote).

await db.from("tarefas").insert({ titulo: "Comprar pão", feita: false });

await db.from("tarefas").insert([
  { titulo: "A" },
  { titulo: "B" },
]);

update(dados)

Usa sempre um filtro — sem ele, actualizas a tabela toda.

await db.from("tarefas").update({ feita: true }).eq("id", 42);

upsert(dados, onConflict?)

Insere ou actualiza se já existir. onConflict são as colunas que identificam o conflito.

await db.from("definicoes").upsert({ chave: "tema", valor: "escuro" }, ["chave"]);

delete()

await db.from("tarefas").delete().eq("id", 42);

sql(query, params)

SQL parametrizado completo ($1, $2…) — joins, CTEs, window functions, DDL. Requer serviceKey; no browser devolve erro forbidden.

// numa edge function / backend
const db = new GoLiveData({ projectId, apiKey, serviceKey: env.GOLIVE_SERVICE_KEY });

const { data, error } = await db.sql(
  `select c.nome, count(e.id) as total
     from clientes c
     left join encomendas e on e.cliente_id = c.id
    where c.cidade = $1
    group by c.nome`,
  ["Luanda"],
);

Usa sempre parâmetros ($1) em vez de concatenar strings — é o que te protege de injecção de SQL.

rpc(fn, args)

Chama uma função Postgres. Argumentos posicionais (array) ou nomeados (objecto).

// posicional → fn($1, $2)
await db.rpc("total_por_cliente", [7, "2026-01-01"]);

// nomeado → fn("cliente_id" := $1)
await db.rpc("total_por_cliente", { cliente_id: 7 });

Funções SECURITY DEFINER que leiam dados de outros utilizadores exigem serviceKey.

Erros

Nunca lança: o erro vem em error, como GoLiveDataError com .code e .message. O status traz o código HTTP.

const { data, error, status } = await db.from("tarefas").select();
if (error) {
  if (status === 401) irParaLogin();          // token inválido/ausente
  else if (error.code === "forbidden") …      // sql() sem serviceKey
  else mostrarErro(error.message);
}

Segurança

Dev Pack (local)

Com a Database ligada no Dev Pack (golive dev init), aponta o endpoint para o host local:

const db = new GoLiveData({
  projectId: "dev",
  apiKey: "dev",
  endpoint: "http://localhost:18321/__golive/data",
  getToken: () => auth.getIdToken(),
});

Podes correr SQL directamente no painel /__golive/, separador Database, e semear a base com dev.seed no golive.json.

© 2026 GoLive · Documentação
Home Preços Termos Privacidade