diff --git a/.changeset/eighty-apples-sniff.md b/.changeset/eighty-apples-sniff.md deleted file mode 100644 index 45c75ad727..0000000000 --- a/.changeset/eighty-apples-sniff.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@core/sync-service": patch -"@electric-sql/client": patch ---- - -Support for managing multiple databases on one Electric (multi tenancy). diff --git a/.changeset/funny-spoons-trade.md b/.changeset/funny-spoons-trade.md deleted file mode 100644 index ed50fdd2a2..0000000000 --- a/.changeset/funny-spoons-trade.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@core/sync-service": patch ---- - -Refactored the tenant manager to store tenant information in an ETS table for improved read performance. diff --git a/.changeset/modern-taxis-guess.md b/.changeset/modern-taxis-guess.md deleted file mode 100644 index b0d1d04d83..0000000000 --- a/.changeset/modern-taxis-guess.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@core/sync-service": patch ---- - -Drops the replication slot when `DELETE /v1/admin/database/:database_id` is called diff --git a/packages/react-hooks/CHANGELOG.md b/packages/react-hooks/CHANGELOG.md index 09ce0220ab..23aae754e5 100644 --- a/packages/react-hooks/CHANGELOG.md +++ b/packages/react-hooks/CHANGELOG.md @@ -1,5 +1,13 @@ # @electric-sql/react +## 0.5.2 + +### Patch Changes + +- Updated dependencies [65af31c] +- Updated dependencies [90ead4f] + - @electric-sql/client@0.7.2 + ## 0.5.1 ### Patch Changes diff --git a/packages/react-hooks/package.json b/packages/react-hooks/package.json index e39e0e02ee..e3a916a182 100644 --- a/packages/react-hooks/package.json +++ b/packages/react-hooks/package.json @@ -1,6 +1,6 @@ { "name": "@electric-sql/react", - "version": "0.5.1", + "version": "0.5.2", "description": "React hooks for ElectricSQL", "type": "module", "main": "dist/cjs/index.cjs", diff --git a/packages/sync-service/CHANGELOG.md b/packages/sync-service/CHANGELOG.md index 1487a212b0..90f5cac6a2 100644 --- a/packages/sync-service/CHANGELOG.md +++ b/packages/sync-service/CHANGELOG.md @@ -1,5 +1,14 @@ # @core/sync-service +## 0.8.2 + +### Patch Changes + +- d98d9ed: Fix root table parameter validation to return 400 when missing +- 90ead4f: Support for managing multiple databases on one Electric (multi tenancy). +- 5e60e71: Refactored the tenant manager to store tenant information in an ETS table for improved read performance. +- ae18f4a: Drops the replication slot when `DELETE /v1/admin/database/:database_id` is called + ## 0.8.1 ### Patch Changes diff --git a/packages/sync-service/lib/electric/plug/serve_shape_plug.ex b/packages/sync-service/lib/electric/plug/serve_shape_plug.ex index 7fca9241f7..bad8e694b8 100644 --- a/packages/sync-service/lib/electric/plug/serve_shape_plug.ex +++ b/packages/sync-service/lib/electric/plug/serve_shape_plug.ex @@ -153,6 +153,8 @@ defmodule Electric.Plug.ServeShapePlug do end end + def cast_root_table(%Ecto.Changeset{valid?: false} = changeset, _), do: changeset + def cast_root_table(%Ecto.Changeset{} = changeset, opts) do table = fetch_change!(changeset, :table) where = fetch_field!(changeset, :where) diff --git a/packages/sync-service/package.json b/packages/sync-service/package.json index 3f3ccdbdd1..66f97a24db 100644 --- a/packages/sync-service/package.json +++ b/packages/sync-service/package.json @@ -1,7 +1,7 @@ { "name": "@core/sync-service", "private": true, - "version": "0.8.1", + "version": "0.8.2", "scripts": { "publish:hex": "mix do deps.get, hex.publish --yes" } diff --git a/packages/sync-service/test/electric/plug/serve_shape_plug_test.exs b/packages/sync-service/test/electric/plug/serve_shape_plug_test.exs index 0b98c8c1bb..cf395216a5 100644 --- a/packages/sync-service/test/electric/plug/serve_shape_plug_test.exs +++ b/packages/sync-service/test/electric/plug/serve_shape_plug_test.exs @@ -152,22 +152,43 @@ defmodule Electric.Plug.ServeShapePlugTest do :with_tenant_id ] - test "returns 400 for invalid params", ctx do + test "returns 400 for invalid table", ctx do conn = ctx - |> conn(:get, %{"table" => ".invalid_shape"}, "?offset=invalid") + |> conn(:get, %{"table" => ".invalid_shape"}, "?offset=-1") |> ServeShapePlug.call([]) assert conn.status == 400 assert Jason.decode!(conn.resp_body) == %{ - "offset" => ["has invalid format"], "table" => [ "Invalid zero-length delimited identifier" ] } end + test "returns 400 for invalid offset", ctx do + conn = + ctx + |> conn(:get, %{"table" => "foo"}, "?offset=invalid") + |> ServeShapePlug.call([]) + + assert conn.status == 400 + + assert Jason.decode!(conn.resp_body) == %{"offset" => ["has invalid format"]} + end + + test "returns 400 when table param is missing", ctx do + conn = + ctx + |> conn(:get, %{}, "?offset=-1") + |> ServeShapePlug.call([]) + + assert conn.status == 400 + + assert %{"table" => ["can't be blank"]} = Jason.decode!(conn.resp_body) + end + test "returns 400 when table does not exist", ctx do # this will pass table name validation # but will fail to find the table diff --git a/packages/typescript-client/CHANGELOG.md b/packages/typescript-client/CHANGELOG.md index 8374f622af..ca84029624 100644 --- a/packages/typescript-client/CHANGELOG.md +++ b/packages/typescript-client/CHANGELOG.md @@ -1,5 +1,12 @@ # @electric-sql/client +## 0.7.2 + +### Patch Changes + +- 65af31c: Add params option when creating shapes +- 90ead4f: Support for managing multiple databases on one Electric (multi tenancy). + ## 0.7.1 ### Patch Changes diff --git a/packages/typescript-client/README.md b/packages/typescript-client/README.md index 034ffae5cc..ac48eabfd7 100644 --- a/packages/typescript-client/README.md +++ b/packages/typescript-client/README.md @@ -54,6 +54,18 @@ const stream = new ShapeStream({ table: `foo`, }) +// You can also add custom headers and URL parameters +const streamWithParams = new ShapeStream({ + url: `${BASE_URL}/v1/shape`, + table: `foo`, + headers: { + 'Authorization': 'Bearer token' + }, + params: { + 'custom-param': 'value' + } +}) + stream.subscribe(messages => { // messages is an array with one or more row updates // and the stream will wait for all subscribers to process them diff --git a/packages/typescript-client/package.json b/packages/typescript-client/package.json index 8e7e612bd4..0f8d6acd14 100644 --- a/packages/typescript-client/package.json +++ b/packages/typescript-client/package.json @@ -1,6 +1,6 @@ { "name": "@electric-sql/client", - "version": "0.7.1", + "version": "0.7.2", "description": "Postgres everywhere - your data, in sync, wherever you need it.", "type": "module", "main": "dist/cjs/index.cjs", diff --git a/packages/typescript-client/src/client.ts b/packages/typescript-client/src/client.ts index b4369cc837..57c0695596 100644 --- a/packages/typescript-client/src/client.ts +++ b/packages/typescript-client/src/client.ts @@ -31,6 +31,18 @@ import { REPLICA_PARAM, } from './constants' +const RESERVED_PARAMS = new Set([ + DATABASE_ID_QUERY_PARAM, + COLUMNS_QUERY_PARAM, + LIVE_CACHE_BUSTER_QUERY_PARAM, + SHAPE_HANDLE_QUERY_PARAM, + LIVE_QUERY_PARAM, + OFFSET_QUERY_PARAM, + TABLE_QUERY_PARAM, + WHERE_QUERY_PARAM, + REPLICA_PARAM, +]) + type Replica = `full` | `default` /** @@ -98,6 +110,12 @@ export interface ShapeStreamOptions { */ headers?: Record + /** + * Additional request parameters to attach to the URL. + * These will be merged with Electric's standard parameters. + */ + params?: Record + /** * Automatically fetch updates to the Shape. If you just want to sync the current * shape and stop, pass false. @@ -246,6 +264,25 @@ export class ShapeStream = Row> this.options.subscribe ) { const fetchUrl = new URL(url) + + // Add any custom parameters first + if (this.options.params) { + // Check for reserved parameter names + const reservedParams = Object.keys(this.options.params).filter( + (key) => RESERVED_PARAMS.has(key) + ) + if (reservedParams.length > 0) { + throw new Error( + `Cannot use reserved Electric parameter names in custom params: ${reservedParams.join(`, `)}` + ) + } + + for (const [key, value] of Object.entries(this.options.params)) { + fetchUrl.searchParams.set(key, value) + } + } + + // Add Electric's internal parameters if (table) fetchUrl.searchParams.set(TABLE_QUERY_PARAM, table) if (where) fetchUrl.searchParams.set(WHERE_QUERY_PARAM, where) if (columns && columns.length > 0) diff --git a/website/about/contact.md b/website/about/contact.md index af2671fe85..d1f7a60732 100644 --- a/website/about/contact.md +++ b/website/about/contact.md @@ -2,6 +2,7 @@ title: Contact description: >- Get in touch with us by email or say hello on our community Discord. +image: /img/about/vizinada.jpg outline: deep --- diff --git a/website/about/jobs/index.md b/website/about/jobs/index.md index cc3744a8ed..6f8ba82ff1 100644 --- a/website/about/jobs/index.md +++ b/website/about/jobs/index.md @@ -2,7 +2,7 @@ title: Jobs description: >- Join a small, technical, multi-disciplinary team that's passionate - about product, developer experience and database engineering. + about product, engineering and developer experience. image: /img/about/villa-discussion.jpg outline: deep --- diff --git a/website/about/team.md b/website/about/team.md index 69aa83064f..c1125c6ebe 100644 --- a/website/about/team.md +++ b/website/about/team.md @@ -1,7 +1,8 @@ --- title: Team description: >- - Meet the team behind ElectricSQL. + Meet the team, advisors and investors behind ElectricSQL. +image: /img/about/team.jpg outline: deep --- diff --git a/website/docs/api/clients/elixir.md b/website/docs/api/clients/elixir.md index 06ca35f246..1bc11d8602 100644 --- a/website/docs/api/clients/elixir.md +++ b/website/docs/api/clients/elixir.md @@ -1,4 +1,8 @@ --- +title: Elixir Client +description: >- + Electric provides an Elixir client and a Phoenix integration. +image: /img/integrations/electric-phoenix.jpg outline: deep --- diff --git a/website/docs/api/clients/typescript.md b/website/docs/api/clients/typescript.md index 34a591687d..7232f9cf02 100644 --- a/website/docs/api/clients/typescript.md +++ b/website/docs/api/clients/typescript.md @@ -1,4 +1,9 @@ --- +title: Typescript Client +description: >- + Electric provides an Typescript client for streaming Shapes from Postgres + into the web browser and other Javascript environments. +image: /img/integrations/electric-typescript.jpg outline: [2, 4] --- @@ -121,9 +126,12 @@ export interface ShapeStreamOptions { headers?: Record /** - * Alternatively you can override the fetch function. + * Additional request parameters to attach to the URL. + * These will be merged with Electric's standard parameters. + * Note: You cannot use Electric's reserved parameter names + * (table, where, columns, offset, etc.). */ - fetchClient?: typeof fetch + params?: Record /** * Automatically fetch updates to the Shape. If you just want to sync the current @@ -131,12 +139,56 @@ export interface ShapeStreamOptions { */ subscribe?: boolean - backoffOptions?: BackoffOptions - parser?: Parser + /** + * Signal to abort the stream. + */ signal?: AbortSignal + + /** + * Custom fetch client implementation. + */ + fetchClient?: typeof fetch + + /** + * Custom parser for handling specific data types. + */ + parser?: Parser + + backoffOptions?: BackoffOptions } ``` +Note that certain parameter names are reserved for Electric's internal use and cannot be used in custom params: +- `table` +- `where` +- `columns` +- `offset` +- `handle` +- `live` +- `cursor` +- `database_id` +- `replica` + +Attempting to use these reserved names will throw an error. + +Example usage with custom headers and parameters: + +```ts +const stream = new ShapeStream({ + url: 'http://localhost:3000/v1/shape', + table: 'items', + // Add authentication header + headers: { + 'Authorization': 'Bearer token' + }, + // Add custom URL parameters + params: { + 'tenant': 'acme-corp', + 'version': '1.0' + } +}) +``` + #### Messages A `ShapeStream` consumes and emits a stream of messages. These messages can either be a `ChangeMessage` representing a change to the shape data: @@ -148,7 +200,7 @@ export type ChangeMessage = Row> = { headers: Header & { operation: `insert` | `update` | `delete` } offset: Offset } -```` +``` Or a `ControlMessage`, representing an instruction to the client, as [documented here](../http#control-messages). diff --git a/website/docs/guides/shapes.md b/website/docs/guides/shapes.md index 26f4495174..e3d4cce2cf 100644 --- a/website/docs/guides/shapes.md +++ b/website/docs/guides/shapes.md @@ -2,6 +2,7 @@ title: Shapes - Guide description: >- Shapes are the core primitive for controlling sync in the ElectricSQL system. +image: /img/guides/sync-shape.jpg outline: deep --- diff --git a/website/docs/integrations/aws.md b/website/docs/integrations/aws.md index f418ff30ff..2b8592988b 100644 --- a/website/docs/integrations/aws.md +++ b/website/docs/integrations/aws.md @@ -1,6 +1,8 @@ --- outline: deep title: Amazon Web Services (AWS) - Integrations +description: >- + How to deploy Electric on Amazon Web Services (AWS). image: /img/integrations/electric-aws.jpg --- diff --git a/website/docs/integrations/cloudflare.md b/website/docs/integrations/cloudflare.md index e410aa8464..86569eab2a 100644 --- a/website/docs/integrations/cloudflare.md +++ b/website/docs/integrations/cloudflare.md @@ -1,6 +1,8 @@ --- outline: deep title: Cloudflare - Integrations +description: >- + How to use Electric with Cloudflare. image: /img/integrations/electric-cloudflare.jpg --- diff --git a/website/docs/integrations/crunchy.md b/website/docs/integrations/crunchy.md index cdac71e14b..d108952ddb 100644 --- a/website/docs/integrations/crunchy.md +++ b/website/docs/integrations/crunchy.md @@ -1,6 +1,8 @@ --- outline: deep title: Crunchy Data - Integrations +description: >- + How to use Electric with Crunchy Bridge managed Postgres. image: /img/integrations/electric-crunchy.jpg --- diff --git a/website/docs/integrations/digital-ocean.md b/website/docs/integrations/digital-ocean.md index abdc29c904..b6d92cd7ac 100644 --- a/website/docs/integrations/digital-ocean.md +++ b/website/docs/integrations/digital-ocean.md @@ -1,6 +1,8 @@ --- outline: deep title: Digital Ocean - Integrations +description: >- + How to deploy Electric on Digital Ocean. image: /img/integrations/electric-digital-ocean.jpg --- diff --git a/website/docs/integrations/expo.md b/website/docs/integrations/expo.md index 13d3d1c148..528765a3d4 100644 --- a/website/docs/integrations/expo.md +++ b/website/docs/integrations/expo.md @@ -1,6 +1,8 @@ --- outline: deep title: Expo - Integrations +description: >- + How to use Electric to sync data into Expo apps. image: /img/integrations/electric-expo.jpg --- diff --git a/website/docs/integrations/fly.md b/website/docs/integrations/fly.md index 035c6b6ff1..7bb4343885 100644 --- a/website/docs/integrations/fly.md +++ b/website/docs/integrations/fly.md @@ -1,6 +1,8 @@ --- outline: deep title: Fly.io - Integrations +description: >- + How to deploy Electric on Fly. image: /img/integrations/electric-fly.jpg --- diff --git a/website/docs/integrations/gcp.md b/website/docs/integrations/gcp.md index 47d5b70ffb..3ff9262002 100644 --- a/website/docs/integrations/gcp.md +++ b/website/docs/integrations/gcp.md @@ -1,6 +1,8 @@ --- outline: deep title: Google Cloud Platform (GCP) - Integrations +description: >- + How to deploy Electric on Google Cloud Platform (GCP). image: /img/integrations/electric-gcp.jpg --- diff --git a/website/docs/integrations/livestore.md b/website/docs/integrations/livestore.md index b3632411c2..0c46fc5b1e 100644 --- a/website/docs/integrations/livestore.md +++ b/website/docs/integrations/livestore.md @@ -1,6 +1,8 @@ --- outline: deep title: LiveStore - Integrations +description: >- + How to use Electric with LiveStore. image: /img/integrations/electric-livestore.jpg --- diff --git a/website/docs/integrations/mobx.md b/website/docs/integrations/mobx.md index effc84dd44..871aae3608 100644 --- a/website/docs/integrations/mobx.md +++ b/website/docs/integrations/mobx.md @@ -1,6 +1,8 @@ --- outline: deep title: MobX - Integrations +description: >- + How to use Electric with MobX. image: /img/integrations/electric-mobx.jpg --- diff --git a/website/docs/integrations/neon.md b/website/docs/integrations/neon.md index 9b31adf0fd..f1c594d329 100644 --- a/website/docs/integrations/neon.md +++ b/website/docs/integrations/neon.md @@ -1,6 +1,8 @@ --- outline: deep title: Neon - Integrations +description: >- + How to use Electric with Neon's serverless Postgres. image: /img/integrations/electric-neon.jpg --- diff --git a/website/docs/integrations/netlify.md b/website/docs/integrations/netlify.md index b03c87051b..2e1286b03c 100644 --- a/website/docs/integrations/netlify.md +++ b/website/docs/integrations/netlify.md @@ -1,8 +1,11 @@ --- outline: deep title: Netlify - Integrations +description: >- + How to deploy Electric apps on Netlify. image: /img/integrations/electric-netlify.jpg --- + diff --git a/website/docs/integrations/next.md b/website/docs/integrations/next.md index 8f28fb5c70..ef301fb3a1 100644 --- a/website/docs/integrations/next.md +++ b/website/docs/integrations/next.md @@ -1,6 +1,8 @@ --- outline: deep title: Next.js - Integrations +description: >- + How to use Electric with Next.js. image: /img/integrations/electric-next.jpg --- diff --git a/website/docs/integrations/phoenix.md b/website/docs/integrations/phoenix.md index f5b3b6f22e..28f8f0a4e9 100644 --- a/website/docs/integrations/phoenix.md +++ b/website/docs/integrations/phoenix.md @@ -1,6 +1,8 @@ --- outline: deep title: Phoenix - Integrations +description: >- + How to use Electric with Phoenix. image: /img/integrations/electric-phoenix.jpg --- diff --git a/website/docs/integrations/react.md b/website/docs/integrations/react.md index 3acda6ecf8..91c0e41929 100644 --- a/website/docs/integrations/react.md +++ b/website/docs/integrations/react.md @@ -1,6 +1,8 @@ --- outline: deep title: React - Integrations +description: >- + How to use Electric with React. image: /img/integrations/electric-react.jpg --- diff --git a/website/docs/integrations/redis.md b/website/docs/integrations/redis.md index e2b037001e..7e3cd018cd 100644 --- a/website/docs/integrations/redis.md +++ b/website/docs/integrations/redis.md @@ -1,6 +1,8 @@ --- outline: deep title: Redis - Integrations +description: >- + How to use Electric to sync data into Redis. image: /img/integrations/electric-redis.jpg --- diff --git a/website/docs/integrations/render.md b/website/docs/integrations/render.md index d4700a5544..af27dab92c 100644 --- a/website/docs/integrations/render.md +++ b/website/docs/integrations/render.md @@ -1,6 +1,8 @@ --- outline: deep title: Render - Integrations +description: >- + How to deploy Electric on Render. image: /img/integrations/electric-render.jpg --- diff --git a/website/docs/integrations/supabase.md b/website/docs/integrations/supabase.md index 3094a07ae5..f268b54508 100644 --- a/website/docs/integrations/supabase.md +++ b/website/docs/integrations/supabase.md @@ -1,6 +1,8 @@ --- outline: deep title: Supabase - Integrations +description: >- + How to use Electric with Supabase. Including syncing data out of Supabase Postgres and into Supabase Edge Functions. image: /img/integrations/electric-supabase.jpg --- diff --git a/website/docs/integrations/tanstack.md b/website/docs/integrations/tanstack.md index aa01212a8f..6dfa82537c 100644 --- a/website/docs/integrations/tanstack.md +++ b/website/docs/integrations/tanstack.md @@ -1,6 +1,8 @@ --- outline: deep title: TanStack - Integrations +description: >- + How to use Electric with TanStack. Including using Electric for read-path sync and TanStack Query for optimistic writes. image: /img/integrations/electric-tanstack.jpg --- diff --git a/website/docs/intro.md b/website/docs/intro.md index 3afb0cfcfa..df69974ce8 100644 --- a/website/docs/intro.md +++ b/website/docs/intro.md @@ -1,4 +1,7 @@ --- +title: Documentation +description: >- + Welcome to the ElectricSQL developer documentation! outline: deep --- @@ -13,7 +16,7 @@ outline: deep Welcome to the ElectricSQL developer documentation! -ElectricSQL is a sync engine. Use it to sync [little subsets](/docs/guides/shapes) of your Postgres data into [local apps](/use-cases/state-transfer), services and [environments](/use-cases/dev-and-test). +ElectricSQL is a Postgres sync engine. Use it to sync [little subsets](/docs/guides/shapes) of your Postgres data into [local apps](/use-cases/state-transfer), services and [environments](/use-cases/dev-and-test). You don't need to be an expert in database replication or distributed systems to use Electric. You do need to be familiar with basic web development. diff --git a/website/docs/reference/benchmarks.md b/website/docs/reference/benchmarks.md index ee93d2ccf1..db3ae85df4 100644 --- a/website/docs/reference/benchmarks.md +++ b/website/docs/reference/benchmarks.md @@ -1,5 +1,9 @@ --- title: Benchmarks - Reference +description: >- + We run benchmarks for the Electric sync engine and the Electric Cloud, + which hosts the sync engine behind a CDN. +image: /img/guides/diverse-shape-fanout.png outline: [2, 4] --- diff --git a/website/docs/reference/telemetry.md b/website/docs/reference/telemetry.md index ccb4b5740f..a1ab752cbb 100644 --- a/website/docs/reference/telemetry.md +++ b/website/docs/reference/telemetry.md @@ -1,4 +1,7 @@ --- +title: Telemetry +description: >- + Electric provides telemetry data for real-time system monitoring. outline: deep --- diff --git a/website/public/img/about/team.jpg b/website/public/img/about/team.jpg new file mode 100644 index 0000000000..38913fd6a6 Binary files /dev/null and b/website/public/img/about/team.jpg differ diff --git a/website/public/img/guides/diverse-shape-fanout.png b/website/public/img/guides/diverse-shape-fanout.png new file mode 100644 index 0000000000..a40b310b90 Binary files /dev/null and b/website/public/img/guides/diverse-shape-fanout.png differ diff --git a/website/public/img/guides/sync-shape.jpg b/website/public/img/guides/sync-shape.jpg new file mode 100644 index 0000000000..cdedd47b9a Binary files /dev/null and b/website/public/img/guides/sync-shape.jpg differ diff --git a/website/public/img/integrations/electric-typescript.jpg b/website/public/img/integrations/electric-typescript.jpg new file mode 100644 index 0000000000..df1d5d324f Binary files /dev/null and b/website/public/img/integrations/electric-typescript.jpg differ