Skip to content

Commit

Permalink
Write patterns deployment (#2129)
Browse files Browse the repository at this point in the history
Deployment and dockers files for hosting Write patterns example.

I did some changes to load env vars and add db ids, etc.

There seems to be some problem with pattern 4, but I'm not sure if is
just me.

---------

Co-authored-by: James Arthur <[email protected]>
Co-authored-by: Kyle Mathews <[email protected]>
  • Loading branch information
3 people authored Dec 10, 2024
1 parent 359ceed commit e267c28
Show file tree
Hide file tree
Showing 14 changed files with 416 additions and 116 deletions.
33 changes: 33 additions & 0 deletions examples/write-patterns/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
FROM node:lts-alpine AS base

# Stage 1: Install dependencies
FROM base AS deps
WORKDIR /app

RUN npm install -g pnpm

COPY pnpm-*.yaml ./
COPY package.json ./
COPY tsconfig.build.json ./
COPY tsconfig.base.json ./
COPY packages/typescript-client packages/typescript-client/
COPY packages/react-hooks packages/react-hooks/
COPY packages/experimental packages/experimental/
COPY examples/write-patterns/ examples/write-patterns/

# Install dependencies
RUN pnpm install --frozen-lockfile
RUN pnpm run -r build


# Need to make production image more clean
FROM node:lts-alpine AS prod
WORKDIR /app

ENV NODE_ENV=production
COPY --from=deps /app/ ./

WORKDIR /app/examples/write-patterns

EXPOSE 3001
ENTRYPOINT ["node", "shared/backend/api.js"]
33 changes: 17 additions & 16 deletions examples/write-patterns/package.json
Original file line number Diff line number Diff line change
@@ -1,37 +1,24 @@
{
"name": "@electric-examples/write-patterns",
"private": true,
"version": "0.0.1",
"author": "ElectricSQL",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/electric-sql/electric/issues"
},
"type": "module",
"scripts": {
"backend:up": "PROJECT_NAME=write-patterns pnpm -C ../../ run example-backend:up && pnpm db:migrate",
"backend:down": "PROJECT_NAME=write-patterns pnpm -C ../../ run example-backend:down",
"db:migrate": "dotenv -e ../../.env.dev -- pnpm exec pg-migrations apply --directory ./shared/migrations",
"dev": "concurrently \"vite\" \"node shared/backend/api.js\"",
"build": "vite build",
"format": "eslint . --ext ts,tsx --fix",
"stylecheck": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@electric-sql/client": "workspace:*",
"@electric-sql/experimental": "workspace:*",
"@electric-sql/pglite": "^0.2.14",
"@electric-sql/pglite-react": "^0.2.14",
"@electric-sql/pglite-sync": "^0.2.16",
"@electric-sql/pglite-sync": "^0.2.17",
"@electric-sql/react": "workspace:*",
"body-parser": "^1.20.2",
"cors": "^2.8.5",
"express": "^4.19.2",
"pg": "^8.12.0",
"react": "19.0.0-rc.1",
"react-dom": "19.0.0-rc.1",
"sst": "3.3.64",
"uuid": "^10.0.0",
"valtio": "^2.1.2",
"zod": "^3.23.8"
Expand All @@ -50,10 +37,24 @@
"vite": "^5.3.4",
"vite-plugin-pwa": "^0.21.0"
},
"license": "Apache-2.0",
"overrides": {
"@types/react": "npm:types-react@rc",
"@types/react-dom": "npm:types-react-dom@rc",
"react": "19.0.0-rc.1",
"react-dom": "19.0.0-rc.1"
}
},
"private": true,
"scripts": {
"backend:down": "PROJECT_NAME=write-patterns pnpm -C ../../ run example-backend:down",
"backend:up": "PROJECT_NAME=write-patterns pnpm -C ../../ run example-backend:up && pnpm db:migrate",
"build": "vite build",
"db:migrate": "dotenv -e ../../.env.dev -- pnpm exec pg-migrations apply --directory ./shared/migrations",
"dev": "concurrently \"vite\" \"node shared/backend/api.js\"",
"format": "eslint . --fix",
"preview": "vite preview",
"stylecheck": "eslint . --quiet",
"typecheck": "tsc --noEmit"
},
"type": "module"
}
5 changes: 3 additions & 2 deletions examples/write-patterns/patterns/1-online-writes/index.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import React from 'react'
import { v4 as uuidv4 } from 'uuid'
import { useShape } from '@electric-sql/react'
import api from '../../shared/app/client'

const ELECTRIC_URL = import.meta.env.ELECTRIC_URL || 'http://localhost:3000'
import api from '../../shared/app/client'
import { ELECTRIC_URL, envParams } from '../../shared/app/config'

type Todo = {
id: string
Expand All @@ -19,6 +19,7 @@ export default function OnlineWrites() {
url: `${ELECTRIC_URL}/v1/shape`,
params: {
table: 'todos',
...envParams,
},
parser: {
timestamptz: (value: string) => new Date(value),
Expand Down
5 changes: 3 additions & 2 deletions examples/write-patterns/patterns/2-optimistic-state/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import React, { useOptimistic, useTransition } from 'react'
import { v4 as uuidv4 } from 'uuid'
import { matchBy, matchStream } from '@electric-sql/experimental'
import { useShape } from '@electric-sql/react'
import api from '../../shared/app/client'

const ELECTRIC_URL = import.meta.env.ELECTRIC_URL || 'http://localhost:3000'
import api from '../../shared/app/client'
import { ELECTRIC_URL, envParams } from '../../shared/app/config'

type Todo = {
id: string
Expand Down Expand Up @@ -34,6 +34,7 @@ export default function OptimisticState() {
url: `${ELECTRIC_URL}/v1/shape`,
params: {
table: 'todos',
...envParams,
},
parser: {
timestamptz: (value: string) => new Date(value),
Expand Down
46 changes: 26 additions & 20 deletions examples/write-patterns/patterns/3-shared-persistent/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import { matchBy, matchStream } from '@electric-sql/experimental'
import { useShape } from '@electric-sql/react'

import api from '../../shared/app/client'
import { ELECTRIC_URL, envParams } from '../../shared/app/config'

const ELECTRIC_URL = import.meta.env.ELECTRIC_URL || 'http://localhost:3000'
const KEY = 'electric-sql/examples/write-patterns/shared-persistent'

type Todo = {
Expand Down Expand Up @@ -112,6 +112,7 @@ export default function SharedPersistent() {
url: `${ELECTRIC_URL}/v1/shape`,
params: {
table: 'todos',
...envParams,
},
parser: {
timestamptz: (value: string) => new Date(value),
Expand All @@ -123,25 +124,30 @@ export default function SharedPersistent() {
// Get the local optimistic state.
const localWrites = useSnapshot<Map<string, LocalWrite>>(optimisticState)

// Merge the synced state with the local state.
const todos = localWrites
.values()
.reduce((synced: Todo[], { operation, value }: LocalWrite) => {
switch (operation) {
case 'insert':
return synced.some((todo) => todo.id === value.id)
? synced
: [...synced, value as Todo]

case 'update':
return synced.map((todo) =>
todo.id === value.id ? { ...todo, ...value } : todo
)

case 'delete':
return synced.filter((todo) => todo.id !== value.id)
}
}, sorted)
const computeOptimisticState = (
synced: Todo[],
writes: LocalWrite[]
): Todo[] => {
return writes.reduce(
(synced: Todo[], { operation, value }: LocalWrite): Todo[] => {
switch (operation) {
case 'insert':
return [...synced, value as Todo]
case 'update':
return synced.map((todo) =>
todo.id === value.id ? { ...todo, ...value } : todo
)
case 'delete':
return synced.filter((todo) => todo.id !== value.id)
default:
return synced
}
},
synced
)
}

const todos = computeOptimisticState(sorted, [...localWrites.values()])

// These are the same event handler functions from the previous optimistic
// state pattern, adapted to add the state to the shared, persistent store.
Expand Down
14 changes: 10 additions & 4 deletions examples/write-patterns/patterns/4-through-the-db/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,19 @@ import { electricSync } from '@electric-sql/pglite-sync'

import localSchemaMigrations from './local-schema.sql?raw'

import { ELECTRIC_URL, envParams } from '../../shared/app/config'

const DATA_DIR = 'idb://electric-write-patterns-example'
const ELECTRIC_URL = import.meta.env.ELECTRIC_URL || 'http://localhost:3000'

const registry = new Map<string, Promise<PGliteWithLive>>()

export default async function loadPGlite(): Promise<PGliteWithLive> {
const loadingPromise = registry.get('loadingPromise')
let loadingPromise = registry.get('loadingPromise')

if (loadingPromise === undefined) {
registry.set('loadingPromise', _loadPGlite())
loadingPromise = _loadPGlite()

registry.set('loadingPromise', loadingPromise)
}

return loadingPromise as Promise<PGliteWithLive>
Expand All @@ -32,7 +35,10 @@ async function _loadPGlite(): Promise<PGliteWithLive> {
await pglite.electric.syncShapeToTable({
shape: {
url: `${ELECTRIC_URL}/v1/shape`,
table: 'todos',
params: {
table: 'todos',
...envParams,
},
},
shapeKey: 'todos',
table: 'todos_synced',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,7 @@ END;
$$ LANGUAGE plpgsql;

CREATE OR REPLACE TRIGGER delete_local_on_synced_insert
AFTER INSERT ON todos_synced
FOR EACH ROW
EXECUTE FUNCTION delete_local_on_synced_insert_trigger();

CREATE OR REPLACE TRIGGER delete_local_on_synced_insert_and_update
AFTER UPDATE ON todos_synced
AFTER INSERT OR UPDATE ON todos_synced
FOR EACH ROW
EXECUTE FUNCTION delete_local_on_synced_insert_and_update_trigger();

Expand Down
2 changes: 1 addition & 1 deletion examples/write-patterns/shared/app/client.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const API_URL = import.meta.env.API_URL || 'http://localhost:3001'
const API_URL = import.meta.env.VITE_SERVER_URL || 'http://localhost:3001'

type RequestOptions = {
method: string
Expand Down
11 changes: 11 additions & 0 deletions examples/write-patterns/shared/app/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export const ELECTRIC_URL =
import.meta.env.VITE_ELECTRIC_URL || 'http://localhost:3000'

export const envParams: { database_id?: string; token?: string } =
import.meta.env.VITE_ELECTRIC_TOKEN &&
import.meta.env.VITE_ELECTRIC_DATABASE_ID
? {
database_id: import.meta.env.VITE_ELECTRIC_DATABASE_ID,
token: import.meta.env.VITE_ELECTRIC_TOKEN,
}
: {}
Loading

0 comments on commit e267c28

Please sign in to comment.