Skip to main content

Procedure: Create Sidecar Records

Create two records with different collection NSIDs but the same rkey, linking them together by key.

Lexicon type: procedure

function handle()
local rkey = TID()

local post = Record("xyz.statusphere.post", {
text = input.text,
createdAt = now(),
})
post:set_rkey(rkey)

local metadata = Record("xyz.statusphere.postMetadata", {
lang = input.lang or "en",
source = input.source or "web",
createdAt = now(),
})
metadata:set_rkey(rkey)

Record.save_all({ post, metadata })

return {
post = { uri = post._uri, cid = post._cid },
metadata = { uri = metadata._uri, cid = metadata._cid },
}
end

How it works

  1. Generate a single TID() to use as the rkey for both records.
  2. Create a Record for each collection and call r:set_rkey() with the shared rkey.
  3. Save both records in parallel with Record.save_all().
  4. Return both URIs so the client knows the identity of each record.

Usage

curl -X POST http://localhost:3000/xrpc/xyz.statusphere.createPost \
-H "X-Client-Key: $CLIENT_KEY" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "text": "Hello world", "lang": "en", "source": "web" }'

The response includes URIs for both the post and its metadata:

{
"post": {
"uri": "at://did:plc:abc/xyz.statusphere.post/3abc123",
"cid": "bafyrei..."
},
"metadata": {
"uri": "at://did:plc:abc/xyz.statusphere.postMetadata/3abc123",
"cid": "bafyrei..."
}
}

Use case

Sidecar records are useful when you want to associate related data across collections without embedding everything in a single record. Because they share an rkey, you can derive one URI from the other:

at:// did:plc:abc /xyz.statusphere.post /3abc123
at:// did:plc:abc /xyz.statusphere.postMetadata /3abc123
^^^^^^^ same rkey

This is a common atproto pattern for keeping a primary record lean while storing auxiliary data (metadata, reactions, settings) in a companion collection.