TinyJoin logoTinyJoin

API

The Essentials

create

Essential

The create function opens a TinyJoin database in a dedicated Worker.

create(): Promise<Client>
returnsPromise<Client>

A Promise resolving to a Client whose Worker and database are ready.

This overload creates an ephemeral in-memory database using the packaged Worker. Its data is lost when the Client closes or the page reloads.

Example

import {create} from 'tinyjoin';

const db = await create();
await db.exec(`
  CREATE TABLE IF NOT EXISTS tasks (
    id INTEGER PRIMARY KEY,
    title TEXT NOT NULL
  )
`);
const {rows} = await db.query('SELECT * FROM tasks ORDER BY id');
await db.close();

Since

v0.0.5

This create overload opens a database with ClientOptions.

create(options: ClientOptions): Promise<Client>
TypeDescription
optionsClientOptions

Storage and Worker configuration.

returnsPromise<Client>

A Promise resolving to a Client whose Worker and database are ready.

Use the options object to select storage or provide a custom Worker. Omitting its dataDir property selects an ephemeral in-memory database.

Example

import {create} from 'tinyjoin';

const db = await create({dataDir: 'memory://'});
await db.close();

Since

v0.0.5

This create overload opens a database at the supplied storage URL.

create(
  dataDir: string | undefined,
  options?: ClientOptions,
): Promise<Client>
TypeDescription
dataDirstring | undefined

The memory:// URL, a named opfs:// URL, or undefined for the default in-memory database.

options?ClientOptions

Optional Worker configuration.

returnsPromise<Client>

A Promise resolving to a Client whose Worker and database are ready.

Use a stable opfs://name to persist data in the same browser, or memory:// for an ephemeral database. When providing the second argument, supply a storage URL and leave options.dataDir unset.

Example

import {create} from 'tinyjoin';

const db = await create('opfs://my-app');
await db.close();

Since

v0.0.5

exec

Essential

The exec method runs one or more parameter-free statements as one implicit transaction.

exec(
  sql: string,
  options?: QueryOptions,
): Promise<Results<Row>[]>
TypeDescription
sqlstring
options?QueryOptions
returnsPromise<Results<Row>[]>

A Promise resolving to one result per statement, in script order.

Since

v0.0.5

query

Essential

The query method executes exactly one parameterized SQL statement.

query<RowType>(
  sql: string,
  params?: JsonValue[],
  options?: QueryOptions,
): Promise<Results<RowType>>
TypeDescription
sqlstring

A statement in the documented TinyJoin SQL subset.

params?JsonValue[]

JSON-compatible values for $1, $2, and so on.

options?QueryOptions

Result-shape options.

returnsPromise<Results<RowType>>

A Promise resolving to the statement results.

Since

v0.0.5

sql

Essential

The sql method is a parameterizing tagged-template form of query.

sql<RowType>(
  strings: TemplateStringsArray,
  ...params: JsonValue[],
): Promise<Results<RowType>>
TypeDescription
stringsTemplateStringsArray
...paramsJsonValue[]
returnsPromise<Results<RowType>>

A Promise resolving to the statement results.

Interpolated values become $n parameters. It does not interpolate raw identifiers or SQL fragments.

Since

v0.0.5

transaction

Essential

The transaction method stages row mutations and publishes them together when the callback succeeds, unless it explicitly rolls back.

transaction<Result>(callback: (transaction: Transaction) => Result | Promise<Result>): Promise<Result>
TypeDescription
callback(transaction: Transaction) => Result | Promise<Result>
returnsPromise<Result>

A Promise resolving to the callback's result after the transaction commits or explicitly rolls back.

Transaction calls on this Client queue in order. Do not await another transaction on this Client inside the callback; pass its Transaction to helpers instead. Use that object for all SQL in the callback and prepare handles beforehand. Other Clients for the same OPFS name wait for the whole callback to finish. Do not await work on those Clients from inside this callback. An uncaught callback error before commit discards staged work. A caught statement error leaves earlier writes staged unless rollback is called.

There is no cancellation or timeout option. Promise.race only stops waiting, and queued work can still commit. Rejection during commit can leave its outcome uncertain; see the recovery guide before replaying a failed write.

Since

v0.0.5

tinyjoin

The tinyjoin module provides a small PostgreSQL-shaped relational database that runs in a dedicated browser Worker and stores data in memory or OPFS.

Start with the create function. It constructs the Worker and loads the WebAssembly engine, so applications do not need to manage either directly.

Since

v0.0.5

Classes

Lifecycle classes

Client

The Client class represents one open TinyJoin database and its dedicated Worker.

Prefer the async create function so initialization failures are reported before the Client is returned.

Since

v0.0.5

Constructors
constructor

The constructor begins opening a Client immediately.

new Client(options?: ClientOptions): Client
TypeDescription
options?ClientOptions
returnsClient

Prefer create unless code specifically needs the waitReady lifecycle.

Since

v0.0.5

Methods
Lifecycle methods
check

The check method reads every row and index entry of the committed database, and every page that holds them, and resolves when all of them are sound.

check(): Promise<void>

Opening a database checks only its catalog, and each page and row is checked as a statement reads it, so rows that are never read, and whether each index still matches its table, are checked only here. The check takes time in proportion to the database, and statements from every Client of the same OPFS name wait while it runs. Call it outside a transaction.

TinyJoin cannot yet repair a database that fails its check.

Since

v0.4.0

close

The close method detaches this Client and releases its prepared statements and Worker. Other Clients for the same OPFS name stay connected; ownership transfers automatically when necessary. Repeated calls share the same asynchronous cleanup. A closed Client cannot resume; create a new one and recreate its prepared statements and subscriptions. Closing is not a cancellation or rollback guarantee for a write already in flight.

close(): Promise<void>
Since

v0.0.5

getSchema

The getSchema method reads the database's tables, with their columns, primary keys, and indexes, as the engine's catalog holds them.

getSchema(): Promise<Schema>
returnsPromise<Schema>

A Promise resolving to the Schema.

Tables and each table's indexes are in name order, and columns and key columns are in the order the table declares them. Call it outside a transaction.

Example
import {create} from 'tinyjoin';

const db = await create();
await db.exec(`
  CREATE TABLE tasks (id TEXT PRIMARY KEY, done BOOLEAN NOT NULL DEFAULT false);
  CREATE INDEX tasks_done ON tasks (done);
`);
const {tables} = await db.getSchema();
console.log(tables[0].columns[1]);
// -> {name: 'done', type: 'boolean', nullable: false, default: false}
await db.close();
Since

v0.5.0

setSchema

The setSchema method makes the database's tables, columns, primary keys, and indexes match a Schema, as getSchema returns one, in one change that commits whole or not at all.

setSchema(
  schema: Schema,
  options?: SetSchemaOptions,
): Promise<boolean>
TypeDescription
schemaSchema

The schema to make the database's.

options?SetSchemaOptions

Whether to drop what the schema leaves out.

returnsPromise<boolean>

A Promise resolving to true if the database changed, or false if it already had the schema.

It creates the tables and columns the schema has and the database lacks, renames a table or column from the name its renamedFrom gives while the database still has that name, and changes a column's default, nullability, or VARCHAR length to the schema's. Each table's indexes and foreign keys become exactly the schema's, with keys added once every table they reference exists. Tables and columns the schema leaves out stay, unless the options' drop property is true. Rows keep their values, as ALTER TABLE keeps them: a change no statement could make, to a primary key or a column's runtime type, is refused, and so is one that a row prevents, such as NOT NULL where a row holds NULL.

A schema the database already has changes nothing, so every tab of an application can call setSchema as it starts. A schema whose version is lower than the database's is refused with SCHEMA_OUTDATED, so that a tab still running an older copy of the application cannot undo a newer one's changes; a higher version is recorded. Call it outside a transaction.

Example
import {create} from 'tinyjoin';

const db = await create();
await db.setSchema({
  version: 1,
  tables: [
    {
      name: 'tasks',
      columns: [
        {name: 'id', type: 'text', nullable: false},
        {name: 'title', type: 'text', nullable: false},
        {name: 'done', type: 'boolean', nullable: false, default: false},
      ],
      primaryKey: ['id'],
      indexes: [{name: 'tasks_done', columns: ['done'], unique: false}],
      foreignKeys: [],
    },
  ],
});
await db.close();
Since

v0.5.0

SQL methods
exec
Essential

The exec method runs one or more parameter-free statements as one implicit transaction.

exec(
  sql: string,
  options?: QueryOptions,
): Promise<Results<Row>[]>
TypeDescription
sqlstring
options?QueryOptions
returnsPromise<Results<Row>[]>

A Promise resolving to one result per statement, in script order.

Since

v0.0.5

prepare

The prepare method parses and retains one reusable read or row-mutation statement in the Worker.

prepare<RowType>(sql: string): Promise<PreparedStatement<RowType>>
TypeDescription
sqlstring
returnsPromise<PreparedStatement<RowType>>

A Promise resolving to a reusable PreparedStatement handle.

Since

v0.0.5

query
Essential

The query method executes exactly one parameterized SQL statement.

query<RowType>(
  sql: string,
  params?: JsonValue[],
  options?: QueryOptions,
): Promise<Results<RowType>>
TypeDescription
sqlstring

A statement in the documented TinyJoin SQL subset.

params?JsonValue[]

JSON-compatible values for $1, $2, and so on.

options?QueryOptions

Result-shape options.

returnsPromise<Results<RowType>>

A Promise resolving to the statement results.

Since

v0.0.5

sql
Essential

The sql method is a parameterizing tagged-template form of query.

sql<RowType>(
  strings: TemplateStringsArray,
  ...params: JsonValue[],
): Promise<Results<RowType>>
TypeDescription
stringsTemplateStringsArray
...paramsJsonValue[]
returnsPromise<Results<RowType>>

A Promise resolving to the statement results.

Interpolated values become $n parameters. It does not interpolate raw identifiers or SQL fragments.

Since

v0.0.5

Transactions methods
transaction
Essential

The transaction method stages row mutations and publishes them together when the callback succeeds, unless it explicitly rolls back.

transaction<Result>(callback: (transaction: Transaction) => Result | Promise<Result>): Promise<Result>
TypeDescription
callback(transaction: Transaction) => Result | Promise<Result>
returnsPromise<Result>

A Promise resolving to the callback's result after the transaction commits or explicitly rolls back.

Transaction calls on this Client queue in order. Do not await another transaction on this Client inside the callback; pass its Transaction to helpers instead. Use that object for all SQL in the callback and prepare handles beforehand. Other Clients for the same OPFS name wait for the whole callback to finish. Do not await work on those Clients from inside this callback. An uncaught callback error before commit discards staged work. A caught statement error leaves earlier writes staged unless rollback is called.

There is no cancellation or timeout option. Promise.race only stops waiting, and queued work can still commit. Rejection during commit can leave its outcome uncertain; see the recovery guide before replaying a failed write.

Since

v0.0.5

Subscriptions methods
getRevision

The getRevision method returns the newest database revision observed by this Client.

getRevision(): number
returnsnumber

The newest database revision observed by this Client.

Since

v0.0.5

subscribe

The subscribe method listens for committed table changes and returns an unsubscribe function. OPFS Clients receive changes from every connected tab. After handover or page restoration, reset events notify every subscriber to re-query even though tables is empty.

subscribe(
  options: SubscriptionOptions,
  listener: (event: TablesChangedEvent) => void,
): () => void
TypeDescription
optionsSubscriptionOptions
listener(event: TablesChangedEvent) => void
returns() => void

A function that removes this subscription when called.

Since

v0.0.5

Properties
closed

The closed property indicates that Client cleanup has completed. A terminal Worker failure does not itself complete Client cleanup. Call close even when subsequent operations reject with WORKER_TERMINATED.

boolean
Since

v0.0.5

ready

The ready property indicates that initialization finished and closing has not started. It is not a health check: it can remain true after a terminal Worker error while operations reject. Close and reopen the Client and reconcile writes; see the lifecycle guide. Temporary OPFS owner handover reconnects the same Client automatically.

boolean
Since

v0.0.5

waitReady

The waitReady property resolves when the Worker and database are ready. It settles once for initialization; it does not track later Worker health or temporary OPFS owner handover.

Promise<void>
Since

v0.0.5

Errors classes

ClientError

The ClientError class extends JavaScript Error with a validated error returned by the TinyJoin Worker.

RECOVERY_REQUIRED, STORAGE_COMMIT_OUTCOME_UNKNOWN, and STORAGE_ENGINE_POISONED mean the engine must no longer be used. Stop work, close the Client, reopen the same OPFS name, and reconcile stored state using stable operation identifiers before replaying a write. A rejected operation may already have committed. See the recovery guide.

Since

v0.0.5

Constructors
constructor

The constructor creates an Error from its serialized Worker envelope.

new ClientError(error: SerializedError): ClientError
TypeDescription
errorSerializedError
returnsClientError

Since

v0.0.5

Properties
code

The code property identifies the TinyJoin error family.

string
Since

v0.0.5

details

The details property contains optional JSON-compatible context.

JsonValue | undefined
Since

v0.0.5

retryable

The retryable property indicates whether reopening or retrying may succeed. It is not a safe-replay guarantee. An uncertain write requires reopening and reconciliation even when application code wants to retry it.

boolean
Since

v0.0.5

Interfaces

SQL interfaces

PreparedStatement

The PreparedStatement interface represents one parsed SELECT, INSERT, UPDATE, or DELETE statement retained by an open Client.

Since

v0.0.5

Methods
Lifecycle methods
close

The close method seals the handle and releases its Worker resources after already-started executions settle.

close(): Promise<void>
Since

v0.0.5

SQL methods
execute

The execute method binds a complete parameter list and runs the statement.

execute(
  params?: JsonValue[],
  options?: QueryOptions,
): Promise<Results<RowType>>
TypeDescription
params?JsonValue[]

JSON-compatible values for $1, $2, and so on.

options?QueryOptions

Result-shape options.

returnsPromise<Results<RowType>>

A Promise resolving to the statement results.

Since

v0.0.5

Properties
closed

The closed property indicates whether the handle has been sealed. It is not a Worker health check. A terminal Worker failure can leave it false while execute rejects. Closing the Client seals its handles.

boolean
Since

v0.0.5

Transactions interfaces

Transaction

The Transaction interface runs reads and row mutations against one isolated staged database inside a Client.transaction callback.

Do not retain this object after its callback completes. Run schema DDL in a standalone Client.query call or Client.exec script. Pass this object to helpers instead of awaiting another transaction on the same Client, which would queue behind the active callback and deadlock.

Since

v0.0.5

Methods
exec

The exec method runs a parameter-free DML and read script as one savepoint.

exec(
  sql: string,
  options?: QueryOptions,
): Promise<Results<Row>[]>
TypeDescription
sqlstring
options?QueryOptions
returnsPromise<Results<Row>[]>

A Promise resolving to one result per statement, in script order.

Since

v0.0.5

execute

The execute method runs a prepared statement owned by the same Client.

execute<RowType>(
  statement: PreparedStatement<RowType>,
  params?: JsonValue[],
  options?: QueryOptions,
): Promise<Results<RowType>>
TypeDescription
statementPreparedStatement<RowType>
params?JsonValue[]
options?QueryOptions
returnsPromise<Results<RowType>>

A Promise resolving to the statement results.

Since

v0.0.5

query

The query method runs one parameterized statement against staged data.

query<RowType>(
  sql: string,
  params?: JsonValue[],
  options?: QueryOptions,
): Promise<Results<RowType>>
TypeDescription
sqlstring
params?JsonValue[]
options?QueryOptions
returnsPromise<Results<RowType>>

A Promise resolving to the statement results.

Since

v0.0.5

rollback

The rollback method explicitly discards the staged transaction.

rollback(): Promise<void>
Since

v0.0.5

sql

The sql method is a parameterizing tagged-template form of query.

sql<RowType>(
  strings: TemplateStringsArray,
  ...params: JsonValue[],
): Promise<Results<RowType>>
TypeDescription
stringsTemplateStringsArray
...paramsJsonValue[]
returnsPromise<Results<RowType>>

A Promise resolving to the statement results.

Since

v0.0.5

Properties
closed

The closed property indicates that the transaction is sealed, including after an explicit rollback or after its callback finishes.

boolean
Since

v0.0.5

Query results interfaces

QueryOptions

The QueryOptions interface configures the shape of query results.

Only rowMode is supported. There is no AbortSignal or timeout option; Promise.race with a timer does not cancel database work.

Since

v0.0.5

rowMode

The rowMode property selects object rows by default, or arrays whose values follow the order of the fields property. Only array rows can return a SELECT whose output names repeat.

RowMode
Since

v0.0.5

ResultField

The ResultField interface describes one projected result column.

Since

v0.0.5

dataTypeID

The dataTypeID property contains the closest stable PostgreSQL OID for the TinyJoin runtime type.

number
Since

v0.0.5

name

The name property contains the projected column name or alias, or ?column? for an expression without an alias.

string
Since

v0.0.5

Results

The Results interface is returned by queries, prepared statements, and scripts.

It follows the familiar PGlite result shape and adds the observed database revision and the names of changed tables.

Since

v0.0.5

affectedRows

The affectedRows property reports the number of rows changed by a write.

number
Since

v0.0.5

command

The command property contains the executed statement family.

string
Since

v0.0.5

fields

The fields property describes the projected columns in result order.

ResultField[]
Since

v0.0.5

keys

The keys property contains the primary keys the statement changed, keyed by table name.

ChangedKeys

A table appears only when every key it changed fits the reporting bound of 1,000 keys per table. A table that changed more rows than that is absent, so an absent table means the rows that changed cannot be named, not that nothing changed. The tables property stays authoritative either way.

Since

v0.1.1

revision

The revision property contains the database revision observed by the statement.

number
Since

v0.0.5

rowCount

The rowCount property reports the number of rows returned or changed.

number
Since

v0.0.5

rows

The rows property contains the rows returned by the statement.

RowType[]
Since

v0.0.5

tables

The tables property contains the tables changed by the statement.

string[]
Since

v0.0.5

Subscriptions interfaces

SubscriptionOptions

The SubscriptionOptions interface filters invalidations by table name.

Since

v0.0.5

tables

The tables property limits delivery to invalidations that include at least one of these tables. Omit it to observe every table-change invalidation; adjacent changes may be coalesced.

string[]
Since

v0.0.5

TablesChangedEvent

The TablesChangedEvent interface describes one committed table-level invalidation or a request to refresh after database-owner handover.

Since

v0.0.5

keys

The keys property contains the primary keys that changed, keyed by table name, for the tables whose complete set is known.

ChangedKeys

Use it to refresh individual rows instead of re-reading a whole table. A table listed in tables but absent here changed more rows than the bound of 1,000 keys per table allows an event to name, or changed across a handover; re-query that table instead. It is always empty when reset is true.

Since

v0.1.1

reset

The reset property is true when database-owner handover or page restoration requires a re-query even though the changed tables are unknown. In that case tables is empty and every subscription is notified, including filtered ones. Normal committed-change events omit this property.

boolean
Since

v0.1.0

revision

The revision property contains the committed database revision.

number
Since

v0.0.5

tables

The tables property contains the names of changed tables. It is empty when reset is true, which means the subscriber should refresh its query anyway.

string[]
Since

v0.0.5

Configuration interfaces

ClientOptions

The ClientOptions interface configures storage or an application-owned Worker.

The zero-boilerplate default constructs TinyJoin's packaged module Worker. Provide at most one of worker, workerFactory, or workerUrl.

Since

v0.0.5

worker

The worker property provides an already-created Worker-compatible object.

WorkerLike
Since

v0.0.5

dataDir

The dataDir property selects memory or a named OPFS database.

string
Since

v0.0.5

workerFactory

The workerFactory property creates an application-owned Worker lazily.

() => WorkerLike
Since

v0.0.5

workerUrl

The workerUrl property identifies an application-owned module Worker.

string | URL
Since

v0.0.5

Errors interfaces

SerializedError

The SerializedError interface is the stable error envelope sent by the Worker.

Since

v0.0.5

code

The code property identifies the TinyJoin error family.

string
Since

v0.0.5

details

The details property contains optional JSON-compatible context.

JsonValue
Since

v0.0.5

message

The message property explains the failure.

string
Since

v0.0.5

retryable

The retryable property indicates whether reopening or retrying may succeed. It does not guarantee that replaying a write is safe or that the current Client remains usable. Reconcile uncertain writes after reopening.

boolean
Since

v0.0.5

Workers interfaces

WorkerLike

The WorkerLike interface is the minimal Worker surface accepted by a custom Client configuration.

Most applications should let create construct the packaged Worker instead.

Since

v0.0.5

Methods
addEventListener

This addEventListener overload listens for Worker messages.

addEventListener(
  type: "message",
  listener: (event: (
    Omit<MessageEvent<any>, "data"> &
    {data: unknown}
  )) => void,
): void
TypeDescription
type"message"

The message event type.

listener(event: ( Omit<MessageEvent<any>, "data"> & {data: unknown} )) => void

The callback receiving messages from the Worker.

returnsvoid

This has no return value.

Since

v0.0.5

This addEventListener overload listens for message decoding failures.

addEventListener(
  type: "messageerror",
  listener: (event: (
    Omit<MessageEvent<any>, "data"> &
    {data: unknown}
  )) => void,
): void
TypeDescription
type"messageerror"

The messageerror event type.

listener(event: ( Omit<MessageEvent<any>, "data"> & {data: unknown} )) => void

The callback receiving message decoding failures.

returnsvoid

This has no return value.

Since

v0.0.5

This addEventListener overload listens for Worker runtime failures.

addEventListener(
  type: "error",
  listener: (event: ErrorEvent) => void,
): void
TypeDescription
type"error"

The error event type.

listener(event: ErrorEvent) => void

The callback receiving Worker runtime failures.

returnsvoid

This has no return value.

Since

v0.0.5

postMessage

The postMessage method sends a request to the Worker.

postMessage(message: unknown): void
TypeDescription
messageunknown
returnsvoid

This has no return value.

Since

v0.0.5

removeEventListener

This removeEventListener overload removes a message listener.

removeEventListener(
  type: "message",
  listener: (event: (
    Omit<MessageEvent<any>, "data"> &
    {data: unknown}
  )) => void,
): void
TypeDescription
type"message"

The message event type.

listener(event: ( Omit<MessageEvent<any>, "data"> & {data: unknown} )) => void

The same callback passed to addEventListener.

returnsvoid

This has no return value.

Since

v0.0.5

This removeEventListener overload removes a message-error listener.

removeEventListener(
  type: "messageerror",
  listener: (event: (
    Omit<MessageEvent<any>, "data"> &
    {data: unknown}
  )) => void,
): void
TypeDescription
type"messageerror"

The messageerror event type.

listener(event: ( Omit<MessageEvent<any>, "data"> & {data: unknown} )) => void

The same callback passed to addEventListener.

returnsvoid

This has no return value.

Since

v0.0.5

This removeEventListener overload removes a Worker error listener.

removeEventListener(
  type: "error",
  listener: (event: ErrorEvent) => void,
): void
TypeDescription
type"error"

The error event type.

listener(event: ErrorEvent) => void

The same callback passed to addEventListener.

returnsvoid

This has no return value.

Since

v0.0.5

Properties
terminate

The terminate method stops an application-owned Worker when available.

() => void
Since

v0.0.5

Schema interfaces

ColumnSchema

The ColumnSchema interface describes one column of a table.

Since

v0.5.0

default

The default property contains the column's literal DEFAULT value, and is absent when the column declares none. A column declared DEFAULT NULL has a default of null.

JsonValue
Since

v0.5.0

maxLength

The maxLength property contains the most characters a VARCHAR(n) column holds, its n, and is absent for any other column.

number
Since

v0.5.0

name

The name property contains the column's name, as the table declares it.

string
Since

v0.5.0

nullable

The nullable property is false when the column is declared NOT NULL or is part of the primary key.

boolean
Since

v0.5.0

renamedFrom

The renamedFrom property names a column's former name, which setSchema renames the column from while the table still has a column of that name. getSchema never includes it.

string
Since

v0.5.0

type

The type property contains the column's runtime ColumnType.

ColumnType
Since

v0.5.0

ForeignKeySchema

The ForeignKeySchema interface describes one foreign key of a table: columns whose values, unless one is NULL, must be those of a row of the table it references.

Since

v0.5.0

columns

The columns property contains the referencing columns, in key order.

string[]
Since

v0.5.0

name

The name property contains the foreign key's name.

string
Since

v0.5.0

onDelete

The onDelete property says what deleting a referenced row does to the rows that reference it.

ForeignKeyAction
Since

v0.5.0

onUpdate

The onUpdate property says what changing a referenced row's referenced values does to the rows that reference it.

ForeignKeyAction
Since

v0.5.0

referencedColumns

The referencedColumns property contains the referenced table's columns, by position in columns: its primary key, or a unique index's columns.

string[]
Since

v0.5.0

references

The references property names the table the key references, which may be the table's own.

string
Since

v0.5.0

IndexSchema

The IndexSchema interface describes one secondary index on a table.

A primary key is not listed as an index; TableSchema's primaryKey holds it.

Since

v0.5.0

columns

The columns property contains the indexed column names, in index order.

string[]
Since

v0.5.0

name

The name property contains the index's name.

string
Since

v0.5.0

unique

The unique property is true for an index created with CREATE UNIQUE INDEX.

boolean
Since

v0.5.0

Schema

The Schema interface is returned by the getSchema method, and describes every table in the database. The setSchema method takes one to make the database's.

Since

v0.5.0

tables

The tables property describes the database's tables, in name order.

TableSchema[]
Since

v0.5.0

version

The version property contains the version the application last gave the schema with setSchema, or zero. setSchema refuses a schema whose version is lower, as an older copy of the application would give it.

number
Since

v0.5.0

SetSchemaOptions

The SetSchemaOptions interface configures the setSchema method.

Since

v0.5.0

drop

The drop property, when true, makes setSchema drop each table, and each column of a table the schema keeps, that the schema leaves out, and their data with them. Otherwise setSchema leaves them as they are.

boolean
Since

v0.5.0

TableSchema

The TableSchema interface describes one table, its columns, its primary key, its indexes, and its foreign keys.

Since

v0.5.0

columns

The columns property describes the table's columns, in declaration order, including any added later by ALTER TABLE.

ColumnSchema[]
Since

v0.5.0

foreignKeys

The foreignKeys property describes the table's foreign keys, in the order they were declared.

ForeignKeySchema[]
Since

v0.5.0

indexes

The indexes property describes the table's secondary indexes, in name order.

IndexSchema[]
Since

v0.5.0

name

The name property contains the table's name.

string
Since

v0.5.0

primaryKey

The primaryKey property contains the primary-key column names, in key order. A composite key has more than one.

string[]
Since

v0.5.0

renamedFrom

The renamedFrom property names a table's former name, which setSchema renames the table from while the database still has a table of that name. getSchema never includes it.

string
Since

v0.5.0

Functions

create

Essential

The create function opens a TinyJoin database in a dedicated Worker.

create(): Promise<Client>
returnsPromise<Client>

A Promise resolving to a Client whose Worker and database are ready.

This overload creates an ephemeral in-memory database using the packaged Worker. Its data is lost when the Client closes or the page reloads.

Example
import {create} from 'tinyjoin';

const db = await create();
await db.exec(`
  CREATE TABLE IF NOT EXISTS tasks (
    id INTEGER PRIMARY KEY,
    title TEXT NOT NULL
  )
`);
const {rows} = await db.query('SELECT * FROM tasks ORDER BY id');
await db.close();
Since

v0.0.5

This create overload opens a database with ClientOptions.

create(options: ClientOptions): Promise<Client>
TypeDescription
optionsClientOptions

Storage and Worker configuration.

returnsPromise<Client>

A Promise resolving to a Client whose Worker and database are ready.

Use the options object to select storage or provide a custom Worker. Omitting its dataDir property selects an ephemeral in-memory database.

Example
import {create} from 'tinyjoin';

const db = await create({dataDir: 'memory://'});
await db.close();
Since

v0.0.5

This create overload opens a database at the supplied storage URL.

create(
  dataDir: string | undefined,
  options?: ClientOptions,
): Promise<Client>
TypeDescription
dataDirstring | undefined

The memory:// URL, a named opfs:// URL, or undefined for the default in-memory database.

options?ClientOptions

Optional Worker configuration.

returnsPromise<Client>

A Promise resolving to a Client whose Worker and database are ready.

Use a stable opfs://name to persist data in the same browser, or memory:// for an ephemeral database. When providing the second argument, supply a storage URL and leave options.dataDir unset.

Example
import {create} from 'tinyjoin';

const db = await create('opfs://my-app');
await db.close();
Since

v0.0.5

Type Aliases

Query results type aliases

ChangedKeys

The ChangedKeys type maps a table name to the primary keys that changed in it.

{[table: string]: Row[]}

Each key is an object holding only that table's primary-key columns, so a composite key arrives as one object with one property per column.

Since

v0.1.1

RowMode

The RowMode type selects object rows or positional array rows.

"array" | "object"

An object row holds one value for each name, so a SELECT whose output names repeat is rejected unless its rows are arrays.

Since

v0.0.5

Data types type aliases

JsonPrimitive

The JsonPrimitive type represents a scalar value accepted by TinyJoin.

null | boolean | number | string
Since

v0.0.5

JsonValue

The JsonValue type represents a parameter or result value accepted by the TinyJoin JavaScript API.

JsonPrimitive | JsonValue[] | {[key: string]: JsonValue}
Since

v0.0.5

Row

The Row type represents the default object form of a result row.

Record<string, JsonValue>
Since

v0.0.5

Configuration type aliases

DataDir

The DataDir type identifies the database storage mode.

string

Use memory:// for an ephemeral database, or opfs://name for a persistent database. Calling create without a data directory also uses memory. Clients using the same OPFS name automatically share an elected database owner, including across tabs. Pending operations interrupted by owner loss reject without replay; subsequent operations reconnect automatically. Different names have independent data and do not synchronize.

Since

v0.0.5

Schema type aliases

ColumnType

The ColumnType type names one of the five runtime types a column holds.

"boolean" | "integer" | "float" | "text" | "json"

Each SQL type name maps to one of them: INTEGER and BIGINT to integer, REAL and DOUBLE PRECISION to float, TEXT and VARCHAR to text, and JSON and JSONB to json. The schema keeps the runtime type, not the name the table was declared with, and the length of a VARCHAR(n) as its column's maxLength.

Since

v0.5.0

ForeignKeyAction

The ForeignKeyAction type names what deleting or updating a referenced row does to the rows that reference it: no action and restrict refuse it, cascade deletes or updates them too, and set null and set default change their referencing columns to NULL or to the columns' defaults.

"no action" | "restrict" | "cascade" | "set null" | "set default"
Since

v0.5.0

node

The node module opens ephemeral TinyJoin databases in Node.js 22 or later. It constructs a Node Worker thread and loads the packaged WebAssembly engine automatically, with no extra dependencies, bundler, or polyfills.

The returned Client has the same SQL, transaction, prepared-statement, and subscription APIs as the browser Client. The module also exports ClientError and the shared Client API types, so Node applications can import their query, result, transaction, and subscription contracts from this entry point. ClientError is the same class exported by the browser entry point. See the Node guide.

Since

v0.1.0

Functions

create

The create function opens a fresh in-memory database in a dedicated Node Worker thread. It resolves when the database is ready.

create(dataDir?: "memory://"): Promise<Client>
TypeDescription
dataDir?"memory://"

The optional memory:// URL.

returnsPromise<Client>

A ready Client. Always await its close method when finished.

Omit the argument or pass memory://. Each call owns an independent database and Worker; close the Client to release the Worker. All data is lost when the Client closes or the process exits. OPFS, filesystem persistence, and remote synchronization are not supported by this entry point.

Example
import {type Client, create} from 'tinyjoin/node';

const db: Client = await create();
try {
  await db.exec('CREATE TABLE notes (id INTEGER PRIMARY KEY, body TEXT)');
  await db.query('INSERT INTO notes VALUES ($1, $2)', [1, 'Hello from Node']);
  console.log((await db.query('SELECT * FROM notes ORDER BY id')).rows);
} finally {
  await db.close();
}
Since

v0.1.0

References

Client

ClientError

ColumnSchema

ColumnType

ForeignKeyAction

ForeignKeySchema

IndexSchema

JsonPrimitive

JsonValue

PreparedStatement

QueryOptions

ResultField

Results

Row

RowMode

Schema

SerializedError

SetSchemaOptions

SubscriptionOptions

TableSchema

TablesChangedEvent

Transaction

worker

The worker module lets advanced applications start TinyJoin inside a Worker that they construct and bundle themselves.

Most applications should use create from the main tinyjoin module, which constructs the packaged Worker automatically.

Since

v0.0.5

startWorker

The startWorker function starts the TinyJoin request host in the current dedicated Worker. Persistent databases participate in automatic same-name coordination across tabs, just like the packaged Worker. Memory databases remain independent.

startWorker(): void

Example

import {startWorker} from 'tinyjoin/worker';

startWorker();

Since

v0.0.5

drizzle

The drizzle module connects the Drizzle ORM query builder to a TinyJoin Client. It needs drizzle-orm 0.45 or later, which the application installs itself: TinyJoin declares it as an optional peer dependency and never bundles it.

Drizzle writes PostgreSQL, so its queries run in TinyJoin's SQL dialect, and its schemas use the column types that dialect has. See the Drizzle guide for what works, and what does not.

Since

v0.5.0

Interfaces

Migrations interfaces

MigrationConfig

The MigrationConfig interface describes the migrations that the migrate function applies.

Since

v0.5.0

journal

The journal property is the Drizzle Kit journal, which an application can import from meta/_journal.json.

MigrationJournal
Since

v0.5.0

migrations

The migrations property maps each migration's file to its SQL text. A key may be the migration's tag, its file name, or any path ending in its file name, so the object that a bundler's glob import returns can be passed as it is.

Record<string, string>
Since

v0.5.0

migrationsTable

The migrationsTable property names the table that records the migrations applied, __drizzle_migrations unless it is given.

string
Since

v0.5.0

MigrationJournal

The MigrationJournal interface is the journal that drizzle-kit generate writes beside its migrations, as meta/_journal.json, of which the migrate function reads each entry's tag and time.

Since

v0.5.0

entries

The entries property lists the migrations in the order to apply them, each with its tag, which names its SQL file, and the time it was generated.

{
  tag: string;
  when: number;
}[]
Since

v0.5.0

Push interfaces

PushOptions

The PushOptions interface configures the push function.

Since

v0.5.0

drop

The drop property, when true, drops each table, and each column of a table the schema keeps, that the schema leaves out, with their data. Otherwise they stay as they are.

boolean
Since

v0.5.0

renames

The renames property maps the new name of each renamed table, as table, or column, as table.column, to the name it had, so that push renames it, keeping its data, rather than creating it empty. A rename that has already happened changes nothing.

Record<string, string>
Since

v0.5.0

version

The version property gives the schema a version, zero unless it is given, which the database records. A schema whose version is lower than the database's is refused with SCHEMA_OUTDATED, so that a tab running an older copy of the application cannot undo a newer one's changes.

number
Since

v0.5.0

Functions

Drizzle functions

drizzle

The drizzle function creates a Drizzle database over a TinyJoin Client.

drizzle<TSchema>(
  client: Client,
  config?: DrizzleConfig<TSchema>,
): TinyJoinDatabase<TSchema>
TypeDescription
clientClient

The TinyJoin Client to query, from create().

config?DrizzleConfig<TSchema>

Drizzle's configuration: its schema, for relational queries, and its logger, casing, and cache.

returnsTinyJoinDatabase<TSchema>

A Drizzle database.

Queries run on the Client as array rows, which Drizzle maps to its own result shapes. Drizzle's db.transaction runs on the Client's callback transaction, so it holds the database as any TinyJoin transaction does, tx.rollback discards its work, and a transaction cannot nest, since there are no savepoints, or take an isolation level.

A value Drizzle encodes for a json or jsonb column is bound as the JSON value it is, rather than as the JSON text Drizzle makes of it, which TinyJoin would store as a string.

Close the Client yourself when the database is no longer needed.

Example
import {create} from 'tinyjoin';
import {drizzle} from 'tinyjoin/drizzle';
import {eq} from 'drizzle-orm';
import {boolean, pgTable, text} from 'drizzle-orm/pg-core';

const tasks = pgTable('tasks', {
  id: text('id').primaryKey(),
  title: text('title').notNull(),
  done: boolean('done').notNull().default(false),
});

const client = await create('opfs://my-app-v1');
const db = drizzle(client, {schema: {tasks}});
await db.insert(tasks).values({id: crypto.randomUUID(), title: 'Try Drizzle'});
const open = await db.select().from(tasks).where(eq(tasks.done, false));
Since

v0.5.0

Migrations functions

migrate

The migrate function applies the migrations that drizzle-kit generate wrote, which the database has not yet recorded, in the journal's order.

migrate<TSchema>(
  db: TinyJoinDatabase<TSchema>,
  config: MigrationConfig,
): Promise<void>
TypeDescription
dbTinyJoinDatabase<TSchema>

The Drizzle database, from drizzle.

configMigrationConfig

The journal and the SQL text of each migration.

returnsPromise<void>

This has no return value.

Each migration runs as one script together with the row that records it, so it commits whole or not at all, and a migration that fails leaves the database as the previous one left it, and rejects with its error. A migration another Client of the same database applied first is skipped, so every tab of an application can call migrate as it starts.

A migration is recorded by its tag and applied once: a file changed after it was applied is not applied again, and a migration that joins the journal before others already applied is still applied. The migrations need no file system, so a browser application bundles them.

Example
import {create} from 'tinyjoin';
import {drizzle, migrate} from 'tinyjoin/drizzle';
import journal from './drizzle/meta/_journal.json';

const db = drizzle(await create('opfs://my-app-v1'));
await migrate(db, {
  journal,
  migrations: import.meta.glob<string>('./drizzle/*.sql', {
    query: '?raw',
    import: 'default',
    eager: true,
  }),
});
Since

v0.5.0

Push functions

push

The push function makes the database hold the tables of a Drizzle schema, as Drizzle Kit's push would for PostgreSQL, but from the application and as one change that commits whole or not at all.

push<TSchema>(
  db: TinyJoinDatabase<TSchema>,
  schema: Record<string, unknown>,
  options?: PushOptions,
): Promise<boolean>
TypeDescription
dbTinyJoinDatabase<TSchema>

The Drizzle database, from drizzle.

schemaRecord<string, unknown>

The Drizzle schema, whose tables push reads, leaving out its relations.

options?PushOptions

The schema's version, whether to drop what it leaves out, and what it renames.

returnsPromise<boolean>

A Promise resolving to true if the database changed, or false if it already held the schema.

It reads each table's columns, primary key, unique constraints, and indexes from Drizzle, and passes them to the Client's setSchema, which creates what the database lacks and changes what differs. A schema the database already holds changes nothing, so every tab of an application can call push as it starts, in place of migrations.

A table that declares what TinyJoin does not have, such as a foreign key, a timestamp column, an SQL default, or an index on an expression, is refused before anything changes. A default Drizzle works out in JavaScript, with $defaultFn, is Drizzle's, not the database's.

Example
import {create} from 'tinyjoin';
import {drizzle, push} from 'tinyjoin/drizzle';
import * as schema from './schema';

const db = drizzle(await create('opfs://my-app-v1'), {schema});
await push(db, schema, {version: 2, renames: {'tasks.name': 'title'}});
Since

v0.5.0

Type Aliases

TinyJoinDatabase

The TinyJoinDatabase type is the Drizzle database the drizzle function returns: Drizzle's PostgreSQL database with the TinyJoin Client it queries as its $client.

(
  PgRemoteDatabase<TSchema> &
  {$client: Client}
)
TypeDescription
$clientClient

Drizzle's raw db.execute resolves to the rows its statement returns, as objects.

Since

v0.5.0

kysely

The kysely module connects the Kysely query builder to a TinyJoin Client. It needs kysely 0.28 or later, which the application installs itself: TinyJoin declares it as an optional peer dependency and never bundles it.

Kysely writes PostgreSQL, so its queries run in TinyJoin's SQL dialect. See the Kysely guide for what works, and what does not.

Since

v0.5.0

Classes

TinyJoinDialect

The TinyJoinDialect class is a Kysely dialect that runs Kysely's PostgreSQL queries on a TinyJoin Client.

Kysely runs one query at a time on the Client, so a query outside a transaction waits until the transaction ends rather than failing. Kysely's db.transaction runs on the Client's callback transaction, which holds the database, and every tab sharing it, until it commits or rolls back. It takes no isolation level or access mode, and there are no savepoints.

Its introspector reads the Client's getSchema, and Kysely's Migrator runs each migration's statements in turn, since TinyJoin runs DDL outside transactions, with a Web Lock keeping one tab migrating at a time.

Example
import {create} from 'tinyjoin';
import {TinyJoinDialect} from 'tinyjoin/kysely';
import {Kysely} from 'kysely';

interface Database {
  tasks: {id: string; title: string; done: boolean};
}

const client = await create('opfs://my-app-v1');
const db = new Kysely<Database>({dialect: new TinyJoinDialect({client})});
const open = await db
  .selectFrom('tasks')
  .select(['id', 'title'])
  .where('done', '=', false)
  .execute();
Since

v0.5.0

Constructors
constructor

The constructor creates a dialect over a TinyJoin Client.

new TinyJoinDialect(config: TinyJoinDialectConfig): TinyJoinDialect
TypeDescription
configTinyJoinDialectConfig

The dialect's configuration, with its Client.

returnsTinyJoinDialect

Since

v0.5.0

Methods
createAdapter

The createAdapter method creates the adapter that tells Kysely how TinyJoin differs from PostgreSQL. Kysely calls it.

createAdapter(): DialectAdapter
returnsDialectAdapter

A Kysely dialect adapter.

Since

v0.5.0

createDriver

The createDriver method creates the driver that runs Kysely's queries on the Client. Kysely calls it.

createDriver(): Driver
returnsDriver

A Kysely driver.

Since

v0.5.0

createIntrospector

The createIntrospector method creates the introspector that lists the database's tables and columns from the Client's getSchema. Kysely calls it.

createIntrospector(): DatabaseIntrospector
returnsDatabaseIntrospector

A Kysely database introspector.

Since

v0.5.0

createQueryCompiler

The createQueryCompiler method creates Kysely's PostgreSQL query compiler. Kysely calls it.

createQueryCompiler(): QueryCompiler
returnsQueryCompiler

A Kysely query compiler.

Since

v0.5.0

Interfaces

TinyJoinDialectConfig

The TinyJoinDialectConfig interface configures a TinyJoinDialect.

Since

v0.5.0

client

The client property is the TinyJoin Client, from create(), on which Kysely runs its queries. The application closes it: Kysely's destroy leaves it open.

Client
Since

v0.5.0

vite

The vite module provides optional production offline caching for Vite applications, including TinyJoin's lazily loaded Worker, OPFS, and WASM files. This Node-only build integration is separate from the browser runtime.

Since

v0.1.0

Interfaces

TinyjoinOfflineOptions

The TinyjoinOfflineOptions interface configures production offline caching.

Since

v0.1.0

mode

The mode property selects automatic service-worker registration or integration with an existing service worker. The default is service-worker.

"service-worker" | "manifest"

The manifest mode emits tinyjoin-precache.json and tinyjoin-precache.js without registering or replacing a service worker.

Since

v0.1.0

navigationFallback

The navigationFallback property names the emitted HTML file served for otherwise unmatched navigation requests within the application scope. It defaults to index.html; use false to disable SPA navigation fallback.

string | false
Since

v0.1.0

Functions

tinyjoinOffline

The tinyjoinOffline function returns a Vite plugin that precaches the complete emitted production build, including lazy runtime assets, with verified content hashes. It does not register a service worker during development.

tinyjoinOffline(options?: TinyjoinOfflineOptions): Plugin
TypeDescription
options?TinyjoinOfflineOptions

Optional offline-caching settings.

returnsPlugin

A Vite plugin that configures production offline caching.

The first visit requires a network connection. Wait for navigator.serviceWorker.ready before expecting a subsequent navigation to work offline. Storage persistence and application-file caching remain separate: OPFS stores the database, while the service worker stores the application.

Updates wait until all tabs controlled by the old service worker close. The plugin does not force a new worker onto running tabs. Serve the whole build together at one same-origin root-relative or relative Vite base. External requests and files created after the build are not cached.

Example
import {defineConfig} from 'vite';
import {tinyjoinOffline} from 'tinyjoin/vite';

export default defineConfig({plugins: [tinyjoinOffline()]});
Since

v0.1.0