| title | Node.js client SDK (Beta) |
|---|---|
| description | SDK reference for using PowerSync in Node.js clients. |
| sidebarTitle | SDK Reference |
import SdkFeatures from '/snippets/sdk-features.mdx'; import NodeInstallation from '/snippets/node/installation.mdx'; import JavaScriptAsyncWatch from '/snippets/basic-watch-query-javascript-async.mdx'; import JavaScriptCallbackWatch from '/snippets/basic-watch-query-javascript-callback.mdx'; import GenerateSchemaAutomatically from '/snippets/generate-schema-automatically.mdx'; import MutationConfirmationWithReturning from '/snippets/javascript-mutation-confirmation-returning.mdx'; import LocalOnly from '/snippets/local-only-escape.mdx';
This page describes the PowerSync _client_ SDK for Node.js. If you're interested in using PowerSync for your Node.js backend, no special package is required. Instead, follow our guides on [app backend setup](/configuration/app-backend/setup). This SDK is distributed via NPM Refer to `packages/node` in the `powersync-js` repo on GitHub Full API reference for the SDK Gallery of example projects/demo apps built with Node.js and PowerSync Changelog for the SDK This SDK is currently in a [**beta** release](/resources/feature-status) and can be considered production-ready for tested use cases.Prerequisites: To sync data between your client-side app and your backend source database, you must have completed the necessary setup for PowerSync, which includes connecting your source database to the PowerSync Service and deploying Sync Streams (or legacy Sync Rules) (steps 1-4 in the Setup Guide).
import SdkClientSideSchema from '/snippets/sdk-client-side-schema.mdx';
You can use this example as a reference when defining your schema.
The types available are text, integer and real. These should map directly to the values produced by your Sync Streams (or legacy Sync Rules). If a value doesn't match, it is cast automatically. For details on how backend source database types are mapped to the SQLite types, see Types.
Select JavaScript and replace the suggested import with @powersync/node.
Next, you need to instantiate the PowerSync database. PowerSync streams changes from your backend source database into the client-side SQLite database, based on your Sync Streams (or legacy Sync Rules). In your client-side app, you can read from and write to the local SQLite database, whether the user is online or offline.
Example:
import { PowerSyncDatabase } from '@powersync/node';
import { Connector } from './Connector';
import { AppSchema } from './Schema';
export const db = new PowerSyncDatabase({
// The schema you defined in the previous step
schema: AppSchema,
database: {
// Filename for the SQLite database — it's important to only instantiate one instance per file.
dbFilename: 'powersync.db',
// Optional. Directory where the database file is located.
// dbLocation: 'path/to/directory'
},
});The PowerSync backend connector provides the connection between your application backend and the PowerSync client-side managed SQLite database. It is used to:
- Retrieve an auth token to connect to the PowerSync instance.
- Upload client-side writes to your backend API. Any writes that are made to the SQLite database are placed into an upload queue by the PowerSync Client SDK and automatically uploaded to your app backend (where you apply those changes to the backend source database) when the user is connected.
Accordingly, the connector must implement two methods:
- PowerSyncBackendConnector.fetchCredentials - This method is automatically invoked by the PowerSync Client SDK to obtain authentication credentials. The SDK caches credentials internally and only calls this method when needed (e.g. on initial connection or when the token is near expiry). See When
fetchCredentials()is Called for details, and Authentication Setup for instructions on how the credentials should be generated. - PowerSyncBackendConnector.uploadData - This method will be automatically invoked by the PowerSync Client SDK whenever it needs to upload client-side writes to your app backend via your backend API. Therefore, in your implementation, you need to define how your backend API is called. See When
uploadData()is Called for details on triggers, throttling, and retry behavior, and Writing Client Changes for considerations on the app backend implementation.
Example:
import { UpdateType } from '@powersync/node';
export class Connector implements PowerSyncBackendConnector {
constructor() {
// set up a connection to your server for uploads
this.serverConnectionClient = TODO;
}
async fetchCredentials() {
// Implement fetchCredentials to obtain a JWT from your authentication service.
// See https://docs.powersync.com/configuration/auth/overview
return {
endpoint: '[Your PowerSync instance URL or self-hosted endpoint]',
// Use a development token (see Authentication Setup https://docs.powersync.com/configuration/auth/development-tokens) to get up and running quickly
token: 'An authentication token'
};
}
async uploadData(database) {
// Implement uploadData to send local changes to your backend service.
// You can omit this method if you only want to sync data from the database to the client
// See example implementation here: https://docs.powersync.com/client-sdks/reference/javascript-web#3-integrate-with-your-backend
}
}With your database instantiated and your connector ready, call connect() to start syncing data with your backend:
await db.connect(new Connector());
await db.waitForFirstSync(); // Optional, to wait for a complete snapshot of data to be availableAfter connecting the client database, it is ready to be used. The API to run queries and updates is identical to our JavaScript/Web SDK:
// Use db.get() to fetch a single row:
console.log(await db.get('SELECT powersync_rs_version();'));
// Or db.getAll() to fetch all:
console.log(await db.getAll('SELECT * FROM lists;'));
// And db.execute for inserts, updates and deletes:
await db.execute(
"INSERT INTO lists (id, created_at, name, owner_id) VALUEs (uuid(), datetime('now'), ?, uuid());",
['My new list']
);The db.watch() method executes a read query whenever a change to a dependent table is made.
For advanced watch query features like incremental updates and differential results, see Live Queries / Watch Queries.
PowerSync runs queries asynchronously on a background pool of workers and automatically configures WAL to allow a writer and multiple readers to operate in parallel.
import { createBaseLogger, LogLevel } from '@powersync/node';
const logger = createBaseLogger();
// Configure the logger to use the default console output
logger.useDefaults();
// Set the minimum log level to DEBUG to see all log messages
// Available levels: DEBUG, INFO, WARN, ERROR, TRACE, OFF
logger.setLevel(LogLevel.DEBUG);For more usage examples including accessing connection status, monitoring sync progress, and waiting for initial sync, see the Usage Examples page.
See JavaScript ORM Support for details.
See Troubleshooting for pointers to debug common issues.
See Supported Platforms -> Node.js SDK.
Run the below command in your project folder:
```bash npm upgrade @powersync/node ``` ```bash yarn upgrade @powersync/node ``` ```bash pnpm upgrade @powersync/node ```The SDK has an optional dependency on better-sqlite3 which is used as the default SQLite
driver for that package.
Because that dependency is optional, it can be replaced or removed to customize how SQLite
gets loaded. This section lists common options.
To encrypt databases managed by the PowerSync SDK for Node.js, replace the better-sqlite3
dependency with the better-sqlite3-multiple-ciphers fork.
That package has the same API as better-sqlite3 while bundling SQLite3MultipleCiphers
instead of upstream SQLite.
Because PowerSync attempts to dynamically load better-sqlite3 at runtime, using a different package
requires patching the database worker. To do that, create a file (say database.worker.js) with the following
contents:
// This worker uses bindings to sqlite3 multiple ciphers instead of the original better-sqlite3 worker.
import Database from 'better-sqlite3-multiple-ciphers';
import { startPowerSyncWorker } from '@powersync/node/worker.js';
async function resolveBetterSqlite3() {
return Database;
}
startPowerSyncWorker({ loadBetterSqlite3: resolveBetterSqlite3 });When opening the database, instruct PowerSync to use the custom worker.
Also use the initializeConnection option to install an encryption key:
const encryptionKey = 'todo: generate encryption key and store it safely';
const db = new PowerSyncDatabase({
schema: AppSchema,
database: {
dbFilename: 'app.db',
openWorker: (_, options) => {
return new Worker(new URL('./database.worker.js', import.meta.url), options);
},
initializeConnection: async (db) => {
if (encryptionKey.length) {
const escapedKey = encryptionKey.replace("'", "''");
await db.execute(`pragma key = '${escapedKey}'`);
}
// Make sure the database is readable, this fails early if the key is wrong.
await db.execute('pragma user_version');
}
},
logger
});Recent versions of Node.js contain an experimental SQLite API.
Using the builtin SQLite API can reduce code size and external native dependencies. To enable it,
remove your dependency on better-sqlite3 and configure PowerSync to use the builtin APIs:
const database = new PowerSyncDatabase({
schema: AppSchema,
database: {
dbFilename: 'app.db',
dbLocation: directory,
// Use node:sqlite instead of better-sqlite3
implementation: { type: 'node:sqlite' }
}
});