Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit edee0e5

Browse files
Joshua BrinkJoshua Brink
authored andcommittedJun 9, 2025·
Update to step based guide, support multiple bundlers, and add supabase example
1 parent af11f96 commit edee0e5

File tree

2 files changed

+446
-108
lines changed

2 files changed

+446
-108
lines changed
 
Lines changed: 445 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -1,169 +1,507 @@
11
---
2-
title: "TypeScript + Node.js + PowerSync"
3-
description: "A guide for creating a new Node.js application using TypeScript with PowerSync for offline/local first functionality"
4-
keywords: ["clientsdk", "node.js"]
2+
title: "Node.js + PowerSync"
3+
description: "A guide for creating a Node.js application with PowerSync for offline/local first functionality."
4+
keywords: ["node.js", "typescript", "clientsdks"]
55
---
66

77
## Introduction
8-
In this tutorial, we’ll explore how to create a new Node.js applicaiton with TypeScript and PowerSync for offline-first capabilities. In the following sections, we’ll walk through the process of integrating PowerSync into a Node.js application, setting up local-first storage, and handling synchronization efficiently.
98

10-
## Prerequisits
9+
In this tutorial, you'll set up a Node.js application with PowerSync.
10+
In the following sections, we’ll walk through the process of integrating PowerSync into a Node.js application, setting up local-first storage, and handling synchronization.
1111

12-
In this guide we'll be using Node.js 22.4.0 and pnpm.
12+
## Prerequisites
1313

14-
## Setup
14+
Before you begin, you'll need to have [Node.js](https://nodejs.org/en/download/current) set up.
1515

16-
### Creating a new project
16+
And a running PowerSync instance, with an authentication backend - for a quick setup checkout the [Supabase Guide](https://docs.powersync.com/integration-guides/supabase-+-powersync#supabase-powersync)
17+
18+
### Step 1 - Initializing the project
19+
20+
Create a new directory for your Node.js application and navigate into it:
1721

18-
Create a new directory and init your Node.js project
1922
```shell
20-
pnpm init
23+
mkdir my-powersync-app
24+
cd my-powersync-app
2125
```
2226

23-
Install TypeScript
24-
```shell
25-
pnpm install typescript -D
27+
Once you are in the directory, create a new `package.json`. This will be where you manage your dependencies. If you are unfamiliar, you can create a new `package.json` file by running the following command::
28+
29+
<CodeGroup>
30+
31+
```shell npm
32+
npm init
33+
```
34+
35+
```shell yarn
36+
yarn init
37+
```
38+
39+
```shell pnpm
40+
pnpm init
41+
```
42+
43+
</CodeGroup>
44+
45+
Set the type to `module` in your `package.json` file to enable ES module support:
46+
47+
```json package.json {6}
48+
{
49+
"name": "my-powersync-app",
50+
"version": "1.0.0",
51+
"description": "",
52+
"main": "index.js",
53+
"type": "module",
54+
"scripts": {
55+
"test": "echo \"Error: no test specified\" && exit 1"
56+
},
57+
"keywords": [],
58+
"author": "",
59+
"license": "ISC"
60+
}
2661
```
2762

28-
Install PowerSync
29-
```shell
30-
pnpm install @powersync/node @powersync/better-sqlite3
63+
Install the PowerSync Node.js package. This package provides the core functionality for integrating PowerSync into your Node.js application.
64+
65+
<CodeGroup>
66+
67+
```shell npm
68+
npm install @powersync/node
69+
```
70+
71+
```shell yarn
72+
yarn add @powersync/node
73+
```
74+
75+
```shell pnpm
76+
pnpm add @powersync/node
77+
```
78+
79+
</CodeGroup>
80+
81+
For `@powersync/node@0.1.1` or earlier, also install `@powersync/better-sqlite3` as a peer dependency.
82+
83+
<CodeGroup>
84+
85+
```shell npm
86+
npm install @powersync/better-sqlite3
87+
```
88+
89+
```shell yarn
90+
yarn add @powersync/better-sqlite3
91+
```
92+
93+
```shell pnpm
94+
pnpm add @powersync/better-sqlite3
3195
```
3296

33-
Update TypeScript Compiler Options
97+
</CodeGroup>
98+
99+
### Step 2 - Set up TypeScript (Optional)
100+
101+
To use [TypeScript](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes.html) in your Node.js application, you need to install TypeScript and the Node.js type definitions.
102+
103+
<CodeGroup>
104+
105+
```shell npm
106+
npm add -D typescript ts-node @types/node
107+
```
108+
109+
```shell yarn
110+
yarn add -D typescript ts-node @types/node
111+
```
112+
113+
```shell pnpm
114+
pnpm add -D typescript ts-node @types/node
115+
```
116+
117+
</CodeGroup>
118+
119+
Create a `tsconfig.json` file in the root of your project directory to configure TypeScript. This file will define how TypeScript compiles your code and where it outputs the compiled files.
120+
34121
```json tsconfig.json
35122
{
36123
"compilerOptions": {
37-
"target": "ES2022",
38-
"module": "ESNext",
39-
"moduleResolution": "node",
124+
"module": "nodenext",
125+
"target": "esnext",
40126
"outDir": "./dist",
41127
"esModuleInterop": true,
42128
"strict": true,
43129
"skipLibCheck": true
44130
},
45-
"include": [
46-
"src/**/*.ts"
47-
],
48-
"exclude": [
49-
"node_modules"
50-
]
131+
"include": ["src/**/*.ts"],
132+
"exclude": ["node_modules"]
51133
}
52134
```
53135

54-
Update your package.json
55-
```json package.json
56-
...
57-
"type": "module",
58-
"scripts": {
59-
"build": "pnpm exec tsc",
60-
"start": "node ./dist/index.js"
61-
}
62-
...
136+
### Step 3 - Create the Application Files
137+
138+
Create the PowerSync application files in a `src` directory. This will help keep your project organized.
139+
140+
```shell
141+
mkdir src
63142
```
64143

65-
<Note>The PowerSync Node.js SDK requires `@powersync/better-sqlite3` to be installed</Note>
144+
#### Schema
145+
146+
This file contains the schema definitions for your local SQLite database. It defines the structure of the data you will be working with, including tables and their columns.
147+
148+
<CodeGroup>
149+
150+
```shell javascript
151+
nano src/AppSchema.js
152+
```
66153

67-
## Project Setup
154+
```shell typescript
155+
nano src/AppSchema.ts
156+
```
68157

69-
### Adding PowerSync
158+
</CodeGroup>
70159

71-
Create a `src` directory and add three files:
72-
* AppSchema.ts
73-
* Connector.ts
74-
* index.ts
160+
These should map directly to the values produced by the [SyncRules](https://docs.powersync.com/usage/sync-rules). If a value doesn’t match, it is cast automatically. For details on how database types are mapped to the types below, see the section on [Types](https://docs.powersync.com/usage/sync-rules/types).
75161

76-
#### AppSchema.ts
77-
This file essentially contains the table definitions for your local database and would look something like this:
78-
```typescript AppSchema.ts
79-
import { Schema, Table, column } from '@powersync/node';
162+
<CodeGroup>
80163

81-
const todos = new Table(
164+
```javascript src/AppSchema.js
165+
import {
166+
Schema,
167+
Table,
168+
column
169+
} from '@powersync/node';
170+
171+
const todos = new Table(
82172
{
83-
list_id: column.text,
84-
created_at: column.text,
85-
completed_at: column.text,
86-
description: column.text,
87-
created_by: column.text,
88-
completed_by: column.text,
89-
completed: column.integer,
90-
photo_id: column.text
173+
list_id: column.text,
174+
created_at: column.text,
175+
completed_at: column.text,
176+
description: column.text,
177+
created_by: column.text,
178+
completed_by: column.text,
179+
completed: column.integer,
180+
photo_id: column.text
91181
},
92182
{ indexes: { list: ['list_id'] } }
93-
);
94-
95-
const lists = new Table({
183+
);
184+
185+
const lists = new Table({
96186
created_at: column.text,
97187
name: column.text,
98188
owner_id: column.text
99-
});
189+
});
190+
191+
export const AppSchema = new Schema({
192+
lists,
193+
todos
194+
});
195+
```
100196

101-
export const AppSchema = new Schema({
197+
```typescript src/AppSchema.ts
198+
import {
199+
Schema,
200+
Table,
201+
column
202+
} from '@powersync/node';
203+
204+
const todos = new Table(
205+
{
206+
list_id: column.text,
207+
created_at: column.text,
208+
completed_at: column.text,
209+
description: column.text,
210+
created_by: column.text,
211+
completed_by: column.text,
212+
completed: column.integer,
213+
photo_id: column.text
214+
},
215+
{ indexes: { list: ['list_id'] } }
216+
);
217+
const lists = new Table({
218+
created_at: column.text,
219+
name: column.text,
220+
owner_id: column.text
221+
});
222+
223+
export const AppSchema = new Schema({
102224
lists,
103225
todos
104-
});
105-
```
226+
});
227+
```
106228

107-
#### Connector.ts
108-
This file will be the connector which will fetch a JWT used by PowerSync for authentication and another function which will handle uploads to the backend source database.
109-
```typescript Connector.ts
110-
import { PowerSyncBackendConnector, AbstractPowerSyncDatabase } from '@powersync/node';
229+
</CodeGroup>
111230

112-
export class Connector implements PowerSyncBackendConnector {
113-
constructor() {
114-
// Setup a connection to your server for uploads
115-
// this.serverConnectionClient = TODO;
116-
}
231+
#### Connector
117232

118-
async fetchCredentials() {
119-
// Implement fetchCredentials to obtain a JWT from your authentication service.
120-
// See https://docs.powersync.com/installation/authentication-setup
121-
// If you're using Supabase or Firebase, you can re-use the JWT from those clients, see
122-
// - https://docs.powersync.com/installation/authentication-setup/supabase-auth
123-
// - https://docs.powersync.com/installation/authentication-setup/firebase-auth
124-
return {
125-
endpoint: '[Your PowerSync instance URL or self-hosted endpoint]',
126-
// Use a development token (see Authentication Setup https://docs.powersync.com/installation/authentication-setup/development-tokens) to get up and running quickly
127-
token: 'An authentication token'
128-
};
129-
}
233+
This file contains the connector for your PowerSync instance. The connector is responsible for fetching authentication credentials and handling data uploads to your backend service.
130234

131-
async uploadData(database: AbstractPowerSyncDatabase) {
132-
// Implement uploadData to send local changes to your backend service.
133-
// You can omit this method if you only want to sync data from the database to the client
235+
If you are using [Supabase](https://docs.powersync.com/integration-guides/supabase-+-powersync#supabase-powersync) you can use the [Supabase Connector](https://github.com/powersync-ja/powersync-js/blob/main/demos/react-native-supabase-todolist/library/supabase/SupabaseConnector.ts) class instead of implementing your own connector.
134236

135-
// See example implementation here: https://docs.powersync.com/client-sdk-references/javascript-web#3-integrate-with-your-backend
237+
<CodeGroup>
238+
239+
```shell javascript
240+
nano src/Connector.js
241+
```
242+
243+
```shell typescript
244+
nano src/Connector.ts
245+
```
246+
247+
</CodeGroup>
248+
249+
<CodeGroup>
250+
251+
```javascript src/Connector.js
252+
253+
/**
254+
* @implements {import('@powersync/node').PowerSyncConnector}
255+
*/
256+
export class Connector {
257+
async fetchCredentials() {
258+
// Implement fetchCredentials to obtain a JWT from your authentication service.
259+
// See https://docs.powersync.com/installation/authentication-setup
260+
// If you're using Supabase or Firebase, you can re-use the JWT from those clients, see
261+
// - https://docs.powersync.com/installation/authentication-setup/supabase-auth
262+
// - https://docs.powersync.com/installation/authentication-setup/firebase-auth
263+
return {
264+
endpoint: '[Your PowerSync instance URL or self-hosted endpoint]',
265+
// Use a development token (see Authentication Setup https://docs.powersync.com/installation/authentication-setup/development-tokens) to get up and running quickly
266+
token: 'An authentication token'
267+
};
268+
}
269+
270+
/**
271+
* @param {import('@powersync/node').AbstractPowerSyncDatabase} database
272+
* @returns {Promise<void>}
273+
*/
274+
async uploadData(database) {
275+
// Implement uploadData to send local changes to your backend service.
276+
// You can omit this method if you only want to sync data from the database to the client
277+
278+
// See example implementation here: https://docs.powersync.com/client-sdk-references/javascript-web#3-integrate-with-your-backend
279+
}
280+
}
281+
282+
```
283+
284+
```typescript src/Connector.ts
285+
import {
286+
PowerSyncBackendConnector,
287+
AbstractPowerSyncDatabase
288+
} from '@powersync/node';
289+
290+
export class Connector implements PowerSyncBackendConnector {
291+
async fetchCredentials() {
292+
// Implement fetchCredentials to obtain a JWT from your authentication service.
293+
// See https://docs.powersync.com/installation/authentication-setup
294+
// If you're using Supabase or Firebase, you can re-use the JWT from those clients, see
295+
// - https://docs.powersync.com/installation/authentication-setup/supabase-auth
296+
// - https://docs.powersync.com/installation/authentication-setup/firebase-auth
297+
return {
298+
endpoint: '[Your PowerSync instance URL or self-hosted endpoint]',
299+
// Use a development token (see Authentication Setup https://docs.powersync.com/installation/authentication-setup/development-tokens) to get up and running quickly
300+
token: 'An authentication token'
301+
};
302+
}
303+
304+
async uploadData(database: AbstractPowerSyncDatabase) {
305+
// Implement uploadData to send local changes to your backend service.
306+
// You can omit this method if you only want to sync data from the database to the client
307+
308+
// See example implementation here: https://docs.powersync.com/client-sdk-references/javascript-web#3-integrate-with-your-backend
309+
}
136310
}
137-
}
138-
```
311+
```
312+
313+
</CodeGroup>
314+
315+
- `fetchCredentials` - This is called every couple of minutes and is used to obtain credentials for your app backend API. -> See [Authentication Setup](https://docs.powersync.com/installation/authentication-setup) for instructions on how the credentials should be generated.
316+
- `uploadData` - Use this to upload client-side changes to your app backend. -> See [Writing Client Changes](https://docs.powersync.com/installation/app-backend-setup/writing-client-changes) for considerations on the app backend implementation.
317+
318+
#### Index
139319

140-
#### index.ts
141320
The main application file used in this guide to show you how to set up and initialize PowerSync.
142-
```typescript index.ts
143-
import { PowerSyncDatabase } from '@powersync/node';
144-
import { Connector } from './Connector.js';
145-
import { AppSchema } from './AppSchema.js';
146321

147-
export const db = new PowerSyncDatabase({
322+
<CodeGroup>
323+
324+
```shell javascript
325+
nano src/index.js
326+
```
327+
328+
```shell typescript
329+
nano src/index.ts
330+
```
331+
332+
</CodeGroup>
333+
334+
This code initializes the PowerSync database, connects to the backend, and logs the current status of the database.
335+
336+
<CodeGroup>
337+
338+
```javascript src/index.js
339+
import { PowerSyncDatabase } from '@powersync/node';
340+
import { Connector } from './Connector.js';
341+
import { AppSchema } from './AppSchema.js';
342+
export const db = new PowerSyncDatabase({
343+
schema: AppSchema,
344+
database: {
345+
dbFilename: 'powersync.db'
346+
},
347+
});
348+
await db.connect(new Connector());
349+
console.log(db.currentStatus);
350+
```
351+
352+
```typescript src/index.ts
353+
import { PowerSyncDatabase } from '@powersync/node';
354+
import { Connector } from './Connector.js';
355+
import { AppSchema } from './AppSchema.js';
356+
357+
export const db = new PowerSyncDatabase({
148358
schema: AppSchema,
149359
database: {
150-
dbFilename: 'powersync.db'
360+
dbFilename: 'powersync.db'
151361
},
152-
});
362+
});
363+
364+
await db.connect(new Connector());
365+
console.log(db.currentStatus);
366+
```
153367

154-
(async () => {
155-
await db.connect(new Connector());
156-
console.log(db.currentStatus);
157-
})()
368+
</CodeGroup>
158369

159-
```
370+
If you are using [Supabase](https://docs.powersync.com/integration-guides/supabase-+-powersync#supabase-powersync), replace the `index.js` or `index.ts` file with the following code:
371+
372+
<CodeGroup>
373+
374+
```javascript src/index.js [expandable]
375+
import 'dotenv/config'
376+
import { PowerSyncDatabase } from '@powersync/node';
377+
import * as readline from 'node:readline/promises';
378+
import { stdin as input, stdout as output } from 'node:process';
379+
import { AppSchema } from './AppSchema.js';
380+
import { SupabaseConnector } from './SupabaseConnector.js';
381+
382+
export const db = new PowerSyncDatabase({
383+
schema: AppSchema,
384+
database: {
385+
dbFilename: 'powersync.db'
386+
},
387+
});
388+
389+
const connector = new SupabaseConnector();
390+
db.init();
391+
connector.registerListener({
392+
initialized: () => { },
393+
sessionStarted: () => {
394+
db.connect(connector);
395+
console.log("status", db.currentStatus);
396+
}
397+
});
398+
399+
connector.init()
400+
401+
const readlineInstance = readline.createInterface({ input, output });
402+
const isLogin = await readlineInstance.question('Is this a login? (y/n): ');
403+
const email = await readlineInstance.question('Enter your email: ');
404+
const password = await readlineInstance.question('Enter your password: ')
405+
406+
if (isLogin.toLowerCase() === 'y') {
407+
console.log('Logging in...');
408+
await connector.login(email, password);
409+
} else {
410+
console.log('Creating a new account...');
411+
const {
412+
data: { session },
413+
error
414+
} = await connector.client.auth.signUp({
415+
email,
416+
password
417+
});
418+
419+
if (error) {
420+
console.error('Error creating account:', error.message);
421+
process.exit(1);
422+
}
423+
connector.updateSession(session);
424+
}
425+
426+
const todos = await db.execute(`SELECT * FROM todos`);
427+
console.log('Todos:', todos.rows?._array);
428+
```
429+
430+
```typescript src/index.ts [expandable]
431+
import 'dotenv/config'
432+
import { PowerSyncDatabase } from '@powersync/node';
433+
import * as readline from 'node:readline/promises';
434+
import { stdin as input, stdout as output } from 'node:process';
435+
import { AppSchema } from './AppSchema.js';
436+
import { SupabaseConnector } from './SupabaseConnector.js';
437+
438+
export const db = new PowerSyncDatabase({
439+
schema: AppSchema,
440+
database: {
441+
dbFilename: 'powersync.db'
442+
},
443+
});
444+
445+
const connector = new SupabaseConnector();
446+
db.init();
447+
connector.registerListener({
448+
initialized: () => { },
449+
sessionStarted: () => {
450+
db.connect(connector);
451+
console.log("status", db.currentStatus);
452+
}
453+
});
454+
455+
connector.init()
456+
457+
const readlineInstance = readline.createInterface({ input, output });
458+
const isLogin = await readlineInstance.question('Is this a login? (y/n): ');
459+
const email = await readlineInstance.question('Enter your email: ');
460+
const password = await readlineInstance.question('Enter your password: ')
461+
462+
if (isLogin.toLowerCase() === 'y') {
463+
console.log('Logging in...');
464+
await connector.login(email, password);
465+
} else {
466+
console.log('Creating a new account...');
467+
const {
468+
data: { session },
469+
error
470+
} = await connector.client.auth.signUp({
471+
email,
472+
password
473+
});
474+
475+
if (error) {
476+
console.error('Error creating account:', error.message);
477+
process.exit(1);
478+
}
479+
connector.updateSession(session);
480+
}
481+
482+
const todos = await db.execute(`SELECT * FROM todos`);
483+
console.log('Todos:', todos.rows?._array);
484+
```
485+
486+
</CodeGroup>
487+
488+
### Step 4 - Running the Application
489+
490+
JavaScript users can run the application using Node.js directly:
491+
<CodeGroup>
492+
493+
```shell javascript
494+
node src/index.js
495+
```
496+
497+
```shell typescript
498+
node --loader ts-node/esm -r dotenv/config src/index.ts
499+
```
500+
501+
</CodeGroup>
160502
161-
## Run the App
162-
```
163-
pnpm start
164-
```
165503
This will start the app, initialize the database and connect to the PowerSync instance.
166504
167505
<Warning>
168-
Depending on what you set for `dbFilename`, the app start (and all is working correctly) three files will be created; `<dbFilename>.db`, `<dbFilename>.db-shm` and `<dbFilename>.db-wal`. You should exclude these from version control when creating the project so make sure to update your `.gitignore` accordingly.
506+
Depending on what you set for `dbFilename`, the app start (and all is working correctly) three files will be created; `<dbFilename>.db`, `<dbFilename>.db-shm` and `<dbFilename>.db-wal`. You should exclude these from version control when creating the project so make sure to update your `.gitignore` accordingly.
169507
</Warning>

‎tutorials/client/sdks/overview.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@ description: "A collection of tutorials on how to use PowerSync in supported cli
44
---
55

66
<CardGroup>
7-
<Card title="Next.js + PowerSync" icon="paperclip" href="/tutorials/client/sdks/web/next-js" horizontal/>
7+
<Card title="Node.js + PowerSync" icon="paperclip" href="/tutorials/client/sdks/node/node-js" horizontal/>
88
</CardGroup>

0 commit comments

Comments
 (0)
Please sign in to comment.