1. guides
prorm
  • prorm documentation
  • Known test gaps
  • Architecture
  • Core concepts
  • Database types & coverage
  • Getting started
  • Going further
  • compliance
    • Compliance
    • The security toolkit: `prorm audit`
    • Checklists by regulation
    • Declaring personal data
    • Evidence bundles
    • Evidence
    • Frameworks: `@Security`
    • Jurisdictions
    • Platform policies
    • Protecting data
    • The regulation dataset
    • Residency and cross-border transfers
    • Retention & integrity
    • Status
    • Subject rights
  • decorators
    • prorm with decorators
    • Models with decorators
    • Querying a decorated model
    • Relations with decorators
  • diagrams
    • Diagram generators
    • Chen diagram
    • Class diagram
    • Dependency diagram
    • ER diagram
    • Flow diagram
    • From your models to a diagram
    • Gantt diagram
    • Index diagram
    • Migration diagram
    • Model diagram
    • Package diagram
    • Crow’s-foot diagram
    • Schema documentation
    • Sequence diagram
    • State diagram
    • Tree diagram
  • dialects
    • SQL dialects
    • AlloyDB Dialect
    • AlloyDB Omni Dialect
    • Amazon Athena Dialect
    • Aurora DSQL Dialect
    • Amazon Aurora MySQL Dialect
    • Amazon Aurora PostgreSQL Dialect
    • Azure Database for MySQL Dialect
    • Azure Database for PostgreSQL Dialect
    • Azure SQL Database Dialect
    • Azure SQL Managed Instance Dialect
    • Google BigQuery Dialect
    • Citus Dialect
    • ClickHouse dialect
    • Cloud Authentication (`cloud-auth`)
    • Cloud SQL for SQL Server Dialect
    • Cloud SQL for MySQL Dialect
    • Cloud SQL for PostgreSQL Dialect
    • CockroachDB Dialect
    • Azure Cosmos DB for PostgreSQL Dialect
    • CrateDB Dialect
    • Cloudflare D1 Dialect
    • Databricks dialect
    • IBM Db2 Dialect
    • Dolt Dialect
    • Apache Doris Dialect
    • Druid Dialect
    • DuckDB Dialect
    • Exasol dialect
    • Microsoft Fabric Data Warehouse Dialect
    • Firebird Dialect
    • Greenplum Dialect
    • SAP HANA Dialect
    • MySQL HeatWave Dialect
    • Azure Data Explorer (Kusto) Dialect — T-SQL
    • MariaDB Dialect
    • Materialize Dialect
    • MonetDB Dialect
    • MotherDuck Dialect
    • Microsoft SQL Server Dialect
    • MySQL Dialect
    • Neon Dialect
    • OceanBase Dialect
    • openGauss Dialect
    • Oracle Dialect
    • Oracle Autonomous Database Dialect
    • ParadeDB Dialect
    • PolarDB Dialect
    • PostgreSQL Dialect
    • Presto Dialect
    • QuestDB Dialect
    • Aurora RDS Data API Dialect (`rdsdata`)
    • Amazon RDS for Db2 Dialect
    • Amazon RDS for MariaDB Dialect
    • Amazon RDS for SQL Server Dialect
    • Amazon RDS for MySQL Dialect
    • Amazon RDS for Oracle Dialect
    • Amazon RDS for PostgreSQL Dialect
    • Amazon Redshift
    • Amazon Redshift Data API Dialect
    • RisingWave Dialect
    • rqlite Dialect
    • SingleStore Dialect
    • Snowflake Dialect
    • Google Cloud Spanner Dialect
    • Spanner (PostgreSQL interface) Dialect
    • SQLite Dialect
    • StarRocks Dialect
    • Supabase Dialect
    • Azure Synapse Analytics (dedicated SQL pool)
    • Teradata Dialect
    • TiDB Dialect
    • TimescaleDB Dialect
    • Trino Dialect
    • Turso / libSQL Dialect
    • Vertica dialect
    • Vitess Dialect
    • Yellowbrick Dialect
    • YugabyteDB Dialect
  • guides
    • Access rules and behaviour profiles
    • ArangoDB (AQL)
    • Associations
    • Audit levels and escalation
    • Audit logging
    • In the browser
    • Bulk operations
    • Caching
    • Capabilities, tiers and team roles
    • CLI
    • Cloudflare
    • Compliance & data protection
    • Connection Pooling & Performance
    • Security in the console
    • Couchbase: structured N1QL queries
    • Data types
    • Decorators
    • Eager loading
    • Enums and constants
    • Error handling & error types
    • Extensions
    • External fields
    • Foreign data wrappers
    • Graph databases
    • Hooks & lifecycle events
    • Indexes & constraints
    • InfluxDB: structured Flux pipelines
    • klock-errors
    • klock — the `.klock` file
    • Local databases (`prorm db up`)
    • Structured log search — Splunk, Graylog, Papertrail, Sumo Logic
    • Logging
    • Grafana Loki: structured LogQL queries
    • MCP server
    • Migrate — the data transfer subsystem
    • Migrations & Schema Sync
    • Models from an existing database
    • Defining models
    • Multiple databases
    • OrientDB, Neptune and TigerGraph
    • Passwords
    • Importing a Prisma schema
    • Structured PromQL and MetricsQL
    • Query firewall
    • QueryInterface
    • Query operators reference
    • Query optimization
    • Composing find options with `pipe()` and `Q`
    • Query safety: the fail-closed WHERE guard
    • Querying
    • Raw Queries & Query Building
    • Read replicas
    • Schema diffing & migration generation
    • Views, triggers & other schema objects
    • Scopes & default scopes
    • Seeders
    • SPARQL / RDF triplestores
    • SQL constants
    • SQL features
    • SQL function builders
    • SQLite advanced features
    • StoreKey — a prod-orm database as the storage
    • Streaming results
    • Terraform
    • Transactions & Isolation Levels
    • Type mapping: legacy and modern
    • TypeScript types
    • Database users, roles & privileges
    • Validation & constraints
    • Virtual & Computed Fields
    • Writing docs
  • javascript
    • prorm in JavaScript
    • Models in JavaScript
    • Querying in JavaScript
    • Relations in JavaScript
  • runbooks
    • Operational runbooks
    • Connection failures
    • Credential rotation
    • Failover
    • Incident checklist
    • Deploying klock v2
    • Migration rollback
  • running-databases
    • Running databases in Docker
    • Running prorm under Bun in Docker
    • Running Cassandra in Docker with prorm
    • Running ClickHouse in Docker with prorm
    • Running CockroachDB in Docker with prorm
    • Running prorm under Deno in Docker
    • Running Elasticsearch in Docker with prorm
    • Running MariaDB in Docker with prorm
    • Running MongoDB in Docker with prorm
  • prorm control plane
    • Introduction
    • Authentication
    • Errors
    • Getting started
      • Service
        • Is it up
        • This description
    • Account
      • Sign-up and verification
        • Create an account
        • Exchange credentials for a token
        • Verify with the emailed code
        • Verify by clicking the emailed link
        • Name the parent or guardian
        • What a consent link is about
        • Give consent for a child's account
        • Ask the parent or guardian again
        • Send a new code and link
      • Family
        • Everyone this account is responsible for
        • One child, in detail
        • Turn a limit on or off
      • Profile
        • Who am I
        • Change the account
        • Every address on the account
        • Add an address
        • Confirm an address with the code mailed to it
        • Mail a fresh code to an unconfirmed address
        • Make an address primary
        • Remove an address
        • What this caller may do
    • Security
      • Sessions
        • Everywhere this account is signed in
        • Sign out everywhere else
        • Sign one out
        • A token for the CLI on a named machine
        • A token for the customer console, from the staff one
        • Park a sign-in for the CLI to collect
        • The CLI collecting its sign-in
        • The team's members' live sessions
        • Sign a member's session out
        • Sign an account out everywhere
        • Everywhere the account is signed in
        • Sign an account's session out
      • Single sign-on
        • Begin a single sign-on
        • The provider's return
        • Link the signed-in account to a team's provider
        • Which team an address signs in to
        • The team's single sign-on, as a member sees it
        • Set the team's OpenID Connect provider up, or change it
        • Remove the provider
        • The team's domain claims and the DNS record each needs
        • Prove a claimed domain
      • MFA and passwords
        • Start enrolling a second factor
        • Prove the app has it
        • Remove the second factor
        • Ask for a reset link
        • Set a new password with the emailed token
      • Klock
        • Open a device-bound klock session
        • Check in, for the next grant
        • Step up the session asking
        • Rotate the device key
        • Freeze a session
        • Unfreeze a session
        • A guest grant
        • The revocation feed
        • The published keyset
        • What this control plane supports
      • Devices
        • Your devices, or a team's
        • Approve a new device's pairing code
        • One device
        • Rename a device
        • Revoke a device
        • Suspend a device
        • Reinstate a suspended device
        • Wipe a device
        • Ask a device to rotate its key
        • The team's members' devices
        • Revoke a member's device
        • An account's devices
        • Revoke an account's device
        • Wipe an account's device
        • The device fleet
      • Access rules and behaviour
        • The access rules that bear on you
        • Add an access rule
        • This request, as the rules see it
        • One access rule
        • Remove an access rule
        • The behaviour profiles that bear on you
        • Add or change a behaviour profile
        • The profile presets
        • Your behaviour findings
        • Acknowledge a finding
        • One behaviour profile
        • Remove a behaviour profile
        • The team's access rules
        • Add a team access rule
        • Remove a team access rule
        • The team's behaviour profiles
        • Add or change a team behaviour profile
        • Remove a team behaviour profile
        • What the team's profiles caught
        • Acknowledge a team finding
      • Recovery and lockdown
        • Ask for a recovery link
        • Recover access with the emailed code
        • Is the account locked down
        • Lock the account down
        • Release a lockdown
        • Lock a member down
        • Release a member's lockdown
      • MCP tokens
        • Make a delegated token for one MCP client
        • The account's delegated tokens
        • Revoke a delegated token
        • What a delegated token may do now
        • Run one MCP tool with a delegated token
        • Run a device-signed MCP tool call relayed by the MCP Worker
      • Kerberos
        • Give the team its own Kerberos realm
        • The team's realm
        • Remove the team's realm
    • Logging and monitoring
      • Audit log
        • What has been done to the account
        • What has happened to this account
        • The account's audit trail
      • Operations
        • Everything that has been asked of this database
        • One, with its steps
        • Stop it
        • Put a failed one back in the queue
        • Every operation, across all accounts
      • Metrics and events
        • Every measurement, for a graph
        • Who did what to it
    • Billing
      • Plans and pricing
        • What the plans allow
        • The rate card
        • What one database would cost
        • What this account is paying for, itemised
      • Subscriptions and invoices
        • What this month's invoice will say
        • What this account is on
        • What it has been charged
        • Start a Stripe Checkout session
        • Open the customer portal
        • Stripe's events
        • Buy more connection pools
      • Team billing
        • The team's billing account
        • What the team has been charged
        • What this month's invoice will say
        • The team's cards
        • Add a card, through a Stripe-hosted setup session
        • Make a card the team's default
        • Remove a card
        • Start the team paying
        • Open the Stripe portal on the team's customer
        • Change the team's plan
        • Who receives the team's bills
        • How the team pays for transfers
    • Support
      • Support chat
        • My conversations
        • Start a conversation
        • Every support conversation, open first
        • A ticket for the socket
        • One conversation, with who is in it and who is connected
        • Rename a group; open or close a support conversation
        • The lines, oldest first
        • Say something
        • Add somebody
        • Mark read
    • Administration
      • Users
        • Every account
        • One account, with everything hanging off it
        • Grant or revoke admin
        • Verify an account by hand
        • Lock or unlock sign-in
        • Allow or hold provisioning
        • Send the verification mail again
        • Set the plan by hand
      • Platform
        • Every database and store, across all accounts
        • The counts worth putting on a dashboard
      • Staff and permissions
        • What kind of admin the caller is, and which scopes they hold
        • The admin presets
        • Every admin, and the scopes each resolves to
        • Make an account an admin of a kind
        • Change an admin's kind, or a custom admin's scopes
        • Revoke admin
      • Team administration
        • Every team, with its owner and size
        • One team in full — members with their accounts, and projects
        • Hand the team to another member
        • Put an account in the team, active at once
        • Change a member's role
        • Remove a member
      • Assumed identities
        • Assume an identity — act as a customer account
        • The identities the caller holds open
        • End an assumed identity
      • Support inbox
        • The platform's addresses
        • Add a platform address
        • One address, with its counts
        • Change an address's label, routing, or whether it accepts mail
        • Remove an address that holds no mail
        • Make, adopt or repair the address's routing rule
        • Every admin's personal address, made where missing
        • File a raw message, as if it had arrived
        • The inbox
        • One message, whole
        • Mark a message
        • Hand a message to an admin, or take it back
        • Leave an internal note
        • Reply, from the address the message came to
        • What is waiting
    • Databases
      • Databases
        • Data stores
          • Every data store on the account
          • Provision a data store
          • Change a data store that is already running
          • One data store
          • Deprovision it
          • Report what it is using
          • How to reach a data store
        • Connections and credentials
          • The api keys on a database
          • Issue a key
          • Revoke a key
          • The engine users we created
          • Create a user inside the database
          • Give a user a new password
          • Drop a user
          • How the database is reached
          • Set how it is reached
          • Revoke every api key on it
          • How to connect to it
          • The ssh keys that open its bastion
          • Add a key, or have one made
          • The private half of a generated key, once
          • Revoke an ssh key
        • Schema and objects
          • The SQL objects we put in it
          • Create one, and apply it
          • What is in the database, as the engine reports it
          • One table
          • What it would run, without running it
          • Redefine an object in place
          • Drop it
          • The extensions installed in it
        • Migrations
          • A plan compiled to statements, recording nothing
          • The migrations recorded for it
          • Record one
          • Apply everything recorded and not yet run
          • Apply everything pending, oldest version first
        • Lifecycle
          • Grow or shrink it
          • Stop it accepting connections
          • Start it again
          • Move it to another region
        • Backups and replicas
          • Take a backup
          • The backups it has
          • Restore one into it
          • What has been restored into it, and from where
          • Its read copies
          • Add one
        • Policies
          • The catalog
          • One policy
          • What this database is held to
          • Switch a policy on, or change its knobs
          • Switch it off
          • Mark the policies reviewed
          • Evaluate the periodic policies now
          • Who is behind a credential
        • Passwordless
          • The database's passwordless sessions
          • Issue a passwordless session (v1)
          • Revoke a passwordless session
          • Bind a session to a device key
          • Issue a v2 session for a host to bind
          • Redeem an enrolment ticket on the host
          • Check a passwordless credential
          • Run one described query under a passwordless credential
        • Database roles
          • The database's roles
          • Define a role
          • One role
          • Change what a role permits
          • Delete a role, and revoke every key granted it
          • Grant a role, which mints a key
          • Who holds a role
          • The api keys bound to a role
        • Pools
          • A database's connection pools, and how busy each is
          • Open another pool
          • Rename a pool or change its size
          • Close a pool
        • Data plane
          • Run a query against a database
          • Several statements, in order, over one connection
          • Is this database answering
          • What the proxy itself is holding
        • Transfers
          • The strategies this account's tier may use
          • Price a transfer without creating it
          • The account's transfer jobs, newest first
          • Create a transfer job
          • One job, and whether its certificate holds
          • Cancel an unfinished job
          • Start a created job, confirming its price
          • Report a job's status (the runner, or the owner's side of a local run)
        • Every database on the account
        • Provision a database
        • Change a database that is already running
        • One database
        • Deprovision it
        • Read or write it
        • Report what it is using
        • The engines this API will provision
        • Where a managed database can be put
    • Teams
      • Teams
        • Members
          • Who is on it
          • Invite somebody
          • Accept an invitation
          • Change somebody's role
          • Remove somebody
        • Projects
          • The projects a team runs
          • Start one
          • Rename a project, or move its environment
          • Delete a project
          • One project, and what is in it
          • Put a database or store in it
          • What is in a project
          • Take one out
        • The teams this account is on
        • Start one
        • The twelve roles, and what each may do
        • Rename a team
        • Close a team
        • One team
        • Hand the team to somebody else
        • Leave a team
    • Schemas
      • Error
      • ValidationErrors
      • Token
      • PolicyCategory
      • PolicySeverity
      • PolicyAction
      • PolicyTrigger
      • PolicyParam
      • Policy
      • PolicyCatalog
      • PolicyAssignment
      • PolicyOutcome
      • PolicyFiring
      • CredentialTrace
      • Relation
      • Restrictions
      • ChildSummary
      • SignupRequest
      • SigninRequest
      • UpdateMeRequest
      • User
      • Address
      • GeoPoint
      • D1Region
      • Usage
      • UsageReport
      • Resource
      • Database
      • DataStore
      • NewDatabase
      • NewDataStore
      • QueryRequest
      • DatabasePrivilege
      • ApiKey
      • DbUser
      • Access
      • AccessRequest
      • DatabaseObject
      • NewDatabaseObject
      • NewMigration
      • SchemaOperation
      • Migration
      • Operation
      • Session
      • AdminPowers
      • Admin
      • AuditEntry
      • TeamRole
      • Conversation
      • ChatMessage
      • TeamSso
      • Team
      • TeamMember
      • SshKey
      • ConnectionInfo
      • InboxAddress
      • InboxMailbox
      • InboxMessageSummary
      • InboxMessage
      • InboxNote
      • Page
      • Quote
      • Plan
      • ApiError
      • CapabilityRefusal
      • KlockErrorCode
      • PageWindow
      • SessionView
      • KlockStepUpRequest
      • KlockOpenRequest
      • KlockOpenedSession
      • UpdateMeResult
      • CapabilitiesView
      • SessionLimits
      • Device
      • DeviceListed
      • DeviceTeamRef
      • DeviceCommand
      • DeviceAuditLine
      • DeviceActionRequest
      • StaffActionRequest
      • ManagedSession
      • SessionRevoked
      • AccessRule
      • AccessRuleRequest
      • BehaviourProfile
      • BehaviourProfileRequest
      • BehaviourProfileSaved
      • BehaviourFinding
      • BehaviourFindingAck
      • StepUpProof
      • Removed
      • MemberLockdownRequest
      • McpClient
      • McpDelegatedView
      • McpToolCall
      • McpForwardRequest
      • PasswordlessScope
      • RoleScope
      • PasswordlessSession
      • PasswordlessTlsMaterial
      • PasswordlessKerberosMaterial
      • ServiceDeviceKeys
      • ServiceSession
      • DatabaseRole
      • TransferEndpoint
      • TransferJobRequest
      • TransferQuote
      • TransferStrategy
      • TransferJob
      • TransferJobAccess
      • Pool
      • PoolAllowance
      • BillingSubscription
      • Invoice
      • BillingContact
      • PaymentMethod
      • UpcomingCharges
      • TeamBilling
      • TeamKerberosRealm
      • TeamSsoDomain
      • TeamSsoResponse
      • StoreConnection
      • BatchFailure
      • ProxyStatus
  1. guides

In the browser

prorm ships a browser build, served from this site:
or as a module:
Roughly 470 KB minified. It is hosted here rather than on a public CDN, so the
library and the site it documents come from one origin and one deploy.

What is in it#

Code written against the npm package reads the same here — same names, same
shapes, same decorators:
AvailableNot available
Prorm, Model, ModelInstance, DataTypes, QueryTypesThe 77 server-side SQL dialects (PostgreSQL, MySQL, …)
Every model decorator — @Table, @Column, @PrimaryKey, @HasMany, …The 106 store adapters (Redis, S3, Mongo, …)
Every schema decorator — @View, @Trigger, @Procedure, @SqlFunctionMigrations (they read the filesystem)
The query vocabulary — Op, fn(), col(), where, gte, between, …Anything needing a socket
The bare type names — STRING, INTEGER, BOOLEAN, …
The error hierarchy and its guards
The stream transforms
The 15 diagram generators
SQLite (Web SQL or WebAssembly), localStorage, sessionStorage, cookiesPouchDB and RxDB — adapters ship, the libraries do not
That is 483 exports against the package's 1,064; the difference is almost
entirely the dialects and store adapters, which reach for Node drivers. They are
not silently missing — the bundler resolves each to a stub that names itself if
used.
The excluded modules are not merely absent: importing one gives a stub that
names itself if you use it, so a mistake reads as a sentence rather than a
mangled driver.

Pinning a version#

https://prod-orm.io/cdn/prorm-2.1.0.min.js     ← pinned
https://prod-orm.io/cdn/prorm.min.js           ← latest
https://prod-orm.io/cdn/manifest.json          ← what is published
prorm.VERSION reports the build's version at runtime.

Web storage#

localStorage and sessionStorage are key-value stores every browser already
has, so this is persistence with nothing installed and no server:
localStore() survives the tab closing; sessionStore() is cleared when it
does. That is the only difference between them.
MethodDoes
get(key) / set(key, value, ttl?)Read and write, JSON-serialised
has(key) / del(key)Test and remove
mget(keys) / mdel(keys)Several at once
incr(key, by?) / decr(key, by?)Counters, starting from zero
list(prefix?) / entries(prefix?)Keys, or keys with values
clear()Remove this namespace only
purgeExpired()Drop expired entries now
size()Approximate keys and bytes
For data the server must see on each request, use cookies instead —
they are a different tool with a different cost.

Three things that make web storage awkward#

Each is handled, but worth knowing because they explain the API.
It can throw when you merely touch it. With cookies blocked for the site,
or in some private modes, reading the localStorage property raises a
SecurityError — before any write. connect() probes with a real write and
raises WebStorageUnavailableError explaining why, and
WebStorageStore.isAvailable() answers without throwing:
It has no expiry. Nothing you write ever leaves on its own, so a ttl is
stored with the value and enforced when the value is read. A key past its
lifetime reads as null and is removed then. purgeExpired() sweeps eagerly
when you would rather reclaim space up front.
It is small, and throws when full. Around 5 MB per origin, and a write past
that raises QuotaExceededError — which browsers spell four different ways. A
full store first drops its own expired entries and retries; only if that fails
does it raise WebStorageQuotaError, naming the key and the byte count, because
"quota exceeded" on its own tells you nothing about what to shrink.

Namespaces#

Every key is prefixed, so a store finds its own entries and leaves the rest of
the origin alone — clear() removes only what this store wrote:
Note: a page is single-threaded, so incr() needs no locking. Two tabs can
still interleave writes to the same key, and nothing in the browser prevents
that.

Cookies#

The same shape as the storage API — get, set, has, del, list,
entries, incr, decr, clear, size — over document.cookie, or the
async Cookie Store API where the browser has it.

Cookies are not a storage backend#

Warning: everything you put here is uploaded to the server with every
request to this domain
— every image, script and fetch. A megabyte in
localStorage costs nothing per request; a kilobyte in a cookie is paid for
thousands of times. Cookies are for what the server must read: a session id,
a locale, a feature flag. Application data belongs in localStore().
size() reports that cost, including cookies this store did not write, since
they ride along too:

Where they differ from web storage#

CookiesWeb storage
Size~4 KB each, ~50 per domain~5 MB per origin
Sent to the serverEvery requestNever
ExpiryNative, browser-enforcedEmulated on read
OverflowSilently droppedThrows
Readable by JSUnless httpOnlyAlways
Two of those need care, and both are handled:
An oversized cookie is discarded without a word. Browsers do not raise
anything — the write simply does not happen and the next read finds nothing.
Every write is measured first and throws CookieTooLargeError naming the key
and its size:
httpOnly cookies are invisible here. That is the point of the flag: the
cookies most worth protecting are the ones JavaScript must not touch. A get()
returning null therefore means "not readable", which is not quite the same as
"not there".

Attributes#

path, domain, secure and sameSite are set per store and overridable per
write. secure defaults to whether the page itself is secure, because a
Secure cookie on an http: page is rejected silently; sameSite defaults to
Lax.
Note: deleting a cookie requires the same path and domain it was written
with. A cookie set on /app is not removed by deleting at /, and that
failure is also silent — so pass the same attributes to del() if you varied
them on set().

PouchDB and RxDB#

For documents, queries over them, and — the reason most people reach for
either — replication, so a page works offline and reconciles later.
Neither is bundled. PouchDB is around 140 KB and RxDB more; carrying them would
more than double this build for pages that use neither. The page loads the one
it wants and passes the instance in:
That also keeps the choice of adapter yours — IndexedDB, memory, or a remote
CouchDB — since which one is in use is the page's business, not the store's.
Note: PouchDB rejects a write that does not carry the document's current
_rev, so set() fetches the existing document first. That is one read per
write; use setMany() when writing more than a few. _id and _rev are
stripped on the way out — they are PouchDB's bookkeeping, not your data.
find() needs the pouchdb-find plugin registered on the page; without it the
call says so rather than failing on an undefined method.

RxDB#

Schema-first and reactive. The same store surface, plus the observable that is
the point of it:
observe() hands back RxDB's own observable, unwrapped — subscribing,
piping and unsubscribing all work exactly as RxDB documents them, which
wrapping would only take away.

Choosing#

Web storageCookiesPouchDB / RxDBWeb SQL
Size~5 MB~4 KBDisk-bound~50 MB
ShapeKey-valueKey-valueDocumentsRelational
QueriesBy keyBy keySelectorsSQL
ReplicationNoNoYesNo
BundledYesYesNo — page suppliesYes
Works in Chrome/FirefoxYesYesYesNo — use the WebAssembly engine

A real SQL database#

That is the ORM as documented everywhere else — models, finders, operators,
views — running entirely in the page. openBrowserDatabase() picks the engine;
you do not have to.

Which engine you get#

Web SQLWebAssembly
WhereSafari, Chrome < 119Everywhere
DownloadAlready there~1.5 MB, on first use
PersistenceYesYes, via OPFS
Size limit~50 MBDisk-bound
Web SQL is preferred where it exists purely because it is already loaded, which
saves the download. Everywhere else — Chrome, Firefox, and so most visitors —
the WebAssembly build answers.
To get the same engine in every browser, say Safari included, ask for it by
name. Your tests then run against one SQLite build rather than two:
Deprecated: Web SQL was abandoned as a specification in 2010, never
implemented by Firefox, and removed from Chrome in version 119. It is
supported here because Safari still ships it, not because it has a future.

WebAssembly#

This is the SQLite project's own build, so the SQL the ORM generates runs
unchanged. It is served from this site — the same origin as the library — and
fetched the first time a database is opened, so a page that never opens one
downloads none of it.
Persistence uses the OPFS SAHPool VFS: real files in the browser's Origin
Private File System, surviving reloads. That mode is chosen deliberately — the
other OPFS mode needs SharedArrayBuffer, which needs the page to be
cross-origin isolated with COOP and COEP headers. Requiring two response headers
on your HTML in order to use a database is a poor trade; SAHPool needs
neither.
Where OPFS cannot be opened — a sandboxed iframe, a second tab already holding
the pool — it falls back to an in-memory database and warns, rather than
appearing to persist and losing everything on reload:

Serving the engine yourself#

wasmUrl points at a directory holding the three files of the SQLite
project's build. To serve them from your own origin:

Copy the files#

$ mkdir -p public/vendor/sqlite
$ cd public/vendor/sqlite
$ curl -fsSLO https://prod-orm.io/cdn/sqlite/index.mjs
$ curl -fsSLO https://prod-orm.io/cdn/sqlite/sqlite3.wasm
$ curl -fsSLO https://prod-orm.io/cdn/sqlite/sqlite3-opfs-async-proxy.js
public/
  vendor/
    prorm.esm.min.js
    sqlite/
      index.mjs                     # text/javascript
      sqlite3.wasm                  # application/wasm
      sqlite3-opfs-async-proxy.js   # the OPFS worker
  index.html

Serve them with the right types#

Point the library at them#

[!IMPORTANT]
Serving the engine yourself requires two content types to be right —
.mjs as text/javascript and .wasm as application/wasm. A module served
as application/octet-stream cannot be imported, and the browser will not
stream-compile a wasm file without the correct type.
Streaming is not offered on either engine: both hand back a whole result set
with no cursor, so a "stream" would be the full result buffered in memory
pretending otherwise. Model.iterate() pages through
findAll() and works on both.

A complete offline page#

A notes page with no server: the notes live in SQLite, the last filter the
reader picked lives in localStorage, and the page says so when the browser
could not give it persistent storage.

Diagrams in the browser#

The generators build SVG through a DOM. Node has none, so they use xmldom
there; in the browser they are mapped onto the real DOMImplementation, which
means diagrams render client-side with nothing extra loaded:

Related reading#

Streaming results — Model.iterate(), which works here
Schema objects — views and triggers, all SQL-free
Diagram generators
Modified at 2026-10-04 00:40:16
Previous
Audit logging
Next
Bulk operations
Built with