# Database

URL: /code/platform/database · Id: `page:code/platform/database`

> db.sb stores every record, Relationship and Event of a Startup, typed by its Nouns, with reads at any past moment and SQL analytics.

[db.sb](/code/platform/db-sb) stores every [Thing](/business/nouns/thing), [Relationship](/business/nouns/relationship) and [Event](/business/nouns/event) of a [Startup](/business/nouns/startup), typed by its [Nouns](/business/nouns/noun). It is the record: search, caches and files are projections rebuilt from it.

## Read and write
Declaring a Noun gives it typed create, get, list, update and delete on every [Interface](/business/nouns/interface). Every write runs as an [Action](/business/nouns/action) attributed to its [Worker](/business/nouns/worker). Leaseline declares a `Lease` Noun:

```ts
import { $ } from 'startup-builder'

const lease = await $.Lease.create({ unit: '4B', endsOn: '2027-01-31', rent: { amount: 1800, currency: 'USD' } })
const due = await $.Lease.list({ where: 'status = active and endsOn < 2027-03-01', sort: 'endsOn', limit: 50 })
const next = await $.Lease.list({ where: 'status = active', after: due.after })
```

A list returns one page with a cursor and filters by any property or Relationship.

## Relationships and find-or-create
A Relationship has a name from each end. Write it with `Thing.relate`, and read it from either side:

```ts
await $.Thing.relate({ thing: 'contact_7', predicate: 'rents', to: 'lease_1' })
const { thing, decision } = await $.Thing.resolve({ noun: 'contact', match: { email: 'ana@example.com' }, set: { name: 'Ana Costa' } })
```

`decision` is `found`, `created` or `held`. Two matches write nothing and open a merge [Task](/business/nouns/task).

## All or nothing
Writes inside one transaction commit together as one Action; a throw cancels every write in it.

```ts
await $.transaction(async (tx) => {
  const lease = await tx.Lease.create({ unit: '4C', endsOn: '2027-06-30' })
  await tx.Thing.relate({ thing: 'contact_9', predicate: 'rents', to: lease.$id })
})
```

## Past states and analytics
Nothing is overwritten. `get` with `at` returns a Thing as it stood, and `@` does the same on the API: `GET /leaseline.app/leases/lease_1@2026-10-01`.

Every Action and Event also lands in an append-only analytics store, so a year of rows never slows a write:

```bash
npx startup-builder do Collection.query --json '{"query":"select date_trunc(month, endsOn) as month, count(*) from Lease group by month"}'
```

## Changing a Noun
A Noun changes only through a [Change](/business/nouns/change). Edit `nouns/Lease.mdx`, and `Change.review` classifies the diff as additive or breaking.

A breaking Change opens a new [Version](/business/nouns/version), and pinned callers keep the old one. Rows that fail a type conversion are set aside, never dropped.

| Guarantee | Detail |
| --- | --- |
| Consistency | One writer per Startup; a read after a write sees it |
| Isolation | No query crosses Startups; each [Customer](/business/nouns/customer)'s records sit in its [Tenant](/business/nouns/tenant) |
| History | A delete hides a Thing and keeps its Events |
| Scale | About 1,000 writes a second per Startup; past that a [Collection](/business/nouns/collection) shards by key under the same ids |