Skip to content

Commit 3f8f3f6

Browse files
authored
Refactor Next.js demo (#920)
1 parent c2d0f9e commit 3f8f3f6

40 files changed

Lines changed: 930 additions & 268 deletions
Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,16 @@
1-
# Copy to .env.local, and complete these variables.
2-
# Leave blank to test local-only.
3-
NEXT_PUBLIC_POWERSYNC_URL=
4-
NEXT_PUBLIC_POWERSYNC_TOKEN=
1+
# Copy this file: cp .env.local.template .env.local
2+
#
3+
# App-level environment variables for Next.js.
4+
# Docker Compose variables live in powersync/docker/.env.
5+
6+
# ── PowerSync service ────────────────────────────────────────────────────────
7+
POWERSYNC_URL=http://localhost:8080
8+
9+
# Used by Next.js API routes (localhost, since it runs outside Docker)
10+
DATABASE_URL=postgresql://postgres:changeme@localhost:5432/postgres
11+
12+
# ── JWT key pair (required) ──────────────────────────────────────────────────
13+
# Generate with: pnpm generate-keys
14+
# Then paste the output below.
15+
POWERSYNC_PRIVATE_KEY=
16+
POWERSYNC_PUBLIC_KEY=

demos/example-nextjs/.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,19 @@ yarn-debug.log*
2525
# local env files
2626
.env*.local
2727

28+
# unignore docker env (overrides root .gitignore)
29+
!powersync/docker/.env
30+
2831
# vercel
2932
.vercel
3033

3134
# typescript
3235
*.tsbuildinfo
3336
next-env.d.ts
3437

38+
# override root gitignore's "lib" rule (which targets build output)
39+
!src/library/
40+
3541
# ide
3642
.idea
3743
.fleet

demos/example-nextjs/README.md

Lines changed: 116 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,119 @@
1-
# PowerSync Next.js example
1+
# PowerSync Next.js Example
22

3-
This example is built using [Next.js](https://nextjs.org/) and the [PowerSync JS web SDK](https://docs.powersync.com/client-sdk-references/js-web).
3+
PowerSync demo using [Next.js](https://nextjs.org/) and the [PowerSync JS web SDK](https://docs.powersync.com/client-sdk-references/js-web).
44

5-
To see it in action:
5+
Syncs data from a local Postgres through a self-hosted PowerSync service. No login required; the Next.js server hands out anonymous JWTs signed with a local key pair.
66

7-
1. `cd` into this directory and run `pnpm install`.
8-
2. Copy `.env.local.template` to `.env.local`, and complete the environment variables. You can generate a [temporary development token](https://docs.powersync.com/usage/installation/authentication-setup/development-tokens), or leave blank to test with local-only data.
9-
3. Run `pnpm start`.
10-
4. Open the localhost URL displayed in the terminal output in your browser.
7+
## Architecture
8+
9+
```
10+
Browser (WASQLite)
11+
↕ sync (WebSocket)
12+
PowerSync service <-> Postgres (source DB)
13+
↕ bucket storage
14+
Postgres (storage DB)
15+
16+
Browser -> POST /api/data -> Next.js API route -> Postgres (writes)
17+
Browser -> GET /api/auth/token -> Next.js API route -> signed JWT
18+
PowerSync -> GET /api/auth/keys -> Next.js API route -> JWKS (public key)
19+
```
20+
21+
## Prerequisites
22+
23+
- [Docker](https://www.docker.com/) (running)
24+
- [PowerSync CLI](https://docs.powersync.com/self-hosting/installation) (`npm i -g @powersync/cli`)
25+
- Node.js >= 18 and [pnpm](https://pnpm.io/)
26+
27+
## Setup
28+
29+
```bash
30+
# 1. Install deps
31+
pnpm install
32+
33+
# 2. Create env file
34+
cp .env.local.template .env.local
35+
36+
# 3. Generate a JWT key pair and paste the output into .env.local
37+
pnpm generate-keys
38+
39+
# 4. Start local Postgres + PowerSync
40+
pnpm local:up
41+
42+
# 5. Start Next.js
43+
pnpm dev
44+
```
45+
46+
Open [http://localhost:3000](http://localhost:3000).
47+
48+
### About the key pair
49+
50+
PowerSync validates JWTs the Next.js app issues by fetching a JWKS at [/api/auth/keys](src/app/api/auth/keys/route.ts). The private key signs tokens; the public key is what PowerSync fetches. Both must be set in `.env.local` — the app will refuse to start token issuance without them.
51+
52+
`pnpm generate-keys` prints base64-encoded JWKs to stdout. Copy the two lines into `.env.local`:
53+
54+
```
55+
POWERSYNC_PRIVATE_KEY=<base64>
56+
POWERSYNC_PUBLIC_KEY=<base64>
57+
```
58+
59+
Restart `pnpm dev` after changing these values.
60+
61+
## Project structure
62+
63+
```
64+
src/
65+
├── app/
66+
│ ├── api/
67+
│ │ ├── auth/
68+
│ │ │ ├── keys/route.ts JWKS endpoint for PowerSync
69+
│ │ │ └── token/route.ts Anonymous JWT endpoint
70+
│ │ └── data/route.ts CRUD writes to Postgres
71+
│ ├── globals.css
72+
│ ├── layout.tsx
73+
│ └── page.tsx
74+
├── components/
75+
│ ├── CustomerList.tsx
76+
│ ├── StatusPanel.tsx
77+
│ └── SyncedContent.tsx
78+
└── library/
79+
├── auth-keys.ts Loads RSA key pair from env (server only)
80+
├── db.ts Postgres pool (server only)
81+
└── powersync/
82+
├── connector.ts Fetch token + upload mutations
83+
├── powersync-provider.tsx PowerSync context provider
84+
└── schema.ts Client-side table schema
85+
```
86+
87+
## Environment variables
88+
89+
All config lives in `.env.local`. Docker Compose also reads from this file (via a symlink at `powersync/docker/.env`).
90+
91+
| Variable | Required | What it does |
92+
|---|---|---|
93+
| `POWERSYNC_URL` | yes | PowerSync service URL, also used as the JWT audience |
94+
| `DATABASE_URL` | yes | Postgres connection for Next.js API routes (uses `localhost`) |
95+
| `POWERSYNC_PRIVATE_KEY` | yes | Base64-encoded JWK private key — generate with `pnpm generate-keys` |
96+
| `POWERSYNC_PUBLIC_KEY` | yes | Base64-encoded JWK public key — generate with `pnpm generate-keys` |
97+
| `PS_DATABASE_*` | yes | Postgres credentials used by Docker |
98+
| `PS_STORAGE_*` | yes | Separate Postgres for PowerSync internal storage |
99+
| `PS_DATA_SOURCE_URI` | yes | Postgres URI inside Docker (uses `pg-db` hostname) |
100+
| `PS_STORAGE_SOURCE_URI` | yes | Storage Postgres URI inside Docker (uses `pg-storage` hostname) |
101+
102+
## Scripts
103+
104+
| Command | What it does |
105+
|---|---|
106+
| `pnpm dev` | Start Next.js dev server |
107+
| `pnpm build` | Production build |
108+
| `pnpm generate-keys` | Print a fresh JWT key pair for `.env.local` |
109+
| `pnpm local:up` | Start PowerSync + Postgres via Docker |
110+
| `pnpm local:down` | Stop Docker stack |
111+
112+
## Resetting the database
113+
114+
If you change the schema, wipe the Docker volumes so the init SQL runs again:
115+
116+
```bash
117+
powersync docker stop --directory powersync --remove --remove-volumes
118+
powersync docker reset --directory powersync
119+
```

demos/example-nextjs/next.config.js

Lines changed: 0 additions & 6 deletions
This file was deleted.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import type { NextConfig } from 'next';
2+
3+
const config: NextConfig = {
4+
serverExternalPackages: ['pg', 'jose'],
5+
turbopack: {
6+
root: import.meta.dirname
7+
}
8+
};
9+
10+
export default config;

demos/example-nextjs/package.json

Lines changed: 15 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,39 @@
11
{
22
"name": "example-nextjs",
3-
"version": "0.1.15",
3+
"version": "0.2.0",
44
"private": true,
55
"scripts": {
6+
"dev": "next dev",
67
"build": "next build",
7-
"clean": "rm -rf .next",
8-
"watch": "next dev",
98
"start": "next start",
109
"lint": "next lint",
11-
"test:build": "pnpm build",
10+
"clean": "rm -rf .next",
11+
"generate-keys": "node --experimental-strip-types scripts/generate-keys.mts",
1212
"copy-assets": "powersync-web copy-assets -o public",
13-
"postinstall": "pnpm copy-assets"
13+
"postinstall": "pnpm copy-assets",
14+
"local:up": "powersync docker start --directory powersync",
15+
"local:down": "powersync docker stop --directory powersync",
16+
"test:build": "pnpm build"
1417
},
1518
"dependencies": {
16-
"@emotion/react": "^11.11.4",
17-
"@emotion/styled": "^11.11.5",
18-
"@fontsource/roboto": "^5.0.13",
1919
"@journeyapps/wa-sqlite": "^1.6.0",
20-
"@lexical/react": "^0.15.0",
21-
"@mui/icons-material": "^5.15.18",
22-
"@mui/material": "^5.15.18",
2320
"@powersync/react": "^1.10.0",
2421
"@powersync/web": "^1.37.2",
25-
"lato-font": "^3.0.0",
26-
"lexical": "^0.15.0",
22+
"@tailwindcss/postcss": "^4.2.2",
23+
"jose": "^5.9.6",
2724
"next": "^16.1.1",
25+
"pg": "^8.13.1",
2826
"react": "^19.0.0",
29-
"react-dom": "^19.0.0"
27+
"react-dom": "^19.0.0",
28+
"tailwindcss": "^4.2.2"
3029
},
3130
"devDependencies": {
3231
"@types/node": "^20.12.12",
32+
"@types/pg": "^8.11.10",
3333
"@types/react": "^19.0.0",
3434
"@types/react-dom": "^19.0.0",
35-
"autoprefixer": "^10.4.19",
36-
"babel-loader": "^9.1.3",
37-
"css-loader": "^6.11.0",
3835
"eslint": "^9.0.0",
3936
"eslint-config-next": "^16.0.0",
40-
"postcss": "^8.4.35",
41-
"sass": "^1.77.2",
42-
"sass-loader": "^13.3.3",
43-
"style-loader": "^3.3.4",
44-
"tailwindcss": "^3.4.3"
37+
"typescript": "^5.0.0"
4538
}
4639
}

demos/example-nextjs/postcss.config.js

Lines changed: 0 additions & 6 deletions
This file was deleted.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
const config = {
2+
plugins: {
3+
'@tailwindcss/postcss': {}
4+
}
5+
};
6+
7+
export default config;
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Adds YAML Schema support for VSCode users with the YAML extension installed. This enables features like validation and autocompletion based on the provided schema.
2+
# yaml-language-server: $schema=https://unpkg.com/@powersync/cli-schemas@latest/json-schema/cli-config.json
3+
4+
# This file is managed by the PowerSync CLI.
5+
# Run \`powersync link --help\` for more information.
6+
type: self-hosted
7+
api_url: http://localhost:8080
8+
api_key: dev-token-do-not-use-in-production
9+
plugins:
10+
docker:
11+
project_name: powersync_example-nextjs
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# ── Application database (Postgres) ──────────────────────────────────────────
2+
PS_DATABASE_USER=postgres
3+
PS_DATABASE_PASSWORD=changeme
4+
PS_DATABASE_NAME=postgres
5+
PS_DATABASE_PORT=5432
6+
7+
# ── PowerSync storage database (internal) ────────────────────────────────────
8+
PS_STORAGE_USER=postgres
9+
PS_STORAGE_PASSWORD=changeme
10+
PS_STORAGE_DATABASE=powersync_storage
11+
PS_STORAGE_PORT=5433
12+
13+
# ── Docker-internal URIs (pg-db / pg-storage are Docker service names) ────────
14+
PS_DATA_SOURCE_URI=postgresql://postgres:changeme@pg-db:5432/postgres
15+
PS_STORAGE_SOURCE_URI=postgresql://postgres:changeme@pg-storage:5433/powersync_storage

0 commit comments

Comments
 (0)