Auto-indexing service and GraphQL API for AT Protocol Records quickslice.slices.network/
atproto gleam graphql
at main 347 lines 6.0 kB view raw view rendered
1# Working with Blobs 2 3Blobs store binary data like images, videos, and files. Upload separately and reference by CID (Content Identifier). 4 5## Upload Blob 6 7Upload binary data encoded as base64: 8 9```graphql 10mutation { 11 uploadBlob( 12 data: "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAY..." 13 mimeType: "image/png" 14 ) { 15 ref 16 mimeType 17 size 18 } 19} 20``` 21 22Response: 23 24```json 25{ 26 "data": { 27 "uploadBlob": { 28 "ref": "bafkreiabc123xyz...", 29 "mimeType": "image/png", 30 "size": 1024 31 } 32 } 33} 34``` 35 36## Blob Reference 37 38A blob reference contains: 39 40- `ref`: CID of the blob content 41- `mimeType`: MIME type (e.g., `image/jpeg`, `image/png`) 42- `size`: Size in bytes 43 44## Using Blobs in Records 45 46### Profile Avatar 47 48```graphql 49mutation { 50 updateAppBskyActorProfile( 51 rkey: "self" 52 input: { 53 displayName: "Alice" 54 avatar: { 55 ref: "bafkreiabc123..." 56 mimeType: "image/jpeg" 57 size: 125000 58 } 59 } 60 ) { 61 displayName 62 avatar { 63 ref 64 mimeType 65 size 66 url 67 } 68 } 69} 70``` 71 72### Profile Banner 73 74```graphql 75mutation { 76 updateAppBskyActorProfile( 77 rkey: "self" 78 input: { 79 displayName: "Alice" 80 banner: { 81 ref: "bafkreixyz789..." 82 mimeType: "image/jpeg" 83 size: 450000 84 } 85 } 86 ) { 87 displayName 88 banner { 89 ref 90 mimeType 91 size 92 url 93 } 94 } 95} 96``` 97 98## Blob URLs 99 100Blobs generate CDN URLs automatically. Use the `url` field with optional presets: 101 102### Default URL 103 104```graphql 105query { 106 appBskyActorProfile { 107 edges { 108 node { 109 avatar { 110 ref 111 url 112 } 113 } 114 } 115 } 116} 117``` 118 119Returns: `https://cdn.bsky.app/img/feed_fullsize/plain/did:plc:.../bafkreiabc123@jpeg` 120 121### Avatar Preset 122 123```graphql 124query { 125 appBskyActorProfile { 126 edges { 127 node { 128 avatar { 129 ref 130 url(preset: "avatar") 131 } 132 } 133 } 134 } 135} 136``` 137 138Returns: `https://cdn.bsky.app/img/avatar/plain/did:plc:.../bafkreiabc123@jpeg` 139 140### Banner Preset 141 142```graphql 143query { 144 appBskyActorProfile { 145 edges { 146 node { 147 banner { 148 url(preset: "banner") 149 } 150 } 151 } 152 } 153} 154``` 155 156Returns: `https://cdn.bsky.app/img/banner/plain/did:plc:.../bafkreixyz789@jpeg` 157 158## Available Presets 159 160- `avatar` - Optimized for profile avatars (square, small) 161- `banner` - Optimized for profile banners (wide, medium) 162- `feed_thumbnail` - Thumbnails in feed view 163- `feed_fullsize` - Full size images in feed (default) 164 165## Complete Example: Update Profile with Images 166 167### Step 1: Upload Avatar 168 169```graphql 170mutation UploadAvatar($avatarData: String!) { 171 uploadBlob(data: $avatarData, mimeType: "image/jpeg") { 172 ref 173 mimeType 174 size 175 } 176} 177``` 178 179Variables: 180```json 181{ 182 "avatarData": "base64EncodedJpegData..." 183} 184``` 185 186Response: 187```json 188{ 189 "data": { 190 "uploadBlob": { 191 "ref": "bafkreiabc123avatar", 192 "mimeType": "image/jpeg", 193 "size": 125000 194 } 195 } 196} 197``` 198 199### Step 2: Upload Banner 200 201```graphql 202mutation UploadBanner($bannerData: String!) { 203 uploadBlob(data: $bannerData, mimeType: "image/jpeg") { 204 ref 205 mimeType 206 size 207 } 208} 209``` 210 211Variables: 212```json 213{ 214 "bannerData": "base64EncodedJpegData..." 215} 216``` 217 218Response: 219```json 220{ 221 "data": { 222 "uploadBlob": { 223 "ref": "bafkreixyz789banner", 224 "mimeType": "image/jpeg", 225 "size": 450000 226 } 227 } 228} 229``` 230 231### Step 3: Update Profile 232 233```graphql 234mutation UpdateProfileWithImages { 235 updateAppBskyActorProfile( 236 rkey: "self" 237 input: { 238 displayName: "Alice Smith" 239 description: "Software engineer & designer" 240 avatar: { 241 ref: "bafkreiabc123avatar" 242 mimeType: "image/jpeg" 243 size: 125000 244 } 245 banner: { 246 ref: "bafkreixyz789banner" 247 mimeType: "image/jpeg" 248 size: 450000 249 } 250 } 251 ) { 252 uri 253 displayName 254 description 255 avatar { 256 ref 257 mimeType 258 size 259 url(preset: "avatar") 260 } 261 banner { 262 ref 263 mimeType 264 size 265 url(preset: "banner") 266 } 267 } 268} 269``` 270 271Response: 272```json 273{ 274 "data": { 275 "updateAppBskyActorProfile": { 276 "uri": "at://did:plc:xyz/app.bsky.actor.profile/self", 277 "displayName": "Alice Smith", 278 "description": "Software engineer & designer", 279 "avatar": { 280 "ref": "bafkreiabc123avatar", 281 "mimeType": "image/jpeg", 282 "size": 125000, 283 "url": "https://cdn.bsky.app/img/avatar/plain/did:plc:xyz/bafkreiabc123avatar@jpeg" 284 }, 285 "banner": { 286 "ref": "bafkreixyz789banner", 287 "mimeType": "image/jpeg", 288 "size": 450000, 289 "url": "https://cdn.bsky.app/img/banner/plain/did:plc:xyz/bafkreixyz789banner@jpeg" 290 } 291 } 292 } 293} 294``` 295 296## JavaScript Example 297 298```javascript 299// Convert file to base64 300async function fileToBase64(file) { 301 return new Promise((resolve, reject) => { 302 const reader = new FileReader(); 303 reader.readAsDataURL(file); 304 reader.onload = () => { 305 const base64 = reader.result.split(',')[1]; // Remove data:image/jpeg;base64, prefix 306 resolve(base64); 307 }; 308 reader.onerror = reject; 309 }); 310} 311 312// Upload blob 313async function uploadBlob(file, token) { 314 const base64Data = await fileToBase64(file); 315 316 const response = await fetch('/graphql', { 317 method: 'POST', 318 headers: { 319 'Content-Type': 'application/json', 320 'Authorization': `Bearer ${token}` 321 }, 322 body: JSON.stringify({ 323 query: ` 324 mutation UploadBlob($data: String!, $mimeType: String!) { 325 uploadBlob(data: $data, mimeType: $mimeType) { 326 ref 327 mimeType 328 size 329 } 330 } 331 `, 332 variables: { 333 data: base64Data, 334 mimeType: file.type 335 } 336 }) 337 }); 338 339 const result = await response.json(); 340 return result.data.uploadBlob; 341} 342 343// Usage 344const avatarFile = document.getElementById('avatar-input').files[0]; 345const blob = await uploadBlob(avatarFile, 'your-token'); 346console.log('Uploaded blob:', blob.ref); 347```