-
Notifications
You must be signed in to change notification settings - Fork 72
Refactor Next.js demo #920
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 11 commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
78cf978
Refactor example-nextjs demo by scaffolding a local PowerSync service…
bean1352 7f558de
Clean up wording and comments
bean1352 7f06328
Added tailwind
bean1352 0690185
Unignored lib directory
bean1352 db02ad4
Renamed lib to library and adopted globalThis singleton pattern
bean1352 c25e190
Remove singleton pattern
bean1352 98ead15
Support env-based JWT keys with auto-generated fallback for dev
bean1352 7e0e1cb
Updated README
bean1352 0b21500
Unignore PowerSync env file
bean1352 a5046ab
Require explicit JWT key pair
bean1352 6b4e7df
Merge branch 'main' into refactor-nextjs-demo
bean1352 72342a9
Merge branch 'main' into refactor-nextjs-demo
bean1352 22bb590
fix(example-nextjs): secure data endpoint and fix Linux host resolution
bean1352 5430e63
bump PS dependencies
bean1352 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,16 @@ | ||
| # Copy to .env.local, and complete these variables. | ||
| # Leave blank to test local-only. | ||
| NEXT_PUBLIC_POWERSYNC_URL= | ||
| NEXT_PUBLIC_POWERSYNC_TOKEN= | ||
| # Copy this file: cp .env.local.template .env.local | ||
| # | ||
| # App-level environment variables for Next.js. | ||
| # Docker Compose variables live in powersync/docker/.env. | ||
|
|
||
| # ── PowerSync service ──────────────────────────────────────────────────────── | ||
| POWERSYNC_URL=http://localhost:8080 | ||
|
|
||
| # Used by Next.js API routes (localhost, since it runs outside Docker) | ||
| DATABASE_URL=postgresql://postgres:changeme@localhost:5432/postgres | ||
|
|
||
| # ── JWT key pair (required) ────────────────────────────────────────────────── | ||
| # Generate with: pnpm generate-keys | ||
| # Then paste the output below. | ||
| POWERSYNC_PRIVATE_KEY= | ||
| POWERSYNC_PUBLIC_KEY= |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,119 @@ | ||
| # PowerSync Next.js example | ||
| # PowerSync Next.js Example | ||
|
|
||
| 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). | ||
| PowerSync demo using [Next.js](https://nextjs.org/) and the [PowerSync JS web SDK](https://docs.powersync.com/client-sdk-references/js-web). | ||
|
|
||
| To see it in action: | ||
| 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. | ||
|
|
||
| 1. `cd` into this directory and run `pnpm install`. | ||
| 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. | ||
| 3. Run `pnpm start`. | ||
| 4. Open the localhost URL displayed in the terminal output in your browser. | ||
| ## Architecture | ||
|
|
||
| ``` | ||
| Browser (WASQLite) | ||
| ↕ sync (WebSocket) | ||
| PowerSync service <-> Postgres (source DB) | ||
| ↕ bucket storage | ||
| Postgres (storage DB) | ||
|
|
||
| Browser -> POST /api/data -> Next.js API route -> Postgres (writes) | ||
| Browser -> GET /api/auth/token -> Next.js API route -> signed JWT | ||
| PowerSync -> GET /api/auth/keys -> Next.js API route -> JWKS (public key) | ||
| ``` | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| - [Docker](https://www.docker.com/) (running) | ||
| - [PowerSync CLI](https://docs.powersync.com/self-hosting/installation) (`npm i -g @powersync/cli`) | ||
| - Node.js >= 18 and [pnpm](https://pnpm.io/) | ||
|
|
||
| ## Setup | ||
|
|
||
| ```bash | ||
| # 1. Install deps | ||
| pnpm install | ||
|
|
||
| # 2. Create env file | ||
| cp .env.local.template .env.local | ||
|
|
||
| # 3. Generate a JWT key pair and paste the output into .env.local | ||
| pnpm generate-keys | ||
|
|
||
| # 4. Start local Postgres + PowerSync | ||
| pnpm local:up | ||
|
|
||
| # 5. Start Next.js | ||
| pnpm dev | ||
| ``` | ||
|
|
||
| Open [http://localhost:3000](http://localhost:3000). | ||
|
|
||
| ### About the key pair | ||
|
|
||
| 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. | ||
|
|
||
| `pnpm generate-keys` prints base64-encoded JWKs to stdout. Copy the two lines into `.env.local`: | ||
|
|
||
| ``` | ||
| POWERSYNC_PRIVATE_KEY=<base64> | ||
| POWERSYNC_PUBLIC_KEY=<base64> | ||
| ``` | ||
|
|
||
| Restart `pnpm dev` after changing these values. | ||
|
|
||
| ## Project structure | ||
|
|
||
| ``` | ||
| src/ | ||
| ├── app/ | ||
| │ ├── api/ | ||
| │ │ ├── auth/ | ||
| │ │ │ ├── keys/route.ts JWKS endpoint for PowerSync | ||
| │ │ │ └── token/route.ts Anonymous JWT endpoint | ||
| │ │ └── data/route.ts CRUD writes to Postgres | ||
| │ ├── globals.css | ||
| │ ├── layout.tsx | ||
| │ └── page.tsx | ||
| ├── components/ | ||
| │ ├── CustomerList.tsx | ||
| │ ├── StatusPanel.tsx | ||
| │ └── SyncedContent.tsx | ||
| └── library/ | ||
| ├── auth-keys.ts Loads RSA key pair from env (server only) | ||
| ├── db.ts Postgres pool (server only) | ||
| └── powersync/ | ||
| ├── connector.ts Fetch token + upload mutations | ||
| ├── powersync-provider.tsx PowerSync context provider | ||
| └── schema.ts Client-side table schema | ||
| ``` | ||
|
|
||
| ## Environment variables | ||
|
|
||
| All config lives in `.env.local`. Docker Compose also reads from this file (via a symlink at `powersync/docker/.env`). | ||
|
|
||
| | Variable | Required | What it does | | ||
| |---|---|---| | ||
| | `POWERSYNC_URL` | yes | PowerSync service URL, also used as the JWT audience | | ||
| | `DATABASE_URL` | yes | Postgres connection for Next.js API routes (uses `localhost`) | | ||
| | `POWERSYNC_PRIVATE_KEY` | yes | Base64-encoded JWK private key — generate with `pnpm generate-keys` | | ||
| | `POWERSYNC_PUBLIC_KEY` | yes | Base64-encoded JWK public key — generate with `pnpm generate-keys` | | ||
| | `PS_DATABASE_*` | yes | Postgres credentials used by Docker | | ||
| | `PS_STORAGE_*` | yes | Separate Postgres for PowerSync internal storage | | ||
| | `PS_DATA_SOURCE_URI` | yes | Postgres URI inside Docker (uses `pg-db` hostname) | | ||
| | `PS_STORAGE_SOURCE_URI` | yes | Storage Postgres URI inside Docker (uses `pg-storage` hostname) | | ||
|
|
||
| ## Scripts | ||
|
|
||
| | Command | What it does | | ||
| |---|---| | ||
| | `pnpm dev` | Start Next.js dev server | | ||
| | `pnpm build` | Production build | | ||
| | `pnpm generate-keys` | Print a fresh JWT key pair for `.env.local` | | ||
| | `pnpm local:up` | Start PowerSync + Postgres via Docker | | ||
| | `pnpm local:down` | Stop Docker stack | | ||
|
|
||
| ## Resetting the database | ||
|
|
||
| If you change the schema, wipe the Docker volumes so the init SQL runs again: | ||
|
|
||
| ```bash | ||
| powersync docker stop --directory powersync --remove --remove-volumes | ||
| powersync docker reset --directory powersync | ||
| ``` |
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| import type { NextConfig } from 'next'; | ||
|
|
||
| const config: NextConfig = { | ||
| serverExternalPackages: ['pg', 'jose'], | ||
| turbopack: { | ||
| root: import.meta.dirname | ||
| } | ||
| }; | ||
|
|
||
| export default config; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,46 +1,39 @@ | ||
| { | ||
| "name": "example-nextjs", | ||
| "version": "0.1.15", | ||
| "version": "0.2.0", | ||
| "private": true, | ||
| "scripts": { | ||
| "dev": "next dev", | ||
| "build": "next build", | ||
| "clean": "rm -rf .next", | ||
| "watch": "next dev", | ||
| "start": "next start", | ||
| "lint": "next lint", | ||
| "test:build": "pnpm build", | ||
| "clean": "rm -rf .next", | ||
| "generate-keys": "node --experimental-strip-types scripts/generate-keys.mts", | ||
| "copy-assets": "powersync-web copy-assets -o public", | ||
| "postinstall": "pnpm copy-assets" | ||
| "postinstall": "pnpm copy-assets", | ||
| "local:up": "powersync docker start --directory powersync", | ||
| "local:down": "powersync docker stop --directory powersync", | ||
| "test:build": "pnpm build" | ||
| }, | ||
| "dependencies": { | ||
| "@emotion/react": "^11.11.4", | ||
| "@emotion/styled": "^11.11.5", | ||
| "@fontsource/roboto": "^5.0.13", | ||
| "@journeyapps/wa-sqlite": "^1.5.0", | ||
| "@lexical/react": "^0.15.0", | ||
| "@mui/icons-material": "^5.15.18", | ||
| "@mui/material": "^5.15.18", | ||
| "@powersync/react": "^1.10.0", | ||
| "@powersync/web": "^1.37.1", | ||
| "lato-font": "^3.0.0", | ||
| "lexical": "^0.15.0", | ||
| "@powersync/react": "^1.9.1", | ||
| "@powersync/web": "^1.37.0", | ||
| "@tailwindcss/postcss": "^4.2.2", | ||
| "jose": "^5.9.6", | ||
| "next": "^16.1.1", | ||
| "pg": "^8.13.1", | ||
| "react": "^19.0.0", | ||
| "react-dom": "^19.0.0" | ||
| "react-dom": "^19.0.0", | ||
| "tailwindcss": "^4.2.2" | ||
| }, | ||
| "devDependencies": { | ||
| "@types/node": "^20.12.12", | ||
| "@types/pg": "^8.11.10", | ||
| "@types/react": "^19.0.0", | ||
| "@types/react-dom": "^19.0.0", | ||
| "autoprefixer": "^10.4.19", | ||
| "babel-loader": "^9.1.3", | ||
| "css-loader": "^6.11.0", | ||
| "eslint": "^9.0.0", | ||
| "eslint-config-next": "^16.0.0", | ||
| "postcss": "^8.4.35", | ||
| "sass": "^1.77.2", | ||
| "sass-loader": "^13.3.3", | ||
| "style-loader": "^3.3.4", | ||
| "tailwindcss": "^3.4.3" | ||
| "typescript": "^5.0.0" | ||
| } | ||
| } | ||
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| const config = { | ||
| plugins: { | ||
| '@tailwindcss/postcss': {} | ||
| } | ||
| }; | ||
|
|
||
| export default config; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| # Adds YAML Schema support for VSCode users with the YAML extension installed. This enables features like validation and autocompletion based on the provided schema. | ||
| # yaml-language-server: $schema=https://unpkg.com/@powersync/cli-schemas@latest/json-schema/cli-config.json | ||
|
|
||
| # This file is managed by the PowerSync CLI. | ||
| # Run \`powersync link --help\` for more information. | ||
| type: self-hosted | ||
| api_url: http://localhost:8080 | ||
| api_key: dev-token-do-not-use-in-production | ||
| plugins: | ||
| docker: | ||
| project_name: powersync_example-nextjs |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| # ── Application database (Postgres) ────────────────────────────────────────── | ||
| PS_DATABASE_USER=postgres | ||
| PS_DATABASE_PASSWORD=changeme | ||
| PS_DATABASE_NAME=postgres | ||
| PS_DATABASE_PORT=5432 | ||
|
|
||
| # ── PowerSync storage database (internal) ──────────────────────────────────── | ||
| PS_STORAGE_USER=postgres | ||
| PS_STORAGE_PASSWORD=changeme | ||
| PS_STORAGE_DATABASE=powersync_storage | ||
| PS_STORAGE_PORT=5433 | ||
|
|
||
| # ── Docker-internal URIs (pg-db / pg-storage are Docker service names) ──────── | ||
| PS_DATA_SOURCE_URI=postgresql://postgres:changeme@pg-db:5432/postgres | ||
| PS_STORAGE_SOURCE_URI=postgresql://postgres:changeme@pg-storage:5433/powersync_storage |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| # Composed PowerSync Docker stack (generated by powersync docker configure). | ||
| # Modules add entries to include and to services.powersync.depends_on. | ||
| # Relative paths: . = powersync/docker, .. = powersync. | ||
| # Include syntax requires Docker Compose v2.20.3+ | ||
|
|
||
| include: | ||
| [ | ||
| modules/database-postgres/postgres.database.compose.yaml, | ||
| modules/storage-postgres/postgres.storage.compose.yaml | ||
| ] | ||
|
|
||
| services: | ||
| powersync: | ||
| restart: unless-stopped | ||
| image: journeyapps/powersync-service:latest | ||
| command: [ 'start', '-r', 'unified' ] | ||
| env_file: | ||
| - .env | ||
| volumes: | ||
| - ../service.yaml:/config/service.yaml | ||
| - ../sync-config.yaml:/config/sync-config.yaml | ||
| environment: | ||
| POWERSYNC_CONFIG_PATH: /config/service.yaml | ||
| NODE_OPTIONS: --max-old-space-size=1000 | ||
| healthcheck: | ||
| test: | ||
| - 'CMD' | ||
| - 'node' | ||
| - '-e' | ||
| - "fetch('http://localhost:${PS_PORT:-8080}/probes/liveness').then(r => | ||
| r.ok ? process.exit(0) : process.exit(1)).catch(() => | ||
| process.exit(1))" | ||
| interval: 5s | ||
| timeout: 1s | ||
| retries: 15 | ||
| ports: | ||
| - '${PS_PORT:-8080}:${PS_PORT:-8080}' | ||
| depends_on: | ||
| { | ||
| pg-db: { condition: service_healthy }, | ||
| pg-storage: { condition: service_healthy } | ||
| } | ||
| name: powersync_example-nextjs |
Empty file.
14 changes: 14 additions & 0 deletions
14
demos/example-nextjs/powersync/docker/modules/database-postgres/init-scripts/01-init.sql
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| CREATE TABLE IF NOT EXISTS customers ( | ||
| id TEXT PRIMARY KEY, | ||
| name TEXT NOT NULL, | ||
| created_at TEXT NOT NULL | ||
| ); | ||
|
|
||
| -- Seed data | ||
| INSERT INTO customers (id, name, created_at) VALUES | ||
| (gen_random_uuid()::text, 'Alice Johnson', now()::text), | ||
| (gen_random_uuid()::text, 'Bob Smith', now()::text), | ||
| (gen_random_uuid()::text, 'Carol White', now()::text); | ||
|
|
||
| -- PowerSync requires a logical replication publication | ||
| CREATE PUBLICATION powersync FOR TABLE customers; |
27 changes: 27 additions & 0 deletions
27
.../example-nextjs/powersync/docker/modules/database-postgres/postgres.database.compose.yaml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| # Database module: PostgreSQL for replication source (logical replication). | ||
| # Paths are relative to this file's directory when using include (Compose resolves from the included file). | ||
| # Include from main: path: modules/database-postgres/postgres.database.compose.yaml | ||
|
|
||
| services: | ||
| pg-db: | ||
| image: postgres:18 | ||
| restart: always | ||
| environment: | ||
| - POSTGRES_USER=${PS_DATABASE_USER} | ||
| - POSTGRES_DB=${PS_DATABASE_NAME} | ||
| - POSTGRES_PASSWORD=${PS_DATABASE_PASSWORD} | ||
| - PGPORT=${PS_DATABASE_PORT} | ||
| volumes: | ||
| - pg_data:/var/lib/postgresql | ||
| - ./init-scripts:/docker-entrypoint-initdb.d | ||
| ports: | ||
| - '${PS_DATABASE_PORT}:${PS_DATABASE_PORT}' | ||
| command: ['postgres', '-c', 'wal_level=logical'] | ||
| healthcheck: | ||
| test: ['CMD-SHELL', 'pg_isready -U ${PS_DATABASE_USER} -d ${PS_DATABASE_NAME}'] | ||
| interval: 5s | ||
| timeout: 5s | ||
| retries: 5 | ||
|
|
||
| volumes: | ||
| pg_data: |
2 changes: 2 additions & 0 deletions
2
...s/example-nextjs/powersync/docker/modules/storage-postgres/init-scripts/01-storage-db.sql
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| -- Storage database is created by the Postgres image from POSTGRES_DB (e.g. powersync_storage). | ||
| -- Add any extra schema or grants here if needed. |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.