- Examples
Algolia sync
Push records to an Algolia search index whenever they are created, updated, or deleted on the network.
Trigger: record.index:<nsid>. Needs: happyview-http. Script variables: ALGOLIA_APP_ID, ALGOLIA_API_KEY.
local json = require("internal.json")local http = require("happyview.http")
-- Percent-encode everything but unreserved characters, so the AT URI fits in a path segmentlocal function enc(s) return (s:gsub("[^%w%-_%.~]", function(c) return string.format("%%%02X", c:byte()) end))end
function handle(input, ctx) local url = "https://" .. ctx.env.ALGOLIA_APP_ID .. ".algolia.net/1/indexes/records/" .. enc(input.uri) local headers = { ["X-Algolia-API-Key"] = ctx.env.ALGOLIA_API_KEY, ["X-Algolia-Application-Id"] = ctx.env.ALGOLIA_APP_ID, ["Content-Type"] = "application/json", }
local res if input.action == "delete" then res = http.delete(url, { headers = headers }) else res = http.put(url, { headers = headers, body = json.encode({ objectID = input.uri, collection = input.collection, did = input.did, record = input.record, }), }) end
if res.status >= 400 then error("algolia answered " .. res.status) end
-- On delete `record` is null, which still lets the delete proceed. return input.record or trueendimport http from "happyview.http";
export default async function handle(input, ctx) { const url = `https://${ctx.env.ALGOLIA_APP_ID}.algolia.net/1/indexes/records/${encodeURIComponent(input.uri)}`; const headers = { "X-Algolia-API-Key": ctx.env.ALGOLIA_API_KEY, "X-Algolia-Application-Id": ctx.env.ALGOLIA_APP_ID, "Content-Type": "application/json", };
const res = input.action === "delete" ? await http.delete(url, { headers }) : await http.put(url, { headers, body: JSON.stringify({ objectID: input.uri, collection: input.collection, did: input.did, record: input.record, }), });
if (res.status >= 400) { throw new Error(`algolia answered ${res.status}`); }
// `record` is null on delete, so this returns `true` and the delete proceeds. return input.record ?? true;}import http from "happyview.http";
interface RecordEvent { action: "create" | "update" | "delete"; uri: string; did: string; collection: string; record: Record<string, unknown> | null;}
interface Context { env: Record<string, string>;}
export default async function handle(input: RecordEvent, ctx: Context) { const url = `https://${ctx.env.ALGOLIA_APP_ID}.algolia.net/1/indexes/records/${encodeURIComponent(input.uri)}`; const headers = { "X-Algolia-API-Key": ctx.env.ALGOLIA_API_KEY, "X-Algolia-Application-Id": ctx.env.ALGOLIA_APP_ID, "Content-Type": "application/json", };
const res = input.action === "delete" ? await http.delete(url, { headers }) : await http.put(url, { headers, body: JSON.stringify({ objectID: input.uri, collection: input.collection, did: input.did, record: input.record, }), });
if (res.status >= 400) { throw new Error(`algolia answered ${res.status}`); }
// `record` is null on delete, so this returns `true` and the delete proceeds. return input.record ?? true;}How it works
Section titled “How it works”- On create or update: sends a
PUTrequest to Algolia’s index API with the record data, using the AT URI as theobjectID. Algolia replaces an object that already exists. - On delete: sends a
DELETErequest to remove the object from the index by its AT URI.
The AT URI is percent-encoded in the request path, with enc in Lua and encodeURIComponent in JavaScript and TypeScript, because its / characters would otherwise be read as path separators.
input is the record event. happyview.http returns a 4xx or 5xx answer as an ordinary result, so the script checks status and raises for it. A failed record script is retried and then dead-lettered.
Configuration
Section titled “Configuration”Set these as script variables from the dashboard or the admin API:
| Variable | Value |
|---|---|
ALGOLIA_APP_ID | Your Algolia Application ID |
ALGOLIA_API_KEY | An Algolia API key with the addObject and deleteObject ACLs |
The index name is the records segment of the URL. Change it to any name you like.
Use case
Section titled “Use case”Combine this with a query script that searches Algolia instead of the local database for full-text search beyond the substring match of happyview.db’s search.
© HappyProto

