|
Download node_modules/@supabase/realtime-js/README.md from metarm/QMetaRam: direct link, hf CLI and curl.
- Browser
- Download file 15.2 kB
-
https://huggingface.co/metarm/QMetaRam/resolve/main/node_modules/%40supabase/realtime-js/README.md
- Command line
-
hf download hf://metarm/QMetaRam/node_modules/@supabase/realtime-js/README.md
-
curl -L -o README.md https://huggingface.co/metarm/QMetaRam/resolve/main/node_modules/%40supabase/realtime-js/README.md
15.2 kB
| <br /> | |
| <p align="center"> | |
| <a href="https://supabase.io"> | |
| <picture> | |
| <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/supabase/supabase/master/packages/common/assets/images/supabase-logo-wordmark--dark.svg"> | |
| <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/supabase/supabase/master/packages/common/assets/images/supabase-logo-wordmark--light.svg"> | |
| <img alt="Supabase Logo" width="300" src="https://raw.githubusercontent.com/supabase/supabase/master/packages/common/assets/images/logo-preview.jpg"> | |
| </picture> | |
| </a> | |
| <h1 align="center">Supabase Realtime JS SDK</h1> | |
| <h3 align="center">Send ephemeral messages with <b>Broadcast</b>, track and synchronize state with <b>Presence</b>, and listen to database changes with <b>Postgres Change Data Capture (CDC)</b>.</h3> | |
| <p align="center"> | |
| <a href="https://supabase.com/docs/guides/realtime">Guides</a> | |
| Β· | |
| <a href="https://supabase.com/docs/reference/javascript">Reference Docs</a> | |
| Β· | |
| <a href="https://multiplayer.dev">Multiplayer Demo</a> | |
| </p> | |
| </p> | |
| <div align="center"> | |
| [](https://github.com/supabase/supabase-js/actions?query=branch%3Amaster) | |
| [](https://www.npmjs.com/package/@supabase/realtime-js) | |
| [](#license) | |
| [](https://pkg.pr.new/~/supabase/realtime-js) | |
| </div> | |
| # Overview | |
| This SDK enables you to use the following Supabase Realtime's features: | |
| - **Broadcast**: send ephemeral messages from client to clients with minimal latency. Use cases include sharing cursor positions between users. | |
| - **Presence**: track and synchronize shared state across clients with the help of CRDTs. Use cases include tracking which users are currently viewing a specific webpage. | |
| - **Postgres Change Data Capture (CDC)**: listen for changes in your PostgreSQL database and send them to clients. | |
| # Usage | |
| ## Installing the Package | |
| ```bash | |
| npm install @supabase/realtime-js | |
| ``` | |
| ## Creating a Channel | |
| ```js | |
| import { RealtimeClient } from '@supabase/realtime-js' | |
| const client = new RealtimeClient(REALTIME_URL, { | |
| params: { | |
| apikey: API_KEY, | |
| }, | |
| }) | |
| const channel = client.channel('test-channel', {}) | |
| channel.subscribe((status, err) => { | |
| if (status === 'SUBSCRIBED') { | |
| console.log('Connected!') | |
| } | |
| if (status === 'CHANNEL_ERROR') { | |
| console.log(`There was an error subscribing to channel: ${err.message}`) | |
| } | |
| if (status === 'TIMED_OUT') { | |
| console.log('Realtime server did not respond in time.') | |
| } | |
| if (status === 'CLOSED') { | |
| console.log('Realtime channel was unexpectedly closed.') | |
| } | |
| }) | |
| ``` | |
| ### Notes: | |
| - `REALTIME_URL` is `'ws://localhost:4000/socket'` when developing locally and `'wss://<project_ref>.supabase.co/realtime/v1'` when connecting to your Supabase project. | |
| - `API_KEY` is a JWT whose claims must contain `exp` and `role` (existing database role). | |
| - Channel name can be any `string`. | |
| - Setting `private` to `true` means that the client will use RLS to determine if the user can connect or not to a given channel. | |
| ## Broadcast | |
| Your client can send and receive messages based on the `event`. | |
| ```js | |
| // Setup... | |
| const channel = client.channel('broadcast-test', { broadcast: { ack: false, self: false } }) | |
| channel.on('broadcast', { event: 'some-event' }, (payload) => console.log(payload)) | |
| channel.subscribe(async (status) => { | |
| if (status === 'SUBSCRIBED') { | |
| // Send message to other clients listening to 'broadcast-test' channel | |
| await channel.send({ | |
| type: 'broadcast', | |
| event: 'some-event', | |
| payload: { hello: 'world' }, | |
| }) | |
| } | |
| }) | |
| ``` | |
| ### Notes: | |
| - Setting `ack` to `true` means that the `channel.send` promise will resolve once server replies with acknowledgment that it received the broadcast message request. | |
| - Setting `self` to `true` means that the client will receive the broadcast message it sent out. | |
| ### Broadcast Replay | |
| Broadcast Replay enables **private** channels to access messages that were sent earlier. Only messages published via [Broadcast From the Database](https://supabase.com/docs/guides/realtime/broadcast#trigger-broadcast-messages-from-your-database) are available for replay. | |
| You can configure replay with the following options: | |
| - **`since`** (Required): The epoch timestamp in milliseconds, specifying the earliest point from which messages should be retrieved. | |
| - **`limit`** (Optional): The number of messages to return. This must be a positive integer, with a maximum value of 25. | |
| Example: | |
| ```typescript | |
| const twelveHours = 12 * 60 * 60 * 1000 | |
| const twelveHoursAgo = Date.now() - twelveHours | |
| const config = { private: true, broadcast: { replay: { since: twelveHoursAgo, limit: 10 } } } | |
| supabase | |
| .channel('main:room', { config }) | |
| .on('broadcast', { event: 'my_event' }, (payload) => { | |
| if (payload?.meta?.replayed) { | |
| console.log('This message was sent earlier:', payload) | |
| } else { | |
| console.log('This is a new message', payload) | |
| } | |
| // ... | |
| }) | |
| .subscribe() | |
| ``` | |
| ## Presence | |
| Your client can track and sync state that's stored in the channel. | |
| ```js | |
| // Setup... | |
| const channel = client.channel('presence-test', { | |
| config: { | |
| presence: { | |
| key: '', | |
| }, | |
| }, | |
| }) | |
| channel.on('presence', { event: 'sync' }, () => { | |
| console.log('Online users: ', channel.presenceState()) | |
| }) | |
| channel.on('presence', { event: 'join' }, ({ newPresences }) => { | |
| console.log('New users have joined: ', newPresences) | |
| }) | |
| channel.on('presence', { event: 'leave' }, ({ leftPresences }) => { | |
| console.log('Users have left: ', leftPresences) | |
| }) | |
| channel.subscribe(async (status) => { | |
| if (status === 'SUBSCRIBED') { | |
| const status = await channel.track({ user_id: 1 }) | |
| console.log(status) | |
| } | |
| }) | |
| ``` | |
| > `config.presence.enabled` (set automatically if you add an `.on('presence', ...)` listener) | |
| > controls whether _this_ client receives presence state and updates from other clients β | |
| > without it, `presenceState()` stays empty for you. It does not affect whether other clients | |
| > see you: calling `track()` always makes you visible to subscribers that do have presence | |
| > enabled. On RLS-protected channels, receiving presence updates additionally requires the | |
| > `presence.read` policy to authorize this client. | |
| ## Postgres CDC | |
| Receive database changes on the client. | |
| ```js | |
| // Setup... | |
| const channel = client.channel('db-changes') | |
| channel.on('postgres_changes', { event: '*', schema: 'public' }, (payload) => { | |
| console.log('All changes in public schema: ', payload) | |
| }) | |
| channel.on( | |
| 'postgres_changes', | |
| { event: 'INSERT', schema: 'public', table: 'messages' }, | |
| (payload) => { | |
| console.log('All inserts in messages table: ', payload) | |
| } | |
| ) | |
| channel.on( | |
| 'postgres_changes', | |
| { event: 'UPDATE', schema: 'public', table: 'users', filter: 'username=eq.Realtime' }, | |
| (payload) => { | |
| console.log('All updates on users table when username is Realtime: ', payload) | |
| } | |
| ) | |
| channel.subscribe(async (status) => { | |
| if (status === 'SUBSCRIBED') { | |
| console.log('Ready to receive database changes!') | |
| } | |
| }) | |
| ``` | |
| ### Filters | |
| The `filter` option accepts **either** a raw string **or** a | |
| `postgresChangesFilter()` builder β both produce the exact same wire format, so | |
| you can mix and match and existing string filters keep working unchanged: | |
| ```js | |
| // Raw string β always supported, fully backward compatible | |
| { event: 'UPDATE', schema: 'public', table: 'users', filter: 'id=eq.1' } | |
| // Builder β type-checked, ergonomic; the SDK serializes it for you | |
| { event: 'UPDATE', schema: 'public', table: 'users', filter: postgresChangesFilter().eq('id', 1) } | |
| ``` | |
| A filter is a `column=operator.value` expression evaluated server-side. The | |
| following operators are supported: | |
| | Operator | String form | Builder | Meaning | | |
| | --------------------- | ---------------------------- | -------------------------------------- | --------------------------------- | | |
| | `eq` | `id=eq.1` | `.eq('id', 1)` | equal | | |
| | `neq` | `id=neq.1` | `.neq('id', 1)` | not equal | | |
| | `lt` `lte` `gt` `gte` | `age=gte.18` | `.gte('age', 18)` | comparison | | |
| | `in` | `status=in.(active,pending)` | `.in('status', ['active', 'pending'])` | in list | | |
| | `like` `ilike` | `title=like.%foo%` | `.like('title', '%foo%')` | pattern match (case in/sensitive) | | |
| | `is` | `deleted_at=is.null` | `.is('deleted_at', null)` | `IS null/true/false/unknown` | | |
| | `match` `imatch` | `title=match.^foo` | `.match('title', '^foo')` | POSIX regex match (`~` / `~*`) | | |
| | `isdistinct` | `value=isdistinct.1` | `.isDistinct('value', 1)` | NULL-safe inequality | | |
| **Negation** β prefix any operator with `not.` (string) or use | |
| `.not(column, operator, value)` (builder): | |
| ```js | |
| // String | |
| { event: '*', schema: 'public', table: 'posts', filter: 'status=not.in.(draft,archived)' } | |
| // Builder | |
| { | |
| event: '*', | |
| schema: 'public', | |
| table: 'posts', | |
| filter: postgresChangesFilter().not('status', 'in', ['draft', 'archived']), | |
| } | |
| ``` | |
| **AND composition** β multiple conditions are combined with commas and applied | |
| as an `AND`. With the builder you just chain calls: | |
| ```js | |
| // String | |
| { event: 'UPDATE', schema: 'public', table: 'orders', filter: 'amount=gt.100,status=in.(open,pending)' } | |
| // Builder β equivalent, chained | |
| { | |
| event: 'UPDATE', | |
| schema: 'public', | |
| table: 'orders', | |
| filter: postgresChangesFilter().gt('amount', 100).in('status', ['open', 'pending']), | |
| } | |
| ``` | |
| #### Building filters with `postgresChangesFilter()` | |
| The builder (modeled on the `postgrest-js` filter methods) is the recommended, | |
| type-checked way to compose filters β but it is entirely optional; raw strings | |
| remain fully supported. | |
| ```js | |
| import { postgresChangesFilter } from '@supabase/realtime-js' | |
| channel.on( | |
| 'postgres_changes', | |
| { | |
| event: 'UPDATE', | |
| schema: 'public', | |
| table: 'orders', | |
| // β 'amount=gt.100,status=not.in.(draft,archived)' | |
| filter: postgresChangesFilter().gt('amount', 100).not('status', 'in', ['draft', 'archived']), | |
| }, | |
| (payload) => console.log(payload) | |
| ) | |
| ``` | |
| The builder exposes `eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `in`, `like`, | |
| `ilike`, `match`, `imatch`, `is`, `isDistinct` and `not`. Call `.build()` if you | |
| need the raw string yourself (e.g. to log it or store it). | |
| **Values are sent verbatim** β the server has no quoting/escaping, so spaces and | |
| quotes are preserved as-is. The server separates conditions by commas outside | |
| parentheses, so a literal comma in a scalar value can't be expressed (commas | |
| inside `in.(β¦)` are fine); the builder throws on such values rather than | |
| silently producing a broken filter. | |
| > **Note for PostgREST users:** Realtime evaluates filters server-side over a | |
| > single table's WAL β there is no resource embedding (`!inner`, embedded | |
| > filters) and no `or()` grouping. Use `%` (not `*`) for `like`/`ilike` | |
| > wildcards, since filters travel in the WebSocket payload rather than a URL. | |
| ### Selecting columns | |
| Use `select` to receive only a subset of columns instead of the full row. This | |
| reduces payload size (helpful for large `bytea`/`jsonb` columns). The selected | |
| columns must be selectable by the subscribing role: | |
| ```js | |
| channel.on( | |
| 'postgres_changes', | |
| { event: '*', schema: 'public', table: 'users', select: ['id', 'first_name'] }, | |
| (payload) => { | |
| // payload.new only contains { id, first_name } | |
| console.log(payload) | |
| } | |
| ) | |
| ``` | |
| ## Get All Channels | |
| You can see all the channels that your client has instantiatied. | |
| ```js | |
| // Setup... | |
| client.getChannels() | |
| ``` | |
| ## Cleanup | |
| It is highly recommended that you clean up your channels after you're done with them. | |
| - Remove a single channel | |
| ```js | |
| // Setup... | |
| const channel = client.channel('some-channel-to-remove') | |
| channel.unsubscribe() | |
| client.removeChannel(channel) | |
| ``` | |
| - Remove all channels and close the connection | |
| ```js | |
| // Setup... | |
| client.removeAllChannels() | |
| client.disconnect() | |
| ``` | |
| ## Development | |
| This package is part of the [Supabase JavaScript monorepo](https://github.com/supabase/supabase-js). To work on this package: | |
| ### Building | |
| ```bash | |
| # Complete build (from monorepo root) | |
| pnpm nx build realtime-js | |
| # Build with watch mode for development | |
| pnpm nx build realtime-js --watch | |
| # Individual build targets | |
| pnpm nx build:main realtime-js # CommonJS build (dist/main/) | |
| pnpm nx build:module realtime-js # ES Modules build (dist/module/) | |
| # Other useful commands | |
| pnpm nx clean realtime-js # Clean build artifacts | |
| pnpm nx lint realtime-js # Run ESLint | |
| pnpm nx typecheck realtime-js # TypeScript type checking | |
| ``` | |
| #### Build Outputs | |
| - **CommonJS (`dist/main/`)** - For Node.js environments | |
| - **ES Modules (`dist/module/`)** - For modern bundlers (Webpack, Vite, Rollup) | |
| - **TypeScript definitions (`dist/module/index.d.ts`)** - Type definitions for TypeScript projects | |
| Note: Unlike some other packages, realtime-js doesn't include a UMD build since it's primarily used in Node.js or bundled applications. | |
| #### Validating Package Exports | |
| ```bash | |
| # Check if package exports are correctly configured | |
| pnpm nx check-exports realtime-js | |
| ``` | |
| This command uses ["Are the types wrong?"](https://github.com/arethetypeswrong/arethetypeswrong.github.io) to verify that the package exports work correctly in different environments. Run this before publishing to ensure your package can be imported correctly by all consumers. | |
| ### Testing | |
| **No Docker or Supabase instance required!** The realtime-js tests use mocked WebSocket connections, so they're completely self-contained. | |
| ```bash | |
| # Run unit tests (from monorepo root) | |
| pnpm nx test realtime-js | |
| # Run tests with coverage report | |
| pnpm nx test:coverage realtime-js | |
| # Run tests in watch mode during development | |
| pnpm nx test:watch realtime-js | |
| ``` | |
| #### Test Scripts Explained | |
| - **test** - Runs all unit tests once using Vitest | |
| - **test:coverage** - Runs tests and generates coverage report with terminal output | |
| - **test:watch** - Runs tests in interactive watch mode for development | |
| The tests mock WebSocket connections using `mock-socket`, so you can run them anytime without any external dependencies. | |
| ### Contributing | |
| We welcome contributions! Please see our [Contributing Guide](../../../CONTRIBUTING.md) for details on how to get started. | |
| For major changes or if you're unsure about something, please open an issue first to discuss your proposed changes. | |
| ## Credits | |
| This repo draws heavily from [phoenix-js](https://github.com/phoenixframework/phoenix/tree/master/assets/js/phoenix). | |
| ## License | |
| MIT. | |