Keep Meilisearch in sync with Postgres.
Upserts through the documents API, deletes through delete-batch, both in per-row order.
The Meilisearch sink posts each run of inserts and updates as documents with primaryKey=id and each run of deletes as a delete-batch of ids, in order. Meilisearch acknowledges with a task; delivery counts as done on acceptance.
How delivery works
| Aspect | Behaviour for Meilisearch |
|---|---|
| Operation | Insert/update → POST /indexes/<index>/documents?primaryKey=id with the row data + id; delete → POST /documents/delete-batch with ids. |
| Batching | Runs of upserts and deletes are flushed in order within each batch. |
| Ordering | Per-row order preserved. |
| Idempotency | Upsert by id. |
| On failure | Any 2xx is accepted. A 429 pauses the sink for the Retry-After period (30 s if absent) without spending a retry or metering egress. A failed request is retried up to 5 times with exponential backoff (30 s, 1, 2, 4 min, with jitter). After the fifth failure the message is parked as undelivered, the console shows it with the last error, and one click replays it — later changes to the same row wait behind it so order is preserved. |
| Connection check | GET /indexes?limit=1 — reachability and key. |
Set up in three steps
-
Connect your database
Paste a connection string and press Test. Waltail checks the version (12+), that
wal_levelislogical, that the user may replicate, and that a slot is free — and shows the fix for anything that fails. Then it creates its own publication and replication slot. Connection guide → -
Add Meilisearch as a destination
Pick the tables to stream, choose Meilisearch, and fill in Server URL, API key, Index. Press Test — Waltail checks it can reach and write to Meilisearch before anything is saved.
-
Change a row
Insert or update a row. The console shows the first event as it is captured and delivered; from then on, every committed change follows within about a second. Have rows that already exist? Run a backfill — confirm the estimate and they arrive through Meilisearch as
readevents.
Configuration
Fields as the console asks for them. Secrets are encrypted at rest and never shown again.
| Field | Console label | Required | Notes |
|---|---|---|---|
endpoint | Server URL | Yes | |
api_key | API key | Yes | |
index | Index | Yes | Created by the first write. |
Every sink also has two tuning settings: batch size (messages per request, default 100) and rate limit (messages per second, default unlimited). Changes to one row are never in flight twice at once.
Example
Every change is this JSON envelope — data is the row after the change, old the row before it when replica identity provides it, null otherwise:
{
"op": "update",
"table": "public.orders",
"data": { "id": 42, "status": "shipped", "total": 19.99 },
"old": { "id": 42, "status": "paid", "total": 19.99 },
"seq": 137,
"idempotency_key": "9f1c3d2e-6b4a-4c7e-9d0f-2a1b3c4d5e6f",
"commit_lsn": "0/1A2B3C8"
}
Questions
Is delivery synchronous?
Meilisearch processes writes as async tasks. Waltail treats the 202 acceptance as delivered; the index catches up within Meilisearch's task queue.
Does it work with Meilisearch Cloud?
Yes — the project URL and an admin or write-scoped key.