1. dialects
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. dialects

SQLite Dialect

Overview#

SQLite is an embedded, serverless, zero-configuration
SQL database engine that stores an entire database (schema, tables, indexes,
data) in a single file on disk — or entirely in memory. There is no separate
server process: the database runs in-process with your application.
PropertyValue
Dialect namesqlite
Extendsnothing — implements Dialect directly
Driverbetter-sqlite3
Cloud SDKnone
Default portnone — embedded
Local runtimeembedded — a file or :memory:
This ORM's SQLiteDialect (in src/dialects/sqlite/index.ts) is built on the
synchronous better-sqlite3
driver. Because the engine is embedded, several operations that are network
round trips in client/server dialects are pure in-process calls here — most
notably streaming, which uses better-sqlite3's native Statement#iterate()
cursor rather than a paginated LIMIT/OFFSET fallback.
Key facts (from src/dialects/sqlite/index.ts):
Dialect name is 'sqlite'; the underlying driver library is
'better-sqlite3'.
Identifiers are double-quoted: escapeId('col') → "col", with embedded
" doubled ("").
Foreign key enforcement is off by default in SQLite, so connect()
explicitly runs PRAGMA foreign_keys = ON on every new connection.
SQLite's dynamic type system means the ORM's type mapping produces column
affinities rather than strictly-enforced types (unless STRICT tables are
used — see below).

Quick start#

Install#

$ npm install ts-prorm-orm better-sqlite3
$ pnpm add ts-prorm-orm better-sqlite3
$ yarn add ts-prorm-orm better-sqlite3
$ bun add ts-prorm-orm better-sqlite3

Connect#

import { Prorm } from 'ts-prorm-orm';

export const db = new Prorm({
  dialect: 'sqlite',
  storage: './data/app.db', // or ':memory:'
});
import { Prorm } from 'ts-prorm-orm';

export const db = new Prorm({
  dialect: 'sqlite',
  url: 'file:./data/app.db',
});
A URL is folded into host, port, database, username and password before the dialect is built; any of those passed alongside it wins.

Define a model#

import { DataTypes } from 'ts-prorm-orm';

const User = db.define('User', {
  id: { type: DataTypes.INTEGER(), primaryKey: true, autoIncrement: true },
  email: { type: DataTypes.STRING(255), allowNull: false, unique: true },
  name: { type: DataTypes.STRING(100) },
});

await db.sync(); // CREATE TABLE — see Generated SQL below

Query#

import { Op } from 'ts-prorm-orm';

await User.create({ email: 'ada@example.com', name: 'Ada' });

const page = await User.findAll({
  where: { name: { [Op.like]: 'A%' } },
  order: [['id', 'ASC']],
  limit: 20,
  offset: 40,
});

Generated SQL#

Each block is the statement the model call above it hands the dialect's query(), captured with the driver stubbed out. The model layer inlines the values.

Create table#

db.sync() for the model in the Quick start:
CREATE TABLE

Insert, update and delete#

await User.create({ email: 'ada@example.com', name: 'Ada' });
await User.update({ name: 'Grace' }, { where: { email: 'ada@example.com' } });
await User.destroy({ where: { id: 1 } });

Upsert#

await User.upsert({ id: 1, email: 'ada@example.com', name: 'Ada Lovelace' });
upsert() also reads the row by primary key before and after the write, to return the instance.

Pagination#

await User.findAll({
  where: { name: { [Op.like]: 'A%' } },
  order: [['id', 'ASC']],
  limit: 20,
  offset: 40,
});

Transaction with a savepoint#

await db.transaction(async (t) => {
  await User.create({ email: 'grace@example.com', name: 'Grace' }, { transaction: t });
  // A nested transaction is a SAVEPOINT; its failure undoes only its own work.
  await db
    .transaction(async (sp) => {
      await User.destroy({ where: { id: 1 }, transaction: sp });
      throw new Error('keep the row');
    }, { transaction: t })
    .catch(() => {});
});
Type mapping for the portable descriptors: STRING → VARCHAR(255), auto-increment INTEGER → INTEGER, BOOLEAN → INTEGER, DATE → DATETIME, UUID → TEXT, JSON → TEXT. The type comparison lines the major engines up side by side.

Connection#

SQLiteDialectOptions extends DialectOptions and adds SQLite-specific fields.
The top-level PrormOptions exposes dialect and storage; the remaining
driver options (readonly, fileMustExist, timeout, verbose) pass through
dialectOptions.
import { Prorm } from 'ts-prorm-orm';

// File-backed database
const db = new Prorm({
  dialect: 'sqlite',
  storage: './data/app.db',
});

// In-memory database (the default when storage is omitted)
const mem = new Prorm({
  dialect: 'sqlite',
  storage: ':memory:',
});

// With extra better-sqlite3 driver options
const ro = new Prorm({
  dialect: 'sqlite',
  storage: './data/app.db',
  dialectOptions: {
    readonly: true,       // open the file read-only
    fileMustExist: true,  // fail instead of creating a new file
    timeout: 5000,        // busy-handler timeout in ms (default 5000)
    // verbose: console.log, // log every executed statement
  },
});
Notes:
storage defaults to ':memory:'. An in-memory database is discarded when
the connection closes.
storage may also point at an attached-database path or :memory: for use
with attachDatabase().
On connect(), the constructed better-sqlite3 options are
{ readonly, fileMustExist, timeout, verbose }, and PRAGMA foreign_keys = ON is issued immediately.

Type mapping#

getDataTypeSql(dataType, columnName?) maps the ORM's type descriptors to
SQLite column types. Because SQLite uses type affinity, several logical types
collapse onto TEXT, INTEGER, or BLOB.
ORM type descriptorGenerated SQL
{ key: 'STRING' }VARCHAR(255)
{ key: 'STRING', length: 100 }VARCHAR(100)
{ key: 'CHAR', length: 4 }CHAR(4)
{ key: 'TEXT' }TEXT
{ key: 'INTEGER' }INTEGER
{ key: 'INTEGER', unsigned: true }INTEGER UNSIGNED
{ key: 'BIGINT' }BIGINT
{ key: 'FLOAT' }FLOAT(10,2)
{ key: 'DOUBLE' }DOUBLE(10,2)
{ key: 'DECIMAL', precision: 12, scale: 4 }DECIMAL(12,4)
{ key: 'BOOLEAN' }INTEGER (SQLite has no boolean type)
{ key: 'DATE' }DATETIME
{ key: 'DATEONLY' }DATE
{ key: 'TIME' }TIME
{ key: 'BLOB' }BLOB
{ key: 'JSON' } / { key: 'JSONB' }TEXT (JSON stored as text)
{ key: 'UUID' }TEXT
{ key: 'GEOMETRY' }BLOB
{ key: 'ENUM', values: ['a','b'] }TEXT CHECK("col" IN ('a','b'))
{ key: 'VIRTUAL' }'' (no column emitted)
'TEXT' (raw string)TEXT (passed through verbatim)
Booleans have no dedicated SQLite type, so BOOLEAN maps to INTEGER, and the
escape()/bind-coercion paths convert JS true/false to 1/0. Date
values are serialized to ISO-8601 strings; plain objects/arrays are
JSON.stringify-ed; Buffer values are emitted as X'...' blob literals.

ENUM emulated as TEXT + CHECK#

SQLite has no native ENUM. The object form
{ key: 'ENUM', values: [...] } and the raw string form ENUM('a','b') both
compile to a TEXT column with a CHECK(... IN (...)) constraint that
references the actual column being defined (via the columnName argument),
so the constraint validates the correct column:
dialect.getDataTypeSql({ key: 'ENUM', values: ['draft', 'live'] } as any, 'status');

SQL generation & dialect features#

DDL — CREATE TABLE, STRICT, WITHOUT ROWID#

createTable() builds a standard CREATE TABLE, optionally with
IF NOT EXISTS. Two SQLite-specific table modifiers are supported after the
column list and can be combined as ) STRICT, WITHOUT ROWID:
strict: true → appends STRICT (SQLite 3.37+ rigid type checking).
withoutRowid: true → appends WITHOUT ROWID (stores rows keyed by the
PRIMARY KEY instead of the implicit rowid).
AUTOINCREMENT is emitted only when a column is both primaryKey and
autoIncrement and its resolved type is exactly INTEGER — this mirrors
SQLite's rule that AUTOINCREMENT is valid only on an INTEGER PRIMARY KEY.
Options that other dialects honor but SQLite cannot are ignored with a
console.warn: engine, charset, table-level collate, rowFormat, and
initialAutoIncrement. uniqueKeys and indexes are realized as separate
CREATE [UNIQUE] INDEX statements after the table is created.
await dialect.createTable(
  'users',
  {
    id: { type: { key: 'INTEGER' } as any, primaryKey: true, autoIncrement: true },
    email: { type: { key: 'STRING' } as any, allowNull: false, unique: true },
    role: { type: { key: 'ENUM', values: ['admin', 'user'] } as any },
  },
  { strict: true },
);

Indexes#

addIndex() emits CREATE [UNIQUE] INDEX IF NOT EXISTS, and supports partial
indexes (where, SQLite 3.8+) and expression indexes (expression, SQLite
3.9+, wrapped as ((expr))).

INSERT and upsert (ON CONFLICT)#

buildInsertQuery() produces a parameterized INSERT, inlining Literal
values directly rather than binding them. With no columns it degrades to
INSERT INTO ... DEFAULT VALUES. Upserts use SQLite's ON CONFLICT syntax:
dialect.buildInsertQuery('kv', { k: 'a', v: 1 });
// { sql: 'INSERT INTO "kv" ("k", "v") VALUES (?, ?)', values: ['a', 1] }
ON CONFLICT ... DO UPDATE requires an explicit conflictFields target
(SQLite cannot infer it), and the dialect throws a clear error if it is missing:
dialect.buildInsertQuery(
  'kv',
  { k: 'a', v: 1 },
  { upsert: true, conflictFields: ['k'] },
);
DO NOTHING is the one form that may omit a target:
dialect.buildInsertQuery('kv', { k: 'a', v: 1 }, { upsert: true, doNothing: true });
// ... VALUES (?, ?) ON CONFLICT DO NOTHING
buildUpsertQuery() behaves the same way, additionally honoring
updateOnDuplicate (restrict the SET list) and returning (SQLite 3.35+
RETURNING * or a column list).

Chained multi-target conflicts#

SQLite allows several ON CONFLICT clauses in one INSERT, each targeting a
different constraint and evaluated in order. Pass conflictTargets (a
ConflictTarget[]), which takes precedence over conflictFields/doNothing:
dialect.buildInsertQuery(
  'accounts',
  { id: 1, email: 'a@x.io', handle: 'ada' },
  {
    upsert: true,
    conflictTargets: [
      { fields: ['id'], action: 'DO NOTHING' },
      { fields: ['email'], action: 'DO UPDATE', updateColumns: ['handle'] },
    ],
  },
);
Each ConflictTarget supports fields (required), action
('DO NOTHING' | 'DO UPDATE', default DO UPDATE), updateColumns
(defaults to all inserted columns), and a raw where restricting when the
DO UPDATE applies.

UPDATE / DELETE / RETURNING#

buildUpdateQuery() and buildDeleteQuery() build parameterized statements
with an optional LIMIT and an optional RETURNING clause (SQLite 3.35+).
Literal values in an UPDATE set are inlined.

WHERE-clause operators#

buildWhereClause() produces parameterized SQL and supports both string keys
($gt) and Symbol operators (normalized via operatorToWhereKey). Sibling
$and/$or/$not are AND-ed with plain field conditions rather than
replacing them, and multiple operators on one field (e.g. { age: { $gte, $lte } }) are split and AND-ed.
OperatorSQL
$eq / $ne= ? / != ?
$gt / $gte / $lt / $lte> ? / >= ? / < ? / <= ?
$like / $notLikeLIKE ? / NOT LIKE ?
$iLike / $notILikeLIKE ? / NOT LIKE ? (LIKE is ASCII-case-insensitive)
$startsWith / $endsWith / $substringLIKE ? with % padding on the bound value
$in / $notInIN (...) / NOT IN (...)
$between / $notBetweenBETWEEN ? AND ? / NOT BETWEEN ? AND ?
$isNull / $isNotNull / $is / $notIS NULL / IS NOT NULL / IS ? / != ?
$containsjson_each(...) subquery counting matched elements
$col"key" = "otherColumn" (column-to-column)
A bare array value is treated as an IN:
dialect.buildWhereClause({ id: [1, 2, 3] });
// { sql: '"id" IN (?,?,?)', values: [1, 2, 3] }

JSON (JSON1) support#

SQLite stores JSON as TEXT and operates on it via the JSON1 extension. The
dialect exposes builders that emit JSON1 functions:
buildJsonExtract(col, path, asText?) → json_extract("col", '$.path'),
wrapped in CAST(... AS TEXT) when asText (the default) is set.
buildJsonContains, buildJsonHasKey, buildJsonPathQuery — containment /
key-existence / path comparisons.
buildJsonGroupArray, buildJsonGroupObject — the json_group_array() /
json_group_object() aggregates for rolling child rows into nested JSON.
buildJsonPatch(col, patch) → json_patch("col", json(?)) (RFC 7396 merge
patch; keys set to null are removed).

Transactions & savepoints#

The outer transaction runs a real BEGIN TRANSACTION / COMMIT / ROLLBACK;
nested transactions are emulated with SAVEPOINT / RELEASE SAVEPOINT /
ROLLBACK TO SAVEPOINT. Standalone savepoint SQL is available via
createSavepointSQL, releaseSavepointSQL, and rollbackToSavepointSQL.

Retry on lock contention#

query() supports retry options and, by default when retry is enabled,
retries on SQLITE_BUSY, SQLITE_LOCKED, database is locked, and
database table is locked — the transient errors that arise when multiple
connections contend for the single database file.

Quirks & limitations#

No ALTER TABLE ADD CONSTRAINT / DROP COLUMN / ALTER COLUMN. SQLite's
ALTER TABLE is limited, so these are implemented via the documented
table-rebuild dance:
removeColumn() and changeColumn() recreate the table without / with the
modified column.
addForeignKey() runs the full 12-step rebuild: read the existing
CREATE TABLE from sqlite_master, splice the FOREIGN KEY (...) REFERENCES ... clause in before the closing paren, then (with
PRAGMA foreign_keys=OFF) RENAME the old table aside, recreate it,
INSERT INTO new SELECT * FROM old, drop the temp, and restore
PRAGMA foreign_keys=ON in a finally.
DROP TABLE ... CASCADE is unsupported. SQLite has no CASCADE on
DROP TABLE; cascade: true is treated as a no-op (dropping a table simply
removes its schema entry) rather than emitting invalid syntax. This was
needed for sync({ force: true }) to work at all.
No schemas. SQLite has only the main schema (plus attached databases).
createSchema/dropSchema are no-ops, showAllSchemas()/listSchemas()
return ['main'], and quoteTable(name, schema) treats the schema as a
prefix ("schema"."table") for attached databases.
No users, roles, grants, DDL sequences, or identity columns.
createUser, createRole, grant, revoke, etc. all throw
"... not supported by SQLite"; identity columns throw and direct you to
AUTOINCREMENT. Sequences are simulated with an
INTEGER PRIMARY KEY AUTOINCREMENT table.
No native partitioning. addPartition() throws; the other partition
helpers (createPartitionedTable, createPartition, attach/detach) are a
metadata-table + UNION ALL view emulation, not real partitions.
Table/column comments are not supported — comment options are accepted
but stored nowhere (documented no-op).
CREATE DATABASE / DROP DATABASE generate CREATE DATABASE "name" /
DROP DATABASE "name" strings for interface parity, but the SQLite way to add
a database is attachDatabase(path, alias) (a real ATTACH DATABASE ? AS alias), with detachDatabase(alias) to remove it.

Runnable examples#

import { SQLiteDialect } from 'ts-prorm-orm';

// The dialect class itself is exported, so its SQLite-only options (`strict`)
// and helpers (`buildJsonExtract`) are typed. `new Prorm({ dialect: 'sqlite' })`
// reaches the same class through `getDialectInstance()`.
const dialect = new SQLiteDialect({ storage: ':memory:' });
await dialect.connect(); // opens the DB and runs PRAGMA foreign_keys = ON

// Create a STRICT table with an emulated ENUM
await dialect.createTable(
  'todos',
  {
    id: { type: { key: 'INTEGER' } as any, primaryKey: true, autoIncrement: true },
    title: { type: { key: 'STRING', length: 200 } as any, allowNull: false },
    status: { type: { key: 'ENUM', values: ['open', 'done'] } as any, defaultValue: 'open' },
    meta: { type: { key: 'JSON' } as any },
  },
  { strict: true },
);

// Insert (booleans/objects are coerced automatically on bind)
const insert = dialect.buildInsertQuery('todos', {
  title: 'Write docs',
  meta: { priority: 3 },
});
await dialect.query(insert.sql, { replacements: insert.values as any });

// Upsert on the primary key
const up = dialect.buildUpsertQuery(
  'todos',
  { id: 1, title: 'Write docs (v2)', status: 'done' },
  { conflictFields: ['id'], updateOnDuplicate: ['title', 'status'], returning: true },
);
await dialect.query(up.sql, { replacements: up.values as any });

// Query with operators + JSON extraction
const where = dialect.buildWhereClause({ status: 'done', id: { $gte: 1 } });
const jsonPri = dialect.buildJsonExtract('meta', 'priority');
const rows = await dialect.query(
  `SELECT id, title, ${jsonPri} AS priority FROM "todos" WHERE ${where.sql}`,
  { replacements: where.values as any },
);

await dialect.disconnect();

Caveats / not-yet-verified#

The build* methods shown here are pure/synchronous string builders; the SQL
they emit is grounded in src/dialects/sqlite/index.ts. Live execution
behavior (e.g. that a rebuilt foreign key enforces referential integrity, or
that STRICT rejects a mistyped value) depends on the running SQLite version
and is exercised only where the SQLite test suites cover it
(tests/dialects/sqlite-*.test.ts).
Several RETURNING, STRICT, expression-index, and partial-index features
require reasonably recent SQLite versions (3.35 / 3.37 / 3.9 / 3.8
respectively); better-sqlite3 bundles a modern SQLite, but a system SQLite
behind these versions would reject them.

Capabilities#

Computed from the code — capabilitiesOf() (src/transaction-support.ts),
whereSupportOf() (src/query-builders/expression-render.ts),
sqlObjectCapabilitiesOf() (src/sql-objects/capabilities.ts) and the
builders — by the scratch test described in the
capability matrix.
Capabilitysqlite
Transactions atomicyes
Savepointsyes
Isolationup to SERIALIZABLE
Transaction modesread-only no; deferrable no; constraint deferral no; SQLite locking type
RETURNINGnot emitted; returning is accepted and ignored
UpsertINSERT … ON CONFLICT (…) DO UPDATE
JSON columnTEXT (JSONB → TEXT)
Vector columnrefused — The VECTOR column type is not supported on SQLite. SQLite has no vector type; use Turso/libSQL (F32_BLOB) or the sqlite-vec extension
Array columnTEXT
WHERE builder32 operator keys; $and/$or/$not; $exists: yes; $in subquery: yes; expression operands: yes
SQL objectsfamily sqlite: triggers yes; procedures no; functions driverUdf; materialized views emulated (app); sequences no; enums check; RLS no
StreamingqueryStream() present; LISTEN absent; createExtension() present

Related reading#

All dialects — what each engine supports
Data types — how each type maps to this engine
Querying — how a FindOptions becomes SQL
Modified at 2026-10-04 00:40:16
Previous
Spanner (PostgreSQL interface) Dialect
Next
StarRocks Dialect
Built with