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/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/docs/api/clients/typescript.md b/website/docs/api/clients/typescript.md index 34a591687d..66f6b6086c 100644 --- a/website/docs/api/clients/typescript.md +++ b/website/docs/api/clients/typescript.md @@ -121,9 +121,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 +134,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 +195,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).