Merge pull request #17371 from supabase/docs/updates_on_a_plane

Docs: readability updates
This commit is contained in:
Copple authored and GitHub committed 2023-09-13 20:51:22 +02:00
commit 5a6fee07c8
11 files changed
+669 -753

No files matched your search

@@ -875,7 +875,7 @@ export const realtime: NavMenuConstant = {
url: '/guides/realtime/concepts',
},
{
name: 'Features',
name: 'Usage',
url: undefined,
items: [
{ name: 'Broadcast', url: '/guides/realtime/broadcast' },
@@ -891,13 +891,12 @@ export const realtime: NavMenuConstant = {
url: undefined,
items: [
{
name: 'Subscribing to Database Changes',
url: '/guides/realtime/subscribing-to-database-changes',
name: 'Throttling messages',
url: '/guides/realtime/guides/client-side-throttling',
},
{
name: 'Bring Your Own Database',
url: '/guides/realtime/bring-your-own-database',
items: [],
name: 'Subscribing to Database Changes',
url: '/guides/realtime/subscribing-to-database-changes',
},
{
name: 'Using Realtime with Next.js',
@@ -911,7 +910,12 @@ export const realtime: NavMenuConstant = {
items: [
{ name: 'Quotas', url: '/guides/realtime/quotas' },
{ name: 'Architecture', url: '/guides/realtime/architecture' },
{ name: 'Protocol', url: '/guides/realtime/protocol' },
{ name: 'Message Protocol', url: '/guides/realtime/protocol' },
{
name: 'Bring Your Own Database',
url: '/guides/realtime/bring-your-own-database',
items: [],
},
],
},
],
@@ -15,7 +15,7 @@ Supabase provides several options for programmatically connecting to your Postgr
## Serverless APIs
Supabase provides auto-updating [APIs](/docs/guides/database/api). This is the easiest way to get started if you are managing data (fetching, inserting, updating). We provides several types of API to suit your preferences:
Supabase provides auto-updating [APIs](/docs/guides/database/api). This is the easiest way to get started if you are managing data (fetching, inserting, updating). We provide several types of API to suit your preferences:
- [REST](/docs/guides/api#rest-api-overview): interact with your database through a REST interface.
- [GraphQL](/docs/guides/api#graphql-api-overview): interact with your database through a GraphQL interface.
+50 -6
View File
@@ -3,7 +3,8 @@ import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'realtime',
title: 'Realtime',
description: 'Supabase Realtime with Broadcast, Presence, and Postgres Changes.',
description: 'Send and receive messages to connected clients.',
subtitle: 'Send and receive messages to connected clients.',
sidebar_label: 'Overview',
}
@@ -13,14 +14,57 @@ Supabase provides a globally distributed cluster of [Realtime](https://github.co
- [Presence](/docs/guides/realtime/presence): Track and synchronize shared state between clients.
- [Postgres Changes](/docs/guides/realtime/postgres-changes): Listen to Postgres database changes and send them to authorized clients.
A [channel](https://hexdocs.pm/phoenix/channels.html) is the basic building block of Realtime and narrows the scope of data flow to subscribed clients. You can think of a channel as a chatroom where participants are able to see who's online and send and receive messages; similar to a Discord or Slack channel.
## Examples
All clients can connect to a channel and take advantage of the built-in features, Broadcast and Presence, while extensions, like Postgres Changes, must be enabled prior to use.
<div className="grid md:grid-cols-12 gap-4 not-prose">
{examples.map((x) => (
<div className="col-span-12" key={x.href}>
<Link href={x.href} passHref>
<a>
<GlassPanel title={x.name}>{x.description}</GlassPanel>
</a>
</Link>
</div>
))}
</div>
## See Also
export const examples = [
{
name: 'Multiplayer.dev',
description: 'Mouse movements and chat messages.',
href: 'https://multiplayer.dev',
},
]
- [Realtime: Multiplayer Edition](https://supabase.com/blog/supabase-realtime-multiplayer-general-availability) blog post
## Resources
export const Page = ({ children }) => <Layout meta={meta} children={children} />
Find the source code and documentation in the Supabase GitHub repository.
<div className="grid md:grid-cols-12 gap-4 not-prose">
{resources.map((x) => (
<div className="col-span-6" key={x.href}>
<Link href={x.href} passHref>
<a>
<GlassPanel title={x.name}>{x.description}</GlassPanel>
</a>
</Link>
</div>
))}
</div>
export const resources = [
{
name: 'Supabase Realtime',
description: 'View the source code.',
href: 'https://github.com/supabase/realtime',
},
{
name: 'Realtime: Multiplayer Edition',
description: 'Read more about Supabase Realtime.',
href: 'https://supabase.com/blog/supabase-realtime-multiplayer-general-availability',
},
]
export const Page = ({ children }) => <Layout meta={meta} children={children} hideToc={true} />
export default Page
+82 -217
View File
@@ -1,219 +1,110 @@
import Layout from '~/layouts/DefaultGuideLayout'
import StepHikeCompact from '~/components/StepHikeCompact'
export const meta = {
title: 'Broadcast',
subtitle: "Get up and running with Realtime's Broadcast feature",
breadcrumb: 'Realtime Broadcast Quickstart',
subtitle: 'Send and receive messages using Realtime Broadcast',
description: 'Send and receive messages using Realtime Broadcast',
// breadcrumb: 'Realtime Broadcast Quickstart',
}
Realtime Broadcast follows the [publish-subscribe pattern](https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern) where a client publishes messages to a channel based on a unique topic. For example, a user could send a message to a channel with topic `room-1`.
Let's explore how to implement Realtime Broadcast to send messages between clients.
Other clients can receive the message in real-time by subscribing to the channel with topic `room-1`. These clients can continue to receive messages as long as they continue to be online and subscribed to the same channel topic.
## Usage
An example use-case is sharing a user's cursor position with other clients in an online tool or game.
You can use the Supabase client libraries to send and receive Broadcast messages.
## Quick start
### Initialize the client
Let's explore how to implement Realtime Broadcast so you can integrate it into your use case.
Go to your Supabase project's [API Settings](https://supabase.com/dashboard/project/_/settings/api) and grab the `URL` and `anon` public API key.
<StepHikeCompact>
```js
import { createClient } from '@supabase/supabase-js'
<StepHikeCompact.Step step={1}>
<StepHikeCompact.Details title="Install the client">
const SUPABASE_URL = 'https://<project>.supabase.co'
const SUPABASE_KEY = '<your-anon-key>'
Install the Supabase JavaScript client.
const client = createClient(SUPABASE_URL, SUPABASE_KEY)
```
</StepHikeCompact.Details>
### Listening to Broadcast messages
<StepHikeCompact.Code>
You can provide a callback for the `broadcast` channel to receive message. In this example we will receive any `broadcast` messages in `room-1`:
```bash
npm install @supabase/supabase-js
```
{/* prettier-ignore */}
```js
// Join a room/topic. Can be anything except for 'realtime'.
const channelA = clientA.channel('room-1')
</StepHikeCompact.Code>
// Simple function to log any messages we receive
function messageReceived(payload) {
console.log(payload)
}
</StepHikeCompact.Step>
// Subscribe to the Channel
channelA
.on(
'broadcast',
{ event: 'test' },
(payload) => messageReceived(payload)
)
.subscribe()
```
<StepHikeCompact.Step step={2}>
<StepHikeCompact.Details title="Create the first client">
### Sending Broadcast messages
This client will be used to listen for messages.
We can send Broadcast messages using `channelB.send()`. Let's set up another client to send messages.
Go to your Supabase project's [API Settings](https://supabase.com/dashboard/project/_/settings/api) and grab the `URL` and `anon` public API key.
{/* prettier-ignore */}
```js
// Join a room/topic. Can be anything except for 'realtime'.
const channelB = clientA.channel('room-1')
</StepHikeCompact.Details>
channelB.subscribe((status) => {
// Wait for successful connection
if (status !== 'SUBSCRIBED') {
return null
}
<StepHikeCompact.Code>
// Send a message once the client is subscribed
channelB.send({
type: 'broadcast',
event: 'test',
payload: { message: 'hello, world' },
})
})
```
```js
import {
createClient
} from '@supabase/supabase-js'
const clientA = createClient(
'https://<project>.supabase.co',
'<your-anon-key>'
)
```
</StepHikeCompact.Code>
</StepHikeCompact.Step>
<StepHikeCompact.Step step={3}>
<StepHikeCompact.Details title="Create a channel">
A channel's topic can be anything except for `'realtime'`.
</StepHikeCompact.Details>
<StepHikeCompact.Code>
```js
const channelA = clientA.channel('room-1')
```
</StepHikeCompact.Code>
</StepHikeCompact.Step>
<StepHikeCompact.Step step={4}>
<StepHikeCompact.Details title="Listen for messages">
Specify the Broadcast event you want the `on` handler to listen for. This event name can be anything you want. We'll send a broadcast message with this event name later on.
</StepHikeCompact.Details>
<StepHikeCompact.Code>
```js
channelA
.on(
'broadcast',
{ event: 'test' },
(payload) => console.log(payload)
)
.subscribe()
```
</StepHikeCompact.Code>
</StepHikeCompact.Step>
<StepHikeCompact.Step step={5}>
<StepHikeCompact.Details title="Create the second client">
This client will be used to send a message.
</StepHikeCompact.Details>
<StepHikeCompact.Code>
```js
const clientB = createClient(
'https://<project>.supabase.co',
'<your-anon-key>'
)
```
</StepHikeCompact.Code>
</StepHikeCompact.Step>
<StepHikeCompact.Step step={6}>
<StepHikeCompact.Details title="Create another channel">
This channel's topic must match `channelA`'s.
</StepHikeCompact.Details>
<StepHikeCompact.Code>
```js
const channelB = clientB.channel('room-1')
```
</StepHikeCompact.Code>
</StepHikeCompact.Step>
<StepHikeCompact.Step step={7}>
<StepHikeCompact.Details title="Send message">
Subscribe to channel and send a message.
The payload's `event` must match channelA's `event` in the `on` handler.
</StepHikeCompact.Details>
<StepHikeCompact.Code>
```js
channelB.subscribe((status) => {
if (status === 'SUBSCRIBED') {
channelB.send({
type: 'broadcast',
event: 'test',
payload: {
message: 'hello, world'
},
})
}
})
```
</StepHikeCompact.Code>
</StepHikeCompact.Step>
<StepHikeCompact.Step step={8}>
<StepHikeCompact.Details title="First client receives message">
`clientA` receives the message `clientB` sent.
</StepHikeCompact.Details>
</StepHikeCompact.Step>
</StepHikeCompact>
Before sending messages we need to ensure the client is connected, which we have done within the `subscribe()` callback.
## Broadcast options
There are additional Broadcast functionality that you can enable when creating a channel.
You can pass configuration options while initializing the Supabase Client.
### Self-send messages
You can have a client broadcast a message and then receive the same message by setting Broadcast's `self` config to `true`. Without this, broadcast messages are only sent to other clients.
By default, broadcast messages are only sent to other clients. You can broadcast messages back to the sender by setting Broadcast's `self` parameter to `true`.
{/* prettier-ignore */}
```js
const channelC = clientC.channel('room-2', {
const myChannel = supabase.channel('room-2', {
config: {
broadcast: {
self: true,
},
broadcast: { self: true },
},
})
channelC.on('broadcast', { event: 'test-my-messages' }, (payload) => console.log(payload))
myChannel.on(
'broadcast',
{ event: 'test-my-messages' },
(payload) => console.log(payload)
)
channelC.subscribe((status) => {
if (status === 'SUBSCRIBED') {
channelC.send({
type: 'broadcast',
event: 'test-my-messages',
payload: { message: 'talking to myself' },
})
}
myChannel.subscribe((status) => {
if (status !== 'SUBSCRIBED') { return }
channelC.send({
type: 'broadcast',
event: 'test-my-messages',
payload: { message: 'talking to myself' },
})
})
```
@@ -221,55 +112,29 @@ channelC.subscribe((status) => {
You can confirm that Realtime received your message by setting Broadcast's `ack` config to `true`.
{/* prettier-ignore */}
```js
const channelD = clientD.channel('room-3', {
const myChannel = clientD.channel('room-3', {
config: {
broadcast: {
ack: true,
},
broadcast: { ack: true },
},
})
channelD.subscribe(async (status) => {
if (status === 'SUBSCRIBED') {
const resp = await channelD.send({
type: 'broadcast',
event: 'acknowledge',
payload: {},
})
myChannel.subscribe(async (status) => {
if (status !== 'SUBSCRIBED') { return }
console.log('resp', resp)
}
const serverResponse = await myChannel.send({
type: 'broadcast',
event: 'acknowledge',
payload: {},
})
console.log('serverResponse', serverResponse)
})
```
Use this to guarantee that the server has received the message before resolving `channelD.send`'s promise. If the `ack` config is not set to `true` when creating the channel, the promise returned by `channelD.send` will resolve immediately.
## Client-side rate limit
By default the client will rate limit itself at 10 messages per second (1 message every 100 milliseconds). You can customize this when creating the client:
```js
import { createClient } from '@supabase/supabase-js'
const clientE = createClient('https://<project>.supabase.co', '<your-anon-key>', {
realtime: {
params: {
eventsPerSecond: 20,
},
},
})
```
By setting `eventsPerSecond` to 20, you can send one message every 50 milliseconds on a per client basis.
Learn more by visiting the [Quotas](/docs/guides/realtime/quotas) section.
## More Realtime Quickstarts
- [Presence Quickstart](/docs/guides/realtime/presence)
- [Postgres Changes Quickstart](/docs/guides/realtime/postgres-changes)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
+62 -6
View File
@@ -4,10 +4,11 @@ export const meta = {
id: 'channels',
title: 'Realtime Concepts',
description: 'Learn about channels and other features in Supabase Realtime',
subtitle: 'Learn about channels and other features in Supabase Realtime',
sidebar_label: 'Concepts',
}
Supabase Realtime lets you to build real-time applications with collaborative/multiplayer functionality. It includes 3 core features:
You can use Supabase Realtime to build real-time applications with collaborative/multiplayer functionality. It includes 3 core features:
- [Broadcast](/docs/guides/realtime/broadcast): sends rapid, ephemeral messages to other connected clients. You can use it to track mouse movements, for example.
- [Presence](/docs/guides/realtime/presence): sends user state between connected clients. You can use it to show an "online" status, which disappears when a user is disconnected.
@@ -15,23 +16,78 @@ Supabase Realtime lets you to build real-time applications with collaborative/mu
## Channels
When you initialize your Supabase Realtime client, you define a `topic` that uniquely references a channel. Everyone connected to the same Channel `topic` receives the same messages.
A Channel is the basic building block of Realtime. You can think of a Channel as a chatroom, similar to a Discord or Slack channel, where participants are able to see who's online and send and receive messages.
When you initialize your Supabase Realtime client, you define a `topic` that uniquely references a channel. Clients can bi-directionally send and receive messages over a Channel.
```js
import { createClient } from '@supabase/supabase-js'
const client = createClient('https://<project>.supabase.co', '<your-anon-key>')
const channel = client.channel('my-topic') // set your topic here
const roomOne = client.channel('room-one') // set your topic here
```
Clients can bi-directionally send and receive messages over a Channel. The Realtime backend can also push messages to all clients connected to the same Channel.
## Broadcast
A single client can receive change records from Postgres, Broadcast messages from other clients, and Presence updates all over the same Channel.
Realtime Broadcast follows the [publish-subscribe pattern](https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern) where a client publishes messages to a channel based on a unique topic. For example, a user could send a message to a channel with topic `room-one`.
```js
roomOne.send({
type: 'broadcast',
event: 'test',
payload: { message: 'hello, world' },
})
```
Other clients can receive the message in real-time by subscribing to the Channel with topic `room-one`. These clients continue to receive messages as long as they are subscribed and connected to the same Channel topic.
An example use-case is sharing a user's cursor position with other clients in an online game.
## Presence
Presence can be used to share and invidual's state with others within a Channel.
```js
const presenceTrackStatus = await roomOne.track({
user: 'user-1',
online_at: new Date().toISOString(),
})
```
Each client maintains their own state, and this is then combined into a "shared state" for that Channel topic. It's commonly used for sharing statuses (eg: "online" or "inactive"). The neat thing about Presence is that if a client is suddenly disconnected (for example, they go offline), their state is automatically removed from the shared state. If you've ever tried to build an “I'm online” feature which handles unexpected disconnects, you'll appreciate how useful this is.
When a new client subscribes to a channel, it will immediately receive the channel's latest state in a single message because the state is held by the Realtime server.
## Choosing between Broadcast and Presence
We recommend using Broadcast by default, and then Presence when required. Presence merges all changes into a shared state for every client connected to the same channel. If you use Presence, it's best to throttle your changes so that you are sending updates less frequently.
We recommend using Broadcast by default, and then Presence when required. Presence utilizes an in-memory conflict-free replicated data type (CRDT) to track and synchronize shared state in an eventually consistent manner. It computes the difference between existing state and new state changes and sends the necessary updates to clients via Broadcast. This is computationally heavy, so you should use it sparingly. If you use Presence, it's best to throttle your changes so that you are sending updates less frequently.
## Reatlime extensions
Channels provide a generic networking solution. Supabase Realtime is designed to leverage this networking primitive with "extensions". We currently support one extension: Postgres changes.
### Postgres changes
The Postgres Changes extension listens for database changes and sends them to clients. Clients are required to subscribe with a JWT dictating which changes they are allowed to receive based on the database's [Row Level Security](/docs/guides/auth/row-level-security).
```js
const allChanges = client
.channel('schema-db-changes')
.on(
'postgres_changes',
{
event: '*',
schema: 'public',
},
(payload) => console.log(payload)
)
.subscribe()
```
Anyone with access to a valid JWT signed with the project's JWT secret is able to listen to your database's changes, unless tables have [Row Level Security](/docs/guides/auth/row-level-security) enabled and policies in place.
Clients can choose to receive `INSERT`, `UPDATE`, `DELETE`, or `*` (all) changes for all changes in a schema, a table in a schema, or a column's value in a table. Your clients should only listen to tables in the `public` schema and you must first enable the tables you want your clients to listen to.
export const Page = ({ children }) => <Layout meta={meta} children={children} />
@@ -0,0 +1,47 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'realtime',
title: 'Throttling messages',
description: 'Use client-side throttling to manage message frequency.',
subtitle: 'Use client-side throttling to manage message frequency.',
}
The Supabase clients include functionality for throttling messages.
## Managing client-side throttling
You can customize the client-side throttling when creating the client:
```js
import { createClient } from '@supabase/supabase-js'
const SUPABASE_URL = 'https://<project>.supabase.co'
const SUPABASE_ANON_KEY = '<your-anon-key>'
const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
realtime: {
params: {
eventsPerSecond: 2,
},
},
})
```
## Default client throttling
By default the Supabase clients throttles messages to 10 messages per-second (1 message every 100 milliseconds). This is a soft-limit provided as a safe-guard when you're getting started. You'll rarely need to send more messages than this.
Each client has their own throttling behavior. If you instantiate two clients, by default you would send 20 messages per-second to your project.
## Project Quotas
Each Broadcast and Presence message counts towards your [Project Quotas](/docs/guides/realtime/quotas).
It's common to unintentionally flood the Realtime service with messages. For example, tracking mouse movents without throttling would send hundreds of events per second. It's rare that you need so many messages. Updating a mouse movement even a few times per second is usually enough for the human eye.
The throttling parameter protects against these unintended floods.
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -3,19 +3,16 @@ import StepHikeCompact from '~/components/StepHikeCompact'
export const meta = {
title: 'Postgres Changes',
subtitle: "Get up and running with Realtime's Postgres Changes feature",
breadcrumb: 'Realtime Postgres Changes Quickstart',
subtitle: 'Listen to Postgres changes using Supabase Realtime.',
description: 'Listen to Postgres changes using Supabase Realtime.',
// breadcrumb: 'Realtime Postgres Changes Quickstart',
}
Realtime's Postgres Changes feature listens for database changes and sends them to clients. Clients are required to subscribe with a JWT dictating which changes they are allowed to receive based on the database's [Row Level Security](/docs/guides/auth/row-level-security).
Anyone with access to a valid JWT signed with the project's JWT secret is able to listen to your database's changes, unless tables have [Row Level Security](/docs/guides/auth/row-level-security) enabled and policies in place.
Clients can choose to receive `INSERT`, `UPDATE`, `DELETE`, or `*` (all) changes for all changes in a schema, a table in a schema, or a column's value in a table. Your clients should only listen to tables in the `public` schema and you must first enable the tables you want your clients to listen to.
Let's explore how to use Realtime's Postgres Changes feature to listen to database events.
## Quick start
Let's explore how to implement Realtime Postgres Changes so you can integrate it into your use case.
In this example we'll set up a database table, secure it with Row Level Security, and subscribe to all changes using the Supabase client libraries.
<StepHikeCompact>
@@ -81,10 +78,10 @@ Let's explore how to implement Realtime Postgres Changes so you can integrate it
-- Allow anonymous access
create policy "Allow anonymous access"
on todos
for select
to anon
using (true);
on todos
for select
to anon
using (true);
```
</StepHikeCompact.Code>
@@ -130,9 +127,7 @@ Let's explore how to implement Realtime Postgres Changes so you can integrate it
<StepHikeCompact.Code>
```js
import {
createClient
} from '@supabase/supabase-js'
import { createClient } from '@supabase/supabase-js'
const client = createClient(
'https://<project>.supabase.co',
@@ -175,69 +170,12 @@ Let's explore how to implement Realtime Postgres Changes so you can integrate it
</StepHikeCompact.Code>
<StepHikeCompact.Details title="Listen to changes by table">
Listen to just inserts in the `todos` table by setting the `table` property to 'todos' and event name to `INSERT`.
</StepHikeCompact.Details>
<StepHikeCompact.Code>
```js
const channelB = client
.channel('table-db-changes')
.on(
'postgres_changes',
{
event: 'INSERT',
schema: 'public',
table: 'todos',
},
(payload) => console.log(payload)
)
.subscribe()
```
</StepHikeCompact.Code>
<StepHikeCompact.Details title="Listen to changes by filter">
Listen to changes in the `todos` table when a column's value equals a specified value. In this example, we only listen to inserts on `todos` where the row `id` is 1.
</StepHikeCompact.Details>
<StepHikeCompact.Code>
```js
const channelC = client
.channel('table-filter-changes')
.on(
'postgres_changes',
{
event: 'INSERT',
schema: 'public',
table: 'todos',
filter: 'id=eq.1',
},
(payload) => console.log(payload)
)
.subscribe()
```
</StepHikeCompact.Code>
<StepHikeCompact.Details title="More filters">
Check out the [full list of available filters](/docs/guides/realtime/postgres-changes#available-filters).
</StepHikeCompact.Details>
</StepHikeCompact.Step>
<StepHikeCompact.Step step={7}>
<StepHikeCompact.Details title="Insert dummy data">
Now we can add some data to our table which will trigger `channelA`, `channelB`, and `channelC` event handlers.
Now we can add some data to our table which will trigger the `channelA` event handler.
</StepHikeCompact.Details>
@@ -255,11 +193,165 @@ Let's explore how to implement Realtime Postgres Changes so you can integrate it
</StepHikeCompact>
## Usage
You can use the Supabase client libraries to subscribe to database changes.
### Listeniing to specific schemas
Subscribe to specific schema events using the `schema` parameter:
{/* prettier-ignore */}
```js
const changes = client
.channel('schema-db-changes')
.on(
'postgres_changes',
{
schema: 'public', // Subscribes to the "public" schema in Postgres
event: '*', // Listen to all changes
},
(payload) => console.log(payload)
)
.subscribe()
```
The channel name can be any string except 'realtime'.
### Listening to INSERT events
Use the `event` parameter to listen only to database `INSERT`s:
```js
const changes = client
.channel('schema-db-changes')
.on(
'postgres_changes',
{
event: 'INSERT', // Listen only to INSERTs
schema: 'public',
},
(payload) => console.log(payload)
)
.subscribe()
```
The channel name can be any string except 'realtime'.
### Listening to UPDATE events
Use the `event` parameter to listen only to database `UPDATE`s:
```js
const changes = client
.channel('schema-db-changes')
.on(
'postgres_changes',
{
event: 'UPDATE', // Listen only to UPDATEs
schema: 'public',
},
(payload) => console.log(payload)
)
.subscribe()
```
The channel name can be any string except 'realtime'.
### Listening to DELETE events
Use the `event` parameter to listen only to database `UPDATE`s:
```js
const changes = client
.channel('schema-db-changes')
.on(
'postgres_changes',
{
event: 'DELETE', // Listen only to DELETEs
schema: 'public',
},
(payload) => console.log(payload)
)
.subscribe()
```
The channel name can be any string except 'realtime'.
### Listening to specific tables
Subscribe to specific table events using the `table` parameter:
```js
const changes = client
.channel('table-db-changes')
.on(
'postgres_changes',
{
event: '*',
schema: 'public',
table: 'todos',
},
(payload) => console.log(payload)
)
.subscribe()
```
The channel name can be any string except 'realtime'.
### Listening to multiple changes
To listen to different events and schema/tables/filters combinations with the same channel:
```js
const channel = supabase
.channel('db-changes')
.on(
'postgres_changes',
{
event: '*',
schema: 'public',
table: 'messages',
},
(payload) => console.log(payload)
)
.on(
'postgres_changes',
{
event: 'INSERT',
schema: 'public',
table: 'users',
},
(payload) => console.log(payload)
)
.subscribe()
```
### Filtering for specific changes
Use the `filter` paramater for granular changes:
```js
const changes = client
.channel('table-filter-changes')
.on(
'postgres_changes',
{
event: 'INSERT',
schema: 'public',
table: 'todos',
filter: 'id=eq.1',
},
(payload) => console.log(payload)
)
.subscribe()
```
## Available filters
Realtime offers filters so you can specify the data your client receives at a more granular level.
### eq
### Equal to (eq)
To listen to changes when a column's value in a table equals a client-specified value:
@@ -279,9 +371,9 @@ const channel = supabase
.subscribe()
```
<Admonition type="note">This filter uses Postgres' `=`.</Admonition>
This filter uses Postgres's `=` filter.
### neq
### Not equal to (neq)
To listen to changes when a column's value in a table does not equal a client-specified value:
@@ -301,9 +393,9 @@ const channel = supabase
.subscribe()
```
<Admonition type="note">This filter uses Postgres' `!=`.</Admonition>
This filter uses Postgres's `!=` filter.
### lt
### Less than (lt)
To listen to changes when a column's value in a table is less than a client-specified value:
@@ -323,11 +415,9 @@ const channel = supabase
.subscribe()
```
<Admonition type="note">
This filter uses Postgres' `<` so it works for non-numeric types but make sure to check the expected behavior of the compared data's type.
</Admonition>
This filter uses Postgres's `<` filter, so it works for non-numeric types. Make sure to check the expected behavior of the compared data's type.
### lte
### Less than or equal to (lte)
To listen to changes when a column's value in a table is less than or equal to a client-specified value:
@@ -347,11 +437,9 @@ const channel = supabase
.subscribe()
```
<Admonition type="note">
This filter uses Postgres' `<=` so it works for non-numeric types but make sure to check the expected behavior of the compared data's type.
</Admonition>
This filter uses Postgres' `<=` filter, so it works for non-numeric types. Make sure to check the expected behavior of the compared data's type.
### gt
### Greater thank (gt)
To listen to changes when a column's value in a table is greater than a client-specified value:
@@ -371,12 +459,9 @@ const channel = supabase
.subscribe()
```
<Admonition type="note">
This filter uses Postgres' `>` so it works for non-numeric types but make sure to check the
expected behavior of the compared data's type.
</Admonition>
This filter uses Postgres's `>` filter, so it works for non-numeric types. Make sure to check the expected behavior of the compared data's type.
### gte
### Greater than or equal to (gte)
To listen to changes when a column's value in a table is greater than or equal to a client-specified value:
@@ -396,12 +481,9 @@ const channel = supabase
.subscribe()
```
<Admonition type="note">
This filter uses Postgres' `>=` so it works for non-numeric types but make sure to check the
expected behavior of the compared data's type.
</Admonition>
This filter uses Postgres's `>=` filter, so it works for non-numeric types. Make sure to check the expected behavior of the compared data's type.
### in
### Contained in list (in)
To listen to changes when a column's value in a table equals any client-specified values:
@@ -421,40 +503,9 @@ const channel = supabase
.subscribe()
```
<Admonition type="note">
This filter uses Postgres' `= ANY`. Realtime allows a maximum of 100 values for this filter.
</Admonition>
This filter uses Postgres's `= ANY`. Realtime allows a maximum of 100 values for this filter.
## Combination changes
To listen to different events and schema/tables/filters combinations with the same channel:
```js
const channel = supabase
.channel('db-changes')
.on(
'postgres_changes',
{
event: '*',
schema: 'public',
table: 'messages',
filter: 'body=eq.bye',
},
(payload) => console.log(payload)
)
.on(
'postgres_changes',
{
event: 'INSERT',
schema: 'public',
table: 'users',
},
(payload) => console.log(payload)
)
.subscribe()
```
## Full `old` record
## Receiveing `old` records
By default, only `new` record changes are sent but if you want to receive the `old` record (previous values) whenever you `UPDATE` or `DELETE` a record, you can set the `replica identity` of your table to `full`:
@@ -465,7 +516,7 @@ alter table
<Admonition type="caution">
RLS policies are not applied to `DELETE` statements. When RLS is enabled and `replica identity` is set to `full` on a table, the `old` record contains only the primary key(s).
RLS policies are not applied to `DELETE` statements, because there is no way for Postgres to verify that a user has access to a deleted record. When RLS is enabled and `replica identity` is set to `full` on a table, the `old` record contains only the primary key(s).
</Admonition>
@@ -489,9 +540,9 @@ You may choose to sign your own tokens to customize claims that can be checked i
Your project JWT secret is found with your [Project API keys](https://app.supabase.com/project/_/settings/api) in your dashboard.
{/* prettier-ignore */}
<Admonition type="caution">
Do not expose the `service_role` token on the client because the role is authorized to bypass
row-level security.
Do not expose the `service_role` token on the client because the role is authorized to bypass row-level security.
</Admonition>
To use your own JWT with Realtime make sure to set the token after instantiating the Supabase client and before connecting to a Channel.
@@ -533,35 +584,37 @@ supabase.realtime.setAuth('fresh-token')
## Limitations
The current version of Realtime's Postgres Changes feature has some performance limitations to keep in mind. There can be a bottleneck on the database that limits the number of messages streamed to subscribed clients.
Realtime systems usually require forethought because of their scaling dynamics. For the `Postgres Changes` feature, every change event must be checked to see if the subscribed user has access. For instance, if you have 100 users subscribed to a table where you make a single insert, it will then trigger 100 "reads": one for each user.
The polling query that fetches the changes is currently single-threaded. If you are frequently changing your database, the polling query may not be able to fetch the changes rapidly enough. The delivery of the changes will be delayed until you will get timeout. Additionally, there is a polling query per subscribed client, which means that each unique pair of `filters` and `JWT token` (logged-in user) affects the performance of the polling query. In other words, RLS and filters can further limit the number of messages that can be streamed to subscribed clients.
There can be a database bottleneck which limits message throughput. If your database cannot authorize the changes rapidly enough, the changes will be delayed until you receive a timeout.
From our testing and observation of the performance of the polling query, we have found that the following limits are safe to use:
If you are using Postgres Changes at scale, you should consider using separate "public" table without RLS and filters. Alternatively, you can use Realtime server-side only and then re-stream the changes to your clients using a Realtime Broadcast.
| Database Add-on | Filters | RLS Usage | Concurrent Clients | Records per second per client | Messages per second (total) | Latency p95 (ms) |
| ---------------- | ------- | --------- | ------------------ | ----------------------------- | --------------------------- | ---------------- |
| micro < > medium | 🚫 | 🚫 | 500 | 10 | 5,000 | 257 |
| micro < > medium | 🚫 | 🚫 | 1,000 | 10 | 10,000 | 800 |
| micro < > medium | 🚫 | 🚫 | 5,000 | 2 | 10,000 | 1,120 |
| large and above | 🚫 | 🚫 | 1,000 | 10 | 10,000 | 261 |
| large and above | 🚫 | 🚫 | 5,000 | 2 | 10,000 | 952 |
| large and above | 🚫 | 🚫 | 10,000 | 1 | 10,000 | 949 |
| large and above | 🚫 | 🚫 | 200,000 | 0.04 (1 in 25 seconds) | 8,000 | 15,709 |
| large and above | ✅ | ✅ | 500 | 2 | 1,000 | 262 |
| large and above | ✅ | ✅ | 1,000 | 1 | 1,000 | 431 |
| large and above | ✅ | ✅ | 5,000 | 0.2 (1 in 5 seconds) | 1,000 | 702 |
From our observations, we recommend the following limits depending on your database size:
If you want to use Realtime's Postgres Changes feature you should be aware of these limitations. And if you are using this feature at scale, you should consider using separate table without RLS and filters for the purpose of streaming changes to subscribed clients. Alternatively, you can use Realtime server-side only and then re-stream the changes to your clients using a Realtime Broadcast. Don't forget to run your own benchmarks to make sure that the performance is acceptable for your use case.
### Micro to medium
We are making many improvements to Realtime's Postgres Changes.
| Filters | RLS Usage | Concurrent Clients | Records per second per client | Messages per second (total) | Latency p95 (ms) |
| ------- | --------- | ------------------ | ----------------------------- | --------------------------- | ---------------- |
| 🚫 | 🚫 | 500 | 10 | 5,000 | 257 |
| 🚫 | 🚫 | 1,000 | 10 | 10,000 | 800 |
| 🚫 | 🚫 | 5,000 | 2 | 10,000 | 1,120 |
If you are uncertain about the performance of your use case, please reach out using [Support Form](https://supabase.com/dashboard/support/new) and we will be happy to help you. We have a team of engineers that can advise you on the best solution for your use-case.
### Large and above
## More Realtime Quickstarts
| Filters | RLS Usage | Concurrent Clients | Records per second per client | Messages per second (total) | Latency p95 (ms) |
| ------- | --------- | ------------------ | ----------------------------- | --------------------------- | ---------------- |
| 🚫 | 🚫 | 1,000 | 10 | 10,000 | 261 |
| 🚫 | 🚫 | 5,000 | 2 | 10,000 | 952 |
| 🚫 | 🚫 | 10,000 | 1 | 10,000 | 949 |
| 🚫 | 🚫 | 200,000 | 0.04 (1 in 25 seconds) | 8,000 | 15,709 |
| ✅ | ✅ | 500 | 2 | 1,000 | 262 |
| ✅ | ✅ | 1,000 | 1 | 1,000 | 431 |
| ✅ | ✅ | 5,000 | 0.2 (1 in 5 seconds) | 1,000 | 702 |
- [Broadcast Quickstart](/docs/guides/realtime/broadcast)
- [Presence Quickstart](/docs/guides/realtime/presence)
Don't forget to run your own benchmarks to make sure that the performance is acceptable for your use case.
We are making many improvements to Realtime's Postgres Changes. If you are uncertain about the performance of your use case, please reach out using [Support Form](https://supabase.com/dashboard/support/new) and we will be happy to help you. We have a team of engineers that can advise you on the best solution for your use-case.
export const Page = ({ children }) => <Layout meta={meta} children={children} />
+59 -225
View File
@@ -3,233 +3,92 @@ import StepHikeCompact from '~/components/StepHikeCompact'
export const meta = {
title: 'Presence',
subtitle: "Get up and running with Realtime's Presence feature",
breadcrumb: 'Realtime Presence Quickstart',
description: 'Share state between users with Realtime Presence.',
subtitle: 'Share state between users with Realtime Presence.',
// breadcrumb: 'Realtime Presence Quickstart',
}
Presence can be used to share state between clients. Each client maintains their own piece of state within the shared state.
Let's explore how to implement Realtime Presence to track state between multiple users.
Presence utilizes an in-memory conflict-free replicated data type (CRDT) to track and synchronize shared state in an eventually consistent manner. It computes the difference between existing state and new state changes and sends the necessary updates to clients via Broadcast.
## Usage
When a new client subscribes to a channel, it will immediately receive the channel's latest state in a single message instead of waiting for all other clients to send their individual states.
You can use the Supabase client libraries to track Presence state between users.
Clients are free to come-and-go as they please, and as long as they are all subscribed to the same channel then they will all have the same Presence state as each other.
### Initialize the client
The neat thing about Presence is that if a client is suddenly disconnected (for example, they go offline), their state will be automatically removed from the shared state. If you've ever tried to build an “I'm online” feature which handles unexpected disconnects, you'll appreciate how useful this is.
Go to your Supabase project's [API Settings](https://supabase.com/dashboard/project/_/settings/api) and grab the `URL` and `anon` public API key.
## Quick start
```js
import { createClient } from '@supabase/supabase-js'
Let's explore how to implement Realtime Presence so you can integrate it into your use case.
const SUPABASE_URL = 'https://<project>.supabase.co'
const SUPABASE_KEY = '<your-anon-key>'
<StepHikeCompact>
const supabase = createClient(SUPABASE_URL, SUPABASE_KEY)
```
<StepHikeCompact.Step step={1}>
<StepHikeCompact.Details title="Install the client">
### Sync and track state
Install the Supabase JavaScript client.
Listen to the `sync`, `join`, and `leave` events triggered whenever any client joins or leaves the channel or changes their slice of state:
</StepHikeCompact.Details>
```js
const roomOne = supabase.channel('room_01')
<StepHikeCompact.Code>
roomOne
.on('presence', { event: 'sync' }, () => {
const newState = roomOne.presenceState()
console.log('sync', newState)
})
.on('presence', { event: 'join' }, ({ key, newPresences }) => {
console.log('join', key, newPresences)
})
.on('presence', { event: 'leave' }, ({ key, leftPresences }) => {
console.log('leave', key, leftPresences)
})
.subscribe()
```
```bash
npm install @supabase/supabase-js
```
### Sending state
</StepHikeCompact.Code>
You can send state to all subscribers using `track()`:
</StepHikeCompact.Step>
{/* prettier-ignore */}
```js
const roomOne = supabase.channel('room_01')
<StepHikeCompact.Step step={2}>
<StepHikeCompact.Details title="Create the first client">
const userStatus = {
user: 'user-1',
online_at: new Date().toISOString(),
}
This client will be used to track Presence state as new clients join and leave the channel.
roomOne.subscribe(async (status) => {
if (status !== 'SUBSCRIBED') { return }
Go to your Supabase project's [API Settings](https://supabase.com/dashboard/project/_/settings/api) and grab the `URL` and `anon` public API key.
const presenceTrackStatus = await roomOne.track(userStatus)
console.log(presenceTrackStatus)
})
```
</StepHikeCompact.Details>
A client will receive state from any other client that is subscribed to the same topic (in this case `room_01`). It will also automatically trigger its own `sync` and `join` event handlers.
<StepHikeCompact.Code>
### Stop tracking
```js
import {
createClient
} from '@supabase/supabase-js'
You can stop tracking precense using the `untrack()` method. This will trigger the `sync` and `leave` event handlers.
const clientA = createClient(
'https://<project>.supabase.co',
'<your-anon-key>'
)
```
```js
const untrackPresence = async () => {
const presenceUntrackStatus = await roomOne.untrack()
console.log(presenceUntrackStatus)
}
</StepHikeCompact.Code>
untrackPresence()
```
</StepHikeCompact.Step>
## Presence options
<StepHikeCompact.Step step={3}>
<StepHikeCompact.Details title="Create a channel">
You can pass configuration options while initializing the Supabase Client.
A channel's topic can be anything except for `'realtime'`.
</StepHikeCompact.Details>
<StepHikeCompact.Code>
```js
const channelA = clientA.channel('room-1')
```
</StepHikeCompact.Code>
</StepHikeCompact.Step>
<StepHikeCompact.Step step={4}>
<StepHikeCompact.Details title="Sync and track state">
Listen to the `sync`, `join`, and `leave` events triggered whenever any client joins or leaves the channel or changes their slice of state.
To begin tracking state, `clientA` calls `channelA.track()`, passing in the desired state to share. Once `clientA` successfully tracks its state, it will automatically trigger its own `sync` and `join` event handlers.
</StepHikeCompact.Details>
<StepHikeCompact.Code>
```js
channelA
.on(
'presence',
{ event: 'sync' },
() => {
const newState = channelA.presenceState()
console.log('sync', newState)
}
)
.on(
'presence',
{ event: 'join' },
({ key, newPresences }) => {
console.log('join', key, newPresences)
}
)
.on(
'presence',
{ event: 'leave' },
({ key, leftPresences }) => {
console.log('leave', key, leftPresences)
}
)
.subscribe(async (status) => {
if (status === 'SUBSCRIBED') {
const presenceTrackStatus = await channelA.track({
user: 'user-1',
online_at: new Date().toISOString(),
})
console.log(presenceTrackStatus)
}
})
```
</StepHikeCompact.Code>
</StepHikeCompact.Step>
<StepHikeCompact.Step step={5}>
<StepHikeCompact.Details title="Create the second client">
This client will add to and remove from shared state so other clients can be notified of changes to Presence state.
</StepHikeCompact.Details>
<StepHikeCompact.Code>
```js
const clientB = createClient(
'https://<project>.supabase.co',
'<your-anon-key>'
)
```
</StepHikeCompact.Code>
</StepHikeCompact.Step>
<StepHikeCompact.Step step={6}>
<StepHikeCompact.Details title="Create another channel">
This channel's topic must match `channelA`'s.
</StepHikeCompact.Details>
<StepHikeCompact.Code>
```js
const channelB = clientB.channel('room-1')
```
</StepHikeCompact.Code>
</StepHikeCompact.Step>
<StepHikeCompact.Step step={7}>
<StepHikeCompact.Details title="Add to state">
Subscribe to channel and add to state.
This will trigger `clientA`'s `sync` and `join` event handlers.
</StepHikeCompact.Details>
<StepHikeCompact.Code>
```js
channelB.subscribe(async (status) => {
if (status === 'SUBSCRIBED') {
const presenceTrackStatus = await channelA.track({
user: 'user-2',
online_at: new Date().toISOString(),
})
console.log(presenceTrackStatus)
}
})
```
</StepHikeCompact.Code>
</StepHikeCompact.Step>
<StepHikeCompact.Step step={8}>
<StepHikeCompact.Details title="Remove from state">
This will trigger `clientA`'s `sync` and `leave` event handlers.
</StepHikeCompact.Details>
<StepHikeCompact.Code>
```js
const untrackPresence = async () => {
const presenceUntrackStatus = await channelB.untrack()
console.log(presenceUntrackStatus)
}
untrackPresence()
```
</StepHikeCompact.Code>
</StepHikeCompact.Step>
</StepHikeCompact>
## Presence Key
### Presence Key
By default, Presence will generate a unique `UUIDv1` key on the server to track a client channel's state. If you prefer, you can provide a custom key when creating the channel. This key should be unique among clients.
@@ -245,31 +104,6 @@ const channelC = supabase.channel('test', {
})
```
## Client-Side Rate Limit
By default the client will rate limit itself at 10 messages per second (1 message every 100 milliseconds). You can customize this when creating the client:
```js
import { createClient } from '@supabase/supabase-js'
const supabase = createClient('https://<project>.supabase.co', '<your-anon-key>', {
realtime: {
params: {
eventsPerSecond: 5,
},
},
})
```
By setting `eventsPerSecond` to 5, you can send one message every 200 milliseconds on a per client basis.
Learn more by visiting the [Quotas](/docs/guides/realtime/quotas) section.
## More Realtime Quickstarts
- [Broadcast Quickstart](/docs/guides/realtime/broadcast)
- [Postgres Changes Quickstart](/docs/guides/realtime/postgres-changes)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
+23 -29
View File
@@ -7,9 +7,11 @@ export const meta = {
sidebar_label: 'Quotas',
}
Our cluster supports millions of concurrent connections and message throughput for production workloads.
<Admonition type="note">
Upgrade your plan to increase your quotas. Without a spend cap, or on an Enterprise plan, some quotas are still in place to protect budgets. All quotas are configurable per project. [Contact support](https://supabase.com/dashboard/support/new) if you need your quotas increased. Our cluster supports millions of concurrent connections and message throughput for production workloads.
Upgrade your plan to increase your quotas. Without a spend cap, or on an Enterprise plan, some quotas are still in place to protect budgets. All quotas are configurable per project. [Contact support](https://supabase.com/dashboard/support/new) if you need your quotas increased.
</Admonition>
@@ -28,47 +30,39 @@ Upgrade your plan to increase your quotas. Without a spend cap, or on an Enterpr
Beyond the Free and Pro plan you can customize your quotas by [contacting support](https://supabase.com/dashboard/support/new).
## Client-Side Limiting
## Client-Side throttling
Some basic WebSocket message rate limiting is implemented client-side.
For example, the [multiplayer.dev](https://multiplayer.dev) instantiates the Supabase client with an `eventsPerSecond` parameter.
Some basic WebSocket message throttling is implemented client-side. See the [Throttling](/docs/guides/realtime/guides/client-side-throttling) guide for more details.
## Quota Errors
When you reach a quota errors can appear in backend logs and messages in the WebSocket connection.
When you exceed a quota, errors will appear in the backend logs and client-side messages in the WebSocket connection.
<Admonition type="note">
- **Logs**: check the [Realtime logs](https://supabase.com/dashboard/project/_/database/realtime-logs) inside your project Dashboard.
- **Websocket errors**: Use your browser's developer tools to find the WebSocket initiation request and view individual messages.
Use the [Realtime Inspector](https://realtime.supabase.com/inspector/new) to reproduce an error and share those connection details with Supabase support.
<Admonition type="tip" label="Realtime Inspector">
You can use the [Realtime Inspector](https://realtime.supabase.com/inspector/new) to reproduce an error and share those connection details with Supabase support.
</Admonition>
### Backend Logs
If your project is being limited by a quota, check your [Realtime logs](https://supabase.com/dashboard/project/_/database/realtime-logs).
### WebSocket Errors
- `tenant_events`: Clients will be disconnected if your project is generating too many messages per second. `supabase-js` should reconnect automatically when the message throughput decreases below your plan quota.
<Admonition type="note">
An `event` is a WebSocket message delivered to, or sent from a client.
</Admonition>
Some quotas can cause a Channel join to be refused. Realtime will reply with one of the following WebSocket messages:
- `too_many_channels`: Too many channels currently joined for a single client.
- `too_many_connections`: Too many total concurrent connections for a project.
- `too_many_joins`: Too many Channel joins per second.
### `too_many_channels`
<Admonition type="note">
Too many channels currently joined for a single client.
Use your browser's developer tools to find the WebSocket initiation request and view individual messages.
### `too_many_connections`
</Admonition>
Too many total concurrent connections for a project.
### `too_many_joins`
Too many Channel joins per second.
### `tenant_events`
Clients will be disconnected if your project is generating too many messages per second. `supabase-js` will reconnect automatically when the message throughput decreases below your plan quota. An `event` is a WebSocket message delivered to, or sent from a client.
## Postgres Changes Payload Quota
+34 -102
View File
@@ -2,15 +2,43 @@ import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
title: 'Self-Hosting',
description: 'Getting started with self-hosting Supabase.',
description: 'Host Supabase on your own infrastructure.',
subtitle: 'Host Supabase on your own infrastructure.',
}
There are several ways to use Supabase:
There are several ways to host Supabase on your own computer, server, or cloud.
- [Supabase Cloud](https://supabase.com/dashboard): you don't need to deploy anything. We will manage and scale your infrastructure.
- [Docker](/docs/guides/self-hosting/docker): deploy to your own infrastructure.
## Officially Supported
### Community
<div className="grid md:grid-cols-12 gap-4 not-prose">
{official.map((x) => (
<div className="md:col-span-6 xl:col-span-3" key={x.href}>
<Link href={x.href} passHref>
<a>
<GlassPanel title={x.name}>{x.description}</GlassPanel>
</a>
</Link>
</div>
))}
</div>
export const official = [
{
name: 'Docker',
description: 'Deploy Supabase within your own infrastructure using Docker Compose.',
href: '/docs/guides/self-hosting/docker',
},
{
name: 'BYO Cloud',
description:
'Contact our Enterprise sales team if you need Supabase managed in your own cloud.',
href: '/pricing',
},
]
Supabase is also a hosted platform. If you want to get started for free, visit [supabase.com/dashboard](https://supabase.com/dashboard).
## Community Supported
There are several community-driven projects to help you deploy Supabase. We encourage you to try them out and contribute back to the community.
@@ -49,7 +77,7 @@ export const community = [
},
]
### Third-party
## Third-party Guides
The following third-party providers have shown consistent support for the self-hosted version of Supabase:.
@@ -78,102 +106,6 @@ export const external = [
},
]
## Architecture
Supabase is a combination of open source tools, each specifically chosen for Enterprise-readiness.
If the tools and communities already exist, with an MIT, Apache 2, or equivalent open license, we will use and support that tool.
If the tool doesn't exist, we build and open source it ourselves.
![Supabase Architecture](/docs/img/supabase-architecture.png)
- [Kong](https://github.com/Kong/kong) is a cloud-native API gateway.
- [GoTrue](https://github.com/supabase/gotrue) is an SWT based API for managing users and issuing SWT tokens.
- [PostgREST](http://postgrest.org/) is a web server that turns your PostgreSQL database directly into a RESTful API
- [Realtime](https://github.com/supabase/realtime) is an Elixir server that allows you to listen to PostgreSQL inserts, updates, and deletes using websockets. Realtime polls Postgres' built-in replication functionality for database changes, converts changes to JSON, then broadcasts the JSON over websockets to authorized clients.
- [Storage](https://github.com/supabase/storage-api) provides a RESTful interface for managing Files stored in S3, using Postgres to manage permissions.
- [postgres-meta](https://github.com/supabase/postgres-meta) is a RESTful API for managing your Postgres, allowing you to fetch tables, add roles, and run queries, etc.
- [PostgreSQL](https://www.postgresql.org/) is an object-relational database system with over 30 years of active development that has earned it a strong reputation for reliability, feature robustness, and performance.
## Configuration
Each system has a number of configuration options which can be found in the relevant product documentation.
- [Postgres](https://hub.docker.com/_/postgres/)
- [PostgREST](https://postgrest.org/en/stable/configuration.html)
- [Realtime](https://github.com/supabase/realtime#server)
- [GoTrue](https://github.com/supabase/gotrue)
- [Storage](https://github.com/supabase/storage-api)
- [Kong](https://docs.konghq.com/gateway/latest/install/docker/)
## Managing your database
It is recommended that you decouple your database from the middleware so that you can upgrade the middleware without any downtime.
The "middleware" is everything except Postgres, and it should work with any Postgres provider (such as AWS RDS), or your own Postgres cluster.
### Extensions
Supabase requires some Postgres extensions to be enabled by default for the API and Auth system to work. You can find the extensions inside the
[schema migration scripts](https://github.com/supabase/postgres/tree/develop/migrations). These are mounted at `/docker-entrypoint-initdb.d`
to run automatically when starting the database container.
We recommend installing all extensions into an `extensions` schema. This will keep your API clean,
since all tables in the `public` schema are exposed via the API.
{/* prettier-ignore */}
```sql
create schema if not exists extensions;
create extension if not exists "uuid-ossp" with schema extensions;
create extension if not exists pgcrypto with schema extensions;
create extension if not exists pgjwt with schema extensions;
```
##### `uuid-ossp`
For UUID functions, required for PostgreSQL `<13`.
##### `pgcrypto` and `pgjwt`
For working with JWT and Auth functions.
### Roles
Supabase creates several [default roles](/docs/guides/database/postgres-roles) in your Postgres database. To restore defaults at any time you can run the commands inside the [schema initialization scripts](https://github.com/supabase/postgres/tree/develop/migrations/db/init-scripts). Remember to change your [role passwords](/docs/guides/self-hosting/docker#securing-your-setup) before deploying to production environments.
### Realtime Logs
Set your database's `log_min_messages` configuration to `fatal` to prevent redundant database logs generated by Realtime. However, you might miss important log messages such as database errors. Configure `log_min_messages` based on your needs.
## API Keys
The API Gateway (Kong) uses JWT to authenticate access through to the database. The JWT should correspond to a relevant Postgres Role,
and Supabase is designed to work with 2 roles: an `ANON_KEY` for unauthenticated access and a `SERVICE_KEY` for elevated access.
Use this tool to generate keys:
<JwtGenerator />
## Managing your secrets
Many components inside Supabase use secure secrets and passwords. These are listed in the self-hosting
[env file](https://github.com/supabase/supabase/blob/master/docker/.env.example), but we strongly recommend using a
secrets manager when deploying to production. Plain text files like dotenv lead to accidental costly leaks.
Some suggested systems include:
- [Doppler](https://www.doppler.com/)
- [Key Vault](https://docs.microsoft.com/en-us/azure/key-vault/general/overview) by Azure (Microsoft)
- [Secrets Manager](https://aws.amazon.com/secrets-manager/) by AWS
- [Secrets Manager](https://cloud.google.com/secret-manager) by GCP
- [Vault](https://www.hashicorp.com/products/vault) by Hashicorp
## Migrating and Upgrading
If you have decoupled your database from the middleware, then you should be able to redeploy the latest middleware at any time as long as it has no breaking changes.
Supabase is evolving fast, and we'll continue to improve the migration strategy as part of our core offering.
We realize that database migrations are difficult, and this is one of the problems we plan to make easy for developers.
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
+94 -7
View File
@@ -159,11 +159,80 @@ rm -rf volumes/db/data/
This will destroy all data in the database and storage volumes, so be careful!
## Common configuration
## Managing your secrets
Many components inside Supabase use secure secrets and passwords. These are listed in the self-hosting [env file](https://github.com/supabase/supabase/blob/master/docker/.env.example), but we strongly recommend using a secrets manager when deploying to production. Plain text files like dotenv lead to accidental costly leaks.
Some suggested systems include:
- [Doppler](https://www.doppler.com/)
- [Infisical](https://infisical.com/)
- [Key Vault](https://docs.microsoft.com/en-us/azure/key-vault/general/overview) by Azure (Microsoft)
- [Secrets Manager](https://aws.amazon.com/secrets-manager/) by AWS
- [Secrets Manager](https://cloud.google.com/secret-manager) by GCP
- [Vault](https://www.hashicorp.com/products/vault) by Hashicorp
## Advanced
Everything beyond this point in the guide helps you understand how the system works and how you can modify it to suit your needs.
### Architecture
Supabase is a combination of open source tools, each specifically chosen for Enterprise-readiness.
If the tools and communities already exist, with an MIT, Apache 2, or equivalent open license, we will use and support that tool.
If the tool doesn't exist, we build and open source it ourselves.
![Supabase Architecture](/docs/img/supabase-architecture.png)
- [Kong](https://github.com/Kong/kong) is a cloud-native API gateway.
- [GoTrue](https://github.com/supabase/gotrue) is an SWT based API for managing users and issuing SWT tokens.
- [PostgREST](http://postgrest.org/) is a web server that turns your PostgreSQL database directly into a RESTful API
- [Realtime](https://github.com/supabase/realtime) is an Elixir server that allows you to listen to PostgreSQL inserts, updates, and deletes using websockets. Realtime polls Postgres' built-in replication functionality for database changes, converts changes to JSON, then broadcasts the JSON over websockets to authorized clients.
- [Storage](https://github.com/supabase/storage-api) provides a RESTful interface for managing Files stored in S3, using Postgres to manage permissions.
- [postgres-meta](https://github.com/supabase/postgres-meta) is a RESTful API for managing your Postgres, allowing you to fetch tables, add roles, and run queries, etc.
- [PostgreSQL](https://www.postgresql.org/) is an object-relational database system with over 30 years of active development that has earned it a strong reputation for reliability, feature robustness, and performance.
For the system to work cohesively, some services require additional configuration within the Postgres database. For example, the APIs and Auth system require several [default roles](/docs/guides/database/postgres-roles) amd the `pgjwt` Postgres extension.
You can find all the default extensions inside the [schema migration scripts repo](https://github.com/supabase/postgres/tree/develop/migrations). These scripts are mounted at `/docker-entrypoint-initdb.d` to run automatically when starting the database container.
### Configuring services
Each system has a number of configuration options which can be found in the relevant product documentation.
- [Postgres](https://hub.docker.com/_/postgres/)
- [PostgREST](https://postgrest.org/en/stable/configuration.html)
- [Realtime](https://github.com/supabase/realtime#server)
- [GoTrue](https://github.com/supabase/gotrue)
- [Storage](https://github.com/supabase/storage-api)
- [Kong](https://docs.konghq.com/gateway/latest/install/docker/)
These configuration items are generally added to the `env` section of each service, inside the `docker-compose.yml` section. If these configuration items are sensitive, they should be stored in a [secret manager](/docs/guides/self-hosting#managing-your-secrets) or using an `.env` file and then referenced using the `${}` syntax.
<CH.Code>
```yml docker-compose.yml
services:
rest:
image: postgrest/postgrest
environment:
PGRST_JWT_SECRET: ${JWT_SECRET}
```
```bash .env
## Never check your secrets into version control
`${JWT_SECRET}`
```
</CH.Code>
### Common configuration
Each system can be [configured](../self-hosting#configuration) independently. Some of the most common configuration options are listed below.
### Configuring an email server
#### Configuring an email server
You will need to use a production-ready SMTP server for sending emails. You can configure the SMTP server by updating the the following environment variables:
@@ -178,7 +247,7 @@ SMTP_SENDER_NAME=
We recommend using [AWS SES](https://aws.amazon.com/ses/). It's extremely cheap and reliable. Restart all services to pick up the new configuration.
### Configuring S3 Storage
#### Configuring S3 Storage
By default all files are stored locally on the server. You can configure the Storage service to use S3 by updating the following environment variables:
@@ -192,11 +261,11 @@ By default all files are stored locally on the server. You can configure the Sto
You can find all the available options in the [storage repository](https://github.com/supabase/storage-api/blob/master/.env.sample). Restart the `storage` service to pick up the changes: `docker compose restart storage --no-deps`
### Setting database's `log_min_messages`
#### Setting database's `log_min_messages`
By default, `docker compose` sets the database's `log_min_messages` configuration to `fatal` to prevent redundant logs generated by Realtime. You can configure `log_min_messages` using any of the Postgres [Severity Levels](https://www.postgresql.org/docs/current/runtime-config-logging.html#RUNTIME-CONFIG-SEVERITY-LEVELS).
### Exposing your Postgres database
#### Exposing your Postgres database
By default, the Postgres database is only accessible locally. If you want to expose it to the outside world, you can update the `docker-compose.yml` file and remove the `127.0.0.1:` prefix from the `ports` section:
@@ -208,11 +277,11 @@ By default, the Postgres database is only accessible locally. If you want to exp
This is less-secure, so please make sure you are running a firewall in front of your server.
### File storage backend on macOS
#### File storage backend on macOS
By default, Storage backend is set to `file`, which is to use local files as the storage backend. For macOS compatibility, you need to choose `VirtioFS` as the Docker container file sharing implementation (in Docker Desktop -> Preferences -> General).
### Setting up logging with the Analytics server
#### Setting up logging with the Analytics server
Additional configuration is required for self-hosting the Analytics server. For the full setup instructions, see [Self Hosting Analytics](https://supabase.com/docs/reference/self-hosting-analytics/introduction#getting-started).
@@ -238,6 +307,24 @@ DROP PUBLICATION logflare_pub; DROP SCHEMA _analytics CASCADE; CREATE SCHEMA _an
docker rm supabase-db
```
---
{/* Finish with a video. This also appears in the Sidebar via the "tocVideo" metadata */}
## Demo
A minimal setup working on Ubuntu, hosted on Digital Ocean.
<div className="video-container">
<iframe
src="https://www.youtube-nocookie.com/embed/FqiQKRKsfZE"
frameBorder="1"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
></iframe>
</div>
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page