Skip to content

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 stores every Thing, Relationship and Event of a Startup, typed by its Nouns. 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. Every write runs as an Action attributed to its Worker. Leaseline declares a Lease Noun:

TypeScript
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:

TypeScript
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.

All or nothing

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

TypeScript
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:

Terminal
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. Edit nouns/Lease.mdx, and Change.review classifies the diff as additive or breaking.

A breaking Change opens a new Version, and pinned callers keep the old one. Rows that fail a type conversion are set aside, never dropped.

GuaranteeDetail
ConsistencyOne writer per Startup; a read after a write sees it
IsolationNo query crosses Startups; each Customer's records sit in its Tenant
HistoryA delete hides a Thing and keeps its Events
ScaleAbout 1,000 writes a second per Startup; past that a Collection shards by key under the same ids