···119120## Step 3. Fetching the user's profile
121122-Why don't we learn something about our user? Let's start by getting the [Agent](#todo) object. The [Agent](#todo) is the client to the user's `at://` repo server.
123-124-```typescript
125-/** src/routes.ts **/
126-async function getSessionAgent(
127- req: IncomingMessage,
128- res: ServerResponse<IncomingMessage>,
129- ctx: AppContext
130-) {
131- // Fetch the session from their cookie
132- const session = await getIronSession(req, res)
133- if (!session.did) return null
134-135- // "Restore" the agent for the user
136- try {
137- return await ctx.oauthClient.restore(session.did)
138- } catch(err) {
139- ctx.logger.warn({ err }, 'oauth restore failed')
140- await session.destroy()
141- return null
142- }
143-}
144-```
145-146-Users publish JSON records on their `at://` repos. In [Bluesky](https://bsky.app), they publish a "profile" record which looks like this:
147148```typescript
149interface ProfileRecord {
···156}
157```
158159-We're going to use the [Agent](#todo) to fetch this record to include in our app.
00160161```typescript
162await agent.getRecord({
···168169When asking for a record, we provide three pieces of information.
170171-- The [DID](#todo) which identifies the user,
172-- The collection name, and
173-- The record key
174175We'll explain the collection name shortly. Record keys are strings with [some limitations](https://atproto.com/specs/record-key#record-key-syntax) and a couple of common patterns. The `"self"` pattern is used when a collection is expected to only contain one record which describes the user.
176···194 const { data: profileRecord } = await agent.getRecord({
195 repo: agent.accountDid, // our user's repo
196 collection: 'app.bsky.actor.profile', // the bluesky profile record type
197- rkey: 'self', // the record's name
198 })
199200 // Serve the logged-in view
···206```
207208With that data, we can give a nice personalized welcome banner for our user:
00209210```html
211<!-- pages/home.ts -->
···228 </div>`}
229</div>
230```
231-232-
233-234-You can examine this record directly using [atproto-browser.vercel.app](https://atproto-browser.vercel.app). For instance, [this is the profile record for @bsky.app](https://atproto-browser.vercel.app/at?u=at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.actor.profile/self).
235236## Step 4. Reading & writing records
237···325326## Step 5. Creating a custom "status" schema
327328-The collections are typed, meaning that they have a defined schema. The `app.bsky.actor.profile` type definition [can be found here](https://github.com/bluesky-social/atproto/blob/main/lexicons/app/bsky/actor/profile.json).
329330Anybody can create a new schema using the [Lexicon](#todo) language, which is very similar to [JSON-Schema](#todo). The schemas use [reverse-DNS IDs](#todo) which indicate ownership, but for this demo app we're going to use `com.example` which is safe for non-production software.
331···511512
513514-Why read from the event log? Because there are other apps in the network that will write the records we're interested in. By subscribing to the event log, we ensure that we catch all the data we're interested in -- including data published by other apps.
00515516## Step 7. Listing the latest statuses
517···567568## Step 8. Optimistic updates
569570-As a final optimization, let's introduce "optimistic updates." Remember the information flow loop with the repo write and the event log? Since we're updating our users' repos locally, we can short-circuit that flow to our own database:
000000571572
573···633- Design the [Lexicon](#) schemas for the records you'll publish into the Atmosphere.
634- Create a database for aggregating the records into useful views.
635- Build your application to write the records on your users' repos.
636-- Listen to the firehose to hydrate your aggregated database.
637638Remember this flow of information throughout:
639
···119120## Step 3. Fetching the user's profile
121122+Why don't we learn something about our user? In [Bluesky](https://bsky.app), users publish a "profile" record which looks like this:
000000000000000000000000123124```typescript
125interface ProfileRecord {
···132}
133```
134135+You can examine this record directly using [atproto-browser.vercel.app](https://atproto-browser.vercel.app). For instance, [this is the profile record for @bsky.app](https://atproto-browser.vercel.app/at?u=at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.actor.profile/self).
136+137+We're going to use the [Agent](#todo) associated with the user's OAuth session to fetch this record.
138139```typescript
140await agent.getRecord({
···146147When asking for a record, we provide three pieces of information.
148149+- **repo** The [DID](#todo) which identifies the user,
150+- **collection** The collection name, and
151+- **rkey** The record key
152153We'll explain the collection name shortly. Record keys are strings with [some limitations](https://atproto.com/specs/record-key#record-key-syntax) and a couple of common patterns. The `"self"` pattern is used when a collection is expected to only contain one record which describes the user.
154···172 const { data: profileRecord } = await agent.getRecord({
173 repo: agent.accountDid, // our user's repo
174 collection: 'app.bsky.actor.profile', // the bluesky profile record type
175+ rkey: 'self', // the record's key
176 })
177178 // Serve the logged-in view
···184```
185186With that data, we can give a nice personalized welcome banner for our user:
187+188+
189190```html
191<!-- pages/home.ts -->
···208 </div>`}
209</div>
210```
0000211212## Step 4. Reading & writing records
213···301302## Step 5. Creating a custom "status" schema
303304+Repo collections are typed, meaning that they have a defined schema. The `app.bsky.actor.profile` type definition [can be found here](https://github.com/bluesky-social/atproto/blob/main/lexicons/app/bsky/actor/profile.json).
305306Anybody can create a new schema using the [Lexicon](#todo) language, which is very similar to [JSON-Schema](#todo). The schemas use [reverse-DNS IDs](#todo) which indicate ownership, but for this demo app we're going to use `com.example` which is safe for non-production software.
307···487488
489490+Applications write to the repo. The write events are then emitted on the firehose where they're caught by the apps and ingested into their databases.
491+492+Why sync from the event log like this? Because there are other apps in the network that will write the records we're interested in. By subscribing to the event log, we ensure that we catch all the data we're interested in — including data published by other apps!
493494## Step 7. Listing the latest statuses
495···545546## Step 8. Optimistic updates
547548+As a final optimization, let's introduce "optimistic updates."
549+550+Remember the information flow loop with the repo write and the event log?
551+552+
553+554+Since we're updating our users' repos locally, we can short-circuit that flow to our own database:
555556
557···617- Design the [Lexicon](#) schemas for the records you'll publish into the Atmosphere.
618- Create a database for aggregating the records into useful views.
619- Build your application to write the records on your users' repos.
620+- Listen to the firehose to aggregate data across the network.
621622Remember this flow of information throughout:
623