Script Examples
Complex Mutations
Load an existing record, apply multiple transformations, and save it back.
Lexicon type: procedure
function handle()
if not input.uri then
return { error = "uri is required" }
end
local r = Record.load(input.uri)
if not r then
return { error = "not found" }
end
-- Increment a counter
r.likeCount = (r.likeCount or 0) + 1
-- Merge tags, deduplicating and capping at 10
r.tags = r.tags or {}
if input.tags then
for _, tag in ipairs(input.tags) do
local found = false
for _, t in ipairs(r.tags) do
if t == tag then
found = true
break
end
end
if not found then
r.tags[#r.tags + 1] = tag
end
end
-- Keep only the last 10
while #r.tags > 10 do
table.remove(r.tags, 1)
end
end
-- Normalize a string field
if input.title then
r.title = string.gsub(input.title, "^%s+", "")
r.title = string.gsub(r.title, "%s+$", "")
end
-- Set a computed field
r.updatedAt = now()
r:save()
return { uri = r._uri, cid = r._cid }
endHow it works
- Load the existing record with
Record.load. This gives you a mutableRecordinstance with all the current field values. - Apply transformations directly on the record's fields:
- Increment a counter: use
or 0to handle the field beingnilon first access. - Merge tags: iterate over
input.tags, skip duplicates already inr.tags, append new ones, then trim the list to 10. - Normalize a string: use
string.gsubto trim whitespace. - Set a timestamp: use
now()for UTC ISO 8601.
- Increment a counter: use
- Call
r:save(). Since_uriis set (from the load), this callsputRecordto update the record on the user's PDS.
Usage
const response = await fetch("http://127.0.0.1:3000/xrpc/xyz.statusphere.updatePost", {
method: "POST",
headers: {
"X-Client-Key": CLIENT_KEY,
Authorization: `Bearer ${TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
uri: "at://did:plc:abc/xyz.statusphere.post/abc123",
tags: ["tutorial", "atproto"],
title: " My Post Title ",
}),
});
const data = await response.json();Use case
This pattern is useful when updates involve more than simple field replacement: counters, bounded lists, string normalization, or computed fields. All mutations happen in memory before the single r:save() call, so there's no partial save: either all changes are written or none are.
If the record has a schema, HappyView only sends fields defined in the schema's properties to the PDS on save. Extra fields you set on the record instance are ignored.