claude up some atproto stuff
9
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix constellation API docs — correct endpoint params and limits

- getManyToManyCounts is for many-to-many relationships, not batch queries
- getBacklinkDids XRPC endpoint doesn't exist (legacy /links/distinct-dids only)
- default limit is 16, max is 100 (not 100/1000)
- update app-patterns to use parallel getBacklinksCount instead of wrong batch endpoint

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

zzstoatzz e3f5f002 1c6c9811

+75 -48
+25 -22
skills/app-patterns/SKILL.md
··· 29 29 Display like/repost/reply counts on a post, updated in real time. 30 30 31 31 ```javascript 32 - // 1. get initial counts from Constellation 32 + // 1. get initial counts from Constellation (parallel requests — no batch endpoint) 33 33 const base = "https://constellation.microcosm.blue/xrpc"; 34 - const counts = await fetch( 35 - `${base}/blue.microcosm.links.getManyToManyCounts?` + 36 - `subjects=${encodeURIComponent(postUri)}` + 37 - `&sources=app.bsky.feed.like:subject.uri` + 38 - `&sources=app.bsky.feed.repost:subject.uri` 39 - ).then(r => r.json()); 34 + const [likes, reposts] = await Promise.all([ 35 + fetch(`${base}/blue.microcosm.links.getBacklinksCount?subject=${encodeURIComponent(postUri)}&source=app.bsky.feed.like:subject.uri`).then(r => r.json()), 36 + fetch(`${base}/blue.microcosm.links.getBacklinksCount?subject=${encodeURIComponent(postUri)}&source=app.bsky.feed.repost:subject.uri`).then(r => r.json()), 37 + ]); 40 38 41 39 // 2. subscribe to real-time updates via Spacedust 42 40 const ws = new WebSocket( ··· 68 66 ).json() 69 67 70 68 # 2. get user's posts (via public Bluesky API or pdsx) 71 - # 3. for each post, get engagement counts from Constellation 72 - for post_uri in post_uris: 73 - counts = httpx.get( 74 - f"{constellation}/blue.microcosm.links.getManyToManyCounts", 75 - params={ 76 - "subjects": post_uri, 77 - "sources": [ 78 - "app.bsky.feed.like:subject.uri", 79 - "app.bsky.feed.repost:subject.uri", 80 - "app.bsky.feed.post:embed.record.uri", # quotes 81 - ], 82 - }, 83 - ).json() 84 - # 4. score and rank posts 69 + # 3. for each post, get engagement counts from Constellation (parallel) 70 + import asyncio 71 + 72 + async def get_counts(client: httpx.AsyncClient, post_uri: str): 73 + likes, reposts, quotes = await asyncio.gather( 74 + client.get(f"{constellation}/blue.microcosm.links.getBacklinksCount", 75 + params={"subject": post_uri, "source": "app.bsky.feed.like:subject.uri"}), 76 + client.get(f"{constellation}/blue.microcosm.links.getBacklinksCount", 77 + params={"subject": post_uri, "source": "app.bsky.feed.repost:subject.uri"}), 78 + client.get(f"{constellation}/blue.microcosm.links.getBacklinksCount", 79 + params={"subject": post_uri, "source": "app.bsky.feed.post:embed.record.uri"}), 80 + ) 81 + return { 82 + "likes": likes.json()["total"], 83 + "reposts": reposts.json()["total"], 84 + "quotes": quotes.json()["total"], 85 + } 86 + 87 + # 4. score and rank posts 85 88 ``` 86 89 87 90 ## pattern: notifications service ··· 169 172 170 173 ## tips 171 174 172 - - use `getManyToManyCounts` for batch engagement — one request for multiple posts × multiple interaction types 175 + - fire parallel `getBacklinksCount` requests for engagement counts — Constellation is fast enough that concurrency makes up for the lack of batching 173 176 - cache Constellation results client-side (localStorage, 15-minute TTL is reasonable) 174 177 - Spacedust's 21-second delay filters accidental interactions — usually what you want for UX 175 178 - all Microcosm services are unauthenticated for reads — no API keys needed
+50 -26
skills/constellation/SKILL.md
··· 37 37 ?subject=at://did:plc:.../app.bsky.feed.post/3lwcmto4tck2h 38 38 &source=app.bsky.feed.like:subject.uri 39 39 &limit=100 40 - &cursor=<hex> 41 40 ``` 41 + 42 + Parameters: 43 + - `subject` (required) — the target AT-URI or DID being linked to. must be URL-encoded. 44 + - `source` (required) — collection:path format. 45 + - `did` (optional, repeatable) — filter results to specific DIDs. repeat for multiple: `&did=did:plc:...&did=did:plc:...` 46 + - `limit` (optional) — default 16, max 100. 47 + - `reverse` (optional) — boolean, return in reverse order. 42 48 43 49 Returns `{ items: [...], cursor: "..." }`. Items contain the source record AT-URI and metadata. 44 50 ··· 49 55 &source=app.bsky.feed.like:subject.uri 50 56 ``` 51 57 58 + Parameters: 59 + - `subject` (required) — the target. must be URL-encoded. 60 + - `source` (required) — collection:path format. 61 + 52 62 Returns `{ total: 42 }`. 53 63 54 - ### get distinct DIDs 64 + ### many-to-many counts 55 65 ``` 56 - GET /xrpc/blue.microcosm.links.getBacklinkDids 66 + GET /xrpc/blue.microcosm.links.getManyToManyCounts 57 67 ?subject=at://did:plc:.../app.bsky.feed.post/3lwcmto4tck2h 58 68 &source=app.bsky.feed.like:subject.uri 69 + &pathToOther=otherThing.uri 59 70 ``` 60 71 61 - Returns unique identities that created backlinks (deduped by DID). 72 + This is for records that link to TWO things — e.g. a list item linking to both a list and a user. It is NOT a batch endpoint for multiple subjects. 62 73 63 - ### many-to-many queries 64 - ``` 65 - GET /xrpc/blue.microcosm.links.getManyToManyCounts 66 - ?subjects=at://...&subjects=at://... 67 - &sources=app.bsky.feed.like:subject.uri&sources=app.bsky.feed.repost:subject.uri 74 + Parameters: 75 + - `subject` (required) — the target. 76 + - `source` (required) — primary link specification. 77 + - `pathToOther` (required) — path to the secondary link field in the record. 78 + - `did` (optional, repeatable) — filter by DIDs. 79 + - `otherSubject` (optional, repeatable) — filter secondary links to specific subjects. 80 + - `limit` (optional) — default 16, max 100. 81 + 82 + ## getting engagement counts for multiple posts 83 + 84 + There is no batch endpoint for "get like counts for N posts in one request." To get engagement counts for a list of posts, fire parallel requests to `getBacklinksCount`: 85 + 86 + ```javascript 87 + const posts = ["at://did:plc:.../app.bsky.feed.post/abc", "at://did:plc:.../app.bsky.feed.post/def"]; 88 + const counts = await Promise.all( 89 + posts.flatMap(uri => [ 90 + fetch(`https://constellation.microcosm.blue/xrpc/blue.microcosm.links.getBacklinksCount?subject=${encodeURIComponent(uri)}&source=app.bsky.feed.like:subject.uri`).then(r => r.json()), 91 + fetch(`https://constellation.microcosm.blue/xrpc/blue.microcosm.links.getBacklinksCount?subject=${encodeURIComponent(uri)}&source=app.bsky.feed.repost:subject.uri`).then(r => r.json()), 92 + ]) 93 + ); 68 94 ``` 69 95 70 - Batch query: multiple subjects × multiple sources in one request. Use this for efficiency when displaying engagement counts on a list of posts. 96 + This is fast — Constellation responses are typically sub-10ms. 71 97 72 - ## pagination 98 + ## legacy endpoints 73 99 74 - - Cursors are opaque hex-encoded strings 75 - - Default limit: 100, max: 1000 76 - - Pass `cursor` from previous response to get next page 100 + These older REST endpoints still work but prefer the XRPC ones above: 101 + 102 + - `GET /links` — list backlinks. params: `target`, `collection`, `path`, `did`, `limit`, `reverse` 103 + - `GET /links/count` — count backlinks. params: `target`, `collection`, `path`, `cursor` 104 + - `GET /links/distinct-dids` — unique DIDs linking to target. params: `target`, `collection`, `path` 105 + - `GET /links/all` — all sources with links to target, including counts. params: `target` 106 + - `GET /links/count/distinct-dids` — count distinct DIDs. params: `target`, `collection`, `path`, `cursor` 107 + 108 + Legacy uses `target`, `collection`, `path` as separate params instead of `subject` + `source`. 77 109 78 110 ## examples 79 111 ··· 103 135 const { total } = await fetch(url).then(r => r.json()); 104 136 ``` 105 137 106 - Get all followers of a user: 138 + Get followers of a user: 107 139 ```bash 108 - curl "https://constellation.microcosm.blue/xrpc/blue.microcosm.links.getBacklinks?subject=did:plc:z72i7hdynmk6r22z27h6tvur&source=app.bsky.graph.follow:subject&limit=1000" 140 + curl "https://constellation.microcosm.blue/xrpc/blue.microcosm.links.getBacklinks?subject=did:plc:z72i7hdynmk6r22z27h6tvur&source=app.bsky.graph.follow:subject&limit=100" 109 141 ``` 110 142 111 - ## legacy endpoints 112 - 113 - The older REST endpoints still work but prefer the XRPC ones above: 114 - - `GET /links` — equivalent to getBacklinks 115 - - `GET /links/count` — equivalent to getBacklinksCount 116 - - `GET /links/count/distinct-dids` — equivalent to getBacklinkDids 117 - 118 - Legacy uses `target`, `collection`, `path` as separate params instead of `subject` + `source`. 119 - 120 143 ## tips 121 144 122 145 - Always use the full AT-URI for `subject` when querying about a specific record 123 146 - For user-level queries (followers, blocks), use just the DID as subject 124 - - Use `getManyToManyCounts` when displaying engagement on a feed — one request instead of N 147 + - For engagement counts on a feed, fire parallel `getBacklinksCount` requests — they're fast enough that concurrency makes up for the lack of batching 125 148 - Constellation is read-only and unauthenticated — no API key needed 149 + - Default limit is 16, max is 100. Always set `limit` explicitly if you want more results.