Monorepo for Aesthetic.Computer
aesthetic.computer
1# User Pages - API Examples
2
3Real examples of ATProto API responses used by the user pages.
4
5## 1. Resolve Handle to DID
6
7### Request
8```
9GET https://at.aesthetic.computer/xrpc/com.atproto.identity.resolveHandle?handle=fifi.at.aesthetic.computer
10```
11
12### Response
13```json
14{
15 "did": "did:plc:xyz123abc456def789ghi012jkl345"
16}
17```
18
19## 2. List Painting Records
20
21### Request
22```
23GET https://at.aesthetic.computer/xrpc/com.atproto.repo.listRecords?repo=did:plc:xyz123...&collection=computer.aesthetic.painting&limit=100
24```
25
26### Response
27```json
28{
29 "records": [
30 {
31 "uri": "at://did:plc:xyz123.../computer.aesthetic.painting/3jzx7k2m4n5",
32 "cid": "bafyreib...",
33 "value": {
34 "$type": "computer.aesthetic.painting",
35 "slug": "2023.10.20.15.30.45",
36 "code": "abc123",
37 "createdAt": "2023-10-20T15:30:45.123Z",
38 "image": {
39 "$type": "blob",
40 "ref": {
41 "$link": "bafkreiabcd..."
42 },
43 "mimeType": "image/png",
44 "size": 45678
45 },
46 "ref": "65f3d2c1a8b9e4f5a6b7c8d9"
47 }
48 },
49 {
50 "uri": "at://did:plc:xyz123.../computer.aesthetic.painting/2hwy6j1l3m4",
51 "cid": "bafyreic...",
52 "value": {
53 "$type": "computer.aesthetic.painting",
54 "slug": "2023.10.19.12.15.30",
55 "code": "def456",
56 "createdAt": "2023-10-19T12:15:30.456Z",
57 "image": {
58 "$type": "blob",
59 "ref": {
60 "$link": "bafkreixyz..."
61 },
62 "mimeType": "image/png",
63 "size": 38901
64 },
65 "ref": "65f3d2c1a8b9e4f5a6b7c8d8"
66 }
67 }
68 ],
69 "cursor": "3jzx7k2m4n5"
70}
71```
72
73## 3. List Mood Records
74
75### Request
76```
77GET https://at.aesthetic.computer/xrpc/com.atproto.repo.listRecords?repo=did:plc:xyz123...&collection=computer.aesthetic.mood&limit=100
78```
79
80### Response
81```json
82{
83 "records": [
84 {
85 "uri": "at://did:plc:xyz123.../computer.aesthetic.mood/5pzn8r3q4t6",
86 "cid": "bafyreid...",
87 "value": {
88 "$type": "computer.aesthetic.mood",
89 "mood": "Feeling creative today! Just finished a new piece.",
90 "when": "2023-10-20T14:25:15.789Z",
91 "ref": "65f3d2c1a8b9e4f5a6b7c8d7"
92 }
93 },
94 {
95 "uri": "at://did:plc:xyz123.../computer.aesthetic.mood/4nyi7q2p3s5",
96 "cid": "bafyreie...",
97 "value": {
98 "$type": "computer.aesthetic.mood",
99 "mood": "Working on something experimental...",
100 "when": "2023-10-19T09:45:30.123Z",
101 "ref": "65f3d2c1a8b9e4f5a6b7c8d6"
102 }
103 }
104 ],
105 "cursor": null
106}
107```
108
109## 4. Get Blob (Image)
110
111### Request
112```
113GET https://at.aesthetic.computer/xrpc/com.atproto.sync.getBlob?did=did:plc:xyz123...&cid=bafkreiabcd...
114```
115
116### Response
117Binary image data (PNG)
118
119## Record URI Structure
120
121ATProto URIs follow this pattern:
122```
123at://[DID]/[COLLECTION]/[RKEY]
124```
125
126Examples:
127- `at://did:plc:xyz123.../computer.aesthetic.painting/3jzx7k2m4n5`
128- `at://did:plc:xyz123.../computer.aesthetic.mood/5pzn8r3q4t6`
129
130Components:
131- **DID**: Decentralized identifier for the user
132- **Collection**: Lexicon name (e.g., `computer.aesthetic.painting`)
133- **RKEY**: Record key (unique ID within collection)
134
135## Field Descriptions
136
137### Painting Record Fields
138
139| Field | Type | Description |
140|-------|------|-------------|
141| `$type` | string | Always `computer.aesthetic.painting` |
142| `slug` | string | Timestamp-based painting ID |
143| `code` | string | Short alphanumeric code (e.g., `abc123`) |
144| `createdAt` | string | ISO 8601 timestamp |
145| `image` | blob | Reference to image data |
146| `ref` | string | MongoDB ObjectId reference |
147
148### Mood Record Fields
149
150| Field | Type | Description |
151|-------|------|-------------|
152| `$type` | string | Always `computer.aesthetic.mood` |
153| `mood` | string | The mood text content |
154| `when` | string | ISO 8601 timestamp |
155| `ref` | string | MongoDB ObjectId reference |
156
157### Blob Fields
158
159| Field | Type | Description |
160|-------|------|-------------|
161| `$type` | string | Always `blob` |
162| `ref.$link` | string | CID (Content Identifier) |
163| `mimeType` | string | MIME type (e.g., `image/png`) |
164| `size` | number | Size in bytes |
165
166## Error Responses
167
168### Handle Not Found
169```json
170{
171 "error": "InvalidRequest",
172 "message": "Unable to resolve handle: fifi.at.aesthetic.computer"
173}
174```
175Status: 400
176
177### Collection Not Found
178```json
179{
180 "error": "InvalidRequest",
181 "message": "Collection not found: computer.aesthetic.painting"
182}
183```
184Status: 400
185
186### DID Not Found
187```json
188{
189 "error": "InvalidRequest",
190 "message": "Could not find repo: did:plc:xyz123..."
191}
192```
193Status: 400
194
195## Pagination
196
197When there are more than 100 records, use the `cursor`:
198
199### Request with Cursor
200```
201GET https://at.aesthetic.computer/xrpc/com.atproto.repo.listRecords?repo=did:plc:xyz123...&collection=computer.aesthetic.painting&limit=100&cursor=3jzx7k2m4n5
202```
203
204### Response
205```json
206{
207 "records": [
208 // Next 100 records...
209 ],
210 "cursor": "2gwx6i1k2m3" // Or null if no more records
211}
212```
213
214## JavaScript Examples
215
216### Fetch Handle DID
217```javascript
218const PDS_URL = 'https://at.aesthetic.computer';
219
220async function resolveDID(handle) {
221 const url = `${PDS_URL}/xrpc/com.atproto.identity.resolveHandle?handle=${encodeURIComponent(handle)}`;
222 const response = await fetch(url);
223 const data = await response.json();
224 return data.did;
225}
226
227// Usage
228const did = await resolveDID('fifi.at.aesthetic.computer');
229console.log(did); // did:plc:xyz123...
230```
231
232### List All Records (with Pagination)
233```javascript
234async function listRecords(did, collection) {
235 const records = [];
236 let cursor = undefined;
237
238 do {
239 const url = new URL(`${PDS_URL}/xrpc/com.atproto.repo.listRecords`);
240 url.searchParams.append('repo', did);
241 url.searchParams.append('collection', collection);
242 url.searchParams.append('limit', 100);
243 if (cursor) url.searchParams.append('cursor', cursor);
244
245 const response = await fetch(url);
246 const data = await response.json();
247
248 records.push(...data.records);
249 cursor = data.cursor;
250 } while (cursor);
251
252 return records;
253}
254
255// Usage
256const paintings = await listRecords(did, 'computer.aesthetic.painting');
257console.log(`Found ${paintings.length} paintings`);
258```
259
260### Get Image URL
261```javascript
262function getImageUrl(did, blobCid) {
263 return `${PDS_URL}/xrpc/com.atproto.sync.getBlob?did=${did}&cid=${blobCid}`;
264}
265
266// Usage
267const painting = records[0];
268const did = painting.uri.split('/')[2];
269const cid = painting.value.image.ref.$link;
270const imageUrl = getImageUrl(did, cid);
271
272// Use in img tag
273document.querySelector('img').src = imageUrl;
274```
275
276### Extract Record Key (RKEY)
277```javascript
278function getRkey(uri) {
279 return uri.split('/').pop();
280}
281
282// Usage
283const uri = 'at://did:plc:xyz123.../computer.aesthetic.painting/3jzx7k2m4n5';
284const rkey = getRkey(uri); // '3jzx7k2m4n5'
285```
286
287## Testing with cURL
288
289```bash
290# 1. Resolve handle
291curl "https://at.aesthetic.computer/xrpc/com.atproto.identity.resolveHandle?handle=fifi.at.aesthetic.computer"
292
293# 2. List paintings (replace DID)
294curl "https://at.aesthetic.computer/xrpc/com.atproto.repo.listRecords?repo=did:plc:xyz123...&collection=computer.aesthetic.painting&limit=5"
295
296# 3. Get image (replace DID and CID)
297curl "https://at.aesthetic.computer/xrpc/com.atproto.sync.getBlob?did=did:plc:xyz123...&cid=bafkreiabcd..." --output image.png
298
299# 4. List moods
300curl "https://at.aesthetic.computer/xrpc/com.atproto.repo.listRecords?repo=did:plc:xyz123...&collection=computer.aesthetic.mood&limit=5"
301```
302
303## Rate Limiting
304
305The PDS does not currently enforce strict rate limits, but:
306- Recommended: Max 100 requests per minute per client
307- Pagination: Use reasonable limits (100-500)
308- Caching: Cache resolved DIDs client-side
309
310## CORS
311
312The PDS allows CORS requests from any origin:
313```
314Access-Control-Allow-Origin: *
315```
316
317This enables the user pages to work purely client-side.
318
319## Security Notes
320
3211. **No Authentication Required** - All data is public
3222. **Read-Only** - XRPC endpoints used are read-only
3233. **No Personal Data** - Only public ATProto records
3244. **Client-Side Only** - No server-side secrets exposed
325
326## Related Documentation
327
328- [ATProto Lexicon Reference](https://atproto.com/specs/lexicon)
329- [XRPC Specification](https://atproto.com/specs/xrpc)
330- [User Pages Architecture](USER-PAGES-ARCHITECTURE.md)
331- [User Pages Documentation](USER-PAGES.md)
332
333---
334
335**Last Updated:** 2025-10-20