> ## Documentation Index
> Fetch the complete documentation index at: https://zapo.to/llms.txt
> Use this file to discover all available pages before exploring further.

# Referência de stores

> Referência de configuração dos pacotes de backend SQLite, PostgreSQL, MySQL, Redis e MongoDB, com campos, padrões e exemplos de uso.

Cada pacote de backend exporta uma factory `create*Store` que você passa para uma entrada de `backends` do [`createStore`](/pt-br/concepts/stores). Todos os backends implementam os mesmos contratos de store por domínio, então trocar de backend é uma mudança de config, não de código.

<h2 id="sqlite">
  SQLite
</h2>

`@zapo-js/store-sqlite` — `createSqliteStore(config)`.

```ts theme={null}
import { createSqliteStore } from '@zapo-js/store-sqlite'

const sqlite = createSqliteStore({
  path: '.auth/state.sqlite',
  driver: 'auto'
})
```

| Campo        | Tipo                                                              | Descrição                                                                         |
| ------------ | ----------------------------------------------------------------- | --------------------------------------------------------------------------------- |
| `path`       | `string`                                                          | Caminho do arquivo de banco de dados. Mutuamente exclusivo com `connection`.      |
| `connection` | `WaSqliteConnection`                                              | Connection já aberta para reutilizar. Mutuamente exclusivo com `path`.            |
| `driver`     | `WaSqliteDriver`                                                  | Seleção do driver nativo (`'auto'`, …). Ignorado quando `connection` é informado. |
| `pragmas`    | `Record<string, string \| number>`                                | Pragmas do SQLite. Ignorado quando `connection` é informado.                      |
| `tableNames` | `WaSqliteTableNameOverrides`                                      | Sobrescreve os nomes das tabelas. Ignorado quando `connection` é informado.       |
| `batchSizes` | `WaSqliteBatchSizeSelection`                                      | Ajusta os tamanhos de lote.                                                       |
| `cacheTtlMs` | `{ retryMs?, groupMetadataMs?, deviceListMs?, messageSecretMs? }` | TTLs de cache.                                                                    |

Informe exatamente um entre `path` e `connection`. Com `path`, a biblioteca abre (e faz ref-count d)e sua própria connection e a fecha em `store.destroy()`. Com `connection`, você é dono do ciclo de vida — `store.destroy()` mantém a connection aberta para você continuar usando em outros lugares.

<h3 id="bring-your-own-connection">
  Use sua própria connection
</h3>

Compartilhe um único handle do SQLite com o resto da sua aplicação abrindo-o você mesmo com `openSqliteConnection` e passando via `connection`:

```ts theme={null}
import { createStore } from 'zapo-js'
import { createSqliteStore, openSqliteConnection } from '@zapo-js/store-sqlite'

const connection = await openSqliteConnection({
  path: 'app.sqlite',
  sessionId: 'shared',
  pragmas: { journal_mode: 'WAL', synchronous: 'NORMAL' }
})

const store = createStore({
  backends: { sqlite: createSqliteStore({ connection }) },
  providers: {
    auth: 'sqlite',
    signal: 'sqlite',
    preKey: 'sqlite',
    session: 'sqlite',
    identity: 'sqlite',
    senderKey: 'sqlite',
    appState: 'sqlite',
    privacyToken: 'sqlite',
    messages: 'none',
    threads: 'none',
    contacts: 'none'
  }
})

// ... use a connection em outras partes da sua aplicação ...

await store.destroy()
connection.close() // você abriu, você fecha
```

Requer a peer dependency `better-sqlite3`.

<h2 id="postgresql">
  PostgreSQL
</h2>

`@zapo-js/store-postgres` — `createPostgresStore(config)`.

```ts theme={null}
import { createPostgresStore } from '@zapo-js/store-postgres'

const postgres = createPostgresStore({
  pool: { connectionString: process.env.DATABASE_URL },
  tablePrefix: 'wa_'
})
```

| Campo                  | Tipo                        | Descrição                                                                                                                                         |
| ---------------------- | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| `pool`                 | `Pool \| PoolConfig`        | Um pool `pg` existente ou uma config de pool. **Obrigatório.**                                                                                    |
| `tablePrefix`          | `string`                    | Prefixo para as tabelas criadas.                                                                                                                  |
| `cacheTtlMs`           | object                      | TTLs de cache (mesmo formato do SQLite).                                                                                                          |
| `cleanup`              | `{ intervalMs?, onError? }` | Poller de limpeza em segundo plano.                                                                                                               |
| `batchInsertChunkSize` | `number`                    | Limite superior de linhas por `INSERT` multi-linha em escritas em lote. Padrão `500`. Veja [Chunking de inserts em lote](#batch-insert-chunking). |

Também exporta `createPgPool` e `ensurePgMigrations`. Requer a peer dependency `pg`.

<h2 id="mysql">
  MySQL
</h2>

`@zapo-js/store-mysql` — `createMysqlStore(config)`.

```ts theme={null}
import { createMysqlStore } from '@zapo-js/store-mysql'

const mysql = createMysqlStore({
  pool: { uri: process.env.MYSQL_URL },
  tablePrefix: 'wa_'
})
```

| Campo                  | Tipo                                  | Descrição                                                                                                                                         |
| ---------------------- | ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| `pool`                 | `Pool \| PoolOptions`                 | Um pool `mysql2` ou opções. **Obrigatório.**                                                                                                      |
| `tablePrefix`          | `string`                              | Prefixo para as tabelas criadas.                                                                                                                  |
| `cacheTtlMs`           | object                                | TTLs de cache.                                                                                                                                    |
| `cleanup`              | `{ enabled?, intervalMs?, onError? }` | Poller de limpeza em segundo plano.                                                                                                               |
| `batchInsertChunkSize` | `number`                              | Limite superior de linhas por `INSERT` multi-linha em escritas em lote. Padrão `500`. Veja [Chunking de inserts em lote](#batch-insert-chunking). |

Também exporta `createMysqlPool` e `ensureMysqlMigrations`. Requer a peer dependency `mysql2`.

<h3 id="batch-insert-chunking">
  Chunking de inserts em lote
</h3>

`batchInsertChunkSize` limita quantas linhas os backends PostgreSQL e MySQL agrupam em um único `INSERT` multi-linha em escritas em lote — sessões do Signal, identidades remotas, distribuições de sender key, geração de prekeys e os caminhos `upsertBatch` de message/thread/contact usados pela [write-behind persistence](/pt-br/concepts/configuration#write-behind-persistence).

O valor é arredondado **para baixo até a potência de dois mais próxima** internamente (`500 → 256`, `1000 → 512`, …), e cada lote é decomposto em sub-chunks de potência de dois. Isso mantém o conjunto de prepared statements distintos por conexão limitado a `log2(chunkSize) + 1` independentemente de como `N` varia entre chamadas — importante para ficar abaixo do cache do cliente mysql2 e da quota `max_prepared_stmt_count` do MySQL, e para manter o cache de statements nomeados do `pg` estável.

Mantenha o padrão a menos que benchmarks do seu workload mostrem benefício. Aumente para envios em grupo de alto fanout sustentados; reduza se os limites do seu banco forem apertados.

```ts theme={null}
createPostgresStore({
  pool: { connectionString: process.env.DATABASE_URL },
  batchInsertChunkSize: 2000 // → efetivo 1024
})
```

<h2 id="redis">
  Redis
</h2>

`@zapo-js/store-redis` — `createRedisStore(config)`.

```ts theme={null}
import { createRedisStore } from '@zapo-js/store-redis'

const redis = createRedisStore({
  redis: { host: '127.0.0.1', port: 6379 },
  keyPrefix: 'wa:'
})
```

| Campo        | Tipo                    | Descrição                                                                                                            |
| ------------ | ----------------------- | -------------------------------------------------------------------------------------------------------------------- |
| `redis`      | `Redis \| RedisOptions` | Uma instância `ioredis` ou opções. **Obrigatório.**                                                                  |
| `keyPrefix`  | `string`                | Prefixo para todas as chaves.                                                                                        |
| `cacheTtlMs` | object                  | TTLs de cache.                                                                                                       |
| `storeTtlMs` | object                  | TTL opt-in por domínio nos stores normalmente persistentes (veja [TTL por domínio do store](#per-domain-store-ttl)). |

<h3 id="per-domain-store-ttl">
  TTL por domínio do store
</h3>

Domínios de store persistentes nunca expiram por padrão. Defina `storeTtlMs.<domínio>Ms` para o Redis prunar as chaves daquele domínio automaticamente — implementado como `PEXPIRE` em cada escrita, sem precisar de cleanup em background. Qualquer domínio deixado de fora permanece persistente (comportamento byte-idêntico ao anterior).

```ts theme={null}
createRedisStore({
  redis: { host: '127.0.0.1', port: 6379 },
  storeTtlMs: {
    messagesMs: 30 * 24 * 60 * 60_000, // descarta mensagens mais antigas que 30 dias
    sessionMs:   7 * 24 * 60 * 60_000  // descarta sessions ociosas por 7 dias
  }
})
```

Duas semânticas, por tipo de domínio:

| Grupo               | Domínios                                                                       | Refresh em                                                                                              |
| ------------------- | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------- |
| **Dados**           | `messagesMs`, `threadsMs`, `contactsMs`, `privacyTokenMs`                      | **só write** — chaves expiram uma janela fixa após a última escrita (limite de histórico / retenção).   |
| **Crypto & sessão** | `preKeyMs`, `sessionMs`, `identityMs`, `signalMs`, `senderKeyMs`, `appStateMs` | **read e write** (sliding) — uma sessão ativa mantém as chaves vivas, só sessões ociosas são evictadas. |

<Warning>
  `auth` não tem botão de TTL de propósito: expirar credenciais de login deslogaria o device. Escolha um TTL de crypto maior que sua pior janela de ociosidade — uma sessão ociosa além dessa janela evicta as chaves de Signal / app-state e força um novo handshake ou re-sync. Valores `ttlMs` não-positivos são rejeitados na construção.
</Warning>

Requer a peer dependency `ioredis`.

<h2 id="mongodb">
  MongoDB
</h2>

`@zapo-js/store-mongo` — `createMongoStore(config)`.

```ts theme={null}
import { createMongoStore } from '@zapo-js/store-mongo'

const mongo = createMongoStore({
  db: { uri: process.env.MONGO_URL, database: 'zapo' },
  collectionPrefix: 'wa_'
})
```

| Campo              | Tipo                                | Descrição                                                        |
| ------------------ | ----------------------------------- | ---------------------------------------------------------------- |
| `db`               | `Db \| { uri, database, options? }` | Um `Db` do `mongodb` ou informações de conexão. **Obrigatório.** |
| `collectionPrefix` | `string`                            | Prefixo para as collections criadas.                             |
| `cacheTtlMs`       | object                              | TTLs de cache.                                                   |

Requer a peer dependency `mongodb`.

Escritas em lote usam `{ ordered: false }` para que upserts independentes rodem em paralelo — uma falha por documento não aborta o resto do lote.

<h2 id="cache-expiry-and-cleanup">
  Expiração e limpeza de cache
</h2>

Os quatro domínios de cache (`retry`, `groupMetadata`, `deviceList`, `messageSecret`) usam um TTL definido em `cacheTtlMs`. A forma como entradas expiradas são *removidas* varia por backend:

| Backend    | Mecanismo                                                                          | Ação necessária                      |
| ---------- | ---------------------------------------------------------------------------------- | ------------------------------------ |
| `memory`   | Sweep periódico in-process (intervalo `min(60s, ttl/2)`, timer com `unref()`)      | Nenhuma — automático                 |
| `sqlite`   | Filtro na leitura; linhas expiradas são ignoradas e sobrescritas no próximo upsert | Nenhuma                              |
| `postgres` | Filtro na leitura **+** poller de fundo apaga linhas expiradas                     | **Chamar `startCleanup(sessionId)`** |
| `mysql`    | Filtro na leitura **+** poller de fundo apaga linhas expiradas                     | **Chamar `startCleanup(sessionId)`** |
| `redis`    | `EXPIRE` nativo de chave                                                           | Nenhuma                              |
| `mongo`    | TTL index (monitor server-side, latência de \~60s)                                 | Nenhuma                              |

<Warning>
  No PostgreSQL e MySQL, sem `startCleanup` suas tabelas de cache crescem monotonicamente. Leituras continuam ignorando linhas expiradas (dado obsoleto nunca é servido), mas o uso de disco cresce sem limite. Inicie um poller **por session id**.
</Warning>

```ts theme={null}
const result = createPostgresStore({
  pool: { connectionString: process.env.DATABASE_URL },
  cleanup: { intervalMs: 60_000, onError: (e) => log.warn('cache cleanup failed', e) }
})

const store = createStore({ backends: { pg: result }, providers: { /* ... */ } })
const client = new WaClient({ store, sessionId: 'default' }, logger)

const poller = result.startCleanup('default')

process.on('SIGTERM', async () => {
  await client.disconnect()
  await result.destroy() // também para todos os pollers que ele rastreia
})
```

`cleanup.intervalMs` tem default `60_000` (60s). `result.destroy()` para todos os pollers iniciados por ele, então chamar `poller.stop()` manualmente só é útil se você quiser interromper a limpeza antes de derrubar o backend.

No MongoDB, a latência de \~60s do TTL monitor faz entradas de cache poderem persistir um pouco além do TTL configurado. Aceitável para `groupMetadata`/`deviceList`; troque pelo Redis se precisar de eviction mais agressivo.

<h2 id="mixing-backends">
  Misturando backends
</h2>

O `createStore` permite que cada domínio escolha um backend por nome, então você pode combiná-los:

```ts theme={null}
createStore({
  backends: { redis, postgres },
  providers: {
    auth: 'redis',
    signal: 'redis',
    preKey: 'redis',
    session: 'redis',
    identity: 'redis',
    senderKey: 'redis',
    appState: 'redis',
    privacyToken: 'redis',
    messages: 'postgres',
    threads: 'postgres',
    contacts: 'postgres'
  }
})
```

Todo domínio de persistência deve ser listado quando `backends` é definido — veja [Stores](/pt-br/concepts/stores#providers-are-required-when-you-set-backends) para a regra e os valores aceitos (`'<backend>'`, `'memory'`, `'none'`).
