Links

JS Web

Full SDK reference for using PowerSync in JavaScript Web clients
The PowerSync JS Web SDK is distributed via NPM:
The full API reference can be found here:

SDK Features

  • Direct access to the SQLite database - use SQL on the client and server.
  • Operations are asynchronous by default - does not block the UI.
  • No need for client-side database migrations - these are handled automatically.
  • Real-time streaming of changes.
  • Subscribe to queries for live updates.

Installation

See the SDK's README for installation instructions.

Getting Started

Declare the Schema

import {
Column,
ColumnType,
Index,
IndexedColumn,
Schema,
Table
} from '@journeyapps/powersync-sdk-web';
export const AppSchema = new Schema([
new Table({
name: 'todos',
columns: [
new Column({ name: 'list_id', type: ColumnType.TEXT }),
new Column({ name: 'created_at', type: ColumnType.TEXT }),
new Column({ name: 'completed_at', type: ColumnType.TEXT }),
new Column({ name: 'description', type: ColumnType.TEXT }),
new Column({ name: 'completed', type: ColumnType.INTEGER }),
new Column({ name: 'created_by', type: ColumnType.TEXT }),
new Column({ name: 'completed_by', type: ColumnType.TEXT })
],
indexes: [new Index({ name: 'list', columns: [new IndexedColumn({ name: 'list_id' })] })]
}),
new Table({
name: 'lists',
columns: [
new Column({ name: 'created_at', type: ColumnType.TEXT }),
new Column({ name: 'name', type: ColumnType.TEXT }),
new Column({ name: 'owner_id', type: ColumnType.TEXT })
]
})
]);
Refer to Define Your Schema for more details.

Create a Backend Connector

The two methods that must be implemented in your connector are:
  • PowerSyncBackendConnector.fetchCredentials — allows PowerSync client to retrieve an authentication token from your backend which is used to authenticate against the PowerSync service.
  • PowerSyncBackendConnector.uploadData — upload local changes to your backend.
Example implementation:
import { UpdateType} from '@journeyapps/powersync-sdk-web';
/// Postgres Response codes that we cannot recover from by retrying.
const FATAL_RESPONSE_CODES = [
// Class 22 — Data Exception
// Examples include data type mismatch.
new RegExp('^22...$'),
// Class 23 — Integrity Constraint Violation.
// Examples include NOT NULL, FOREIGN KEY and UNIQUE violations.
new RegExp('^23...$'),
// INSUFFICIENT PRIVILEGE - typically a row-level security violation
new RegExp('^42501$')
];
export class Connector {
constructor() {
// Setup a connection to your server for uploads
this.serverConnectionClient = TODO;
}
async fetchCredentials() {
// TODO logic to fetch a session
return {
endpoint: '[The PowerSync instance URL]',
token: 'An authentication token',
expiresAt: 'When the token expires',
userID: 'User ID to associate the session with'
};
}
async uploadData(database) {
const transaction = await database.getNextCrudTransaction();
if (!transaction) {
return;
}
let lastOp = null;
try {
// Note: If transactional consistency is important, use database functions
// or edge functions to process the entire transaction in a single call.
for (let op of transaction.crud) {
lastOp = op;
// Have your server connection setup before uploading
const table = this.serverConnectionClient.from(op.table);
switch (op.op) {
case UpdateType.PUT:
const record = { ...op.opData, id: op.id };
const { error } = await table.upsert(record);
break;
case UpdateType.PATCH:
await table.update(op.opData).eq('id', op.id);
break;
case UpdateType.DELETE:
await table.delete().eq('id', op.id);
break;
}
}
await transaction.complete();
} catch (ex) {
console.debug(ex);
if (typeof ex.code == 'string' && FATAL_RESPONSE_CODES.some((regex) => regex.test(ex.code))) {
/**
* Instead of blocking the queue with these errors,
* discard the (rest of the) transaction.
*
* Note that these errors typically indicate a bug in the application.
* If protecting against data loss is important, save the failing records
* elsewhere instead of discarding, and/or notify the user.
*/
console.error(`Data upload error - discarding ${lastOp}`, ex);
await transaction.complete();
} else {
// Error may be retryable - e.g. network error or temporary server error.
// Throwing an error here causes this call to be retried after a delay.
throw ex;
}
}
}
}

Create a PowerSync Connection

Instantiate a PowerSync Database and Connect it to your backend via your connector:
import { WASQLitePowerSyncDatabaseOpenFactory } from '@journeyapps/powersync-sdk-web';
import { Connector } from './Connector';
import { AppSchema } from './Schema';
const factory = new WASQLitePowerSyncDatabaseOpenFactory({
schema: AppSchema, // Which was created earlier
dbFilename: 'test.sqlite'
//location: 'optional location directory to DB file'
});
export const PowerSync = factory.getInstance();
export const setupPowerSync = async () => {
const connector = new Connector(); // Which was declared above
await PowerSync.init();
await PowerSync.connect(connector);
};

Multiple Tab Support

Multiple tab support is not currently available on Android or Safari.
Using PowerSync between multiple tabs is supported on some web browsers. Multiple tab support relies on shared web workers for DB and sync streaming operations. When enabled shared web workers named shared-sync-[dbFileName] and shared-DB-worker-[dbFileName] will be created.
The shared database worker will ensure writes to the DB will instantly be available between tabs.
The shared sync worker will co-ordinate for one active tab to connect to the PowerSync instance and share the latest sync state between tabs.
Currently using the SDK in multiple tabs without enabling the enableMultiTabs flag will spawn a standard web worker per tab for DB operations. These workers are safe to operate on the DB concurrently, however changes from one tab may not update watches on other tabs. Only one tab can sync from the PowerSync instance at a time. The sync status will not be shared between tabs, only the oldest tab will connect and display the latest sync status.
Support is enabled by default if available. This can be disabled as below:
PowerSync = new WASQLitePowerSyncDatabaseOpenFactory({
schema: AppSchema,
dbFilename: 'test.sqlite',
flags: {
// This is disabled once CSR+SSR functionality is verified to be working correctly
disableSSRWarning: true,
/**
* Multiple tab support is enabled by default if available. This can be disabled by
* setting this flag to false.
*/
enableMultiTabs: false
}}).getInstance();

Logging

This SDK uses js-logger for logging.
Enable JS Logger with your logging interface of choice or use the default console.
import Logger from 'js-logger';
// Log messages will be written to the window's console.
Logger.useDefaults();
Logger.setLevel(Logger.DEBUG);
Enable verbose output in the developer tools for detailed logs.
The WASQLite DB Adapter opens SQLite connections inside a shared webworker. This worker can be inspected in Chrome by accessing:
chrome://inspect/#workers

React Hooks

A separate powersync-react package is available containing React hooks for PowerSync: