Skip to content

Meilisearch sync

Push records to a Meilisearch search index whenever they are created, updated, or deleted on the network.

Script type: record event (e.g. record.index:<nsid>)

local json = require("internal.json")
local http = require("happyview.http")
function handle(input, ctx)
-- Meilisearch ids may contain only letters, digits, `-` and `_`
local id = (input.did .. "_" .. input.collection .. "_" .. input.rkey):gsub("[^%w%-_]", "_")
local headers = {
["Authorization"] = "Bearer " .. ctx.env.MEILISEARCH_API_KEY,
["Content-Type"] = "application/json",
}
if input.action == "delete" then
http.delete(ctx.env.MEILISEARCH_URL .. "/indexes/records/documents/" .. id, {
headers = headers,
})
else
http.post(ctx.env.MEILISEARCH_URL .. "/indexes/records/documents", {
headers = headers,
body = json.encode(json.to_array({
{
id = id,
uri = input.uri,
collection = input.collection,
did = input.did,
record = input.record,
},
})),
})
end
-- A delete has no record to return; `true` lets it proceed
if input.action == "delete" then
return true
end
return input.record
end
  1. On create or update: sends a POST request to Meilisearch’s document API with the record data wrapped in an array. Meilisearch upserts by id, so a document for the same record is replaced.
  2. On delete: sends a DELETE request to remove the document from the index by its id.

A Meilisearch document id may contain only letters, digits, - and _, so an AT URI cannot be one. The script builds the id from the DID, collection and rkey, replaces every other character with _, and stores the AT URI in a uri field.

input is the record event. json.to_array ensures the table is encoded as a JSON array (Meilisearch expects an array of documents). See JSON.

This script uses script variables instead of hardcoded values. Set these via the admin API or dashboard:

VariableValue
MEILISEARCH_URLYour Meilisearch instance URL (e.g. http://meilisearch.railway.internal:7700)
MEILISEARCH_API_KEYA Meilisearch API key with write permissions

Script variables are stored in the happyview_script_variables table and accessible as ctx.env.* in Lua.

This record script keeps an external search index in sync with your indexed records in real time. Users searching through Meilisearch get results that reflect the latest state of the network without polling or scheduled jobs.

Meilisearch is a good fit for self-hosted deployments — colocate it alongside HappyView (e.g. on the same Railway project) for sub-millisecond network latency.

Combine this with a query script that searches Meilisearch instead of the local database for a full-text search experience that goes beyond what happyview.db’s search offers.