- Guides
- Recipes
Update or delete
A single endpoint that handles create, update, and delete based on the input fields.
Trigger: xrpc.procedure:<nsid>. Needs: happyview-db, happyview-record.
local db = require("happyview.db")local record = require("happyview.record")
function handle(input, ctx) if input.delete and input.uri then record.delete(input.uri) return { success = true } end
if input.uri then -- Update existing local existing = db.get(input.uri) if not existing then return { error = "RecordNotFound", message = "no record at that uri" } end local body = existing.record body.status = input.status return record.put(input.uri, body) end
if input.delete then return { error = "UriRequired", message = "a delete needs the uri of the record" } end
-- Create new return record.create(ctx.collection, input)endimport db from "happyview.db";import record from "happyview.record";
export default async function handle(input, ctx) { if (input.delete && input.uri) { await record.delete(input.uri); return { success: true }; }
if (input.uri) { // Update existing const existing = await db.get(input.uri); if (!existing) { return { error: "RecordNotFound", message: "no record at that uri" }; } const body = { ...existing.record, status: input.status }; return record.put(input.uri, body); }
if (input.delete) { return { error: "UriRequired", message: "a delete needs the uri of the record" }; }
// Create new return record.create(ctx.collection, input);}import db from "happyview.db";import record from "happyview.record";
interface Input { uri?: string; status?: string; delete?: boolean;}
export default async function handle(input: Input, ctx: { collection: string }) { if (input.delete && input.uri) { await record.delete(input.uri); return { success: true }; }
if (input.uri) { // Update existing const existing = await db.get(input.uri); if (!existing) { return { error: "RecordNotFound", message: "no record at that uri" }; } const body = { ...existing.record, status: input.status }; return record.put(input.uri, body); }
if (input.delete) { return { error: "UriRequired", message: "a delete needs the uri of the record" }; }
// Create new return record.create(ctx.collection, input);}How it works
Section titled “How it works”- If
input.deleteis truthy andinput.uriis provided, delete the record withrecord.delete, which removes it from the caller’s PDS and the index. - If only
input.uriis provided, load the stored body withdb.get, change its fields, and write it back withrecord.put, which callsputRecord. If no record exists at that URI, return aRecordNotFounderror. - If
input.deleteis set without auri, return aUriRequirederror rather than creating a record. - Otherwise, create a new record from the input with
record.create.
RecordNotFound and UriRequired must be declared under defs.main.errors in the lexicon, as in "errors": [{ "name": "RecordNotFound" }, { "name": "UriRequired" }], for the response to be a 400. An undeclared error is sent as an ordinary 200 response body.
The DPoP proof is signed per request; an app built on the HappyView SDK gets this from its session.
Create:
const response = await fetch("http://127.0.0.1:3000/xrpc/xyz.statusphere.setRecord", { method: "POST", headers: { "X-Client-Key": CLIENT_KEY, Authorization: `DPoP ${ACCESS_TOKEN}`, DPoP: DPOP_PROOF, "Content-Type": "application/json", }, body: JSON.stringify({ status: "hello" }),});const data = await response.json();const response = await fetch("http://127.0.0.1:3000/xrpc/xyz.statusphere.setRecord", { method: "POST", headers: { "X-Client-Key": CLIENT_KEY, Authorization: `DPoP ${ACCESS_TOKEN}`, DPoP: DPOP_PROOF, "Content-Type": "application/json", }, body: JSON.stringify({ status: "hello" }),});const data = await response.json();let response = client .post("http://127.0.0.1:3000/xrpc/xyz.statusphere.setRecord") .header("X-Client-Key", client_key) .header("Authorization", format!("DPoP {access_token}")) .header("DPoP", dpop_proof) .json(&serde_json::json!({ "status": "hello" })) .send() .await?;clientKey := "hvc_..." // your API client keyaccessToken := "..." // DPoP access tokendpopProof := "..." // DPoP proof JWT
body := `{ "status": "hello" }`req, err := http.NewRequest("POST", "http://127.0.0.1:3000/xrpc/xyz.statusphere.setRecord", bytes.NewBufferString(body))if err != nil { log.Fatal(err)}req.Header.Set("X-Client-Key", clientKey)req.Header.Set("Authorization", "DPoP "+accessToken)req.Header.Set("DPoP", dpopProof)req.Header.Set("Content-Type", "application/json")resp, err := http.DefaultClient.Do(req)if err != nil { log.Fatal(err)}defer resp.Body.Close()curl -X POST http://127.0.0.1:3000/xrpc/xyz.statusphere.setRecord \ -H "X-Client-Key: $CLIENT_KEY" \ -H "Authorization: DPoP $ACCESS_TOKEN" \ -H "DPoP: $DPOP_PROOF" \ -H "Content-Type: application/json" \ -d '{ "status": "hello" }'Update:
const response = await fetch("http://127.0.0.1:3000/xrpc/xyz.statusphere.setRecord", { method: "POST", headers: { "X-Client-Key": CLIENT_KEY, Authorization: `DPoP ${ACCESS_TOKEN}`, DPoP: DPOP_PROOF, "Content-Type": "application/json", }, body: JSON.stringify({ uri: "at://did:plc:abc/xyz.statusphere.record/abc123", status: "updated", }),});const data = await response.json();const response = await fetch("http://127.0.0.1:3000/xrpc/xyz.statusphere.setRecord", { method: "POST", headers: { "X-Client-Key": CLIENT_KEY, Authorization: `DPoP ${ACCESS_TOKEN}`, DPoP: DPOP_PROOF, "Content-Type": "application/json", }, body: JSON.stringify({ uri: "at://did:plc:abc/xyz.statusphere.record/abc123", status: "updated", }),});const data = await response.json();let response = client .post("http://127.0.0.1:3000/xrpc/xyz.statusphere.setRecord") .header("X-Client-Key", client_key) .header("Authorization", format!("DPoP {access_token}")) .header("DPoP", dpop_proof) .json(&serde_json::json!({ "uri": "at://did:plc:abc/xyz.statusphere.record/abc123", "status": "updated" })) .send() .await?;clientKey := "hvc_..." // your API client keyaccessToken := "..." // DPoP access tokendpopProof := "..." // DPoP proof JWT
body := `{ "uri": "at://did:plc:abc/xyz.statusphere.record/abc123", "status": "updated" }`req, err := http.NewRequest("POST", "http://127.0.0.1:3000/xrpc/xyz.statusphere.setRecord", bytes.NewBufferString(body))if err != nil { log.Fatal(err)}req.Header.Set("X-Client-Key", clientKey)req.Header.Set("Authorization", "DPoP "+accessToken)req.Header.Set("DPoP", dpopProof)req.Header.Set("Content-Type", "application/json")resp, err := http.DefaultClient.Do(req)if err != nil { log.Fatal(err)}defer resp.Body.Close()curl -X POST http://127.0.0.1:3000/xrpc/xyz.statusphere.setRecord \ -H "X-Client-Key: $CLIENT_KEY" \ -H "Authorization: DPoP $ACCESS_TOKEN" \ -H "DPoP: $DPOP_PROOF" \ -H "Content-Type: application/json" \ -d '{ "uri": "at://did:plc:abc/xyz.statusphere.record/abc123", "status": "updated" }'Delete:
const response = await fetch("http://127.0.0.1:3000/xrpc/xyz.statusphere.setRecord", { method: "POST", headers: { "X-Client-Key": CLIENT_KEY, Authorization: `DPoP ${ACCESS_TOKEN}`, DPoP: DPOP_PROOF, "Content-Type": "application/json", }, body: JSON.stringify({ uri: "at://did:plc:abc/xyz.statusphere.record/abc123", delete: true, }),});const data = await response.json();const response = await fetch("http://127.0.0.1:3000/xrpc/xyz.statusphere.setRecord", { method: "POST", headers: { "X-Client-Key": CLIENT_KEY, Authorization: `DPoP ${ACCESS_TOKEN}`, DPoP: DPOP_PROOF, "Content-Type": "application/json", }, body: JSON.stringify({ uri: "at://did:plc:abc/xyz.statusphere.record/abc123", delete: true, }),});const data = await response.json();let response = client .post("http://127.0.0.1:3000/xrpc/xyz.statusphere.setRecord") .header("X-Client-Key", client_key) .header("Authorization", format!("DPoP {access_token}")) .header("DPoP", dpop_proof) .json(&serde_json::json!({ "uri": "at://did:plc:abc/xyz.statusphere.record/abc123", "delete": true })) .send() .await?;clientKey := "hvc_..." // your API client keyaccessToken := "..." // DPoP access tokendpopProof := "..." // DPoP proof JWT
body := `{ "uri": "at://did:plc:abc/xyz.statusphere.record/abc123", "delete": true }`req, err := http.NewRequest("POST", "http://127.0.0.1:3000/xrpc/xyz.statusphere.setRecord", bytes.NewBufferString(body))if err != nil { log.Fatal(err)}req.Header.Set("X-Client-Key", clientKey)req.Header.Set("Authorization", "DPoP "+accessToken)req.Header.Set("DPoP", dpopProof)req.Header.Set("Content-Type", "application/json")resp, err := http.DefaultClient.Do(req)if err != nil { log.Fatal(err)}defer resp.Body.Close()curl -X POST http://127.0.0.1:3000/xrpc/xyz.statusphere.setRecord \ -H "X-Client-Key: $CLIENT_KEY" \ -H "Authorization: DPoP $ACCESS_TOKEN" \ -H "DPoP: $DPOP_PROOF" \ -H "Content-Type: application/json" \ -d '{ "uri": "at://did:plc:abc/xyz.statusphere.record/abc123", "delete": true }'Use case
Section titled “Use case”This pattern puts create, update and delete behind one procedure, so your app needs fewer endpoints. The uri and delete fields in the input pick the action.
© HappyProto

