···
- `bsky.app` is feeling 🦋 according to `https://bsky.app/status.json`
- `reddit.com` is feeling 🤓 according to `https://reddit.com/status.json`
-
The Atmosphere works the same way, except we're going to check `at://` instead of `https://`. Each user has a data repo under an `at://` URL. We'll crawl all the `at://`s in the Atmosphere for all the `/status.json` records and aggregate them into our SQLite database.
## Step 1. Starting with our ExpressJS app
···
repo: agent.accountDid, // The user
collection: 'app.bsky.actor.profile', // The collection
-
rkey: 'self', // The record name
We write records using a similar API. Since our goal is to write "status" records, let's look at how that will happen:
repo: agent.accountDid, // The user
collection: 'com.example.status', // The collection
-
rkey: 'self', // The record name
record: { // The record value
-
updatedAt: new Date().toISOString()
···
$type: 'com.example.status',
status: req.body?.status,
-
updatedAt: new Date().toISOString(),
···
collection: 'com.example.status',
···
> ### Why create a schema?
-
> Schemas help other applications understand the data your app is creating. By publishing your schemas, you enable compatibility and reduce the chances of bad data affecting your app.
Let's create our schema in the `/lexicons` folder of our codebase. You can [read more about how to define schemas here](#todo).
···
-
"required": ["status", "updatedAt"],
···
···
$type: 'com.example.status',
status: req.body?.status,
-
updatedAt: new Date().toISOString(),
if (!Status.validateRecord(record).success) {
return res.status(400).json({ error: 'Invalid status' })
···
│ ┌───────────────────────────────────────────┐
-
├───┼ 1 PUT /com.example.status/self │
│ └───────────────────────────────────────────┘
│ ┌───────────────────────────────────────────┐
├───┼ 2 DEL /app.bsky.feed.post/3l244rmrxjx2v │
│ └───────────────────────────────────────────┘
│ ┌───────────────────────────────────────────┐
-
├───┼ 3 PUT /app.bsky.actor/self │
▼ └───────────────────────────────────────────┘
···
// Create our statuses table
-
.addColumn('authorDid', 'varchar', (col) => col.primaryKey())
.addColumn('status', 'varchar', (col) => col.notNull())
-
.addColumn('updatedAt', 'varchar', (col) => col.notNull())
.addColumn('indexedAt', 'varchar', (col) => col.notNull())
···
-
updatedAt: record.updatedAt,
indexedAt: new Date().toISOString(),
-
oc.column('authorDid').doUpdateSet({
-
updatedAt: record.updatedAt,
indexedAt: new Date().toISOString(),
···
<!-- src/pages/home.ts -->
${statuses.map((status, i) => {
const handle = didHandleMap[status.authorDid] || status.authorDid
-
const date = ts(status)
<div class="status-line">
···
handler(async (req, res) => {
// Write the status record to the user's repository
-
await agent.putRecord({
collection: 'com.example.status',
ctx.logger.warn({ err }, 'failed to write record')
return res.status(500).json({ error: 'Failed to write record' })
···
authorDid: agent.accountDid,
-
updatedAt: record.updatedAt,
indexedAt: new Date().toISOString(),
-
oc.column('authorDid').doUpdateSet({
-
updatedAt: record.updatedAt,
-
indexedAt: new Date().toISOString(),
···
- `bsky.app` is feeling 🦋 according to `https://bsky.app/status.json`
- `reddit.com` is feeling 🤓 according to `https://reddit.com/status.json`
+
The Atmosphere works the same way, except we're going to check `at://` instead of `https://`. Each user has a data repo under an `at://` URL. We'll crawl all the `at://`s in the Atmosphere for all the "status.json" records and aggregate them into our SQLite database.
+
> `at://` is the URL scheme of the AT Protocol.
## Step 1. Starting with our ExpressJS app
···
repo: agent.accountDid, // The user
collection: 'app.bsky.actor.profile', // The collection
+
rkey: 'self', // The record key
We write records using a similar API. Since our goal is to write "status" records, let's look at how that will happen:
+
// Generate a time-based key for our record
+
const rkey = TID.nextStr()
repo: agent.accountDid, // The user
collection: 'com.example.status', // The collection
+
rkey, // The record key
record: { // The record value
+
createdAt: new Date().toISOString()
···
$type: 'com.example.status',
status: req.body?.status,
+
createdAt: new Date().toISOString(),
···
collection: 'com.example.status',
···
> ### Why create a schema?
+
> Schemas help other applications understand the data your app is creating. By publishing your schemas, you enable compatibility with other apps and reduce the chances of bad data affecting your app.
Let's create our schema in the `/lexicons` folder of our codebase. You can [read more about how to define schemas here](#todo).
···
+
"required": ["status", "createdAt"],
···
···
$type: 'com.example.status',
status: req.body?.status,
+
createdAt: new Date().toISOString(),
if (!Status.validateRecord(record).success) {
return res.status(400).json({ error: 'Invalid status' })
···
│ ┌───────────────────────────────────────────┐
+
├───┼ 1 PUT /app.bsky.feed.post/3l244rmrxjx2v │
│ └───────────────────────────────────────────┘
│ ┌───────────────────────────────────────────┐
├───┼ 2 DEL /app.bsky.feed.post/3l244rmrxjx2v │
│ └───────────────────────────────────────────┘
│ ┌───────────────────────────────────────────┐
+
├───┼ 3 PUT /app.bsky.actor.profile/self │
▼ └───────────────────────────────────────────┘
···
// Create our statuses table
+
.addColumn('uri', 'varchar', (col) => col.primaryKey())
+
.addColumn('authorDid', 'varchar', (col) => col.notNull())
.addColumn('status', 'varchar', (col) => col.notNull())
+
.addColumn('createdAt', 'varchar', (col) => col.notNull())
.addColumn('indexedAt', 'varchar', (col) => col.notNull())
···
+
uri: evt.uri.toString(),
+
createdAt: record.createdAt,
indexedAt: new Date().toISOString(),
+
oc.column('uri').doUpdateSet({
indexedAt: new Date().toISOString(),
···
<!-- src/pages/home.ts -->
${statuses.map((status, i) => {
const handle = didHandleMap[status.authorDid] || status.authorDid
<div class="status-line">
···
handler(async (req, res) => {
// Write the status record to the user's repository
+
const res = await agent.putRecord({
collection: 'com.example.status',
ctx.logger.warn({ err }, 'failed to write record')
return res.status(500).json({ error: 'Failed to write record' })
···
authorDid: agent.accountDid,
+
createdAt: record.createdAt,
indexedAt: new Date().toISOString(),