- Guides
- Recipes
Sync records to a webhook
Send every create, update and delete in a collection to an external service, and let the record be indexed as normal.
Trigger: record.index:<nsid>. Needs: happyview-http. Script variables: WEBHOOK_URL.
The receiving service should treat each request as “set the state of this URI”, keyed by uri. The same record can reach this script more than once, so a service that appends a row per request will see duplicates. See Create scripts may run more than once.
local json = require("internal.json")local http = require("happyview.http")
function handle(input, ctx) local res = http.post(ctx.env.WEBHOOK_URL, { headers = { ["Content-Type"] = "application/json" }, body = json.encode({ action = input.action, uri = input.uri, did = input.did, record = input.record, }), })
if res.status >= 400 then error("webhook answered " .. res.status) end
-- `record` is absent on delete; `true` lets the delete proceed. return input.record or trueendimport http from "happyview.http";
export default async function handle(input, ctx) { const res = await http.post(ctx.env.WEBHOOK_URL, { headers: { "Content-Type": "application/json" }, body: JSON.stringify({ action: input.action, uri: input.uri, did: input.did, record: input.record, }), });
if (res.status >= 400) { throw new Error(`webhook answered ${res.status}`); }
// `record` is absent on delete; `true` lets the delete proceed. return input.record ?? true;}import http from "happyview.http";
interface RecordEvent { action: "create" | "update" | "delete"; uri: string; did: string; record?: Record<string, unknown>;}
interface Context { env: Record<string, string>;}
export default async function handle(input: RecordEvent, ctx: Context) { const res = await http.post(ctx.env.WEBHOOK_URL, { headers: { "Content-Type": "application/json" }, body: JSON.stringify({ action: input.action, uri: input.uri, did: input.did, record: input.record, }), });
if (res.status >= 400) { throw new Error(`webhook answered ${res.status}`); }
// `record` is absent on delete; `true` lets the delete proceed. return input.record ?? true;}How it works
Section titled “How it works”record.indexcatches creates, updates and deletes for the collection, unless a more specificrecord.create,record.updateorrecord.deletescript exists for an action.- The script posts the event to the URL in the
WEBHOOK_URLscript variable. http.postreturns a 4xx or 5xx answer as an ordinary result, so the script checksstatusand raises for it.- It returns the record so indexing goes ahead unchanged. A script that returns nothing would stop the record from being indexed.
If the request fails or the webhook answers with an error status, the script fails. A record script is retried up to 4 times in total and then dead-lettered, and the record is indexed anyway. Wrap the call in pcall (Lua) or try/catch (JavaScript and TypeScript) if you would rather log the failure and carry on.
Record scripts run before indexing, so a slow webhook slows down indexing for everything behind it. Keep the endpoint fast, or have the script create a background job and do the call from there.
© HappyProto

