A calm place to write long-form, and publish it to the open social web.
skypress.blog/
1/**
2 * Read records from a PDS via the public `com.atproto.repo` XRPC (no auth needed).
3 * `safeFetch` guards the (DID-doc-derived) PDS host against SSRF; failures resolve to
4 * null/empty so a bad host or unreachable PDS degrades gracefully.
5 */
6import { safeFetch } from '../net/safe-fetch';
7
8export interface RepoRecord< T = Record< string, unknown > > {
9 uri: string;
10 cid: string;
11 value: T;
12}
13
14/** Fetch a single record, or null if it doesn't exist / can't be fetched. */
15export async function getRecord< T = Record< string, unknown > >(
16 pdsUrl: string,
17 did: string,
18 collection: string,
19 rkey: string
20): Promise< RepoRecord< T > | null > {
21 const url =
22 `${ pdsUrl.replace( /\/$/, '' ) }/xrpc/com.atproto.repo.getRecord` +
23 `?repo=${ encodeURIComponent( did ) }` +
24 `&collection=${ encodeURIComponent( collection ) }` +
25 `&rkey=${ encodeURIComponent( rkey ) }`;
26 try {
27 const res = await safeFetch( url );
28 return res.ok ? ( ( await res.json() ) as RepoRecord< T > ) : null;
29 } catch {
30 return null;
31 }
32}
33
34/** List records in a collection (most recent first). */
35export async function listRecords< T = Record< string, unknown > >(
36 pdsUrl: string,
37 did: string,
38 collection: string,
39 limit = 50
40): Promise< RepoRecord< T >[] > {
41 const url =
42 `${ pdsUrl.replace( /\/$/, '' ) }/xrpc/com.atproto.repo.listRecords` +
43 `?repo=${ encodeURIComponent( did ) }` +
44 `&collection=${ encodeURIComponent( collection ) }&limit=${ limit }`;
45 try {
46 const res = await safeFetch( url );
47 if ( ! res.ok ) {
48 return [];
49 }
50 const data: { records?: RepoRecord< T >[] } = await res.json();
51 return data.records ?? [];
52 } catch {
53 return [];
54 }
55}