# airspace: the database you already have.

every ~~Bluesky~~ atmosphere account comes with a personal data server: a cms, login, file storage and a
public API that you own.

- [try the demo](https://getair.space/demo)
- [get started](https://getair.space/docs)

```sh
pnpm add airspace
```

Node 22+

## define a model

lexicons in TypeScript, in your own namespace.

```ts
// lexicons.ts
import { defineLexicons, field, space } from 'airspace/lexicon'

export default defineLexicons('dev.roe', {
  note: {
    title: field.text({ max: 120 }),
    body: field.markdown(),
    createdAt: field.datetime().optional(),
  },
  workspace: space(['note']),
})
```

```ts
// collections.ts
import { defineCollections, defineSpace } from 'airspace'
import lexicons from './lexicons.ts'

export const { note: notes } = defineCollections(lexicons, {
  note: { sort: [['createdAt', 'desc']] },
})

export const workspace = defineSpace(lexicons.workspace, {
  collections: { notes },
})
```

## access your data

a fully typed client over your repo and your drafts.

```ts
// airspace.ts
import { createAirspace } from 'airspace'
import { workspace } from './collections.ts'

export const airspace = createAirspace({
  identity: 'roe.dev',
  spaces: { workspace }, // its `notes` is a public collection too
  session, // omit for read-only
})

const published = await airspace.notes.list({ limit: 5 })

const draft = await airspace.workspace.notes.create({
  title: 'Not public yet',
  body: '# hello',
})
await airspace.workspace.notes.publish(draft.rkey)
```

## what you get

- **you own your data.** Records live in your own repo under your own schema. Switch tools or hosts, or even stop using airspace, and everything still works.
- **no database to run.** Reads, writes, auth and images are all handled by your own PDS. airspace is a typed client.
- **drafts built in.** Experimental permissioned spaces for private data, with a single call to publish.
- **typed from your schema.** Define the model once. Records, keys, joins and OAuth scopes are inferred magically. ✨

## works with what exists

airspace manages any collection you have a lexicon for, not only the ones you wrote.

- [`com.whtwnd.blog.entry`](https://whtwnd.com): WhiteWind posts, drafted in a space and published to your public repo.
- [`site.standard.*`](https://standard.site): Publications and documents, read from a live repo.
- [`community.lexicon.calendar.*`](https://github.com/lexicon-community/lexicon): Events and RSVPs, joined across two accounts.
- `your.own.lexicon`: Written with [`defineLexicons`](https://getair.space/docs/model), or brought in as JSON.

Nothing in your repo carries an airspace `$type` or is proprietary to airspace.

## if you already know atproto

airspace sits between [`@atproto/lex-schema`](https://www.npmjs.com/package/@atproto/lex-schema) and your site.

Lexicons are TypeScript, and your public repo and your [permissioned spaces](https://github.com/bluesky-social/proposals/tree/main/0016-permissioned-data)
share one typed API. `airspace lexicons emit` writes the JSON when you want to publish your
schemas. airspace itself defines no content model, renders nothing and hosts nothing.

## docs

- [getting started](https://getair.space/docs)
- [atproto in five minutes](https://getair.space/docs/concepts)
- [your content model](https://getair.space/docs/model)
- [reading and writing](https://getair.space/docs/reading-and-writing)
- [spaces](https://getair.space/docs/spaces)
- [blobs and images](https://getair.space/docs/blobs)
- [plugins](https://getair.space/docs/plugins)
- [OAuth and permission sets](https://getair.space/docs/oauth)
- [publishing your lexicons](https://getair.space/docs/publishing-lexicons)
- [validation and migrations](https://getair.space/docs/validation)
- [errors](https://getair.space/docs/errors)
- [advanced](https://getair.space/docs/advanced)

## elsewhere

- [interactive demo](https://getair.space/demo)
- [source and issues](https://github.com/danielroe/airspace)
- [`airspace` on npm](https://www.npmjs.com/package/airspace)
