Communitygithub.com

Padrões Turso / libSQL

Banco de dados SQLite hospedado na borda com driver libSQL e réplicas incorporadas

O que é libSQL?

Este skill orienta desenvolvedores no uso do Turso, um serviço gerenciado de SQLite na borda (edge), com o driver `@libsql/client`. Ele fornece uma API unificada para conectar-se a bancos remotos Turso, bancos locais, bancos em memória ou réplicas incorporadas sincronizadas a partir de um primário remoto. O skill enfatiza a preferência pelo uso de `batch()` para operações atômicas de múltiplas instruções em vez de transações interativas, devido à maior eficiência (uma única viagem de ida e volta) e segurança. Para reduzir a latência de leitura, recomenda configurar réplicas incorporadas com uma URL de arquivo local e `syncUrl`, enquanto todas as escritas são encaminhadas para o banco primário, projetando-se para latência de 15-50ms. Além disso, cobre caminhos de importação distintos: `@libsql/client` para runtimes com acesso ao sistema de arquivos (Node.js) e `@libsql/client/web` para runtimes serverless/edge sem acesso a arquivos (Cloudflare Workers, Vercel Edge). O skill inclui padrões de uso, frameworks de decisão para escolher entre `batch()` e `transaction()`, seleção de caminho de importação e modo de transação, além de sinalizadores vermelhos e armadilhas comuns.

Funciona com✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/agents-inc/skills/tree/HEAD/src/skills/api-baas-turso

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

O que libSQL faz?

Guia Rápido: Use @libsql/client para todo acesso a banco de dados Turso. Use execute() para consultas únicas, batch() para operações atômicas de múltiplas instruções (preferível a transações interativas) e transaction() apenas quando consultas subsequentes dependerem de resultados anteriores. Para runtimes edge/serverless sem acesso ao sistema de arquivos, importe de @libsql/client/web. Para leituras com latência zero, configure réplicas incorporadas com uma URL de arquivo local + syncUrl. Todas as escritas são encaminhadas para o primário -- projete para latência de 15-50ms. Turso é SQLite internamente: modelo de escritor único, sem ALTER TABLE ... ADD CONSTRAINT, sem procedimentos armazenados.


<critical_requirements>

CRÍTICO: Antes de Usar Este Skill

Todo código deve seguir as convenções do projeto em CLAUDE.md (kebab-case, named exports, ordenação de importações, import type, constantes nomeadas)

(Você DEVE usar batch() com um modo de transação para operações atômicas de múltiplas instruções -- é mais rápido e seguro que transaction() interativa, pois executa em uma única viagem de ida e volta)

(Você DEVE importar de @libsql/client/web em runtimes edge/serverless sem acesso ao sistema de arquivos (Cloudflare Workers, Vercel Edge Functions) -- a importação base @libsql/client puxa ligações nativas que falham nesses ambientes)

(Você DEVE especificar um modo de transação ("write", "read" ou "deferred") como segundo argumento para batch() e transaction() -- o padrão é "deferred", que silenciosamente falha ao adquirir um bloqueio de escrita para INSERT/UPDATE/DELETE)

(Você DEVE chamar client.close() quando o cliente não for mais necessário em processos de curta duração -- clientes abertos mantêm conexões e manipuladores de arquivos)

(Você NÃO DEVE acessar o arquivo de banco de dados da réplica incorporada diretamente enquanto o cliente estiver em execução -- acesso concorrente causa corrupção de dados)

</critical_requirements>


Detecção Automática: Turso, libSQL, @libsql/client, createClient, turso.io, réplica incorporada, syncUrl, syncInterval, turso db, turso group, libsql, .turso.io, TURSO_DATABASE_URL, TURSO_AUTH_TOKEN

Quando usar:

  • Consultar um banco de dados SQLite hospedado no Turso de qualquer runtime (Node.js, edge, serverless)
  • Configurar réplicas incorporadas para leituras locais com latência zero sincronizadas a partir de um primário remoto
  • SaaS multi-inquilino com banco de dados por inquilino (Turso suporta milhões de bancos de dados)
  • Funções serverless/edge que precisam de um banco de dados sem complexidade de pool de conexões
  • Executar operações atômicas de múltiplas instruções com batch() ou transaction() interativa
  • Gerenciar grupos de banco de dados e posicionamento multirregião via CLI do Turso

Quando NÃO usar:

  • Cargas de trabalho pesadas de escrita que exigem consistência forte de múltiplos escritores (Turso é escritor único, escritas encaminhadas para o primário)
  • Consultas relacionais complexas que precisam de recursos do PostgreSQL (CTEs com subconsultas mutantes, procedimentos armazenados, restrições avançadas)
  • Transações distribuídas complexas em vários bancos de dados
  • Grandes conjuntos de dados analíticos (limitações de tamanho de linha e concorrência do SQLite se aplicam)

Recursos Detalhados:

  • examples/core.md -- Configuração do cliente, execute, batch, transações, caminhos de importação
  • examples/embedded-replicas.md -- Réplicas locais, sincronização, modo offline, criptografia
  • reference.md -- Frameworks de decisão, definições de tipos, comandos CLI, tabelas de consulta

Filosofia

Turso leva SQLite para a borda hospedando libSQL (um fork do SQLite) como um serviço gerenciado com replicação multirregião. O driver @libsql/client fornece uma API unificada que funciona de forma idêntica, seja conectando a um banco de dados Turso remoto, a um arquivo SQLite local, a um banco de dados em memória ou a uma réplica incorporada que sincroniza a partir de um primário remoto.

Princípios fundamentais:

  1. Batch sobre transação -- batch() envia todas as instruções em uma única viagem de ida e volta e as executa em uma transação implícita. transaction() interativa requer várias viagens de ida e volta e mantém um bloqueio de banco de dados (timeout de 5 segundos). Use batch() a menos que precise de lógica condicional entre consultas.
  2. Escritas sempre vão para o primário -- Mesmo com réplicas incorporadas, as escritas são encaminhadas para o banco de dados primário remoto. A latência de escrita é de 15 a 50 ms, dependendo da distância até a região primária. Projete para isso: UI otimista, sincronização em segundo plano, evite caminhos críticos de muitas escritas.
  3. Réplicas incorporadas para leituras -- Um arquivo SQLite local sincronizado a partir do primário remoto. As leituras são em nível de microssegundos. As escritas encaminham para o remoto. O arquivo local é atualizado após uma escrita bem-sucedida (semântica leia-suas-próprias-escritas).
  4. Dois caminhos de importação -- @libsql/client inclui ligações SQLite nativas para Node.js e suporta URLs file:. @libsql/client/web é JS/WASM puro para runtimes edge (Cloudflare Workers, Vercel Edge Functions) e não pode abrir arquivos locais.
  5. Semântica SQLite -- Turso é SQLite. Sem ADD CONSTRAINT, sem procedimentos armazenados, sem LISTEN/NOTIFY, modo WAL de escritor único. Conheça as limitações do SQLite antes de escolher Turso.

Padrões Principais

Padrão 1: Configuração do Cliente

Crie um cliente com createClient(). A url determina o tipo de conexão: libsql:// para Turso remoto, file: para SQLite local (apenas Node.js), :memory: para em memória (testes). Sempre use variáveis de ambiente para authToken -- nunca codifique credenciais.

Veja examples/core.md para padrões completos de configuração, incluindo módulos singleton e exemplos ruins.


Padrão 2: Executando Consultas

execute() executa uma única instrução SQL. Sempre use consultas parametrizadas com args -- nunca interpolação de strings.

// Posicional: args como array
await client.execute({
  sql: "SELECT * FROM users WHERE id = ?",
  args: [userId],
});

// Nomeado: args como objeto (nomes sem prefixo correspondem a :name, @name, $name no SQL)
await client.execute({
  sql: "INSERT INTO users (name, email) VALUES (:name, :email)",
  args: { name, email },
});

Retorna ResultSet com rows (Array<Row>), columns, rowsAffected, lastInsertRowid (bigint). Veja examples/core.md para mapeamento de resultados tipados e exemplos ruins.


Padrão 3: Operações Batch

batch() executa várias instruções atomicamente em uma única viagem de ida e volta. Todas bem-sucedidas ou todas revertidas. Sempre especifique o modo de transação como segundo argumento.

const results = await client.batch(
  [
    { sql: "INSERT INTO users (name) VALUES (?)", args: ["Alice"] },
    {
      sql: "INSERT INTO audit_log (action, entity_id) VALUES (?, last_insert_rowid())",
      args: ["user_created"],
    },
  ],
  "write", // Obrigatório para INSERT/UPDATE/DELETE
);

Use "write" para mutações, "read" para apenas SELECT (permite execução paralela), "deferred" para iniciar somente leitura e escalar se necessário. last_insert_rowid() funciona entre instruções no mesmo batch. Veja examples/core.md para padrões de multi-insert e batch somente leitura, e reference.md para a tabela de comparação de modos de transação.


Padrão 4: Transações Interativas

Use transaction() apenas quando consultas subsequentes dependerem de resultados de consultas anteriores. Ela mantém um bloqueio de banco de dados (timeout ocioso de 5 segundos) e requer várias viagens de ida e volta. Sempre use try/catch/finally com close().

const tx = await client.transaction("write");
try {
  const { rows } = await tx.execute({
    sql: "SELECT balance FROM accounts WHERE id = ?",
    args: [fromId],
  });
  // ... lógica condicional baseada nos resultados ...
  await tx.commit();
} catch (error) {
  await tx.rollback();
  throw error;
} finally {
  tx.close();
}

Se todas as instruções forem conhecidas antecipadamente sem lógica condicional, use batch() em vez disso. Veja examples/core.md para um exemplo completo de compra com verificação de estoque.


Padrão 5: Caminhos de Importação para Diferentes Runtimes

// Node.js, Bun, Deno (tem acesso ao sistema de arquivos, ligações nativas)
import { createClient } from "@libsql/client";

// Runtimes Edge/Serverless SEM sistema de arquivos (Cloudflare Workers, Vercel Edge)
import { createClient } from "@libsql/client/web";

O @libsql/client base inclui ligações SQLite nativas que falham em runtimes edge. @libsql/client/web é JS/WASM puro, mas não pode abrir URLs file: locais. Veja examples/core.md para um exemplo completo de Cloudflare Worker.


Padrão 6: Réplicas Incorporadas

Um arquivo SQLite local que sincroniza a partir de um primário Turso remoto. Leituras são locais (microssegundos), escritas encaminham para o remoto (15-50ms).

const client = createClient({
  url: "file:local-replica.db",
  syncUrl: process.env.TURSO_DATABASE_URL,
  authToken: process.env.TURSO_AUTH_TOKEN,
  syncInterval: 60, // Sincronização automática a cada 60 segundos
});
await client.sync(); // Popular a réplica local antes da primeira leitura

Pontos-chave: URL file: para a réplica local, syncUrl para o primário remoto, chame sync() na inicialização, leituras são locais, escritas encaminham para o remoto com semântica leia-suas-próprias-escritas.

Quando usar: VMs, VPS, containers ou qualquer processo de longa duração com acesso ao sistema de arquivos. Não disponível em runtimes serverless/edge sem sistema de arquivos. Veja examples/embedded-replicas.md para padrões completos, incluindo sincronização manual, modo offline e criptografia.


Padrão 7: Grupos de Banco de Dados e Multirregião

Turso organiza bancos de dados em grupos. Cada grupo tem uma região primária e locais de réplica opcionais. Todos os bancos de dados em um grupo herdam seus locais.

Todas as escritas vão para a região primária, independentemente de qual réplica atendeu à leitura. Adicionar mais locais melhora a latência de leitura globalmente, mas não reduz a latência de escrita. A latência de escrita é determinada pela distância até a região primária.

Veja reference.md para a referência completa de comandos CLI do Turso (turso db create, turso group create, turso db shell, etc.).


<decision_framework>

Framework de Decisão

batch() vs transaction()

Todas as instruções SQL são conhecidas antecipadamente (sem lógica condicional entre elas)?
+-- SIM --> Use batch() (única viagem de ida e volta, transação implícita, preferido)
+-- NÃO  --> Instruções posteriores dependem de resultados de instruções anteriores?
    +-- SIM --> Use transaction() (interativa, várias viagens de ida e volta, mantém bloqueio)
    +-- NÃO  --> Use batch()

Seleção de Caminho de Importação

Qual ambiente de runtime?
+-- Node.js / Bun / Deno / VM / container
|   +-- Precisa de réplicas incorporadas (arquivo local)?
|   |   +-- SIM --> @libsql/client com URL file: + syncUrl
|   |   +-- NÃO  --> @libsql/client com URL libsql://
+-- Cloudflare Workers / Vercel Edge / navegador / serverless sem sistema de arquivos
    +-- @libsql/client/web (apenas conexões remotas, sem URLs file:)

Réplica Incorporada vs Apenas Remoto

O processo é de longa duração com acesso ao sistema de arquivos?
+-- SIM --> Precisa de latência de leitura abaixo de milissegundo?
|   +-- SIM --> Réplica incorporada (URL file: + syncUrl)
|   +-- NÃO  --> Apenas remoto é mais simples (URL libsql://)
+-- NÃO (serverless, edge, curta duração)
    +-- Apenas remoto (@libsql/client/web, URL libsql://)

Seleção de Modo de Transação

Quais operações o batch/transação executará?
+-- Apenas consultas SELECT --> "read" (permite execução paralela em réplicas)
+-- Qualquer INSERT / UPDATE / DELETE --> "write" (adquire bloqueio exclusivo)
+-- Incerteza no momento da chamada --> "deferred" (inicia leitura, escala se necessário)

</decision_framework>


<red_flags>

SINALIZADORES VERMELHOS

Problemas de Alta Prioridade:

  • Modo de transação ausente em batch()/transaction() -- Omitir o segundo argumento usa como padrão "deferred", que inicia somente leitura e pode silenciosamente falhar ao adquirir um bloqueio de escrita para INSERT/UPDATE/DELETE. Sempre especifique o modo explicitamente.
  • Usando @libsql/client em runtimes edge -- O pacote base inclui ligações SQLite nativas que falham em Cloudflare Workers, Vercel Edge Functions e ambientes semelhantes. Use @libsql/client/web em vez disso.
  • Interpolação de strings em SQL -- execute(\SELECT * FROM users WHERE id = '${id}'`)é uma vulnerabilidade de injeção SQL. Sempre use consultas parametrizadas comargs`.
  • Acessando o arquivo da réplica incorporada diretamente -- Abrir o arquivo .db local com outro cliente SQLite enquanto o cliente libSQL está em execução causa corrupção de dados. Acesse apenas através do cliente.

Problemas de Prioridade Média:

  • Usando transaction() quando batch() é suficiente -- Transações interativas mantêm um bloqueio de banco de dados com timeout de 5 segundos, requerem várias viagens de ida e volta e bloqueiam outros escritores. Use batch() para conjuntos de instruções predeterminados.
  • Não chamar client.close() -- Em processos de curta duração (scripts CLI, desmontagem de teste), esquecer de fechar o cliente deixa conexões e manipuladores de arquivos abertos.
  • Ignorando a latência de escrita com réplicas incorporadas -- Leituras são em microssegundos (locais), mas escritas são de 15 a 50 ms (encaminhadas para o primário remoto). Projete adequadamente -- evite loops de escrita apertados.
  • Definindo syncInterval muito baixo -- Cada sincronização puxa todos os frames alterados (4KB cada). Intervalos abaixo de 1 segundo em bancos de dados com muitas escritas geram sobrecarga significativa de rede e E/S.

Erros Comuns:

  • Nome do pacote errado -- O pacote é @libsql/client, não libsql-client, @turso/client ou turso-client.
  • Prefixo de parâmetro nomeado no objeto args -- Args usam nomes sem prefixo: { name: "Alice" } corresponde a :name, @name e $name no SQL. Não inclua o prefixo: { ":name": "Alice" } não corresponderá.
  • Esperar que lastInsertRowid seja um número -- É bigint | undefined. Se precisar de um número, converta explicitamente: Number(result.lastInsertRowid), mas esteja ciente da perda de precisão para rowids muito grandes.
  • Usando executeMultiple() para operações atômicas -- executeMultiple() executa texto SQL bruto (separado por ponto e vírgula) sem parametrização e sem transação implícita. Use batch() para operações parametrizadas atômicas.

Armadilhas e Casos Extremos:

  • Instruções batch() compartilham uma transação, mas NÃO são paralelas -- Elas executam sequencialmente. last_insert_rowid() reflete a instrução INSERT anterior dentro do mesmo batch.

[O conteúdo continua com a seção truncada...]

Individual skills in this repo

This repo contains 11 individual skills — each has its own dedicated page.

Habilidades Relacionadas