Merge branch 'master' of github.com:supabase/supabase

This commit is contained in:
Terry Sutton committed 2023-02-06 17:13:14 -03:30
commit df51f2ebdd
54 files changed
+1187 -364

No files matched your search

+34 -8
View File
@@ -1,12 +1,38 @@
const FooterHelpCallout = () => {
import { ReactMarkdown } from 'react-markdown/lib/react-markdown'
export type FooterHelpCalloutType = 'default' | 'postgres'
const content = {
default: {
title: 'Need some help?',
description: `Not to worry, our specialist engineers are here to help. Submit a support ticket through the [Dashboard](https://app.supabase.com/support/new).`,
},
postgres: {
title: 'Looking for Serverless Postgres?',
description: `Supabase is the fastest way to get started with Postgres in a serverless environment. [Learn more](https://supabase.com/database?utm=postgres-helpers).`,
},
}
const FooterHelpCallout = ({
footerHelpType = 'default',
title,
}: {
footerHelpType: FooterHelpCalloutType
title: any
}) => {
return (
<div className="mt-32">
<div className="min-w-full px-8 py-6 prose bg-scale-300 dark:bg-whiteA-200 rounded">
<h5 className="text-sm m-0 text-scale-1100">Need some help?</h5>
<p className="m-0 text-sm text-scale-900">
Not to worry, our specialist engineers are here to help. Submit a support ticket through
the <a href="https://app.supabase.com/support/new">Dashboard</a>.
</p>
<div className="mt-32 prose prose--remove-p-margin max-w-none">
<div
className="
min-w-full
px-8 py-6
bg-scale-300 dark:bg-whiteA-200
rounded
text-sm text-scale-900
"
>
<h5 className="text-sm text-scale-1100 m-0">{content[footerHelpType].title}</h5>
<ReactMarkdown>{content[footerHelpType].description}</ReactMarkdown>
</div>
</div>
)
+12 -2
View File
@@ -32,7 +32,12 @@ or you can just copy/paste the SQL from below and run it yourself.
3. Click **Run**.
<video width="99%" muted playsInline controls="true">
<source src="/docs/videos/sql-user-management-starter.mp4" type="video/mp4" muted playsInline />
<source
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/docs/sql-user-management-starter.mp4"
type="video/mp4"
muted
playsInline
/>
</video>
</TabPanel>
@@ -53,5 +58,10 @@ We just need to get the URL and `anon` key from the API settings.
3. Find your API `URL`, `anon`, and `service_role` keys on this page.
<video width="99%" muted playsInline controls="true">
<source src="/docs/videos/api/api-url-and-key.mp4" type="video/mp4" muted playsInline />
<source
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/docs/api/api-url-and-key.mp4"
type="video/mp4"
muted
playsInline
/>
</video>
@@ -243,6 +243,8 @@ export const menuItems: NavMenu = {
items: [
{ name: 'Overview', url: '/guides/realtime', items: [] },
{ name: 'Quickstart', url: '/guides/realtime/quickstart', items: [] },
{ name: 'Broadcast', url: '/guides/realtime/broadcast', items: [] },
{ name: 'Presence', url: '/guides/realtime/presence', items: [] },
{ name: 'Postgres Changes', url: '/guides/realtime/postgres-changes', items: [] },
{ name: 'Rate Limits', url: '/guides/realtime/rate-limits', items: [] },
],
@@ -509,6 +509,8 @@ export const realtime = {
name: 'Channels',
url: undefined,
items: [
{ name: 'Broadcast', url: '/guides/realtime/broadcast', items: [] },
{ name: 'Presence', url: '/guides/realtime/presence', items: [] },
{ name: 'Postgres Changes', url: '/guides/realtime/postgres-changes', items: [] },
{ name: 'Rate Limits', url: '/guides/realtime/rate-limits', items: [] },
],
@@ -623,8 +625,7 @@ export const resources = {
label: 'Resources',
url: '/guides/resources',
items: [
// removing until the examples page is reworked
// { name: 'Examples', url: '/guides/resources/examples', items: [] },
{ name: 'Examples', url: '/guides/resources/examples', items: [] },
{ name: 'Glossary', url: '/guides/resources/glossary', items: [] },
{
name: 'Migrate to Supabase',
@@ -652,6 +653,24 @@ export const resources = {
},
],
},
{
name: 'Postgres resources',
url: undefined,
items: [
{
name: 'Drop all tables in schema',
url: '/guides/resources/postgres/dropping-all-tables-in-schema',
},
{
name: 'Select first row per group',
url: '/guides/resources/postgres/first-row-in-group',
},
{
name: 'Print PostgreSQL version',
url: '/guides/resources/postgres/which-version-of-postgres',
},
],
},
],
}
@@ -793,7 +812,7 @@ export const reference = {
url: '/reference/python/start',
level: 'reference_python',
items: [],
icon: 'docs/img/icons/javascript.svg',
icon: '/img/icons/menu/reference-python',
},
// {
// name: 'supabase-python',
@@ -12,8 +12,17 @@ const NavigationMenuGuideList: React.FC<Props> = ({ id, active }) => {
// get url
const url = router.asPath
// remove the end of the url if a deep link
const firstLevelRoute = url?.split('/')?.slice(0, 4)?.join('/')
// We need to decide how deep we want the menu to be for matching urls
// if the links are really deep, we don't want to match all the way out
// But we need to reach out further to make the structure of /resources/postgres/ work
// look at /resources/postgres/ vs /auth/phone-login for how these are different
let firstLevelRoute
if (url.includes('resources/postgres/')) {
firstLevelRoute = url?.split('/')?.slice(0, 5)?.join('/')
} else {
firstLevelRoute = url?.split('/')?.slice(0, 4)?.join('/')
}
return (
<Accordion.Root
-122
View File
@@ -1,122 +0,0 @@
---
id: initializing
title: 'Initializing'
slug: initializing
custom_edit_url: https://github.com/supabase/supabase/edit/master/web/spec/supabase.yml
---
### Initializing a new Client
<RefSubLayout.EducationRow>
<RefSubLayout.Details>
Initializing a new client is pretty straightforward. Find your project url and public key from the
admin panel and pass it into your client initialization function.
`supabase-csharp` is heavily dependent on Models deriving from `BaseModel`. To interact with the API, one must have the associated model (see example) specified.
Leverage `Table`, `PrimaryKey`, and `Column` attributes to specify names of classes/properties that are different from their C# Versions.
</RefSubLayout.Details>
<RefSubLayout.Examples>
<Tabs
size="small"
type="underlined"
defaultActiveId="csharp-init-standard">
<TabPanel id="csharp-init-standard" label="Standard">
```csharp
var url = Environment.GetEnvironmentVariable("SUPABASE_URL");
var key = Environment.GetEnvironmentVariable("SUPABASE_KEY");
var options = new Supabase.SupabaseOptions
{
AutoConnectRealtime = true
};
var supabase = new Supabase.Client(url, key, options);
await supabase.InitializeAsync();
```
</TabPanel>
<TabPanel id="csharp-init-maui" label="Dependency Injection (Maui-like)">
```csharp
public static MauiApp CreateMauiApp()
{
// ...
var builder = MauiApp.CreateBuilder();
var url = Environment.GetEnvironmentVariable("SUPABASE_URL");
var key = Environment.GetEnvironmentVariable("SUPABASE_KEY");
var options = new SupabaseOptions
{
AutoRefreshToken = true,
AutoConnectRealtime = true,
SessionHandler = new SupabaseSessionHandler()
};
// Note the creation as a singleton.
builder.Services.AddSingleton(provider => new Supabase.Client(url, key, options));
}
```
</TabPanel>
<TabPanel id="csharp-init-showing-models" label="With Models Example">
```csharp
// Given the following Model representing the Supabase Database (Message.cs)
[Table("messages")]
public class Message : BaseModel
{
[PrimaryKey("id")]
public int Id { get; set; }
[Column("username")]
public string UserName { get; set; }
[Column("channel_id")]
public int ChannelId { get; set; }
public override bool Equals(object obj)
{
return obj is Message message &&
Id == message.Id;
}
public override int GetHashCode()
{
return HashCode.Combine(Id);
}
}
void Initialize()
{
// Get All Messages
var response = await client.Table<Message>().Get();
List<Message> models = response.Models;
// Insert
var newMessage = new Message { UserName = "acupofjose", ChannelId = 1 };
await client.Table<Message>().Insert();
// Update
var model = response.Models.First();
model.UserName = "elrhomariyounes";
await model.Update();
// Delete
await response.Models.Last().Delete();
// etc.
}
```
</TabPanel>
</Tabs>
</RefSubLayout.Examples>
</RefSubLayout.EducationRow>
+8 -1
View File
@@ -1,8 +1,15 @@
import { FC } from 'react'
import { FooterHelpCalloutType } from '~/components/FooterHelpCallout'
import GuideLayout from './guides'
interface Props {
meta: { title: string; description?: string; hide_table_of_contents?: boolean; video?: string }
meta: {
title: string
description?: string
hide_table_of_contents?: boolean
video?: string
footerHelpType?: FooterHelpCalloutType
}
children: any
toc?: any
currentPage?: string
-1
View File
@@ -334,7 +334,6 @@ const SiteLayout = ({ children }) => {
</div>
<div className="grow px-5 max-w-7xl mx-auto py-16">
{children}
<FooterHelpCallout />
<Footer />
</div>
<MobileMenuBackdrop />
+3
View File
@@ -6,6 +6,7 @@ import { FC, useEffect, useRef, useState } from 'react'
import { IconExternalLink } from 'ui'
import components from '~/components'
import { highlightSelectedTocItem } from '~/components/CustomHTMLElements/CustomHTMLElements.utils'
import FooterHelpCallout, { FooterHelpCalloutType } from '~/components/FooterHelpCallout'
import GuidesTableOfContents from '~/components/GuidesTableOfContents'
import useHash from '~/hooks/useHash'
@@ -16,6 +17,7 @@ interface Props {
hide_table_of_contents?: boolean
breadcrumb?: string
subtitle?: string
footerHelpType?: FooterHelpCalloutType
video?: string
canonical?: string
}
@@ -161,6 +163,7 @@ const Layout: FC<Props> = (props) => {
</div>
)}
</div>
<FooterHelpCallout footerHelpType={props.meta?.footerHelpType} title={props.meta?.title} />
</>
)
}
+24 -4
View File
@@ -85,7 +85,12 @@ This creates a corresponding route `todos` which can accept `GET`, `POST`, `PATC
1. Click **Save**.
<video width="99%" muted playsInline controls="true">
<source src={'/docs/videos/api/api-create-table-sm.mp4'} type="video/mp4" muted playsInline />
<source
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/docs/api/api-create-table-sm.mp4"
type="video/mp4"
muted
playsInline
/>
</video>
</TabPanel>
@@ -113,7 +118,12 @@ Every Supabase project has a unique API URL. Your API is secured behind an API g
3. Find your API `URL`, `anon`, and `service_role` keys on this page.
<video width="99%" muted playsInline controls="true">
<source src={'/docs/videos/api/api-url-and-key.mp4'} type="video/mp4" muted playsInline />
<source
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/docs/api/api-url-and-key.mp4"
type="video/mp4"
muted
playsInline
/>
</video>
The REST API and the GraphQL API are both accessible through this URL:
@@ -142,7 +152,12 @@ Let's view the documentation for a `countries` table which we created in our dat
3. Switch between the JavaScript and the cURL docs using the tabs.
<video width="99%" muted playsInline controls="true">
<source src={'/docs/videos/api/api-docs.mp4'} type="video/mp4" muted playsInline />
<source
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/docs/api/api-docs.mp4"
type="video/mp4"
muted
playsInline
/>
</video>
#### GraphQL
@@ -287,7 +302,12 @@ By default Realtime is disabled on your database. Let's turn on Realtime for the
4. Control which tables broadcast changes by selecting **Source** and toggling each table.
<video width="99%" muted playsInline controls="true">
<source src={'/docs/videos/api/api-realtime.mp4'} type="video/mp4" muted playsInline />
<source
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/docs/api/api-realtime.mp4"
type="video/mp4"
muted
playsInline
/>
</video>
</TabPanel>
@@ -295,11 +295,12 @@ export default function SupabaseProvider({ children }: { children: React.ReactNo
}
export const useSupabase = () => {
let context = useContext(Context);
if (context === undefined) {
throw new Error("useSupabase must be used inside SupabaseProvider");
} else {
return context;
}
return useContext(Context)
}
```
+18 -3
View File
@@ -116,7 +116,12 @@ Get started with our [Row Level Security Guides](/docs/guides/auth/row-level-sec
Authentication only gets you so far. When you need granular authorization rules, nothing beats PostgreSQL's [Row Level Security (RLS)](https://www.postgresql.org/docs/current/ddl-rowsecurity.html). Supabase makes it simple to turn RLS on and off.
<video width="99%" muted playsInline controls="true">
<source src="/docs/videos/rls-zoom2.mp4" type="video/mp4" muted playsInline />
<source
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/docs/rls-zoom2.mp4"
type="video/mp4"
muted
playsInline
/>
</video>
### Policies
@@ -124,7 +129,12 @@ Authentication only gets you so far. When you need granular authorization rules,
[Policies](https://www.postgresql.org/docs/current/sql-createpolicy.html) are PostgreSQL's rule engine. They are incredibly powerful and flexible, allowing you to write complex SQL rules which fit your unique business needs.
<video width="99%" muted playsInline controls="true">
<source src="/docs/videos/policies-zoom2.mp4" type="video/mp4" muted playsInline />
<source
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/docs/policies-zoom2.mp4"
type="video/mp4"
muted
playsInline
/>
</video>
With policies, your database becomes the rules engine. Instead of repetitively filtering your queries, like this ...
@@ -172,7 +182,12 @@ Supabase provides multiple endpoints to authenticate and manage your users:
When users sign up, Supabase assigns them a unique ID. You can reference this ID anywhere in your database. For example, you might create a `profiles` table referencing `id` in the `auth.users` table using a `user_id` field.
<video width="99%" muted playsInline controls="true">
<source src="/docs/videos/auth-zoom2.mp4" type="video/mp4" muted playsInline />
<source
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/docs/auth-zoom2.mp4"
type="video/mp4"
muted
playsInline
/>
</video>
export const Page = ({ children }) => <Layout meta={meta} children={children} />
+22 -4
View File
@@ -34,7 +34,10 @@ You don't have to be a database expert to start using Supabase. Our table view m
Dig into the relationships within your data.
<video width="99%" loop="" muted="" playsInline="" controls="true">
<source src="/docs/videos/relational-drilldown-zoom.mp4" type="video/mp4" />
<source
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/docs/relational-drilldown-zoom.mp4"
type="video/mp4"
/>
</video>
### Clone tables
@@ -42,7 +45,12 @@ Dig into the relationships within your data.
You can duplicate your tables, just like you would inside a spreadsheet.
<video width="99%" muted playsInline controls={true}>
<source src="/docs/videos/duplicate-tables.mp4" type="video/mp4" muted playsInline />
<source
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/docs/duplicate-tables.mp4"
type="video/mp4"
muted
playsInline
/>
</video>
### The SQL Editor
@@ -50,7 +58,12 @@ You can duplicate your tables, just like you would inside a spreadsheet.
Supabase comes with a SQL Editor. You can also save your favorite queries to run later!
<video width="99%" muted playsInline controls={true}>
<source src="/docs/videos/favorites.mp4" type="video/mp4" muted playsInline />
<source
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/docs/favorites.mp4"
type="video/mp4"
muted
playsInline
/>
</video>
### Additional features
@@ -74,7 +87,12 @@ To expand the functionality of your Postgres database, you can use extensions.
You can enable Postgres extensions with the click of a button within the Supabase dashboard.
<video width="99%" muted playsInline controls={true}>
<source src="/docs/videos/toggle-extensions.mp4" type="video/mp4" muted playsInline />
<source
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/docs/toggle-extensions.mp4"
type="video/mp4"
muted
playsInline
/>
</video>
[Learn more](/docs/guides/database/extensions) about all the extensions provided on Supabase.
@@ -45,7 +45,12 @@ You cannot manage the database schema via the API (for security reasons). To do
You can find the API URL and Keys in the [Dashboard](https://app.supabase.com/project/_/settings/api).
<video width="99%" muted playsInline controls="true">
<source src="/docs/videos/api/api-url-and-key.mp4" type="video/mp4" muted playsInline />
<source
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/docs/api/api-url-and-key.mp4"
type="video/mp4"
muted
playsInline
/>
</video>
## Direct connections
@@ -59,7 +64,12 @@ Every Supabase project provides a full Postgres database. You can connect to the
3. Find your Connection Info and Connection String. Direct connections are on port `5432`.
<video width="99%" muted playsInline controls="true">
<source src="/docs/videos/postgres-connection.mp4" type="video/mp4" muted playsInline />
<source
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/docs/postgres-connection.mp4"
type="video/mp4"
muted
playsInline
/>
</video>
## Connection Pool
@@ -104,7 +114,12 @@ This is the most granular option. Connections are returned to the pool after eve
3. Find your Connection Info and Connection String. Connection pooling is on port `6543`.
<video width="99%" muted playsInline controls="true">
<source src="/docs/videos/connection-pool-config.mp4" type="video/mp4" muted playsInline />
<source
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/docs/connection-pool-config.mp4"
type="video/mp4"
muted
playsInline
/>
</video>
## Connecting with SSL
@@ -24,7 +24,12 @@ Supabase has pre-installed some of the most useful open source extensions.
3. Enable or disable an extension.
<video width="99%" muted playsInline controls={true}>
<source src="/docs/videos/toggle-extensions.mp4" type="video/mp4" muted playsInline />
<source
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/docs/toggle-extensions.mp4"
type="video/mp4"
muted
playsInline
/>
</video>
</TabPanel>
+22 -4
View File
@@ -34,7 +34,10 @@ You don't have to be a database expert to start using Supabase. Our table view m
Dig into the relationships within your data.
<video width="99%" loop="" muted="" playsInline="" controls="true">
<source src="/docs/videos/relational-drilldown-zoom.mp4" type="video/mp4" />
<source
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/docs/relational-drilldown-zoom.mp4"
type="video/mp4"
/>
</video>
### Clone tables
@@ -42,7 +45,12 @@ Dig into the relationships within your data.
You can duplicate your tables, just like you would inside a spreadsheet.
<video width="99%" muted playsInline controls={true}>
<source src="/docs/videos/duplicate-tables.mp4" type="video/mp4" muted playsInline />
<source
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/docs/duplicate-tables.mp4"
type="video/mp4"
muted
playsInline
/>
</video>
### The SQL Editor
@@ -50,7 +58,12 @@ You can duplicate your tables, just like you would inside a spreadsheet.
Supabase comes with a SQL Editor. You can also save your favorite queries to run later!
<video width="99%" muted playsInline controls={true}>
<source src="/docs/videos/favorites.mp4" type="video/mp4" muted playsInline />
<source
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/docs/favorites.mp4"
type="video/mp4"
muted
playsInline
/>
</video>
### Additional features
@@ -74,7 +87,12 @@ To expand the functionality of your Postgres database, you can use extensions.
You can enable Postgres extensions with the click of a button within the Supabase dashboard.
<video width="99%" muted playsInline controls={true}>
<source src="/docs/videos/toggle-extensions.mp4" type="video/mp4" muted playsInline />
<source
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/docs/toggle-extensions.mp4"
type="video/mp4"
muted
playsInline
/>
</video>
[Learn more](/docs/guides/database/extensions) about all the extensions provided on Supabase.
@@ -21,7 +21,12 @@ Replication is done through _publications_, a method of choosing which changes t
4. Control which tables broadcast changes by selecting **Source** and toggling each table.
<video width="99%" muted playsInline controls="true">
<source src="/docs/videos/api/api-realtime.mp4" type="video/mp4" muted playsInline />
<source
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/docs/api/api-realtime.mp4"
type="video/mp4"
muted
playsInline
/>
</video>
## Create a publication
+6 -1
View File
@@ -42,7 +42,12 @@ and run the SQL queries yourself.
<TabPanel id="dashboard" label="Dashboard">
<video width="99%" muted playsInline controls="true">
<source src="/docs/videos/api/api-create-table-sm.mp4" type="video/mp4" muted playsInline />
<source
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/docs/api/api-create-table-sm.mp4"
type="video/mp4"
muted
playsInline
/>
</video>
1. Go to the [Table Editor](https://app.supabase.com/project/_/editor) page in the Dashboard.
@@ -96,11 +96,11 @@ Receive your database changes through websockets. [Docs](/docs/guides/realtime/p
### User Broadcasting
Send messages between connected users through websockets. [Docs](/docs/guides/realtime#broadcast).
Send messages between connected users through websockets. [Docs](/docs/guides/realtime/broadcast).
### User Presence
Synchronize shared state across your users, including online status and typing indicators. [Docs](/docs/guides/realtime#presence).
Synchronize shared state across your users, including online status and typing indicators. [Docs](/docs/guides/realtime/presence).
### Client libraries
@@ -62,7 +62,6 @@ import {
User,
} from '@supabase/supabase-js'
import { environment } from 'src/environments/environment'
import { Database } from 'src/schema'
export interface Profile {
id?: string
@@ -136,7 +135,7 @@ Let's set up an Angular component to manage logins and sign ups. We'll use Magic
Create an **AuthComponent** with `ng g c auth` Angular CLI command.
```ts title=src/app/auth/auth.component.ts
import { Component, OnInit } from '@angular/core'
import { Component } from '@angular/core'
import { FormBuilder } from '@angular/forms'
import { SupabaseService } from '../supabase.service'
@@ -145,7 +144,7 @@ import { SupabaseService } from '../supabase.service'
templateUrl: './auth.component.html',
styleUrls: ['./auth.component.css'],
})
export class AuthComponent implements OnInit {
export class AuthComponent {
loading = false
signInForm = this.formBuilder.group({
@@ -157,8 +156,6 @@ export class AuthComponent implements OnInit {
private readonly formBuilder: FormBuilder
) {}
ngOnInit(): void {}
async onSubmit(): Promise<void> {
try {
this.loading = true
@@ -406,7 +403,7 @@ Let's create an avatar for the user so that they can upload a profile photo.
Create an **AvatarComponent** with `ng g c avatar` Angular CLI command.
```ts title=src/app/avatar/avatar.component.ts
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'
import { Component, EventEmitter, Input, Output } from '@angular/core'
import { SafeResourceUrl, DomSanitizer } from '@angular/platform-browser'
import { SupabaseService } from '../supabase.service'
@@ -415,7 +412,7 @@ import { SupabaseService } from '../supabase.service'
templateUrl: './avatar.component.html',
styleUrls: ['./avatar.component.css'],
})
export class AvatarComponent implements OnInit {
export class AvatarComponent {
_avatarUrl: SafeResourceUrl | undefined
uploading = false
@@ -430,8 +427,6 @@ export class AvatarComponent implements OnInit {
constructor(private readonly supabase: SupabaseService, private readonly dom: DomSanitizer) {}
ngOnInit(): void {}
async downloadImage(path: string) {
try {
const { data } = await this.supabase.downLoadImage(path)
@@ -129,7 +129,12 @@ or you can just copy/paste the SQL from below and run it yourself.
3. Click **Run**.
<video width="99%" muted playsInline controls="true">
<source src="/docs/videos/sql-user-management-starter.mp4" type="video/mp4" muted playsInline />
<source
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/docs/sql-user-management-starter.mp4"
type="video/mp4"
muted
playsInline
/>
</video>
</TabPanel>
@@ -204,7 +209,12 @@ We just need to get the URL as well as the `anon`, `service_role` and `JWT_SECRE
2. Find your API `URL`, `anon`, and `service_role` keys on this page.
<video width="99%" muted playsInline controls="true">
<source src="/docs/videos/api/api-url-and-key.mp4" type="video/mp4" muted playsInline />
<source
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/docs/api/api-url-and-key.mp4"
type="video/mp4"
muted
playsInline
/>
</video>
## Building the App
@@ -106,7 +106,7 @@ To tell Plasmic to look for your code components on your dev server, you’ll ne
<video width="99%" autoPlay="autoplay" muted playsInline controls={true}>
<source
src="/docs/videos/guides/integrations/plasmic/configure-app-host-plasmic-05.mp4"
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/docs/guides/integrations/plasmic/configure-app-host-plasmic-05.mp4"
type="video/mp4"
muted
playsInline
@@ -123,7 +123,7 @@ Create a new page called `Gallery`, and set a path for this page (`/gallery`).
<video width="99%" autoPlay="autoplay" muted playsInline controls={true}>
<source
src="/docs/videos/guides/integrations/plasmic/create-gallery-page-plasmic-06.mp4"
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/docs/guides/integrations/plasmic/create-gallery-page-plasmic-06.mp4"
type="video/mp4"
muted
playsInline
@@ -156,7 +156,7 @@ For your convenience, the following video shows you how to create the page end-t
<video width="99%" autoPlay="autoplay" muted playsInline controls={true}>
<source
src="/docs/videos/guides/integrations/plasmic/end-to-end-plasmic-10.mp4"
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/docs/guides/integrations/plasmic/end-to-end-plasmic-10.mp4"
type="video/mp4"
muted
playsInline
+4 -34
View File
@@ -9,47 +9,17 @@ export const meta = {
Supabase provides a globally distributed cluster of [Realtime](https://github.com/supabase/realtime) servers that enable the following functionality:
- [Broadcast](#broadcast): Send ephemeral messages from client to clients with low latency.
- [Presence](#presence): Track and synchronize shared state between clients.
- [Postgres Changes](#postgres-changes): Listen to Postgres database changes and send them to authorized clients.
- [Broadcast](/docs/guides/realtime/broadcast): Send ephemeral messages from client to clients with low latency.
- [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.
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.
## Broadcast
Broadcast follows the [publish-subscribe pattern](https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern) where a client publishes messages to a channel with a unique identifier. For example, a user could send a message to a channel with id `room-1`.
Other clients can elect to receive the message in real-time by subscribing to the channel with id `room-1`. If these clients are online and subscribed then they will receive the message.
Broadcast works by connecting your client to the nearest Realtime server, which will communicate with other servers to relay messages to other clients.
A common use-case is sharing a user's cursor position with other clients in an online game.
## Presence
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.
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.
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.
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.
## Postgres Changes
Postgres Changes enable you to listen to database changes and have them broadcast to authorized clients based on [Row Level Security (RLS)](/docs/guides/auth/row-level-security) policies.
This works by Realtime polling your database's logical replication slot for changes, passing those changes to the [apply_rls](https://github.com/supabase/walrus#reading-wal) SQL function to determine which clients have permission, and then using Broadcast to send those changes to clients.
Realtime requires a publication called `supabase_realtime` to determine which tables to poll. You must add tables to this publication prior to clients subscribing to channels that want to listen for database changes.
We strongly encourage you to enable RLS on your database tables and have RLS policies in place to prevent unauthorized parties from accessing your data.
## See Also
- [Realtime quickstart](/docs/guides/realtime/quickstart)
- [Quickstart](/docs/guides/realtime/quickstart)
- [Realtime: Multiplayer Edition](https://supabase.com/blog/supabase-realtime-multiplayer-general-availability) blog post
export const Page = ({ children }) => <Layout meta={meta} children={children} />
@@ -0,0 +1,135 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'broadcast',
title: 'Broadcast',
description: "Getting started with Realtime's Broadcast feature",
}
Broadcast follows the [publish-subscribe pattern](https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern) where a client publishes messages to a channel with a unique identifier. For example, a user could send a message to a channel with id `room-1`.
Other clients can elect to receive the message in real-time by subscribing to the channel with id `room-1`. If these clients are online and subscribed then they will receive the message.
Broadcast works by connecting your client to the nearest Realtime server, which will communicate with other servers to relay messages to other clients.
A common use-case is sharing a user's cursor position with other clients in an online game.
## Listen to Messages
You can get started with Broadcast by creating a client and listening to a channel's messages:
```js
const { createClient } = require('@supabase/supabase-js')
const supabase = createClient(process.env.SUPABASE_URL, process.env.SUPABASE_KEY)
supabase
.channel('test')
.on('broadcast', { event: 'supa' }, (payload) => console.log(payload))
.subscribe()
```
## Send Messages
You can create another client and send messages to other clients:
```js
const { createClient } = require('@supabase/supabase-js')
const supabase = createClient(process.env.SUPABASE_URL, process.env.SUPABASE_KEY)
supabase.channel('test').subscribe((status) => {
if (status === 'SUBSCRIBED') {
channel.send({
type: 'broadcast',
event: 'supa',
payload: { org: 'supabase' },
})
}
})
```
In order for clients to successfully send and receive mesages to one another, they must both specify the same `event`.
We recommend that the client has successfully subscribed to the channel prior to sending messages.
### Self-Send Messages
You can also choose for a client to receive messages that it sent:
```js
// Supabase client setup
supabase
.channel('test', {
config: {
broadcast: {
self: true,
},
},
})
.on('broadcast', { event: 'supa' }, (payload) => console.log(payload))
.subscribe((status) => {
if (status === 'SUBSCRIBED') {
channel.send({
type: 'broadcast',
event: 'supa',
payload: { org: 'supabase' },
})
}
})
```
### Acknowledge Messages
You can ensure that Realtime's servers received your message by:
```js
// Supabase client setup
const channel = supabase.channel('receipt', {
config: {
broadcast: { ack: true },
},
})
channel.subscribe(async (status) => {
if (status === 'SUBSCRIBED') {
const resp = await channel.send({
type: 'broadcast',
event: 'latency',
payload: {},
})
console.log(resp)
}
})
```
If `ack` is not set to `true`, Realtime servers will not acknowledge that it received the sent message and `send` promise resolves immediately.
## Client-Side Rate Limit
There is a default client-side rate limit that enables you to send 10 messages per second, or one message every 100 milliseconds. You can customize this when creating the client:
```js
const { createClient } = require('@supabase/supabase-js')
const supabase = createClient(
process.env.SUPABASE_URL,
process.env.SUPABASE_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 [Rate Limits](/docs/guides/realtime/rate-limits) section.
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -19,11 +19,12 @@ GRANT SELECT ON "private_schema"."table" TO authenticated;
```
<Admonition type="caution">
We strongly encourage you to enable RLS and create policies for tables in private schemas. Otherwise, any role you grant access to will have unfettered read access to the table.
We strongly encourage you to enable RLS and create policies for tables in private schemas.
Otherwise, any role you grant access to will have unfettered read access to the table.
</Admonition>
## Replication Setup
You can do this in the [Replication](https://app.supabase.com/project/_/database/replication) section in the Dashboard or with the [SQL editor](https://app.supabase.com/project/_/sql):
```sql
@@ -39,6 +40,8 @@ commit;
alter publication supabase_realtime add table messages;
```
### Full `old` Record
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`:
@@ -46,6 +49,8 @@ you can set the `replica identity` of your table to `full`:
alter table messages replica identity full;
```
## Schema Changes
To listen to all changes in the `public` schema:
```js
@@ -74,6 +79,8 @@ const channel = supabase
.subscribe()
```
## Table Changes
To listen to changes on a table in the `public` schema:
```js
@@ -93,6 +100,8 @@ const channel = supabase
.subscribe()
```
## Filter Changes
To listen to changes when a column's value in a table matches a client-specified value:
```js
@@ -113,6 +122,8 @@ const channel = supabase
.subscribe()
```
## Combination Changes
To listen to different events and schema/tables/filters combinations with the same channel:
```js
@@ -130,7 +141,8 @@ const channel = supabase
},
(payload) => console.log(payload)
)
.on('postgres_changes',
.on(
'postgres_changes',
{
event: 'INSERT',
schema: 'public',
@@ -141,6 +153,38 @@ const channel = supabase
.subscribe()
```
## Custom Tokens
You may choose to sign your own tokens to customize claims that can be checked in your RLS policies.
In order for this to work you must pass `apikey` in both Realtime's `headers` and `params` when creating the client.
The `apikey` in `headers` must be either the `anon` or `service_role` token that Supabase signed.
This will authenticate your request in the API gateway.
<Admonition type="caution">
Do not expose the `service_role` token on the client because the role is authorized to bypass
row-level security.
</Admonition>
The `apikey` in `params` can be your custom token signed with the JWT secret of your Supabase project.
This is forwarded to the Realtime server and it will verify your custom token and use its claims to authorize database changes
when RLS is enabled.
```js
const { createClient } = require('@supabase/supabase-js')
const supabase = createClient(process.env.SUPABASE_URL, process.env.SUPABASE_KEY, {
realtime: {
headers: {
apikey: 'supabase-signed-token',
},
params: {
apikey: 'your-custom-token',
},
},
})
```
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -0,0 +1,165 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'presence',
title: 'Presence',
description: "Getting started with Realtime's Presence feature",
}
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.
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.
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.
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.
## Presence State
You can get started by listening to `sync` event messages notifying the client that a channel's state has been synchronized on the server. You can get the state by calling the channel's `presenceState` helper:
```js
const { createClient } = require('@supabase/supabase-js')
const supabase = createClient(process.env.SUPABASE_URL, process.env.SUPABASE_KEY)
const channel = supabase.channel('test')
channel
.on('presence', { event: 'sync' }, () => {
const state = channel.presenceState()
console.log(state)
})
.subscribe()
```
Whenever there's Presence activity on the `'test'` channel, this `sync` event will be broadcast to all clients subscribed to the channel.
## Listen to Joins
You can create a client and listen to new state joining the channel's Presence:
```js
// Supabase client setup
const channel = supabase.channel('test')
channel
.on('presence', { event: 'join' }, ({ key, newPresences }) => {
console.log(key, newPresences)
})
.subscribe()
```
## Track Presence
On another client, subscribe to the channel and insert state to be tracked by Presence:
```js
// Supabase client setup
const channel = supabase.channel('test')
channel.subscribe(async (status) => {
if (status === 'SUBSCRIBED') {
const presenceTrackStatus = await channel.track({
user: 'user-1',
online_at: new Date().toISOString(),
})
console.log(presenceTrackStatus)
}
})
```
### Presence Key
By default, Presence will generate an `UUIDv1` key on the server to uniquely track a client channel's state but you may pass Presence a custom key when creating the channel.
```js
// Supabase client setup
const channel = supabase.channel('test', {
config: {
presence: {
key: 'userId-1',
},
},
})
channel.subscribe(async (status) => {
if (status === 'SUBSCRIBED') {
const presenceTrackStatus = await channel.track({
user: 'user-1',
online_at: new Date().toISOString(),
})
console.log(presenceTrackStatus)
}
})
```
## Listen to Leaves
You can create a client and listen to a client channel's state leaving:
```js
// Supabase client setup
const channel = supabase.channel('test')
channel
.on('presence', { event: 'leave' }, ({ key, leftPresences }) => {
console.log(key, leftPresences)
})
.subscribe()
```
## Untrack Presence
On another client, subscribe to the channel, and remove tracked state from Presence:
```js
// Supabase client setup
const channel = supabase.channel('test')
channel.subscribe(async (status) => {
if (status === 'SUBSCRIBED') {
const presenceTrackStatus = await channel.track({
user: 'user-1',
online_at: new Date().toISOString(),
})
if (presenceTrackStatus === 'ok') {
const presenceUntrackStatus = await channel.untrack()
console.log(presenceUntrackStatus)
}
}
})
```
## Client-Side Rate Limit
There is a default client-side rate limit that enables you to send 10 messages per second, or one message every 100 milliseconds. You can customize this when creating the client:
```js
const { createClient } = require('@supabase/supabase-js')
const supabase = createClient(
process.env.SUPABASE_URL,
process.env.SUPABASE_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 [Rate Limits](/docs/guides/realtime/rate-limits) section.
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -27,7 +27,7 @@ npm install @supabase/supabase-js
## Cursor Positions
[Broadcast](/docs/guides/realtime#broadcast) allows a client to send messages and multiple clients to receive the messages. The broadcasted messages are ephemeral. They are not persisted to the database and are directly relayed through the Realtime servers. This is ideal for sending information like cursor positions where minimal latency is important, but persisting them is not.
[Broadcast](/docs/guides/realtime/broadcast) allows a client to send messages and multiple clients to receive the messages. The broadcasted messages are ephemeral. They are not persisted to the database and are directly relayed through the Realtime servers. This is ideal for sending information like cursor positions where minimal latency is important, but persisting them is not.
In [multiplayer.dev](https://multiplayer.dev), client's cursor positions are sent to other clients in the room. However, cursor positions will be randomly generated for this example.
@@ -124,7 +124,7 @@ channel.subscribe(async (status) => {
## Track and Display Which Users Are Online
[Presence](/docs/guides/realtime#presence) stores and synchronize shared state across clients. The `sync` event is triggered whenever the shared state changes. The `join` event is triggered when new clients join the channel and `leave` event is triggered when clients leave.
[Presence](/docs/guides/realtime/presence) stores and synchronize shared state across clients. The `sync` event is triggered whenever the shared state changes. The `join` event is triggered when new clients join the channel and `leave` event is triggered when clients leave.
Each client can use the channel's `track` method to store an object in shared state. Each client can only track one object, and if `track` is called again by the same client, then the new object overwrites the previously tracked object in the shared state. You can use one client to track and display users who are online:
+56 -9
View File
@@ -3,10 +3,8 @@ import Link from 'next/link'
import { GlassPanel, IconPanel, Button, IconChevronRight } from 'ui'
export const meta = {
id: 'resources',
title: 'Resources',
description: 'Resources for getting started building with Supabase.',
sidebar_label: 'Overview',
}
<div className="flex flex-col gap-12 my-12">
@@ -64,18 +62,46 @@ export const meta = {
</div>
<div>
<div className="max-w-xl mb-6">
### Postgres Resources
</div>
<div className="grid grid-cols-12 gap-6 not-prose">
{postgres.map((resource) => {
return (
<Link
href={`${resource.href}`}
key={resource.title}
passHref
>
<a className={'col-span-12 md:col-span-4'}>
<GlassPanel {...resource} background={false} showIconBg={true}>
{resource.description}
</GlassPanel>
</a>
</Link>
)
})}
</div>
</div>
{/* end of container */}
</div>
export const resources = [
// removing until the examples page is reworked
// {
// title: 'Examples',
// hasLightIcon: true,
// href: '/guides/resources/examples',
// description: 'Official GitHub examples, curated content from the community, and more.',
// },
{
title: 'Examples',
hasLightIcon: true,
href: '/guides/resources/examples',
description: 'Official GitHub examples, curated content from the community, and more.',
},
{
title: 'Glossary',
hasLightIcon: true,
@@ -111,6 +137,27 @@ export const migrationGuides = [
},
]
export const postgres = [
{
title: 'Drop all tables in schema',
hasLightIcon: true,
href: '/guides/resources/postgres/dropping-all-tables-in-schema',
description: 'Delete all tables in a given schema.',
},
{
title: 'Select first row per group',
hasLightIcon: true,
href: '/guides/resources/postgres/first-row-in-group',
description: 'Retreive the first row in each distinct group.',
},
{
title: 'Print PostgreSQL version',
hasLightIcon: true,
href: '/guides/resources/postgres/which-version-of-postgres',
description: 'Find out which version of Postgres you are running.',
},
]
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -0,0 +1,34 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
title: 'Drop all tables in a PostgreSQL schema',
description: 'Useful snippet for deleting all tables in a given schema',
footerHelpType: 'postgres',
}
Execute the following query to drop all tables in a given schema.
Replace `my-schema-name` with the name of your schema. In Supabase, the default schema is `public`.
<Admonition type="caution">
This deletes all tables and their associated data. Ensure you have a recent [backup](/docs/guides/platform/backups) before proceeding.
</Admonition>
```sql
do $$ declare
r record;
begin
for r in (select tablename from pg_tables where schemaname = 'my-schema-name') loop
execute 'drop table if exists ' || quote_ident(r.tablename) || ' cascade';
end loop;
end $$;
```
This query works by listing out all the tables in the given schema and then executing a `drop table` for each (hence the `for... loop`).
You can run this query using the [SQL Editor](https://app.supabase.com/project/_/sql) in the Supabase Dashboard, or via `psql` if you're [connecting directly to the database](/docs/guides/database/connecting-to-postgres#direct-connections).
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -0,0 +1,52 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
title: 'Select first row for each group in PostgreSQL',
description: 'PostgreSQL snippet for grabbing the first row in each distinct group by group',
footerHelpType: 'postgres',
}
Given a table `seasons`:
| id | team | points |
| --- | :-------: | -----: |
| 1 | Liverpool | 82 |
| 2 | Liverpool | 84 |
| 3 | Brighton | 34 |
| 4 | Brighton | 28 |
| 5 | Liverpool | 79 |
We want to find the rows containing the maximum number of points _per team_.
The expected output we want is:
| id | team | points |
| --- | :-------: | -----: |
| 3 | Brighton | 34 |
| 2 | Liverpool | 84 |
From the [SQL Editor](https://app.supabase.com/project/_/sql), you can run a query like:
```sql
select distinct
on (team) id,
team,
points
from
seasons
order BY
id,
points desc,
team;
```
The important bits here are:
- The `desc` keyword to order the `points` from highest to lowest.
- The `distinct` keyword that tells Postgres to only return a single row per team.
This query can also be executed via `psql` or any other query editor if you prefer to [connect directly to the database](/docs/guides/database/connecting-to-postgres#direct-connections).
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -0,0 +1,28 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
title: 'Print PostgreSQL version',
description: 'Useful snippet for finding out which version of postgres you are running',
footerHelpType: 'postgres',
}
It's important to know which version of PostgreSQL you are running as each major version has different features and may cause breaking changes. You may also need to update your schema when [upgrading](https://www.postgresql.org/docs/current/pgupgrade.html) or downgrading to a major Postgres version.
Run the following query using the [SQL Editor](https://app.supabase.com/project/_/sql) in the Supabase Dashboard:
```sql
select
version ();
```
Which should return something like:
```sql
PostgreSQL 15.1 on aarch64-unknown-linux-gnu, compiled by gcc (Ubuntu 10.3.0-1ubuntu1~20.04) 10.3.0, 64-bit
```
This query can also be executed via `psql` or any other query editor if you prefer to [connect directly to the database](/docs/guides/database/connecting-to-postgres#direct-connections).
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -41,7 +41,10 @@ We've made some massive improvements to our Table Editor that we're excited to s
Last month we made it easy to drill into your table relationships. This month, we make it possible to drill multiple levels deep.
<video width="99%" autoPlay="" loop="" muted="" playsInline="" controls="true">
<source src="/videos/relational-drilldown-zoom.mp4" type="video/mp4" />
<source
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/docs/relational-drilldown-zoom.mp4"
type="video/mp4"
/>
</video>
#### Add, delete, and download rows
@@ -49,7 +52,10 @@ Last month we made it easy to drill into your table relationships. This month, w
We're making it easier to manipulate your data. Next month, you'll be able to add and remove columns directly from the Table view.
<video width="99%" autoPlay="" loop="" muted="" playsInline="" controls="true">
<source src="/videos/csv-download-zoom.mp4" type="video/mp4" />
<source
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/docs/csv-download-zoom.mp4"
type="video/mp4"
/>
</video>
### New Postgres Extensions
+21 -3
View File
@@ -37,7 +37,13 @@ Supabase Auth provides all the backend services you need to authenticate and aut
Supabase makes it simple to onboard your users with our new `supabase.auth.signUp()` and `supabase.auth.signIn()` [functions](/docs/guides/auth).
<video width="99%" autoPlay="autoplay" loop muted playsInline controls="true">
<source src="/docs/videos/auth-zoom2.mp4" type="video/mp4" loop muted playsInline />
<source
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/docs/auth-zoom2.mp4"
type="video/mp4"
loop
muted
playsInline
/>
</video>
### Row Level Security
@@ -45,7 +51,13 @@ Supabase makes it simple to onboard your users with our new `supabase.auth.signU
Authentication only gets you so far. When you need granular authorization rules, nothing beats PostgreSQL's [Row Level Security](https://www.postgresql.org/docs/current/ddl-rowsecurity.html). Supabase makes it simple to turn RLS on and off.
<video width="99%" autoPlay="autoplay" loop muted playsInline controls="true">
<source src="/docs/videos/rls-zoom2.mp4" type="video/mp4" loop muted playsInline />
<source
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/docs/rls-zoom2.mp4"
type="video/mp4"
loop
muted
playsInline
/>
</video>
### Policies
@@ -53,7 +65,13 @@ Authentication only gets you so far. When you need granular authorization rules,
[Policies](https://www.postgresql.org/docs/current/sql-createpolicy.html) are PostgreSQL's rule engine. They are incredibly powerful and flexible, allowing you to write complex SQL rules which fit your unique business needs.
<video width="99%" autoPlay="autoplay" loop muted playsInline controls="true">
<source src="/docs/videos/policies-zoom2.mp4" type="video/mp4" loop muted playsInline />
<source
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/docs/policies-zoom2.mp4"
type="video/mp4"
loop
muted
playsInline
/>
</video>
With policies, your database becomes the rules engine. Instead of repetitively filtering your queries, like this ...
@@ -31,7 +31,13 @@ Watch a full demo:
Set up tables and columns directly from the table view.
<video width="99%" autoPlay="autoplay" loop muted playsInline controls="true">
<source src="/videos/new-tables.mp4" type="video/mp4" loop muted playsInline />
<source
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/docs/new-tables.mp4"
type="video/mp4"
loop
muted
playsInline
/>
</video>
### Invite your team
@@ -39,7 +45,13 @@ Set up tables and columns directly from the table view.
You can now invite team members to your organisation.
<video width="99%" autoPlay="autoplay" loop muted playsInline controls="true">
<source src="/videos/invite-team.mp4" type="video/mp4" loop muted playsInline />
<source
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/docs/invite-team.mp4"
type="video/mp4"
loop
muted
playsInline
/>
</video>
### Auth: Email Confirmations
@@ -47,7 +59,13 @@ You can now invite team members to your organisation.
You can now enable Email Confirmations for new users. This can be toggled on or off and the template for this email can be edited via the dashboard.
<video width="99%" autoPlay="autoplay" loop muted playsInline controls="true">
<source src="/videos/confirm-email.mp4" type="video/mp4" loop muted playsInline />
<source
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/docs/confirm-email.mp4"
type="video/mp4"
loop
muted
playsInline
/>
</video>
### TypeScript support
@@ -38,7 +38,12 @@ We've released OAuth logins! You can now enable third-party logins on your app f
You can duplicate your tables, just like you would inside a spreadsheet.
<video width="99%" autoPlay="autoplay" muted playsInline controls={true}>
<source src="/videos/duplicate-tables.mp4" type="video/mp4" muted playsInline />
<source
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/docs/duplicate-tables.mp4"
type="video/mp4"
muted
playsInline
/>
</video>
### Enable and disable extensions
@@ -46,7 +51,12 @@ You can duplicate your tables, just like you would inside a spreadsheet.
Extensions are easier to use. You can enable Postgres extensions with the click of a button.
<video width="99%" autoPlay="autoplay" muted playsInline controls={true}>
<source src="/videos/toggle-extensions.mp4" type="video/mp4" muted playsInline />
<source
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/docs/toggle-extensions.mp4"
type="video/mp4"
muted
playsInline
/>
</video>
### Save your favorite queries
@@ -54,7 +64,12 @@ Extensions are easier to use. You can enable Postgres extensions with the click
The SQL editor now stores your query history in your browser. You can also save your favorite queries to run later!
<video width="99%" autoPlay="autoplay" muted playsInline controls={true}>
<source src="/videos/favorites.mp4" type="video/mp4" muted playsInline />
<source
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/docs/favorites.mp4"
type="video/mp4"
muted
playsInline
/>
</video>
### GitHub Discussions
@@ -74,7 +74,12 @@ Thanks to some skilled haxors (well, weak passwords mainly) we had crypto miners
The spreadsheet import is triggered when the user is creating a new table. However the code was previously loaded every time the page was visited - even when a new table was not being created. This made it a good candidate for lazy loading. Using [Next.js dynamic imports](https://nextjs.org/docs/advanced-features/dynamic-import) we are able to load this component (313 kb brotlied) dynamically, whenever the user clicks the "Add content" button.
<video width="99%" muted playsInline controls="true">
<source src="/videos/lazy_loading.mov" type="video/mp4" muted playsInline />
<source
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/docs/lazy_loading.mov"
type="video/mp4"
muted
playsInline
/>
</video>
We use the same technique to lazy load some Lottie animations which are relatively large.
@@ -41,7 +41,12 @@ We spent months working on Performance, Security, and Reliability. Read more on
Add comments and descriptions to your Tables directly from our auto-generated docs. Descriptions are stored as PostgreSQL comments (https://postgresql.org/docs/current/sql-comment.html), and are exposed over your OpenAPI spec.
<video width="99%" autoPlay="autoplay" muted playsInline controls={true}>
<source src="/videos/update-docs.mp4" type="video/mp4" muted playsInline />
<source
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/docs/update-docs.mp4"
type="video/mp4"
muted
playsInline
/>
</video>
### Table View now has realtime changes
@@ -49,7 +54,12 @@ Add comments and descriptions to your Tables directly from our auto-generated do
Any updates that happen to your database are reflected in the Table View immediately.
<video width="99%" autoPlay="autoplay" muted playsInline controls={true}>
<source src="/videos/realtime-updates.mp4" type="video/mp4" muted playsInline />
<source
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/docs/realtime-updates.mp4"
type="video/mp4"
muted
playsInline
/>
</video>
### Table Pagination
@@ -57,7 +67,12 @@ Any updates that happen to your database are reflected in the Table View immedia
Our table view now has pagination - better for working with large data sets.
<video width="99%" autoPlay="autoplay" muted playsInline controls={true}>
<source src="/videos/table-pagination.mp4" type="video/mp4" muted playsInline />
<source
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/docs/table-pagination.mp4"
type="video/mp4"
muted
playsInline
/>
</video>
### Supabase raised a Seed Round
+3 -3
View File
@@ -21,7 +21,7 @@ Clients can use the `Content-Range` HTTP header to request specific parts of a f
<video width="99%" autoPlay="autoplay" muted playsInline controls={true}>
<source
src="/videos/blog/launch-week-storage-2/nyan-cat.mp4"
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/marketing/blog/launch-week-storage-2/nyan-cat.mp4"
type="video/mp4"
muted
playsInline
@@ -50,7 +50,7 @@ You can now upload an entire directory to Supabase Storage and the directory str
<video width="99%" autoPlay="autoplay" muted playsInline controls={true}>
<source
src="/videos/blog/launch-week-storage-2/directory-uploads.mp4"
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/marketing/blog/launch-week-storage-2/directory-uploads.mp4"
type="video/mp4"
muted
playsInline
@@ -121,7 +121,7 @@ Here is a demo testing rendering the heaviest object known to mankind - the `nod
<video width="99%" autoPlay="autoplay" muted playsInline controls={true}>
<source
src="/videos/blog/launch-week-storage-2/large-buckets.mp4"
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/marketing/blog/launch-week-storage-2/large-buckets.mp4"
type="video/mp4"
muted
playsInline
@@ -225,7 +225,7 @@ To enable GraphQL in your Supabase instance, enable `pg_graphql` from the dashbo
<video width="99%" autoPlay="autoplay" muted playsInline controls={true}>
<source
src="/videos/blog/launch-week-4/api-enable-graphql.mp4"
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/marketing/blog/launch-week-4/api-enable-graphql.mp4"
type="video/mp4"
muted
playsInline
@@ -124,7 +124,12 @@ Some features are too good to limit to large customers, so today we're introduci
### Spend caps
<video width="99%" autoPlay="autoplay" muted playsInline controls={true}>
<source src="/videos/blog/launch-week-4/spend-caps.mp4" type="video/mp4" muted playsInline />
<source
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/marketing/blog/launch-week-4/spend-caps.mp4"
type="video/mp4"
muted
playsInline
/>
</video>
To simplify pricing, we've merged the “Pro” and “Pay as you go” tiers and introduced monthly spend caps to avoid nasty billing surprises.
@@ -136,7 +141,12 @@ We're also retaining our soft limits while we manage the transition to granular
### Database Add-ons
<video width="99%" autoPlay="autoplay" muted playsInline controls={true}>
<source src="/videos/blog/launch-week-4/database-addons.mp4" type="video/mp4" muted playsInline />
<source
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/marketing/blog/launch-week-4/database-addons.mp4"
type="video/mp4"
muted
playsInline
/>
</video>
Today we're releasing self-serve Database Add-ons.
@@ -149,7 +159,12 @@ Today, Database Add-ons are available for a small set of customers. We will prog
### New Log Explorer
<video width="99%" autoPlay="autoplay" muted playsInline controls={true}>
<source src="/videos/blog/launch-week-4/logexplorer.mp4" type="video/mp4" muted playsInline />
<source
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/marketing/blog/launch-week-4/logexplorer.mp4"
type="video/mp4"
muted
playsInline
/>
</video>
Today we're releasing a brand new [Log Explorer](/docs/guides/platform/logs) in the Supabase Dashboard.
@@ -81,7 +81,7 @@ We've [extended the Supabase CLI](https://supabase.com/docs/guides/local-develop
<video width="99%" autoPlay="autoplay" muted playsInline controls={true}>
<source
src="/videos/blog/launch-week-4/functions-hot-reloading.mp4"
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/marketing/blog/launch-week-4/functions-hot-reloading.mp4"
type="video/mp4"
muted
playsInline
@@ -91,7 +91,12 @@ We've [extended the Supabase CLI](https://supabase.com/docs/guides/local-develop
Once you've tested your Function locally (or gone straight to production cos you live _on the edge_ ... get it?) it takes just seconds to deploy a new function and see the logs streaming in your Dashboard—ensuring a tight iteration loop with your production environment.
<video width="99%" autoPlay="autoplay" muted playsInline controls={true}>
<source src="/videos/blog/launch-week-4/logs-polish.mov" type="video/mp4" muted playsInline />
<source
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/marketing/blog/launch-week-4/logs-polish.mov"
type="video/mp4"
muted
playsInline
/>
</video>
Our observability pipeline has been built [using Logflare](https://supabase.com/blog/supabase-acquires-logflare). Structured logs from your Edge Functions get sent to Logflare, which also processes them into metrics. Your Supabase Dashboard is able to reach out to Logflare endpoints, and with some of the [(not so) secret sauce](https://supabase.com/blog/supabase-studio) from our front-end team, you get responsive charts and logs for your Edge Functions.
@@ -1351,7 +1351,7 @@ We used bloc for our state management solution. One thing we could have done dif
We could also explore some cool feature improvement.
At the top of the rooms page, we are loading the newest created users to start a conversation. This is fine, but it only allows users to start a conversation with new users.
We can for example update this to a list of users that are online at the same time. We can implement this using the [presence feature](https://supabase.com/docs/guides/realtime#presence) of Supabase.
We can for example update this to a list of users that are online at the same time. We can implement this using the [presence feature](https://supabase.com/docs/guides/realtime/presence) of Supabase.
## More Flutter Resources
+2 -2
View File
@@ -11,7 +11,7 @@
"label": "SQL Editor",
"title": "In-built SQL editor for when you need greater control",
"text": "Write, save, and execute SQL queries directly on our dashboard, with templates to save you time. Run common queries and even build applications using our growing list of templates.",
"video_url": "videos/tabSqlEditor.mp4",
"video_url": "https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/marketing/tabSqlEditor.mp4",
"youtube_id": "Hch1mZPZ53A",
"cta": "Explore SQL Editor",
"url": "/database"
@@ -20,7 +20,7 @@
"label": "Auth rules",
"title": "User management as straight-forward as it can be",
"text": "Easily manage your users with Supabase Auth, with email logins, magic links, and third-party logins. Create complex access policies with SQL rules to fit your unique business needs.",
"video_url": "videos/tabAuthRules.mp4",
"video_url": "https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/marketing/tabAuthRules.mp4",
"youtube_id": "vP319FCIZ6Y",
"cta": "Explore Auth",
"url": "/auth"
+16 -11
View File
@@ -1220,13 +1220,13 @@ module.exports = [
},
{
permanent: true,
source: '/docs/guides/realtime/broadcast',
destination: '/docs/guides/realtime#broadcast',
source: '/docs/guides/realtime#broadcast',
destination: '/docs/guides/realtime/broadcast',
},
{
permanent: true,
source: '/docs/guides/realtime/presence',
destination: '/docs/guides/realtime#presence',
source: '/docs/guides/realtime#presence',
destination: '/docs/guides/realtime/presence',
},
{
permanent: true,
@@ -1780,37 +1780,37 @@ module.exports = [
{
permanent: true,
source: '/docs/reference/javascript',
destination: '/docs/reference/javascript/introduction',
destination: '/docs/reference/javascript/start',
},
{
permanent: true,
source: '/docs/reference/dart',
destination: '/docs/reference/dart/introduction',
destination: '/docs/reference/dart/start',
},
{
permanent: true,
source: '/docs/reference/cli',
destination: '/docs/reference/cli/introduction',
destination: '/docs/reference/cli/start',
},
{
permanent: true,
source: '/docs/reference/api',
destination: '/docs/reference/api/introduction',
destination: '/docs/reference/api/start',
},
{
permanent: true,
source: '/docs/reference/auth',
destination: '/docs/reference/auth/introduction',
destination: '/docs/reference/self-hosting-auth/start',
},
{
permanent: true,
source: '/docs/reference/storage',
destination: '/docs/reference/storage/introduction',
destination: '/docs/reference/self-hosting-storage/start',
},
{
permanent: true,
source: '/docs/reference/realtime',
destination: '/docs/reference/realtime/introduction',
destination: '/docs/reference/self-hosting-realtime/start',
},
{
permanent: true,
@@ -1837,4 +1837,9 @@ module.exports = [
source: '/docs/reference/dart/upgrade-guide',
destination: '/docs/reference/dart/v0/upgrade-guide',
},
{
permanent: true,
source: '/docs/guides/examples',
destination: '/docs/guides/resources/examples',
},
]
+74 -75
View File
@@ -28,7 +28,7 @@ const NavFlyOutMenu = (props: any) => {
return (
<>
<div className="border-dark-300 dark:border-dark-400 col-span-12 grid grid-cols-12 items-center border-b border-dashed sm:border-r">
<div className="grid items-center grid-cols-12 col-span-12 border-b border-dashed border-dark-300 dark:border-dark-400 sm:border-r">
{segments.flat().map((segment: any) => (
<div
key={segment.key}
@@ -38,7 +38,7 @@ const NavFlyOutMenu = (props: any) => {
bg-white px-4 py-4 sm:border-l lg:col-span-4 lg:px-6 lg:py-10`}
>
<p className="flex items-center text-black dark:text-white">
<span className="text-dark-300 dark:text-dark-400 mr-2 font-mono text-xs">
<span className="mr-2 font-mono text-xs text-dark-300 dark:text-dark-400">
0{segment.no}
</span>
<span>{segment.name}</span>
@@ -53,12 +53,12 @@ const NavFlyOutMenu = (props: any) => {
const VideoShot = (props: any) => {
const { src } = props
return (
<div className="rounded-xl bg-gradient-to-r from-green-400 via-violet-500 to-blue-500 p-2">
<div className="p-2 rounded-xl bg-gradient-to-r from-green-400 via-violet-500 to-blue-500">
<div className="rounded-md shadow-lg" style={{ height: 'fit-content' }}>
<div className="bg-dark-400 flex h-5 w-full items-center justify-start rounded-t-md px-2">
<div className="bg-dark-500 mr-2 h-2 w-2 rounded-full" />
<div className="bg-dark-500 mr-2 h-2 w-2 rounded-full" />
<div className="bg-dark-500 mr-2 h-2 w-2 rounded-full" />
<div className="flex items-center justify-start w-full h-5 px-2 bg-dark-400 rounded-t-md">
<div className="w-2 h-2 mr-2 rounded-full bg-dark-500" />
<div className="w-2 h-2 mr-2 rounded-full bg-dark-500" />
<div className="w-2 h-2 mr-2 rounded-full bg-dark-500" />
</div>
<video className="rounded-b-md" src={src} autoPlay loop muted playsInline>
Your browser does not support the video tag
@@ -94,7 +94,7 @@ const SectionHeader = (props: any) => {
const { sectionNumber, header } = props
return (
<div className="col-span-12">
<span className="text-scale-1200 mb-3 block font-mono text-3xl">0{sectionNumber}</span>
<span className="block mb-3 font-mono text-3xl text-scale-1200">0{sectionNumber}</span>
<h3 className="text-6xl text-black dark:text-white">{header}</h3>
</div>
)
@@ -112,10 +112,10 @@ const Hero = (props: Props) => {
backgroundSize: '65%',
backgroundPosition: '120% 50%',
}}
className="dark:bg-scale-300 bg-gray-50 bg-no-repeat py-16 lg:py-36"
className="py-16 bg-no-repeat dark:bg-scale-300 bg-gray-50 lg:py-36"
>
<div className="text-dark-300 container mx-auto grid h-full grid-cols-12 items-center gap-4 px-8 py-20 lg:px-28">
<div className="text-scale-900 col-span-12 dark:text-white md:col-span-9 lg:col-span-8 xl:col-span-6">
<div className="container grid items-center h-full grid-cols-12 gap-4 px-8 py-20 mx-auto text-dark-300 lg:px-28">
<div className="col-span-12 text-scale-900 dark:text-white md:col-span-9 lg:col-span-8 xl:col-span-6">
<p className="mb-10 text-4xl">Supabase is an open source Firebase alternative.</p>
<p className="text-2xl">
Today, we're moving to <span className="text-brand-900">Beta</span>
@@ -131,10 +131,10 @@ const Hero = (props: Props) => {
const Introduction = () => {
return (
<div className="text-dark-300 container mx-auto grid grid-cols-12 gap-4 px-8 py-20 lg:px-28">
<div className="container grid grid-cols-12 gap-4 px-8 py-20 mx-auto text-dark-300 lg:px-28">
<div className="col-span-12 sm:col-span-9 xl:col-span-8">
<p>
<span className="text-scale-1200 block text-2xl">
<span className="block text-2xl text-scale-1200">
After the launch of our{' '}
<a
href="https://news.ycombinator.com/item?id=23319901"
@@ -147,7 +147,7 @@ const Introduction = () => {
</span>
</p>
</div>
<div className="text-scale-1200 col-span-12 mb-20 text-base sm:col-span-9 xl:col-span-6">
<div className="col-span-12 mb-20 text-base text-scale-1200 sm:col-span-9 xl:col-span-6">
<p>
we've been fortunate to work with thousands of early adopters on improving both our Open
Source, and Hosted offerings. Companies like{' '}
@@ -179,35 +179,35 @@ const Introduction = () => {
</div>
<div className="col-span-12 mb-10 text-base">
<p className="border-dark-200 text-scale-1100 dark:border-dark-400 w-60 border-b-2 pb-2">
<p className="pb-2 border-b-2 border-dark-200 text-scale-1100 dark:border-dark-400 w-60">
Alpha Program in Numbers
</p>
</div>
<div
id="alphaNumbers"
className="col-span-12 mb-20 grid grid-cols-12 gap-y-12 gap-x-3 sm:gap-x-0 lg:gap-y-20"
className="grid grid-cols-12 col-span-12 mb-20 gap-y-12 gap-x-3 sm:gap-x-0 lg:gap-y-20"
>
{AlphaNumbers.map((stat: any, idx: number) => (
<div
key={`stat_${idx}`}
className="col-span-6 grid grid-cols-8 items-center gap-x-1 sm:col-span-4 sm:grid-cols-12 md:gap-x-0 xl:gap-x-6"
className="grid items-center grid-cols-8 col-span-6 gap-x-1 sm:col-span-4 sm:grid-cols-12 md:gap-x-0 xl:gap-x-6"
>
<div className="col-span-4 sm:col-span-4 md:col-span-3 xl:col-span-2">
<div className="bg-scale-1200 flex h-12 w-12 items-center justify-center rounded-md dark:bg-white">
<div className="flex items-center justify-center w-12 h-12 rounded-md bg-scale-1200 dark:bg-white">
{stat.icon}
</div>
</div>
<div className="sm-col-span-8 text-dark-400 col-span-7 md:col-span-9 xl:col-span-10">
<p className="text-scale-1200 text-5xl lg:text-6xl">
<div className="col-span-7 sm-col-span-8 text-dark-400 md:col-span-9 xl:col-span-10">
<p className="text-5xl text-scale-1200 lg:text-6xl">
<CountUp triggerAnimOnScroll={true} referenceElId="alphaNumbers">
{stat.value}
</CountUp>
{stat.unit && <span className="text-scale-1200 ml-1 text-2xl">{stat.unit}</span>}
{stat.unit && <span className="ml-1 text-2xl text-scale-1200">{stat.unit}</span>}
</p>
</div>
<div className="col-start-0 col-span-12 sm:col-span-9 sm:col-start-5 md:col-span-10 md:col-start-4 xl:col-start-3">
<p className="text-scale-1000 text-xs lg:text-base">{stat.name}</p>
<div className="col-span-12 col-start-0 sm:col-span-9 sm:col-start-5 md:col-span-10 md:col-start-4 xl:col-start-3">
<p className="text-xs text-scale-1000 lg:text-base">{stat.name}</p>
</div>
</div>
))}
@@ -220,29 +220,29 @@ const TableOfContents = (props: any) => {
const { scrollTo } = props
return (
<div className="dark:bg-scale-300 text-dark-400 dark:text-dark-200 bg-gray-50">
<div className="container mx-auto grid grid-cols-12 gap-4 px-8 py-20 lg:px-28">
<div className="container grid grid-cols-12 gap-4 px-8 py-20 mx-auto lg:px-28">
<div className="col-span-12 mb-10 text-base">
<p className="text-2xl text-black dark:text-white">
Supabase <span className="text-brand-900 dark:text-brand-900">Beta</span> is starting
now.
</p>
</div>
<div className="col-span-12 grid grid-cols-12 gap-y-10">
<div className="grid grid-cols-12 col-span-12 gap-y-10">
{IntroductionSegments.map((segment: any, segmentIdx: number) => (
<div key={`introSegment_${segmentIdx}`} className="col-span-12 grid grid-cols-12">
<div className="text-scale-1200 col-span-12 mb-10 sm:col-span-5 sm:mb-0 xl:col-span-4">
<div key={`introSegment_${segmentIdx}`} className="grid grid-cols-12 col-span-12">
<div className="col-span-12 mb-10 text-scale-1200 sm:col-span-5 sm:mb-0 xl:col-span-4">
<p>{segment.description}</p>
</div>
<div className="hidden sm:col-span-1 sm:block xl:col-span-2" />
<div className="col-span-12 flex flex-col sm:col-span-6">
<div className="flex flex-col col-span-12 sm:col-span-6">
{segment.chapters.map((chapter: any, idx: number) => (
<div
key={`section_select_${idx}`}
className="mb-5 flex cursor-pointer items-center"
className="flex items-center mb-5 cursor-pointer"
onClick={() => scrollTo(chapter.key)}
>
<p className="text-scale-1200 font-mono text-xs">{`0${chapter.no}`}</p>
<p className="hover:text-gray-1000 dark:hover:text-gray-1000 ml-4 border-b border-gray-400 text-base text-black transition dark:text-white">
<p className="font-mono text-xs text-scale-1200">{`0${chapter.no}`}</p>
<p className="ml-4 text-base text-black transition border-b border-gray-400 hover:text-gray-1000 dark:hover:text-gray-1000 dark:text-white">
{chapter.name}
</p>
</div>
@@ -302,13 +302,13 @@ const Performance = () => {
{PerformanceComparisonData.map((metric: any) => {
const multiplier = (metric.stats[0].value / metric.stats[1].value).toFixed(1)
return (
<div key={`${metric.key}`} className="text-dark-400 dark:text-dark-300 mb-10">
<p className="mb-4 pb-2 sm:w-36">{metric.title}</p>
<div key={`${metric.key}`} className="mb-10 text-dark-400 dark:text-dark-300">
<p className="pb-2 mb-4 sm:w-36">{metric.title}</p>
<div className="flex flex-col sm:flex-row sm:items-center">
<div className="w-full sm:w-5/6">
{metric.stats.map((stat: any, idx: number) => (
<div key={`metric_${metric.key}_${idx}`} className="flex items-center">
<p className="mr-4 w-20 border-r py-2 pr-4 text-left sm:text-right lg:w-24">
<p className="w-20 py-2 pr-4 mr-4 text-left border-r sm:text-right lg:w-24">
{stat.name}
</p>
<Bar
@@ -324,7 +324,7 @@ const Performance = () => {
))}
</div>
<div className="flex flex-col text-left sm:w-1/6 sm:text-right">
<p className="text-dark-700 dark:text-dark-100 text-6xl">{multiplier}x</p>
<p className="text-6xl text-dark-700 dark:text-dark-100">{multiplier}x</p>
<p className="-mt-2 text-sm">more {metric.key}s per second</p>
</div>
</div>
@@ -336,11 +336,11 @@ const Performance = () => {
}
return (
<div id="performance" className="dark:bg-scale-200 bg-white">
<div className="text-dark-400 dark:text-dark-300 container mx-auto grid grid-cols-12 gap-y-10 px-8 py-20 lg:px-28">
<div id="performance" className="bg-white dark:bg-scale-200">
<div className="container grid grid-cols-12 px-8 py-20 mx-auto text-dark-400 dark:text-dark-300 gap-y-10 lg:px-28">
<SectionHeader sectionNumber={1} header="Performance" />
<div className="text-scale-1200 col-span-12 mb-10 grid grid-cols-12 items-center gap-x-2 lg:gap-x-8">
<div className="grid items-center grid-cols-12 col-span-12 mb-10 text-scale-1200 gap-x-2 lg:gap-x-8">
<div className="col-span-12 text-base sm:col-span-9 xl:col-span-7">
<p className="mb-10">
We started Supabase to give developers a web-ready database that is delightful to use,
@@ -420,10 +420,10 @@ const Performance = () => {
const Security = () => {
return (
<div id="security" className="dark:bg-scale-300 bg-gray-50">
<div className="text-dark-400 dark:text-dark-200 container mx-auto grid grid-cols-12 gap-y-10 px-8 py-20 lg:px-28">
<div className="container grid grid-cols-12 px-8 py-20 mx-auto text-dark-400 dark:text-dark-200 gap-y-10 lg:px-28">
<SectionHeader sectionNumber={2} header="Security" />
<div className="text-scale-1200 col-span-12 grid grid-cols-12 gap-x-2 lg:gap-x-8">
<div className="grid grid-cols-12 col-span-12 text-scale-1200 gap-x-2 lg:gap-x-8">
<div className="col-span-12 text-base sm:col-span-9 xl:col-span-7">
<p className="mb-10">
As an infrastructure provider, security has been a priority from day one. While we had
@@ -434,7 +434,7 @@ const Security = () => {
Approaching the launch of our Beta period, we worked with security advisors and
specialists globally to enforce new measures and processes:
</p>
<ul className="ml-6 list-outside list-disc">
<ul className="ml-6 list-disc list-outside">
<li className="mb-5">
Employed DigitalXRAID to run a full Pen Test on both our internal and customer
infrastructure. We immediately patched one medium priority issue and are currently
@@ -482,19 +482,18 @@ const Security = () => {
}
const Reliability = () => {
const { basePath } = useRouter()
return (
<div id="reliability" className="dark:bg-scale-200 bg-white">
<div className="text-dark-400 dark:text-dark-300 container mx-auto grid grid-cols-12 gap-y-10 px-8 py-12 lg:px-28 ">
<div id="reliability" className="bg-white dark:bg-scale-200">
<div className="container grid grid-cols-12 px-8 py-12 mx-auto text-dark-400 dark:text-dark-300 gap-y-10 lg:px-28 ">
<SectionHeader sectionNumber={3} header="Reliability" />
<div className="text-scale-1200 col-span-12 mb-10 grid grid-cols-12 gap-x-2 lg:gap-x-8">
<div className="grid grid-cols-12 col-span-12 mb-10 text-scale-1200 gap-x-2 lg:gap-x-8">
<div className="col-span-12 text-base sm:col-span-9 xl:col-span-7">
<p className="mb-5">
During Alpha we experienced 2 availability incidents, neither affecting customer
access to their data. These were:
</p>
<ul className="mb-10 ml-6 list-outside list-disc">
<ul className="mb-10 ml-6 list-disc list-outside">
<li className="mb-5">
A third-party CDN API outage. As a result, subdomains were not created for new
projects.
@@ -521,7 +520,7 @@ const Reliability = () => {
to keep track of uptime across all of our services and critical infrastructure.
</p>
<div className="mb-10">
<VideoShot src={`${basePath}/videos/statusPage.mp4`} />
<VideoShot src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/marketing/statusPage.mp4" />
</div>
<p>
For our Alpha & Beta Users we take free, encrypted daily database backups up to 20GB.
@@ -538,10 +537,10 @@ const NewFeaturesAndIntegrations = () => {
const { basePath } = useRouter()
return (
<div id="newFeaturesAndIntegrations" className="dark:bg-scale-300 bg-gray-50">
<div className="text-dark-400 dark:text-dark-200 container mx-auto grid grid-cols-12 gap-y-10 px-8 py-20 lg:px-28">
<div className="container grid grid-cols-12 px-8 py-20 mx-auto text-dark-400 dark:text-dark-200 gap-y-10 lg:px-28">
<SectionHeader sectionNumber={4} header="New Features & Integrations" />
<div className="text-scale-1200 col-span-12 mb-10 grid grid-cols-12 gap-x-2 lg:gap-x-8">
<div className="grid grid-cols-12 col-span-12 mb-10 text-scale-1200 gap-x-2 lg:gap-x-8">
<div className="col-span-12 text-base sm:col-span-9 xl:col-span-7">
<p className="mb-5">
If you're new to Supabase, here's a few of the things you get when you choose us as
@@ -549,7 +548,7 @@ const NewFeaturesAndIntegrations = () => {
</p>
<ul className="">
<li className="mb-10">
<p className="border-dark-200 dark:border-dark-400 mb-2 w-20 border-b-2 pb-2">
<p className="w-20 pb-2 mb-2 border-b-2 border-dark-200 dark:border-dark-400">
Auth
</p>
<p className="mb-5">
@@ -577,10 +576,10 @@ const NewFeaturesAndIntegrations = () => {
transactional emails for you. We support passwordless links, and we offer several
OAuth providers including Google, GitHub, with more on the way.
</p>
<VideoShot src={`${basePath}/videos/tabAuthRules.mp4`} />
<VideoShot src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/marketing/tabAuthRules.mp4" />
</li>
<li className="mb-10">
<p className="border-dark-200 dark:border-dark-400 mb-2 w-20 border-b-2 pb-2">
<p className="w-20 pb-2 mb-2 border-b-2 border-dark-200 dark:border-dark-400">
Realtime
</p>
<p>
@@ -598,7 +597,7 @@ const NewFeaturesAndIntegrations = () => {
</p>
</li>
<li className="mb-10">
<p className="border-dark-200 dark:border-dark-400 mb-2 w-24 border-b-2 pb-2">
<p className="w-24 pb-2 mb-2 border-b-2 border-dark-200 dark:border-dark-400">
CRUD API
</p>
<p>
@@ -623,7 +622,7 @@ const NewFeaturesAndIntegrations = () => {
</p>
</li>
<li className="mb-10">
<p className="border-dark-200 dark:border-dark-400 mb-2 w-44 border-b-2 pb-2">
<p className="pb-2 mb-2 border-b-2 border-dark-200 dark:border-dark-400 w-44">
Quickstart Templates
</p>
<p>
@@ -633,24 +632,24 @@ const NewFeaturesAndIntegrations = () => {
</p>
</li>
<li className="mb-10">
<p className="border-dark-200 dark:border-dark-400 mb-2 w-24 border-b-2 pb-2">
<p className="w-24 pb-2 mb-2 border-b-2 border-dark-200 dark:border-dark-400">
Table View
</p>
<p className="mb-5">
View and edit your data like a spreadsheet from within the Supabase dashboard.
Build your schema, create complex relationships, and import and export to csv.
</p>
<VideoShot src={`${basePath}/videos/tabTableEditor.mp4`} />
<VideoShot src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/marketing/tabTableEditor.mp4" />
</li>
<li>
<p className="border-dark-200 dark:border-dark-400 mb-2 w-24 border-b-2 pb-2">
<p className="w-24 pb-2 mb-2 border-b-2 border-dark-200 dark:border-dark-400">
SQL Editor
</p>
<p className="mb-5">
No need to install third party SQL tools, you can run queries directly from the
Supabase Dashboard.
</p>
<VideoShot src={`${basePath}/videos/tabSqlEditor.mp4`} />
<VideoShot src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/marketing/tabSqlEditor.mp4" />
</li>
</ul>
</div>
@@ -661,11 +660,11 @@ const NewFeaturesAndIntegrations = () => {
}
const BetaPricing = () => (
<div id="betaPricing" className="dark:bg-scale-200 bg-white">
<div className="text-dark-400 dark:text-dark-300 container mx-auto grid grid-cols-12 gap-y-10 px-8 py-20 lg:px-28">
<div id="betaPricing" className="bg-white dark:bg-scale-200">
<div className="container grid grid-cols-12 px-8 py-20 mx-auto text-dark-400 dark:text-dark-300 gap-y-10 lg:px-28">
<SectionHeader sectionNumber={5} header="Beta Pricing" />
<div className="text-scale-1200 col-span-12 mb-10 grid grid-cols-12 gap-x-2 lg:gap-x-8">
<div className="grid grid-cols-12 col-span-12 mb-10 text-scale-1200 gap-x-2 lg:gap-x-8">
<div className="col-span-12 text-base sm:col-span-9 xl:col-span-7">
<p className="mb-10">For up to date pricing info see: https://supabase.com/pricing</p>
<p className="mb-10">
@@ -673,7 +672,7 @@ const BetaPricing = () => (
course our Alpha Users, to provide a predictable and sustainable pricing model.
</p>
<p className="mb-5">Our key aims going into this exercise were:</p>
<ul className="mb-10 ml-6 list-outside list-disc">
<ul className="mb-10 ml-6 list-disc list-outside">
<li className="mb-5">
To continue offering free Supabase instances for Students, Hobbyists, and Early
Adopters
@@ -688,7 +687,7 @@ const BetaPricing = () => (
</li>
</ul>
<p className="mb-10">We are committing to the following initiatives:</p>
<ul className="mb-10 ml-6 list-outside list-decimal">
<ul className="mb-10 ml-6 list-decimal list-outside">
<li className="mb-5">
All Alpha Users will receive credits equivalent of 2 years of base tier usage. These
will automatically be credited to your account if you signed up prior to December
@@ -730,10 +729,10 @@ const BetaPricing = () => (
const OpenSource = () => (
<div id="openSource" className="dark:bg-scale-300 bg-gray-50">
<div className="text-dark-400 dark:text-dark-200 container mx-auto grid grid-cols-12 gap-y-10 px-8 py-20 lg:px-28">
<div className="container grid grid-cols-12 px-8 py-20 mx-auto text-dark-400 dark:text-dark-200 gap-y-10 lg:px-28">
<SectionHeader sectionNumber={6} header="Open Source" />
<div className="text-scale-1200 col-span-12 mb-10 grid grid-cols-12 gap-x-2 lg:gap-x-8">
<div className="grid grid-cols-12 col-span-12 mb-10 text-scale-1200 gap-x-2 lg:gap-x-8">
<div className="col-span-12 text-base sm:col-span-9 xl:col-span-7">
<p className="mb-10">
Great software is multi generational and stretches beyond any single company.
@@ -796,10 +795,10 @@ const OpenSource = () => (
)
const FundingPartners = () => (
<div id="fundingPartners" className="dark:bg-scale-200 bg-white">
<div className="text-dark-400 dark:text-dark-300 container mx-auto grid grid-cols-12 gap-y-10 px-8 py-20 lg:px-28">
<div id="fundingPartners" className="bg-white dark:bg-scale-200">
<div className="container grid grid-cols-12 px-8 py-20 mx-auto text-dark-400 dark:text-dark-300 gap-y-10 lg:px-28">
<SectionHeader sectionNumber={7} header="Funding Partners" />
<div className="text-scale-1200 col-span-12 mb-10 grid grid-cols-12 gap-x-2 lg:gap-x-8">
<div className="grid grid-cols-12 col-span-12 mb-10 text-scale-1200 gap-x-2 lg:gap-x-8">
<div className="col-span-12 text-base sm:col-span-9 xl:col-span-7">
<p className="mb-10">
Building a platform that can offer all the amazing features of Firebase will take
@@ -834,10 +833,10 @@ const FundingPartners = () => (
const ScalingOurTeam = () => (
<div id="scalingOurTeam" className="dark:bg-scale-300 bg-gray-50">
<div className="text-dark-400 dark:text-dark-200 container mx-auto grid grid-cols-12 gap-y-10 px-8 py-20 lg:px-28">
<div className="container grid grid-cols-12 px-8 py-20 mx-auto text-dark-400 dark:text-dark-200 gap-y-10 lg:px-28">
<SectionHeader sectionNumber={8} header="Scaling Our Team" />
<div className="text-scale-1200 col-span-12 mb-10 grid grid-cols-12 gap-x-2 lg:gap-x-8">
<div className="grid grid-cols-12 col-span-12 mb-10 text-scale-1200 gap-x-2 lg:gap-x-8">
<div className="col-span-12 text-base sm:col-span-9 xl:col-span-7">
<p className="mb-10">
We are extremely proud of our team. We're a mix of 11 engineers, from 8 different
@@ -872,12 +871,12 @@ const ScalingOurTeam = () => (
const WhatsNext = () => (
<div
id="whatsNext"
className="border-b-px dark:bg-scale-200 dark:border-scale-600 border-gray-50 bg-white"
className="bg-white border-b-px dark:bg-scale-200 dark:border-scale-600 border-gray-50"
>
<div className="text-dark-400 dark:text-dark-300 container mx-auto grid grid-cols-12 gap-y-10 px-8 py-20 lg:px-28">
<div className="container grid grid-cols-12 px-8 py-20 mx-auto text-dark-400 dark:text-dark-300 gap-y-10 lg:px-28">
<SectionHeader sectionNumber={9} header="What's Next" />
<div className="text-scale-1200 col-span-12 mb-10 grid grid-cols-12 gap-x-2 lg:gap-x-8">
<div className="grid grid-cols-12 col-span-12 mb-10 text-scale-1200 gap-x-2 lg:gap-x-8">
<div className="col-span-12 text-base sm:col-span-9 xl:col-span-7">
<p className="mb-5">
If you watch our repos you'll see that development never stops, we ship every day (and
@@ -885,7 +884,7 @@ const WhatsNext = () => (
performance, security, and reliability improvements, here are some new features we're
working on for 2021:
</p>
<ul className="mb-10 ml-6 list-outside list-disc">
<ul className="mb-10 ml-6 list-disc list-outside">
<li className="mb-5">Storage - Making blob storage easier than ever before</li>
<li className="mb-5">
Functions - write and deploy serverless functions that have access to your Supabase
@@ -998,7 +997,7 @@ const Beta = (props: Props) => {
/>
<Container>
<div className="sticky inset-0 z-50">
<div className="bg-scale-1200 dark:bg-scale-300 flex items-center justify-between px-5 py-5 shadow-lg xl:px-20">
<div className="flex items-center justify-between px-5 py-5 shadow-lg bg-scale-1200 dark:bg-scale-300 xl:px-20">
<Link href="/">
<a>
<Image src={`${basePath}/images/logo-dark.png`} height={24} width={120} />
+1 -1
View File
@@ -238,7 +238,7 @@ function RealtimePage() {
</p>,
]}
// [TODO] Point to the correct docs URL
documentation_link={'/docs/guides/realtime#broadcast'}
documentation_link={'/docs/guides/realtime/broadcast'}
/>
</SectionContainer>
+1 -2
View File
@@ -15,9 +15,8 @@ SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZX
POSTGRES_HOST=db
POSTGRES_DB=postgres
POSTGRES_USER=postgres
POSTGRES_PORT=5432
# default user is postgres
############
# API Proxy - Configuration for the Kong Reverse proxy.
+12 -1
View File
@@ -325,5 +325,16 @@ module.exports = ui({
// height: ['hover'],
// },
// },
plugins: [require('@tailwindcss/typography')],
plugins: [
require('@tailwindcss/typography'),
function ({ addUtilities, addVariant }) {
addUtilities({
// prose (tailwind typography) helpers
// useful for removing margins in prose styled sections
'.prose--remove-p-margin p': {
margin: '0',
},
})
},
],
})
+19 -8
View File
@@ -10,14 +10,17 @@
"id": "installing",
"slug": "installing",
"type": "markdown",
"excludes": ["reference_javascript_v1", "reference_dart_v0", "reference_python_v2"]
"excludes": [
"reference_javascript_v1",
"reference_dart_v0",
"reference_python_v2"
]
},
{
"title": "Initializing",
"id": "initializing",
"slug": "initializing",
"type": "markdown",
"libs": ["csharp"]
"type": "function"
},
{
"title": "Typescript Support",
@@ -355,6 +358,14 @@
"product": "database",
"parent": "modifiers",
"type": "function"
},
{
"id": "returns",
"title": "Override type of successful response",
"slug": "db-returns",
"product": "database",
"parent": "modifiers",
"type": "function"
}
]
}
@@ -367,7 +378,6 @@
"id": "auth-api",
"title": "Overview",
"slug": "auth-api",
"type": "function"
},
{
@@ -570,7 +580,6 @@
"id": "admin-api",
"title": "Auth Admin",
"slug": "admin-api",
"type": "function",
"items": [
{
@@ -704,7 +713,6 @@
"product": "realtime",
"type": "function"
},
{
"id": "remove-channel",
"title": "Unsubscribe from a channel",
@@ -847,7 +855,10 @@
},
{
"title": "Misc",
"excludes": ["reference_dart_v1", "reference_dart_v0"],
"excludes": [
"reference_dart_v1",
"reference_dart_v0"
],
"items": [
{
"title": "Release Notes",
@@ -858,4 +869,4 @@
}
]
}
]
]
+98
View File
@@ -16,6 +16,104 @@ info:
version: '0.8.0'
functions:
- id: initializing
title: Initializing
description: |
Initializing a new client is pretty straightforward. Find your project url and public key from the
admin panel and pass it into your client initialization function.
`supabase-csharp` is heavily dependent on Models deriving from `BaseModel`. To interact with the API, one must have the associated model (see example) specified.
Leverage `Table`, `PrimaryKey`, and `Column` attributes to specify names of classes/properties that are different from their C# Versions.
examples:
- id: csharp-init-standard
name: Standard
code: |
```c#
var url = Environment.GetEnvironmentVariable("SUPABASE_URL");
var key = Environment.GetEnvironmentVariable("SUPABASE_KEY");
var options = new Supabase.SupabaseOptions
{
AutoConnectRealtime = true
};
var supabase = new Supabase.Client(url, key, options);
await supabase.InitializeAsync();
```
- id: csharp-init-maui
name: Dependency Injection (Maui-like)
code: |
```c#
public static MauiApp CreateMauiApp()
{
// ...
var builder = MauiApp.CreateBuilder();
var url = Environment.GetEnvironmentVariable("SUPABASE_URL");
var key = Environment.GetEnvironmentVariable("SUPABASE_KEY");
var options = new SupabaseOptions
{
AutoRefreshToken = true,
AutoConnectRealtime = true,
SessionHandler = new SupabaseSessionHandler()
};
// Note the creation as a singleton.
builder.Services.AddSingleton(provider => new Supabase.Client(url, key, options));
}
```
- id: csharp-init-showing-models
name: With Models Example
code: |
```c#
// Given the following Model representing the Supabase Database (Message.cs)
[Table("messages")]
public class Message : BaseModel
{
[PrimaryKey("id")]
public int Id { get; set; }
[Column("username")]
public string UserName { get; set; }
[Column("channel_id")]
public int ChannelId { get; set; }
public override bool Equals(object obj)
{
return obj is Message message &&
Id == message.Id;
}
public override int GetHashCode()
{
return HashCode.Combine(Id);
}
}
void Initialize()
{
// Get All Messages
var response = await client.Table<Message>().Get();
List<Message> models = response.Models;
// Insert
var newMessage = new Message { UserName = "acupofjose", ChannelId = 1 };
await client.Table<Message>().Insert();
// Update
var model = response.Models.First();
model.UserName = "elrhomariyounes";
await model.Update();
// Delete
await response.Models.Last().Delete();
// etc.
}
```
- id: sign-up
title: 'SignUp()'
description: |
+73 -4
View File
@@ -66,9 +66,9 @@ functions:
db: { schema: 'other_schema' }
})
```
description: |
By default the API server points to the `public` schema. You can enable other database schemas within the Dashboard.
Go to `Settings > API > Schema` and add the schema which you want to expose to the API.
Go to [Settings > API > Exposed schemas](https://app.supabase.com/project/_/settings/api) and add the schema which you want to expose to the API.
Note: each client connection can only access a single schema, so the code above can access the `other_schema` schema but cannot access the `public` schema.
- id: custom-fetch-implementation
@@ -81,7 +81,7 @@ functions:
global: { fetch: fetch.bind(globalThis) }
})
```
description: |
`supabase-js` uses the [`cross-fetch`](https://www.npmjs.com/package/cross-fetch) library to make HTTP requests,
but an alternative `fetch` implementation can be provided as an option.
This is most useful in environments where `cross-fetch` is not compatible (for instance Cloudflare Workers).
@@ -101,7 +101,7 @@ functions:
},
});
```
description: |
For React Native we recommend using `AsyncStorage` as the storage implementation for Supabase Auth.
- id: auth-api
title: 'Overview'
@@ -1194,6 +1194,56 @@ functions:
table returns `[]` or `null` but the parent table is not filtered out.
If you want to filter out the parent table rows, use the `!inner` hint
hideCodeBlock: true
- id: querying-foreign-table-with-count
name: Querying foreign table with count
code: |
```ts
const { data, error } = await supabase
.from('countries')
.select(`*, cities(count)`)
```
data:
sql: |
```sql
create table countries (
"id" "uuid" primary key default "extensions"."uuid_generate_v4"() not null,
"name" text
);
create table cities (
"id" "uuid" primary key default "extensions"."uuid_generate_v4"() not null,
"name" text,
"country_id" "uuid" references public.countries on delete cascade
);
with country as (
insert into countries (name)
values ('united kingdom') returning id
)
insert into cities (name, country_id) values
('London', (select id from country)),
('Manchester', (select id from country)),
('Liverpool', (select id from country)),
('Bristol', (select id from country));
```
response: |
```json
[
{
"id": "693694e7-d993-4360-a6d7-6294e325d9b6",
"name": "United Kingdom",
"cities": [
{
"count": 4
}
]
}
]
```
description: |
You can get the number of rows in a related table by using the
**count** property.
hideCodeBlock: true
- id: querying-with-count-option
name: Querying with count option
code: |
@@ -3845,6 +3895,25 @@ functions:
By default, the data is returned in JSON format, but can also be returned as Comma Separated Values.
hideCodeBlock: true
isSpotlight: true
- id: returns
$ref: '@supabase/postgrest-js.PostgrestTransformBuilder.returns'
title: returns()
examples:
- id: override-type-of-successful-response
name: Override type of successful response
code: |
```ts
const { data } = await supabase
.from('countries')
.select()
.returns<MyType>()
```
response: |
```
let x: typeof data // MyType | null
```
hideCodeBlock: true
isSpotlight: true
# NOTE: Not available on currently deployed PostgREST
# db.geojson():
@@ -166,7 +166,7 @@ const TableList: FC<Props> = ({
<code className="text-sm">{x.size}</code>
</Table.td>
<Table.td className="hidden xl:table-cell text-center">
{realtimePublication.tables.find((table: any) => table.id === x.id) && (
{(realtimePublication?.tables ?? []).find((table: any) => table.id === x.id) && (
<div className="flex justify-center">
<IconCheck strokeWidth={2} />
</div>
+6 -7
View File
@@ -498,21 +498,20 @@ export default class MetaStore implements IMetaStore {
}
) {
const { duplicateTable, isRLSEnabled, isRealtimeEnabled, isDuplicateRows } = metadata
const sourceTableName = duplicateTable.name
const { name: sourceTableName, schema: sourceTableSchema } = duplicateTable
const duplicatedTableName = payload.name
// The following query will copy the structure of the table along with indexes, constraints and
// triggers. However, foreign key constraints are not duplicated over - has to be done separately
const table = await this.rootStore.meta.query(
`CREATE TABLE "${duplicatedTableName}" (LIKE "${sourceTableName}" INCLUDING ALL);`
`CREATE TABLE "${sourceTableSchema}"."${duplicatedTableName}" (LIKE "${sourceTableSchema}"."${sourceTableName}" INCLUDING ALL);`
)
if (table.error) throw table.error
// Duplicate foreign key constraints over
const relationships = duplicateTable.relationships
if (relationships.length > 0) {
// @ts-ignore, but might need to investigate, sounds bad:
// Type instantiation is excessively deep and possibly infinite
// @ts-ignore
relationships.map(async (relationship: PostgresRelationship) => {
const relation = await this.rootStore.meta.addForeignKey({
...relationship,
@@ -525,7 +524,7 @@ export default class MetaStore implements IMetaStore {
// Duplicate rows if needed
if (isDuplicateRows) {
const rows = await this.rootStore.meta.query(
`INSERT INTO "${duplicatedTableName}" SELECT * FROM ${sourceTableName};`
`INSERT INTO "${sourceTableSchema}"."${duplicatedTableName}" SELECT * FROM "${sourceTableSchema}"."${sourceTableName}";`
)
if (rows.error) throw rows.error
@@ -535,7 +534,7 @@ export default class MetaStore implements IMetaStore {
const identityColumns = columns.filter((column) => column.identity_generation !== null)
identityColumns.map(async (column) => {
const identity = await this.rootStore.meta.query(
`SELECT setval('${duplicatedTableName}_${column.name}_seq', (SELECT MAX("${column.name}") FROM "${sourceTableName}"));`
`SELECT setval('"${sourceTableSchema}"."${duplicatedTableName}_${column.name}_seq"', (SELECT MAX("${column.name}") FROM "${sourceTableSchema}"."${sourceTableName}"));`
)
if (identity.error) throw identity.error
})
@@ -543,7 +542,7 @@ export default class MetaStore implements IMetaStore {
await this.tables.load()
const tables = this.tables.list()
const duplicatedTable = find(tables, { name: duplicatedTableName })
const duplicatedTable = find(tables, { schema: sourceTableSchema, name: duplicatedTableName })
if (isRLSEnabled) {
const updateTable: any = await this.tables.update(duplicatedTable!.id, {