UNPKG

tinybase

Version:

A reactive data store and sync engine.

349 lines (344 loc) 13.4 kB
/** * The persister-durable-object-sql-storage module of the TinyBase project lets * you save and load Store data to and from Cloudflare Durable Object SQLite * storage (in an appropriate environment). * * Cloudflare's SQLite storage backend for Durable Objects offers significantly * better pricing compared to the key-value storage backend. The SQLite storage * backend is Cloudflare's recommended storage option for new Durable Object * namespaces. * * **Important:** Before using this persister, you must configure your Durable * Object class to use SQLite storage by adding a migration to your * `wrangler.toml` or `wrangler.json` configuration file. Use * `new_sqlite_classes` in your migration configuration to enable SQLite storage * for your Durable Object class. * * See [Cloudflare's * documentation](https://developers.cloudflare.com/durable-objects/reference/durable-objects-migrations/) * for more details. * @see Cloudflare Durable Objects guide * @see Persistence guides * @packageDocumentation * @module persister-durable-object-sql-storage * @since v6.3.0 */ import type {MergeableStore} from '../../mergeable-store/index.d.ts'; import type {DpcJson, Persister, Persists} from '../index.d.ts'; /** * The DpcFragmented type represents the configuration for fragmented * persistence mode in a DurableObjectSqlStoragePersister. * * This mode stores each table, row, cell, and value as separate database rows, * avoiding Cloudflare's 2MB row limit that can be hit with large stores in JSON * mode. While this creates more database writes, it provides better scalability * for larger datasets. * @example * This example shows how to configure a DurableObjectSqlStoragePersister to use * fragmented mode with a custom storage prefix. * * ```js yolo * import {createMergeableStore} from 'tinybase'; * import {createDurableObjectSqlStoragePersister} from 'tinybase/persisters/persister-durable-object-sql-storage'; * import {WsServerDurableObject} from 'tinybase/synchronizers/synchronizer-ws-server-durable-object'; * * const config = { * mode: 'fragmented', * storagePrefix: 'my_app_', * }; * * export class MyDurableObject extends WsServerDurableObject { * createPersister() { * const store = createMergeableStore(); * const persister = createDurableObjectSqlStoragePersister( * store, * this.ctx.storage.sql, * config, * ); * return persister; * } * } * ``` * @category Configuration * @since v6.3.0 */ export type DpcFragmented = { /** * The mode property must be set to 'fragmented' to enable fragmented * persistence mode. * @category Configuration * @since v6.3.0 */ mode: 'fragmented'; /** * The storagePrefix property lets you specify an optional prefix for the * database table names used in fragmented mode. * * This is useful when you have multiple stores or applications sharing the * same Durable Object SQL storage and want to avoid table name conflicts. * * The prefix will be sanitized to only include alphanumeric characters and * underscores. For example, a prefix of 'my-app!' becomes 'my_app_'. * @example * This example shows a configuration using the storagePrefix setting. With a * `storagePrefix` of 'user_data_', it creates `user_data_tinybase_tables` and * `user_data_tinybase_values` tables. * ```json * { * mode: 'fragmented', * storagePrefix: 'user_data_', * }; * ``` * @category Configuration * @since v6.3.0 */ storagePrefix?: string; }; /** * The DurableObjectSqlDatabasePersisterConfig type represents the union of all * possible configuration types for a DurableObjectSqlStoragePersister. * * This allows the persister to support multiple persistence modes. * - JSON mode (via DpcJson): Stores the entire Store as JSON in a single row. * - Fragmented mode (via DpcFragmented): Stores each piece of data as separate * rows. * @example * These examples show some different configuration options. * ```json * // JSON mode (default) * { * mode: 'json', * storeTableName: 'my_store', * }; * * // Fragmented mode * { * mode: 'fragmented', * storagePrefix: 'app_', * }; * ``` * @category Configuration * @since v6.3.0 */ export type DurableObjectSqlDatabasePersisterConfig = DpcJson | DpcFragmented; /** * The DurableObjectSqlStoragePersister interface represents a Persister that * lets you save and load Store data to and from Cloudflare Durable Object SQL * storage. * * You should use the createDurableObjectSqlStoragePersister function to create * a DurableObjectSqlStoragePersister object, most likely within the * createPersister method of a WsServerDurableObject. * * It is a minor extension to the Persister interface and simply provides an * extra getSqlStorage method for accessing a reference to the SQL storage that * the Store is being persisted to. * @category Persister * @since v6.3.0 */ export interface DurableObjectSqlStoragePersister extends Persister<Persists.MergeableStoreOnly> { /** * The getSqlStorage method returns a reference to the SQL storage that the * Store is being persisted to. * @returns The reference to the SQL storage. * @example * This example creates a Persister object against a newly-created Store * (within the createPersister method of a WsServerDurableObject instance) and * then gets the SQL storage reference back out again. * * ```js yolo * import {createMergeableStore} from 'tinybase'; * import {createDurableObjectSqlStoragePersister} from 'tinybase/persisters/persister-durable-object-sql-storage'; * import {WsServerDurableObject} from 'tinybase/synchronizers/synchronizer-ws-server-durable-object'; * * export class MyDurableObject extends WsServerDurableObject { * createPersister() { * const store = createMergeableStore(); * const persister = createDurableObjectSqlStoragePersister( * store, * this.ctx.storage.sql, * ); * console.log(persister.getSqlStorage() == this.ctx.storage.sql); * // -> true * * return persister; * } * } * ``` * @category Getter * @since v6.3.0 */ getSqlStorage(): SqlStorage; } /** * The createDurableObjectSqlStoragePersister function creates a * DurableObjectSqlStoragePersister object that can persist the Store to and * from Cloudflare Durable Object SQLite storage. * * You will mostly use this within the createPersister method of a * WsServerDurableObject. * * This persister uses Cloudflare's SQLite storage backend, which provides * better pricing and performance compared to the legacy Key-value storage * backend. * * **Important Prerequisites:** Before using this persister, you must configure * your Durable Object class to use SQLite storage by adding a migration to your * Wrangler configuration file. In your `wrangler.toml`, add the following. * ```toml * [[migrations]] * tag = "v1" * new_sqlite_classes = ["YourDurableObjectClass"] * ``` * * Or in your `wrangler.json`, add: * * ```json * { * "migrations": [ * { * "tag": "v1", * "new_sqlite_classes": ["YourDurableObjectClass"] * } * ] * } * ``` * * For more details on Durable Object migrations, see the [Cloudflare * documentation](https://developers.cloudflare.com/durable-objects/reference/durable-objects-migrations/). * * A database Persister uses one of two modes: either a JSON serialization of * the whole Store stored in a single row of a table (the default), a fragmented * mode that stores each piece of data separately to avoid Cloudflare's 2MB row * limit. * * - **JSON Mode (Default)**: Stores the entire Store as JSON in a single * database row. This is efficient for smaller stores but may hit Cloudflare's * 2MB row limit for very large stores and uses fewer database writes. * * - **Fragmented Mode**: Stores each table, row, cell, and value as separate * database rows. Use this mode if you're concerned about hitting Cloudflare's * 2MB row limit with large stores in JSON mode. This mode creates more * database writes but avoids row size limitations. * * The third argument is a DatabasePersisterConfig object that configures which * of those modes to use, and settings for each. If the third argument is simply * a string, it is used as the `storeTableName` property of the JSON * serialization. If it is the string 'fragmented', it enables fragmented mode. * * See the documentation for the DpcJson, DpcFragmented, and DpcTabular types * for more information on how all of those modes can be configured. * * As well as providing a reference to the Store or MergeableStore to persist, * you must provide a `sqlStorage` parameter which identifies the Durable Object * SQLite storage to persist it to. * @param store The Store or MergeableStore to persist. * @param sqlStorage The Durable Object SQL storage to persist the Store to. * @param configOrStoreTableName A DatabasePersisterConfig to configure the * persistence mode (or a string to set the `storeTableName` property of the * JSON serialization). * @param onSqlCommand An optional handler called every time the Persister * executes a SQL command or query. This is suitable for logging persistence * behavior in a development environment. * @param onIgnoredError An optional handler for the errors that the Persister * would otherwise ignore when trying to save or load data. This is suitable for * debugging persistence issues in a development environment. * @returns A reference to the new DurableObjectSqlStoragePersister object. * @example * This example creates a DurableObjectSqlStoragePersister object and persists * the Store to Durable Object SQLite storage as a JSON serialization into the * default `tinybase` table. It uses this within the createPersister method of a * WsServerDurableObject instance. * * ```js yolo * import {createMergeableStore} from 'tinybase'; * import {createDurableObjectSqlStoragePersister} from 'tinybase/persisters/persister-durable-object-sql-storage'; * import {WsServerDurableObject} from 'tinybase/synchronizers/synchronizer-ws-server-durable-object'; * * export class MyDurableObject extends WsServerDurableObject { * createPersister() { * const store = createMergeableStore(); * const persister = createDurableObjectSqlStoragePersister( * store, * this.ctx.storage.sql, * ); * return persister; * } * } * ``` * @example * This example creates a DurableObjectSqlStoragePersister object with a custom * table name and SQL command logging for debugging. * * ```js yolo * import {createMergeableStore} from 'tinybase'; * import {createDurableObjectSqlStoragePersister} from 'tinybase/persisters/persister-durable-object-sql-storage'; * import {WsServerDurableObject} from 'tinybase/synchronizers/synchronizer-ws-server-durable-object'; * * export class MyDurableObject extends WsServerDurableObject { * createPersister() { * const store = createMergeableStore(); * const persister = createDurableObjectSqlStoragePersister( * store, * this.ctx.storage.sql, * 'my_app_store', * (sql, params) => console.log('SQL:', sql, params), * (error) => console.error('Persistence error:', error), * ); * return persister; * } * } * ``` * @example * This example creates a DurableObjectSqlStoragePersister object using * fragmented mode to avoid Cloudflare's 2MB row limit for large stores. * * ```js yolo * import {createMergeableStore} from 'tinybase'; * import {createDurableObjectSqlStoragePersister} from 'tinybase/persisters/persister-durable-object-sql-storage'; * import {WsServerDurableObject} from 'tinybase/synchronizers/synchronizer-ws-server-durable-object'; * * export class MyDurableObject extends WsServerDurableObject { * createPersister() { * const store = createMergeableStore(); * const persister = createDurableObjectSqlStoragePersister( * store, * this.ctx.storage.sql, * {mode: 'fragmented'}, * ); * return persister; * } * } * ``` * @example * This example creates a DurableObjectSqlStoragePersister object using * fragmented mode with a custom storage prefix. * * ```js yolo * import {createMergeableStore} from 'tinybase'; * import {createDurableObjectSqlStoragePersister} from 'tinybase/persisters/persister-durable-object-sql-storage'; * import {WsServerDurableObject} from 'tinybase/synchronizers/synchronizer-ws-server-durable-object'; * * export class MyDurableObject extends WsServerDurableObject { * createPersister() { * const store = createMergeableStore(); * const persister = createDurableObjectSqlStoragePersister( * store, * this.ctx.storage.sql, * {mode: 'fragmented', storagePrefix: 'my_app_'}, * ); * return persister; * } * } * ``` * @category Creation * @since v6.3.0 */ export function createDurableObjectSqlStoragePersister( store: MergeableStore, sqlStorage: SqlStorage, configOrStoreTableName?: DurableObjectSqlDatabasePersisterConfig | string, onSqlCommand?: (sql: string, params?: any[]) => void, onIgnoredError?: (error: any) => void, ): DurableObjectSqlStoragePersister;