mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
Merge branch 'master' into chore/pricing-page-updates
This commit is contained in:
commit
89e622c9be
345 files changed
+35272
-25909
No files matched your search
+19
-6
@@ -1,7 +1,20 @@
|
||||
node_modules/
|
||||
# Root folder of this turbo monorepo
|
||||
# Doesn't send useless context to Docker daemon
|
||||
.git
|
||||
.github
|
||||
about
|
||||
apps
|
||||
docker
|
||||
examples
|
||||
i18n
|
||||
**/node_modules
|
||||
spec
|
||||
supabase
|
||||
tests
|
||||
|
||||
**/.next
|
||||
.vercel
|
||||
.next
|
||||
.env.local
|
||||
.env.production
|
||||
.env.dev
|
||||
.env.*
|
||||
.env.*
|
||||
|
||||
.dockerignore
|
||||
studio/Dockerfile
|
||||
@@ -0,0 +1,20 @@
|
||||
name: reviewdog
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, edited]
|
||||
|
||||
jobs:
|
||||
misspell:
|
||||
name: runner / misspell
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code.
|
||||
uses: actions/checkout@v1
|
||||
- name: misspell
|
||||
uses: reviewdog/action-misspell@v1
|
||||
with:
|
||||
github_token: ${{ secrets.github_token }}
|
||||
locale: "US"
|
||||
reporter: github-pr-review
|
||||
level: error
|
||||
exclude: "*.css"
|
||||
@@ -6,6 +6,7 @@ import { Button, IconCheck, IconCopy } from 'ui'
|
||||
|
||||
import js from 'react-syntax-highlighter/dist/cjs/languages/hljs/javascript'
|
||||
import ts from 'react-syntax-highlighter/dist/cjs/languages/hljs/typescript'
|
||||
import csharp from 'react-syntax-highlighter/dist/cjs/languages/hljs/csharp'
|
||||
import py from 'react-syntax-highlighter/dist/cjs/languages/hljs/python'
|
||||
import sql from 'react-syntax-highlighter/dist/cjs/languages/hljs/sql'
|
||||
import bash from 'react-syntax-highlighter/dist/cjs/languages/hljs/bash'
|
||||
@@ -17,7 +18,7 @@ import { useTheme } from 'common/Providers'
|
||||
|
||||
interface Props {
|
||||
title?: string
|
||||
language: 'js' | 'jsx' | 'sql' | 'py' | 'bash' | 'ts' | 'dart' | 'json'
|
||||
language: 'js' | 'jsx' | 'sql' | 'py' | 'bash' | 'ts' | 'dart' | 'json' | 'csharp'
|
||||
linesToHighlight?: number[]
|
||||
hideCopy?: boolean
|
||||
hideLineNumbers?: boolean
|
||||
@@ -62,6 +63,7 @@ const CodeBlock: FC<Props> = ({
|
||||
SyntaxHighlighter.registerLanguage('sql', sql)
|
||||
SyntaxHighlighter.registerLanguage('bash', bash)
|
||||
SyntaxHighlighter.registerLanguage('dart', dart)
|
||||
SyntaxHighlighter.registerLanguage('csharp', csharp)
|
||||
SyntaxHighlighter.registerLanguage('json', json)
|
||||
|
||||
const large = false
|
||||
|
||||
@@ -14,12 +14,22 @@ import { useInView } from 'react-intersection-observer'
|
||||
*/
|
||||
|
||||
interface Props {
|
||||
tag: string
|
||||
tag?: string
|
||||
parseAnchors?: boolean
|
||||
customAnchor?: string
|
||||
}
|
||||
|
||||
const Heading: React.FC<Props> = ({ tag, children }) => {
|
||||
/**
|
||||
* This TOC is used in .mdx files and in .tsx files.
|
||||
* In mdx files, we need to parse the content and format them to match the
|
||||
* expected tocList format (text, link,level).
|
||||
*
|
||||
* In tsx files, we can generate this tocList directly. For these files, we don't
|
||||
* need to parse the <a> and generate anchors
|
||||
*/
|
||||
const Heading: React.FC<Props> = ({ tag, parseAnchors = false, customAnchor, children }) => {
|
||||
const HeadingTag = `${tag}` as any
|
||||
const anchor = getAnchor(children)
|
||||
const anchor = customAnchor ? customAnchor : getAnchor(children)
|
||||
const link = `#${anchor}`
|
||||
|
||||
const { ref } = useInView({
|
||||
@@ -33,7 +43,7 @@ const Heading: React.FC<Props> = ({ tag, children }) => {
|
||||
|
||||
return (
|
||||
<HeadingTag id={anchor} ref={ref} className="group scroll-mt-24">
|
||||
{removeAnchor(children)}
|
||||
{parseAnchors ? removeAnchor(children) : children}
|
||||
{anchor && (
|
||||
<a href={link} className="ml-2 opacity-0 group-hover:opacity-100 transition">
|
||||
#
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
If you upload additional profile photos, they'll accumulate
|
||||
in the `avatars` bucket because of their random names with only the latest being referenced
|
||||
from `public.profiles` and the older versions getting orphaned.
|
||||
|
||||
To automatically remove obsolete storage objects, extend the database
|
||||
triggers. Note that it is not sufficient to delete the objects from the
|
||||
`storage.objects` table because that would orphan and leak the actual storage objects in
|
||||
the S3 backend. Instead, invoke the storage API within Postgres via the `http` extension.
|
||||
|
||||
Enable the [http extension for the `extensions` schema](https://app.supabase.com/project/_/database/extensions) in the Dashboard.
|
||||
Then, define the following SQL functions in the SQL Editor to delete
|
||||
storage objects via the API:
|
||||
|
||||
```SQL
|
||||
create or replace function delete_storage_object(bucket text, object text, out status int, out content varchar)
|
||||
returns record
|
||||
language 'plpgsql'
|
||||
security definer
|
||||
as $$
|
||||
declare
|
||||
project_url varchar := '<YOURPROJECTURL>';
|
||||
service_role_key varchar := '<YOURSERVICEROLEKEY>'; -- full access needed
|
||||
url varchar := project_url||'/storage/v1/object/'||bucket||'/'||object;
|
||||
begin
|
||||
select
|
||||
into status, content
|
||||
result.status::int, result.content::varchar
|
||||
FROM extensions.http((
|
||||
'DELETE',
|
||||
url,
|
||||
ARRAY[extensions.http_header('authorization','Bearer '||service_role_key)],
|
||||
NULL,
|
||||
NULL)::extensions.http_request) as result;
|
||||
end;
|
||||
$$;
|
||||
|
||||
create or replace function delete_avatar(avatar_url text, out status int, out content varchar)
|
||||
returns record
|
||||
language 'plpgsql'
|
||||
security definer
|
||||
as $$
|
||||
begin
|
||||
select
|
||||
into status, content
|
||||
result.status, result.content
|
||||
from public.delete_storage_object('avatars', avatar_url) as result;
|
||||
end;
|
||||
$$;
|
||||
|
||||
```
|
||||
|
||||
Next, add a trigger that removes any obsolete avatar whenever the
|
||||
profile is updated or deleted:
|
||||
|
||||
```SQL
|
||||
create or replace function delete_old_avatar()
|
||||
returns trigger
|
||||
language 'plpgsql'
|
||||
security definer
|
||||
as $$
|
||||
declare
|
||||
status int;
|
||||
content varchar;
|
||||
begin
|
||||
if coalesce(old.avatar_url, '') <> ''
|
||||
and (tg_op = 'DELETE' or (old.avatar_url <> new.avatar_url)) then
|
||||
select
|
||||
into status, content
|
||||
result.status, result.content
|
||||
from public.delete_avatar(old.avatar_url) as result;
|
||||
if status <> 200 then
|
||||
raise warning 'Could not delete avatar: % %', status, content;
|
||||
end if;
|
||||
end if;
|
||||
if tg_op = 'DELETE' then
|
||||
return old;
|
||||
end if;
|
||||
return new;
|
||||
end;
|
||||
$$;
|
||||
|
||||
create trigger before_profile_changes
|
||||
before update of avatar_url or delete on public.profiles
|
||||
for each row execute function public.delete_old_avatar();
|
||||
|
||||
```
|
||||
|
||||
Finally, delete the `public.profile` row before a user is deleted.
|
||||
If this step is omitted, you won't be able to delete users without
|
||||
first manually deleting their avatar image.
|
||||
|
||||
```SQL
|
||||
create or replace function delete_old_profile()
|
||||
returns trigger
|
||||
language 'plpgsql'
|
||||
security definer
|
||||
as $$
|
||||
begin
|
||||
delete from public.profiles where id = old.id;
|
||||
return old;
|
||||
end;
|
||||
$$;
|
||||
|
||||
create trigger before_delete_user
|
||||
before delete on auth.users
|
||||
for each row execute function public.delete_old_profile();
|
||||
|
||||
```
|
||||
@@ -1,6 +1,6 @@
|
||||
import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
import { Button, IconGitHub, IconTwitter } from '~/../../packages/ui'
|
||||
import { Button, IconGitHub, IconTwitter, IconYoutube } from '~/../../packages/ui'
|
||||
import footerData from '~/data/footer.json'
|
||||
import { useTheme } from 'common/Providers'
|
||||
|
||||
@@ -20,6 +20,16 @@ const Footer = () => {
|
||||
))}
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
type="text"
|
||||
as="a"
|
||||
// @ts-ignore
|
||||
href="https://youtube.com/c/supabase"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
>
|
||||
<IconYoutube size={16} />
|
||||
</Button>
|
||||
<Button
|
||||
type="text"
|
||||
as="a"
|
||||
|
||||
@@ -3,6 +3,7 @@ import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { Fragment } from 'react'
|
||||
import { Badge } from '~/../../packages/ui'
|
||||
|
||||
const home = [
|
||||
[
|
||||
@@ -99,9 +100,17 @@ const home = [
|
||||
icon: '/img/icons/menu/reference-python',
|
||||
href: '/reference/python/introduction',
|
||||
level: 'reference_python',
|
||||
community: true,
|
||||
},
|
||||
{
|
||||
label: 'Tools Reference',
|
||||
label: 'C#',
|
||||
icon: '/img/icons/menu/reference-csharp',
|
||||
href: '/reference/csharp/introduction',
|
||||
level: 'reference_csharp',
|
||||
community: true,
|
||||
},
|
||||
{
|
||||
label: 'Tools',
|
||||
},
|
||||
{
|
||||
label: 'Management API',
|
||||
@@ -112,7 +121,7 @@ const home = [
|
||||
{
|
||||
label: 'Supabase CLI',
|
||||
icon: '/img/icons/menu/reference-cli',
|
||||
href: '/reference/cli/introduction',
|
||||
href: '/guides/cli',
|
||||
level: 'reference_javascript',
|
||||
},
|
||||
],
|
||||
@@ -171,6 +180,7 @@ const NavigationMenuHome = ({ active }) => {
|
||||
className="w-4 h-4 group-hover:scale-110 ease-out transition-all"
|
||||
/>
|
||||
{link.label}
|
||||
{link.community && <Badge size="small">Community</Badge>}
|
||||
</li>
|
||||
</a>
|
||||
</Link>
|
||||
|
||||
@@ -22,6 +22,12 @@ export const REFERENCES: References = {
|
||||
versions: ['v1', 'v0'],
|
||||
icon: '/docs/img/libraries/flutter-icon.svg',
|
||||
},
|
||||
csharp: {
|
||||
name: 'C#',
|
||||
library: 'supabase-csharp',
|
||||
versions: ['v0'],
|
||||
icon: '/docs/img/libraries/c-sharp-icon.svg',
|
||||
},
|
||||
cli: {
|
||||
name: 'CLI',
|
||||
library: undefined,
|
||||
@@ -48,6 +54,9 @@ export const gettingstarted = {
|
||||
{ name: 'ReactJS', url: '/guides/getting-started/quickstarts/reactjs', items: [] },
|
||||
{ name: 'NextJS', url: '/guides/getting-started/quickstarts/nextjs', items: [] },
|
||||
{ name: 'Flutter', url: '/guides/getting-started/quickstarts/flutter', items: [] },
|
||||
{ name: 'SvelteKit', url: '/guides/getting-started/quickstarts/sveltekit', items: [] },
|
||||
{ name: 'SolidJS', url: '/guides/getting-started/quickstarts/solidjs', items: [] },
|
||||
{ name: 'Vue', url: '/guides/getting-started/quickstarts/vue', items: [] },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -379,22 +388,47 @@ export const database = {
|
||||
url: undefined,
|
||||
items: [
|
||||
{ name: 'Overview', url: '/guides/database/extensions', items: [] },
|
||||
{
|
||||
name: 'HypoPG: Hypothetical indexes',
|
||||
url: '/guides/database/extensions/hypopg',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
name: 'plv8: Javascript Language',
|
||||
url: '/guides/database/extensions/plv8',
|
||||
items: [],
|
||||
},
|
||||
{ name: 'http: RESTful Client', url: '/guides/database/extensions/http', items: [] },
|
||||
{
|
||||
name: 'PGRoonga: Multilingual Full Text Search',
|
||||
url: '/guides/database/extensions/pgroonga',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
name: 'pg_cron: Job Scheduling',
|
||||
url: '/guides/database/extensions/pgcron',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
name: 'pg_jsonschema: JSON Schema Validation',
|
||||
url: '/guides/database/extensions/pg_jsonschema',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
name: 'pg_net: Async Networking',
|
||||
url: '/guides/database/extensions/pgnet',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
name: 'pg_stat_statements: SQL Planning and Execution Statistics',
|
||||
url: '/guides/database/extensions/pg_stat_statements',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
name: 'pg_repack: Storage Optimization',
|
||||
url: '/guides/database/extensions/pgrepack',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
name: 'PostGIS: Geo queries',
|
||||
url: '/guides/database/extensions/postgis',
|
||||
@@ -406,6 +440,11 @@ export const database = {
|
||||
url: '/guides/database/extensions/uuid-ossp',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
name: 'RUM: inverted index for full-text search',
|
||||
url: '/guides/database/extensions/rum',
|
||||
items: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -444,6 +483,11 @@ export const functions = {
|
||||
url: '/guides/functions/examples/cloudflare-turnstile',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
name: 'Connect to Postgres',
|
||||
url: '/guides/functions/examples/connect-to-postgres',
|
||||
items: [],
|
||||
},
|
||||
{ name: 'GitHub Actions', url: '/guides/functions/examples/github-actions', items: [] },
|
||||
{ name: 'OG Image', url: '/guides/functions/examples/og-image', items: [] },
|
||||
{ name: 'Storage Caching', url: '/guides/functions/examples/storage-caching', items: [] },
|
||||
@@ -485,6 +529,25 @@ export const storage = {
|
||||
],
|
||||
}
|
||||
|
||||
export const supabase_cli = {
|
||||
icon: 'reference-cli',
|
||||
title: 'Supabase CLI',
|
||||
url: '/guides/cli',
|
||||
items: [
|
||||
{ name: 'Overview', url: '/guides/cli', items: [] },
|
||||
{ name: 'Local Development', url: '/guides/cli/local-development', items: [] },
|
||||
{ name: 'Managing Environments', url: '/guides/cli/managing-environments', items: [] },
|
||||
{
|
||||
name: 'Reference',
|
||||
url: undefined,
|
||||
items: [
|
||||
{ name: 'Commands', url: '/reference/cli/introduction', items: [] },
|
||||
{ name: 'Configuration', url: '/reference/cli/config', items: [] },
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export const platform = {
|
||||
icon: 'platform',
|
||||
label: 'Platform',
|
||||
@@ -589,26 +652,6 @@ export const resources = {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Supabase CLI',
|
||||
items: [
|
||||
{
|
||||
name: 'Overview',
|
||||
url: '/guides/resources/supabase-cli',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
name: 'Local Development',
|
||||
url: '/guides/resources/supabase-cli/local-development',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
name: 'Managing Environments',
|
||||
url: '/guides/resources/supabase-cli/managing-environments',
|
||||
items: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
@@ -704,6 +747,7 @@ export const integrations = {
|
||||
{ name: 'DhiWise', url: '/guides/integrations/dhiwise', items: [] },
|
||||
{ name: 'Directus', url: '/guides/integrations/directus', items: [] },
|
||||
{ name: 'Draftbit', url: '/guides/integrations/draftbit', items: [] },
|
||||
{ name: 'FlutterFlow', url: '/guides/integrations/flutterflow', items: [] },
|
||||
{ name: 'Plasmic', url: '/guides/integrations/plasmic', items: [] },
|
||||
{ name: 'ILLA', url: '/guides/integrations/illa', items: [] },
|
||||
],
|
||||
@@ -737,6 +781,20 @@ export const reference = {
|
||||
items: [],
|
||||
icon: '/img/icons/menu/reference-dart',
|
||||
},
|
||||
{
|
||||
name: 'supabase-csharp',
|
||||
url: '/reference/csharp/start',
|
||||
level: 'reference_csharp',
|
||||
items: [],
|
||||
icon: '/img/icons/menu/reference-csharp',
|
||||
},
|
||||
{
|
||||
name: 'supbase-python',
|
||||
url: '/reference/python/start',
|
||||
level: 'reference_python',
|
||||
items: [],
|
||||
icon: 'docs/img/icons/javascript.svg',
|
||||
},
|
||||
// {
|
||||
// name: 'supabase-python',
|
||||
// url: '/reference/python/start',
|
||||
@@ -786,6 +844,7 @@ export const reference_dart_v0 = {
|
||||
url: '/guides/reference/dart',
|
||||
parent: '/reference',
|
||||
}
|
||||
|
||||
export const reference_dart_v1 = {
|
||||
icon: 'reference-dart',
|
||||
title: 'Flutter',
|
||||
@@ -793,6 +852,13 @@ export const reference_dart_v1 = {
|
||||
parent: '/reference',
|
||||
}
|
||||
|
||||
export const reference_csharp_v0 = {
|
||||
icon: 'reference-csharp',
|
||||
title: 'c#',
|
||||
url: 'guides/reference/csharp',
|
||||
parent: '/reference',
|
||||
}
|
||||
|
||||
export const reference_python_v2 = {
|
||||
icon: 'reference-python',
|
||||
title: 'Python',
|
||||
@@ -893,6 +959,13 @@ export const references = [
|
||||
icon: '/docs/img/icons/dart-icon.svg',
|
||||
url: '/reference/dart/start',
|
||||
},
|
||||
{
|
||||
label: 'supabase-csharp',
|
||||
versions: ['v0'],
|
||||
description: 'something about the reference',
|
||||
icon: '/docs/img/icons/c-sharp-icon.svg',
|
||||
url: '/reference/csharp/start',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -12,6 +12,8 @@ import spec_dart_v1 from '~/../../spec/supabase_dart_v1.yml' assert { type: 'yml
|
||||
// @ts-expect-error
|
||||
import spec_dart_v0 from '~/../../spec/supabase_dart_v0.yml' assert { type: 'yml' }
|
||||
// @ts-expect-error
|
||||
import spec_csharp_v0 from '~/../../spec/supabase_csharp_v0.yml' assert { type: 'yml' }
|
||||
// @ts-expect-error
|
||||
import spec_python_v2 from '~/../../spec/supabase_py_v2.yml' assert { type: 'yml' }
|
||||
|
||||
// import { gen_v3 } from '~/lib/refGenerator/helpers'
|
||||
@@ -50,6 +52,7 @@ export type RefIdOptions =
|
||||
| 'reference_javascript_v2'
|
||||
| 'reference_dart_v0'
|
||||
| 'reference_dart_v1'
|
||||
| 'reference_csharp_v0'
|
||||
| 'reference_python_v2'
|
||||
| 'reference_cli'
|
||||
| 'reference_api'
|
||||
@@ -60,6 +63,7 @@ export type RefIdOptions =
|
||||
export type RefKeyOptions =
|
||||
| 'javascript'
|
||||
| 'dart'
|
||||
| 'csharp'
|
||||
| 'python'
|
||||
| 'cli'
|
||||
| 'api'
|
||||
@@ -105,6 +109,9 @@ const NavigationMenu = () => {
|
||||
case url.includes(`/docs/guides/integrations`) && url:
|
||||
menuState.setMenuLevelId('integrations')
|
||||
break
|
||||
case url.includes(`/docs/guides/cli`) && url:
|
||||
menuState.setMenuLevelId('supabase_cli')
|
||||
break
|
||||
// JS v1
|
||||
case url.includes(`/docs/reference/javascript/v1`) && url:
|
||||
menuState.setMenuLevelId('reference_javascript_v1')
|
||||
@@ -121,10 +128,17 @@ const NavigationMenu = () => {
|
||||
case url.includes(`/docs/reference/dart`) && url:
|
||||
menuState.setMenuLevelId('reference_dart_v1')
|
||||
break
|
||||
// C# v0 (latest)
|
||||
case url.includes(`/docs/reference/csharp`) && url:
|
||||
menuState.setMenuLevelId('reference_csharp_v0')
|
||||
break
|
||||
// puthon v2 (latest)
|
||||
case url.includes(`/docs/reference/python`) && url:
|
||||
menuState.setMenuLevelId('reference_python_v2')
|
||||
break
|
||||
case url.includes(`/docs/reference/cli/config`) && url:
|
||||
menuState.setMenuLevelId('supabase_cli')
|
||||
break
|
||||
case url.includes(`/docs/reference/cli`) && url:
|
||||
menuState.setMenuLevelId('reference_cli')
|
||||
break
|
||||
@@ -164,6 +178,7 @@ const NavigationMenu = () => {
|
||||
const isFunctionsActive = 'functions' === level
|
||||
const isRealtimeActive = 'realtime' === level
|
||||
const isStorageActive = 'storage' === level
|
||||
const issupabase_cliActive = 'supabase_cli' === level
|
||||
const isPlatformActive = 'platform' === level
|
||||
const isResourcesActive = 'resources' === level
|
||||
const isSelfHosting = 'self_hosting' === level
|
||||
@@ -174,6 +189,7 @@ const NavigationMenu = () => {
|
||||
const isReference_Javascript_V2 = 'reference_javascript_v2' === level
|
||||
const isReference_Dart_V0 = 'reference_dart_v0' === level
|
||||
const isReference_Dart_V1 = 'reference_dart_v1' === level
|
||||
const isReference_Csharp_V0 = 'reference_csharp_v0' === level
|
||||
const isReference_Python_V2 = 'reference_python_v2' === level
|
||||
const isReference_Cli = 'reference_cli' === level
|
||||
const isReference_Api = 'reference_api' === level
|
||||
@@ -191,6 +207,7 @@ const NavigationMenu = () => {
|
||||
<NavigationMenuGuideList id={'functions'} active={isFunctionsActive} />
|
||||
<NavigationMenuGuideList id={'realtime'} active={isRealtimeActive} />
|
||||
<NavigationMenuGuideList id={'storage'} active={isStorageActive} />
|
||||
<NavigationMenuGuideList id={'supabase_cli'} active={issupabase_cliActive} />
|
||||
<NavigationMenuGuideList id={'platform'} active={isPlatformActive} />
|
||||
<NavigationMenuGuideList id={'resources'} active={isResourcesActive} />
|
||||
<NavigationMenuGuideList id={'self_hosting'} active={isSelfHosting} />
|
||||
@@ -229,6 +246,15 @@ const NavigationMenu = () => {
|
||||
lib="dart"
|
||||
spec={spec_dart_v1}
|
||||
/>
|
||||
<NavigationMenuRefList
|
||||
key={'reference-csharp-menu-v0'}
|
||||
id={'reference_csharp_v0'}
|
||||
active={isReference_Csharp_V0}
|
||||
commonSections={libCommonSections}
|
||||
lib="csharp"
|
||||
spec={spec_csharp_v0}
|
||||
/>
|
||||
|
||||
<NavigationMenuRefList
|
||||
key={'reference-python-menu-v2'}
|
||||
id={'reference_python_v2'}
|
||||
|
||||
@@ -179,7 +179,6 @@ const Content: React.FC<INavigationMenuRefList> = ({ id, lib, commonSections, sp
|
||||
if (!sections) console.error('no common sections imported')
|
||||
|
||||
const menu = NavItems[id]
|
||||
console.log(menu)
|
||||
const databaseFunctions = find(sections, { title: 'Database' })
|
||||
? find(sections, { title: 'Database' }).items
|
||||
: []
|
||||
|
||||
@@ -20,6 +20,7 @@ import QuickstartIntro from './MDX/quickstart_intro.mdx'
|
||||
import ProjectSetup from './MDX/project_setup.mdx'
|
||||
import SocialProviderSetup from './MDX/social_provider_setup.mdx'
|
||||
import SocialProviderSettingsSupabase from './MDX/social_provider_settings_supabase.mdx'
|
||||
import StorageManagement from './MDX/storage_management.mdx'
|
||||
import { Mermaid } from 'mdx-mermaid/lib/Mermaid'
|
||||
import InlineCodeTag from './CustomHTMLElements/InlineCode'
|
||||
import React from 'react'
|
||||
@@ -49,6 +50,7 @@ const components = {
|
||||
ProjectSetup,
|
||||
SocialProviderSetup,
|
||||
SocialProviderSettingsSupabase,
|
||||
StorageManagement,
|
||||
Mermaid,
|
||||
Extensions,
|
||||
Alert: (props: any) => (
|
||||
|
||||
@@ -122,6 +122,8 @@ const RefFunctionSection: React.FC<IRefFunctionSection> = (props) => {
|
||||
? 'ts'
|
||||
: example?.code?.startsWith('```dart')
|
||||
? 'dart'
|
||||
: example?.code?.startsWith('```c#')
|
||||
? 'csharp'
|
||||
: 'js'
|
||||
// `
|
||||
// import { createClient } from '@supabase/supabase-js'
|
||||
@@ -154,7 +156,8 @@ const RefFunctionSection: React.FC<IRefFunctionSection> = (props) => {
|
||||
.replace(/```/g, '')
|
||||
.replace('js', '')
|
||||
.replace('ts', '')
|
||||
.replace('dart', ''))}
|
||||
.replace('dart', '')
|
||||
.replace('c#', ''))}
|
||||
</CodeBlock>
|
||||
|
||||
{((tables && tables.length > 0) || sql) && (
|
||||
|
||||
@@ -468,11 +468,32 @@
|
||||
"installed_version": "1.1",
|
||||
"comment": "generate universally unique identifiers (UUIDs)"
|
||||
},
|
||||
{
|
||||
"name": "vector",
|
||||
"schema": "extensions",
|
||||
"default_version": "0.4.0",
|
||||
"installed_version": null,
|
||||
"comment": "vector data type with similarity search"
|
||||
},
|
||||
{
|
||||
"name": "xml2",
|
||||
"schema": null,
|
||||
"default_version": "1.1",
|
||||
"installed_version": null,
|
||||
"comment": "XPath querying and XSLT"
|
||||
},
|
||||
{
|
||||
"name": "rum",
|
||||
"schema": "extensions",
|
||||
"default_version": "1.3.13",
|
||||
"installed_version": "1.3.13",
|
||||
"comment": "GIN-like index for text search"
|
||||
},
|
||||
{
|
||||
"name": "pg_repack",
|
||||
"schema": "extensions",
|
||||
"default_version": "1.4.8",
|
||||
"installed_version": "1.4.8",
|
||||
"comment": "optimize physical storage and remove bloat from tables and indexes"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,134 @@
|
||||
const Nav = [
|
||||
{
|
||||
label: 'Getting Started',
|
||||
items: [
|
||||
{ name: 'Supabase C# Library', url: '/reference/csharp', items: [] },
|
||||
{ name: 'Installing', url: '/reference/csharp/installing', items: [] },
|
||||
{ name: 'Initializing', url: '/reference/csharp/initializing', items: [] },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Auth',
|
||||
items: [
|
||||
{ name: 'signUp()', url: '/reference/csharp/auth-signup', items: [] },
|
||||
{
|
||||
name: 'signInWithPassword()',
|
||||
url: '/reference/csharp/auth-signinwithpassword',
|
||||
items: [],
|
||||
},
|
||||
{ name: 'signInWithOtp()', url: '/reference/csharp/auth-signinwithotp', items: [] },
|
||||
{ name: 'signInWithOAuth()', url: '/reference/csharp/auth-signinwithoauth', items: [] },
|
||||
{ name: 'signOut()', url: '/reference/csharp/auth-signout', items: [] },
|
||||
{ name: 'verifyOtp()', url: '/reference/csharp/auth-verifyotp', items: [] },
|
||||
{ name: 'currentSession', url: '/reference/csharp/auth-currentsession', items: [] },
|
||||
{ name: 'currentUser', url: '/reference/csharp/auth-currentuser', items: [] },
|
||||
{ name: 'updateUser()', url: '/reference/csharp/auth-updateuser', items: [] },
|
||||
{ name: 'onAuthStateChange()', url: '/reference/csharp/auth-onauthstatechange', items: [] },
|
||||
{
|
||||
name: 'resetPasswordForEmail()',
|
||||
url: '/reference/csharp/auth-resetpasswordforemail',
|
||||
items: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Functions',
|
||||
items: [{ name: 'invoke()', url: '/reference/csharp/invoke', items: [] }],
|
||||
},
|
||||
{
|
||||
label: 'Database',
|
||||
items: [
|
||||
{ name: 'Fetch data: select()', url: '/reference/csharp/select', items: [] },
|
||||
{ name: 'Create data: insert()', url: '/reference/csharp/insert', items: [] },
|
||||
{ name: 'Modify data: update()', url: '/reference/csharp/update', items: [] },
|
||||
{ name: 'Upsert data: upsert()', url: '/reference/csharp/upsert', items: [] },
|
||||
{ name: 'Delete data: delete()', url: '/reference/csharp/delete', items: [] },
|
||||
{ name: 'Stored Procedures: rpc()', url: '/reference/csharp/rpc', items: [] },
|
||||
{
|
||||
name: 'Filters',
|
||||
url: undefined,
|
||||
items: [
|
||||
{ name: 'Using Filters', url: '/reference/csharp/using-filters', items: [] },
|
||||
{ name: 'eq()', url: '/reference/csharp/eq', items: [] },
|
||||
{ name: 'neq()', url: '/reference/csharp/neq', items: [] },
|
||||
{ name: 'gt()', url: '/reference/csharp/gt', items: [] },
|
||||
{ name: 'gte()', url: '/reference/csharp/gte', items: [] },
|
||||
{ name: 'lt()', url: '/reference/csharp/lt', items: [] },
|
||||
{ name: 'lte()', url: '/reference/csharp/lte', items: [] },
|
||||
{ name: 'like()', url: '/reference/csharp/like', items: [] },
|
||||
{ name: 'ilike()', url: '/reference/csharp/ilike', items: [] },
|
||||
{ name: 'is_()', url: '/reference/csharp/is_', items: [] },
|
||||
{ name: 'in_()', url: '/reference/csharp/in_', items: [] },
|
||||
{ name: 'contains()', url: '/reference/csharp/contains', items: [] },
|
||||
{ name: 'containedBy()', url: '/reference/csharp/containedby', items: [] },
|
||||
{ name: 'rangeGt()', url: '/reference/csharp/rangegt', items: [] },
|
||||
{ name: 'rangeGte()', url: '/reference/csharp/rangegte', items: [] },
|
||||
{ name: 'rangeLt()', url: '/reference/csharp/rangelt', items: [] },
|
||||
{ name: 'rangeLte()', url: '/reference/csharp/rangelte', items: [] },
|
||||
{ name: 'rangeAdjacent()', url: '/reference/csharp/rangeadjacent', items: [] },
|
||||
{ name: 'overlaps()', url: '/reference/csharp/overlaps', items: [] },
|
||||
{ name: 'textSearch()', url: '/reference/csharp/textsearch', items: [] },
|
||||
{ name: 'match()', url: '/reference/csharp/match', items: [] },
|
||||
{ name: 'not()', url: '/reference/csharp/not', items: [] },
|
||||
{ name: 'or()', url: '/reference/csharp/or', items: [] },
|
||||
{ name: 'filter()', url: '/reference/csharp/filter', items: [] },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Modifiers',
|
||||
url: undefined,
|
||||
items: [
|
||||
{ name: 'Using Modifiers', url: '/reference/csharp/using-modifiers', items: [] },
|
||||
{ name: 'order()', url: '/reference/csharp/order', items: [] },
|
||||
{ name: 'limit()', url: '/reference/csharp/limit', items: [] },
|
||||
{ name: 'range()', url: '/reference/csharp/range', items: [] },
|
||||
{ name: 'single()', url: '/reference/csharp/single', items: [] },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Realtime',
|
||||
items: [
|
||||
{ name: 'stream()', url: '/reference/csharp/stream', items: [] },
|
||||
{ name: 'on().subscribe()', url: '/reference/csharp/subscribe', items: [] },
|
||||
{ name: 'removeChannel()', url: '/reference/csharp/removechannel', items: [] },
|
||||
{ name: 'removeAllChannels()', url: '/reference/csharp/removeallchannels', items: [] },
|
||||
{ name: 'getChannels()', url: '/reference/csharp/getchannels', items: [] },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Storage',
|
||||
items: [
|
||||
{ name: 'createBucket()', url: '/reference/csharp/storage-createbucket', items: [] },
|
||||
{ name: 'getBucket()', url: '/reference/csharp/storage-getbucket', items: [] },
|
||||
{ name: 'listBuckets()', url: '/reference/csharp/storage-listbuckets', items: [] },
|
||||
{ name: 'updateBucket()', url: '/reference/csharp/storage-updatebucket', items: [] },
|
||||
{ name: 'deleteBucket()', url: '/reference/csharp/storage-deletebucket', items: [] },
|
||||
{ name: 'emptyBucket()', url: '/reference/csharp/storage-emptybucket', items: [] },
|
||||
{ name: 'from.upload()', url: '/reference/csharp/storage-from-upload', items: [] },
|
||||
{ name: 'from.download()', url: '/reference/csharp/storage-from-download', items: [] },
|
||||
{ name: 'from.list()', url: '/reference/csharp/storage-from-list', items: [] },
|
||||
{ name: 'from.update()', url: '/reference/csharp/storage-from-update', items: [] },
|
||||
{ name: 'from.move()', url: '/reference/csharp/storage-from-move', items: [] },
|
||||
{ name: 'from.remove()', url: '/reference/csharp/storage-from-remove', items: [] },
|
||||
{
|
||||
name: 'from.createSignedUrl()',
|
||||
url: '/reference/csharp/storage-from-createsignedurl',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
name: 'from.createSignedUrls()',
|
||||
url: '/reference/csharp/storage-from-createsignedurls',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
name: 'from.getPublicUrl()',
|
||||
url: '/reference/csharp/storage-from-getpublicurl',
|
||||
items: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
export default Nav
|
||||
@@ -13,6 +13,9 @@ const nonGeneratedReferencePages = [
|
||||
'docs/reference/dart/initializing',
|
||||
'docs/reference/dart/v0/upgrade-guide',
|
||||
'docs/reference/dart/v0',
|
||||
'docs/reference/csharp',
|
||||
'docs/reference/csharp/installing',
|
||||
'docs/reference/csharp/initializing',
|
||||
'docs/reference/api',
|
||||
'docs/reference/api/release-notes',
|
||||
'docs/reference/cli',
|
||||
|
||||
@@ -17,7 +17,7 @@ hideTitle: true
|
||||
The Supabase CLI provides tools to develop your project locally and deploy to the Supabase Platform.
|
||||
The CLI is still under development, but it contains all the functionality for working with your Supabase projects and the Supabase Platform.
|
||||
|
||||
- Run Supabase locally: [`supabase start`](/docs/reference/cli/usage#supabase-start)
|
||||
- Run Supabase locally: [`supabase init`](/docs/reference/cli/usage#supabase-init) and [`supabase start`](/docs/reference/cli/usage#supabase-start)
|
||||
- Manage database migrations: [`supabase migration`](/docs/reference/cli/usage#supabase-migration)
|
||||
- CI/CD for releasing to production: [`supabase db push`](/docs/reference/cli/usage#supabase-db-push)
|
||||
- Manage your Supabase projects: [`supabase projects`](/docs/reference/cli/usage#supabase-projects)
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
---
|
||||
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>
|
||||
@@ -0,0 +1,33 @@
|
||||
---
|
||||
id: installing
|
||||
title: 'Installing & Initialization'
|
||||
slug: installing
|
||||
custom_edit_url: https://github.com/supabase/supabase/edit/master/web/spec/supabase.yml
|
||||
---
|
||||
|
||||
### Install from NuGet
|
||||
|
||||
<RefSubLayout.EducationRow>
|
||||
<RefSubLayout.Details>
|
||||
|
||||
You can install Supabase package from [nuget.org](https://www.nuget.org/packages/supabase-csharp/)
|
||||
|
||||
</RefSubLayout.Details>
|
||||
|
||||
<RefSubLayout.Examples>
|
||||
|
||||
<Tabs
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="csharp">
|
||||
<TabPanel id="csharp" label="Terminal">
|
||||
|
||||
```sh Terminal
|
||||
dotnet add package supabase-csharp
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
</RefSubLayout.Examples>
|
||||
</RefSubLayout.EducationRow>
|
||||
@@ -0,0 +1,31 @@
|
||||
---
|
||||
id: introduction
|
||||
title: Introduction
|
||||
hideTitle: true
|
||||
---
|
||||
|
||||
<div className="flex items-start gap-6 not-prose" id="introduction">
|
||||
<img src="/docs/img/icons/menu/reference-csharp.svg" className="w-8 h-8 rounded" />
|
||||
<div className="flex flex-col gap-2">
|
||||
<h1 className="text-3xl text-scale-1200 m-0">C# Client Library</h1>
|
||||
<h2 className="text-base font-mono text-scale-1100">@supabase-community/supabase-csharp</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="max-w-xl">
|
||||
This reference documents every object and method available in Supabase's C# library,
|
||||
[supabase-csharp](https://www.nuget.org/packages/supabase-csharp). You can use supabase-csharp to
|
||||
interact with your Postgres database, listen to database changes, invoke Deno Edge Functions,
|
||||
build login and user management functionality, and manage large files.
|
||||
</div>
|
||||
|
||||
<div className="max-w-xl bg-slate-300 px-4 py-2 rounded-md">
|
||||
<p>The C# client library is created and maintained by the Supabase community, and is not an official library. Please be tolerant of areas where the library is still being developed, and — as with all the libraries — feel free to contribute wherever you find issues.</p>
|
||||
|
||||
<p>
|
||||
Huge thanks to official maintainer, [Joseph Schultz](https://github.com/acupofjose), and to [Ben
|
||||
Randall](https://github.com/veleek) and [Rhuan Barros](https://github.com/rhuanbarros) for their
|
||||
help.
|
||||
</p>
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,206 @@
|
||||
---
|
||||
id: release-notes
|
||||
title: Release Notes
|
||||
---
|
||||
|
||||
## 0.8.0 - 2023-01-31
|
||||
|
||||
- Update dependency: realtime-csharp@5.0.0
|
||||
- Re: [#21](https://github.com/supabase-community/realtime-csharp/pull/21) Provide API for `presence`, `broadcast` and `postgres_changes`
|
||||
- [Major, New] `Channel.PostgresChanges` event will receive the wildcard `*` changes event, not `Channel.OnMessage`.
|
||||
- [Major] `Channel.OnInsert`, `Channel.OnUpdate`, and `Channel.OnDelete` now conform to the server's payload of `Response.Payload.**Data**`
|
||||
- [Major] `Channel.OnInsert`, `Channel.OnUpdate`, and `Channel.OnDelete` now return `PostgresChangesEventArgs`
|
||||
- [Minor] Rename `Channel` to `RealtimeChannel`
|
||||
- Supports better handling of disconnects in `RealtimeSocket` and adds a `Client.OnReconnect` event.
|
||||
- [Minor] Moves `ChannelOptions` to `Channel.ChannelOptions`
|
||||
- [Minor] Moves `ChannelStateChangedEventArgs` to `Channel.ChannelStateChangedEventArgs`
|
||||
- [Minor] Moves `Push` to `Channel.Push`
|
||||
- [Minor] Moves `Channel.ChannelState` to `Constants.ChannelState`
|
||||
- [Minor] Moves `SocketResponse`, `SocketRequest`, `SocketResponsePayload`, `SocketResponseEventArgs`, and `SocketStateChangedEventArgs` to `Socket` namespace.
|
||||
- [New] Adds `RealtimeBroadcast`
|
||||
- [New] Adds `RealtimePresence`
|
||||
- [Improvement] Better handling of disconnection/reconnection
|
||||
- Update dependency: postgrest-csharp@3.1.3
|
||||
- Another fix for [#61](https://github.com/supabase-community/postgrest-csharp/issues/61) which further typechecks nullable values.
|
||||
|
||||
## 0.7.2 - 2023-01-27
|
||||
|
||||
- Update dependency: gotrue-csharp@3.0.4
|
||||
- Makes `Session.CreatedAt` a publicly settable property, which should fix incorrect dates on retrieved `Session`s.
|
||||
- Update dependency: postgrest-csharp@3.1.2
|
||||
- Fix [#61](https://github.com/supabase-community/postgrest-csharp/issues/61) which did not correctly parse Linq `Where` when encountering a nullable type.
|
||||
- Add missing support for transforming for `== null` and `!= null`
|
||||
|
||||
## 0.7.1 - 2023-01-17
|
||||
|
||||
- Update dependency: postgrest-csharp@3.1.1
|
||||
- Fix issue from supabase-community/supabase-csharp#48 where boolean model properties would not be evaluated in predicate expressions
|
||||
|
||||
## 0.7.0 - 2023-01-16
|
||||
|
||||
- Update dependency: postgrest-csharp@3.1.0
|
||||
- [Minor] Breaking API Change: `PrimaryKey` attribute defaults to `shouldInsert: false` as most uses will have the Database generate the primary key.
|
||||
- Merged [#60](https://github.com/supabase-community/postgrest-csharp/pull/60) which Added linq support for `Select`, `Where`, `OnConflict`, `Columns`, `Order`, `Update`, `Set`, and `Delete`
|
||||
|
||||
## 0.6.2 - 2022-11-22
|
||||
|
||||
- Update dependency: postgrest-csharp@3.0.4
|
||||
- `GetHeaders` is now passed to `ModeledResponse` and `BaseModel` so that the default `Update` and `Delete` methods use the latest credentials
|
||||
- `GetHeaders` is used in `Rpc` calls (re: [#39](https://github.com/supabase-community/supabase-csharp/issues/39))
|
||||
|
||||
## 0.6.1 - 2022-11-12
|
||||
|
||||
- [Hotfix] `GetHeaders` was not passing properly to `SupabaseTable` and `Gotrue.Api`
|
||||
|
||||
## 0.6.0 - 2022-11-12
|
||||
|
||||
[BREAKING CHANGES]
|
||||
|
||||
- `Client` is no longer a singleton, singleton interactions (if desired) are left to the developer to implement.
|
||||
- `Client` supports injection of dependent clients after initialization via property:
|
||||
- `Auth`
|
||||
- `Functions`
|
||||
- `Realtime`
|
||||
- `Postgrest`
|
||||
- `Storage`
|
||||
- `SupabaseModel` contains no logic but remains for backwards compatibility. (Marked `Obsolete`)
|
||||
- `ClientOptions.ShouldInitializeRealtime` was removed (no longer auto initialized)
|
||||
- `ClientOptions` now references an `ISupabaseSessionHandler` which specifies expected functionality for session persistence on Gotrue (replaces `ClientOptions.SessionPersistor`, `ClientOptions.SessionRetriever`, and `ClientOptions.SessionDestroyer`).
|
||||
- `supabase-csharp` and all child libraries now have support `nullity`
|
||||
|
||||
Other Changes:
|
||||
|
||||
- Update dependency: functions-csharp@1.2.1
|
||||
- Update dependency: gotrue-csharp@3.0.2
|
||||
- Update dependency: postgrest-csharp@3.0.2
|
||||
- Update dependency: realtime-csharp@4.0.1
|
||||
- Update dependency: supabase-storage-csharp@1.2.3
|
||||
- Update dependency: supabase-core@0.0.2
|
||||
|
||||
Big thank you to [@veleek](https://github.com/veleek) for his insight into these changes.
|
||||
|
||||
Re: [#35](https://github.com/supabase-community/supabase-csharp/issues/35), [#34](https://github.com/supabase-community/supabase-csharp/issues/34), [#23](https://github.com/supabase-community/supabase-csharp/issues/23), [#36](https://github.com/supabase-community/supabase-csharp/pull/36)
|
||||
|
||||
## 0.5.3 - 2022-10-11
|
||||
|
||||
- Update dependency: postgrest-csharp@2.1.0
|
||||
- [Minor] Breaking API change: Remove `BaseModel.PrimaryKeyValue` and `BaseModel.PrimaryKeyColumn` in favor of a `PrimaryKey` dictionary with support for composite keys.
|
||||
- Re: [#48](https://github.com/supabase-community/postgrest-csharp/issues/48) - Add support for derived models on `ReferenceAttribute`
|
||||
- Re: [#49](https://github.com/supabase-community/postgrest-csharp/issues/49) - Added `Match(T model)`
|
||||
|
||||
## 0.5.2 - 2022-9-13
|
||||
|
||||
- Update dependency: postgrest-csharp@2.0.12
|
||||
- Merged [#47](https://github.com/supabase-community/postgrest-csharp/pull/49) which added cancellation token support to `Table<T>` methods. Thanks [@devpikachu](https://github.com/devpikachu)!
|
||||
|
||||
## 0.5.1 - 2022-8-1
|
||||
|
||||
- Update dependency: postgrest-csharp@2.0.11
|
||||
- Update dependency: supabase-storage-csharp@1.1.1
|
||||
|
||||
## 0.5.0 - 2022-7-17
|
||||
|
||||
- Update dependency: postgrest-csharp@2.0.9
|
||||
- Update dependency: realtime-csharp@3.0.1
|
||||
- Update dependency: supabase-storage-csharp@1.1.0
|
||||
- API Change [Breaking/Minor] Library no longer uses `WebClient` and instead leverages `HttpClient`. Progress events on `Upload` and `Download` are now handled with `EventHandler<float>` instead of `WebClient` EventHandlers.
|
||||
|
||||
## 0.4.4 - 2022-5-24
|
||||
|
||||
- Update dependency: gotrue-csharp@2.4.5
|
||||
- Update dependency: postgrest-csharp@2.0.8
|
||||
|
||||
## 0.4.3 - 2022-5-13
|
||||
|
||||
- Update dependency: gotrue-csharp@2.4.4
|
||||
|
||||
## 0.4.2 - 2022-4-30
|
||||
|
||||
- Update dependency: gotrue-csharp@2.4.3
|
||||
|
||||
## 0.4.1 - 2022-4-23
|
||||
|
||||
- Update dependency: gotrue-csharp@2.4.2
|
||||
|
||||
## 0.4.0 - 2022-4-12
|
||||
|
||||
- Add support for functions-csharp@1.0.1, giving access to invoking Supabase's edge functions.
|
||||
- Update dependency: gotrue-csharp@2.4.1
|
||||
|
||||
## 0.3.5 - 2022-4-11
|
||||
|
||||
- Update dependency: postgres-csharp@2.0.7
|
||||
|
||||
## 0.3.4 - 2022-03-28
|
||||
|
||||
- Update dependency: gotrue-csharp@2.4.0
|
||||
|
||||
## 0.3.3 - 2022-02-27
|
||||
|
||||
- Update dependency: gotrue-csharp@2.3.6
|
||||
- Update dependency: supabase-storage-csharp@1.0.2
|
||||
|
||||
## 0.3.2 - 2022-02-18
|
||||
|
||||
- Update dependency: realtime-csharp@3.0.0
|
||||
- Exchange existing websocket client: [WebSocketSharp](https://github.com/sta/websocket-sharp) for [Marfusios/websocket-client](https://github.com/Marfusios/websocket-client) which adds support for Blazor WASM apps.
|
||||
Ref: [#14](https://github.com/supabase-community/realtime-csharp/pull/14)
|
||||
|
||||
## 0.3.1 - 2022-01-20
|
||||
|
||||
- Update dependency: gotrue-csharp@2.3.5
|
||||
- [#23](https://github.com/supabase-community/gotrue-csharp/pull/23) Added `redirect_url` option for MagicLink sign in (Thanks [@MisterJimson](https://github.com/MisterJimson))
|
||||
- [#21](https://github.com/supabase-community/gotrue-csharp/pull/21) Added SignOut method to Stateless Client (Thanks [@fplaras](https://github.com/fplaras))
|
||||
|
||||
## 0.3.0 - 2021-12-30
|
||||
|
||||
- Update dependency: postgrest-csharp@2.0.6
|
||||
- Add support for `NullValueHandling` to be specified on a `Column` Attribute and for it to be honored on Inserts and Updates. Defaults to: `NullValueHandling.Include`.
|
||||
- Implements [#38](https://github.com/supabase-community/postgrest-csharp/issues/38)
|
||||
- Update dependency: realtime-csharp@2.0.8
|
||||
- Implement Upstream Realtime RLS Error Broadcast Handler
|
||||
- Implements [#12](https://github.com/supabase-community/realtime-csharp/issues/12)
|
||||
- `SocketResponse` now exposes a method: `OldModel`, that hydrates the `OldRecord` property into a model.
|
||||
|
||||
## 0.2.12 - 2021-12-29
|
||||
|
||||
- Update dependency: gotrue-csharp@2.3.3
|
||||
- `SignUp` will return a `Session` with a _populated `User` object_ on an unconfirmed signup.
|
||||
- Fixes [#19](https://github.com/supabase-community/gotrue-csharp/issues/19)
|
||||
- Developers who were using a `null` check on `Session.User` will need to adjust accordingly.
|
||||
- Update dependency: postgrest-csharp@2.0.5
|
||||
- Fix for [#37](https://github.com/supabase-community/postgrest-csharp/issues/37) - Return Type `minimal` would fail to resolve because of incorrect `Accept` headers. Added header and test to verify for future.
|
||||
- Fix for [#36](https://github.com/supabase-community/postgrest-csharp/issues/36) - Inserting/Upserting bulk records would fail while doing an unnecessary generic coercion.
|
||||
|
||||
## 0.2.11 - 2021-12-24
|
||||
|
||||
- Update dependency: gotrue-csharp@2.3.2 (changes CreateUser parameters to conform to `AdminUserAttributes`)
|
||||
- See [#15](https://github.com/supabase-community/supabase-csharp/issues/15)
|
||||
- See [#16](https://github.com/supabase-community/supabase-csharp/issues/16)
|
||||
- Update dependency: realtime-csharp@2.0.7
|
||||
- See [#13](https://github.com/supabase-community/supabase-csharp/issues/13)
|
||||
|
||||
## 0.2.10 - 2021-12-23
|
||||
|
||||
- Update dependency: gotrue-csharp@2.3.0 (adds metadata support for user signup, see [#14](https://github.com/supabase/community/issues/14))
|
||||
|
||||
## 0.2.9 - 2021-12-9
|
||||
|
||||
- Separate Storage client from Supabase repo and into `storage-csharp`, `supabase-csharp` now references new repo.
|
||||
|
||||
## 0.2.8 - 2021-12-4
|
||||
|
||||
- Update gotrue-csharp to 2.2.4
|
||||
- Adds support for `ListUsers` (paginate, sort, filter), `GetUserById`, `CreateUser`, and `UpdateById`
|
||||
|
||||
## 0.2.7 - 2021-12-2
|
||||
|
||||
- Update gotrue-csharp to 2.2.3
|
||||
- Adds support for sending password resets to users.
|
||||
|
||||
## 0.2.6 - 2021-11-29
|
||||
|
||||
- Support for [#12](https://github.com/supabase-community/supabase-csharp/issues/12)
|
||||
- Update realtime-csharp to 2.0.6
|
||||
- Update gotrue-csharp to 2.2.2
|
||||
- Add `StatelessClient` re:[#7](https://github.com/supabase-community/supabase-csharp/issues/7)
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
id: release-notes
|
||||
title: Release Notes
|
||||
---
|
||||
|
||||
## dart v0 this is the release notes file.
|
||||
@@ -1,50 +0,0 @@
|
||||
---
|
||||
id: initializing
|
||||
title: 'Initializing'
|
||||
slug: initializing
|
||||
---
|
||||
|
||||
<RefSubLayout.EducationRow>
|
||||
<RefSubLayout.Details>
|
||||
|
||||
Initialize Supabase with the static `initialize()` method on `Supabase` class.
|
||||
|
||||
</RefSubLayout.Details>
|
||||
|
||||
<RefSubLayout.Examples>
|
||||
|
||||
<Tabs
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="flutter"
|
||||
>
|
||||
<TabPanel id="flutter" label="Flutter">
|
||||
|
||||
```dart main.dart
|
||||
Future<void> main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
await Supabase.initialize(
|
||||
url: 'https://xyzcompany.supabase.co',
|
||||
anonKey: 'public-anon-key',
|
||||
);
|
||||
|
||||
runApp(MyApp());
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="dart" label="Other Dart Project">
|
||||
|
||||
```dart
|
||||
final supabase = SupabaseClient(
|
||||
'https://xyzcompany.supabase.co',
|
||||
'public-anon-key',
|
||||
);
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
</RefSubLayout.Examples>
|
||||
</RefSubLayout.EducationRow>
|
||||
File renamed without changes.
@@ -0,0 +1,67 @@
|
||||
---
|
||||
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.
|
||||
|
||||
</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>
|
||||
</Tabs>
|
||||
|
||||
</RefSubLayout.Examples>
|
||||
</RefSubLayout.EducationRow>
|
||||
@@ -0,0 +1,33 @@
|
||||
---
|
||||
id: installing
|
||||
title: 'Installing & Initialization'
|
||||
slug: installing
|
||||
custom_edit_url: https://github.com/supabase/supabase/edit/master/web/spec/supabase.yml
|
||||
---
|
||||
|
||||
### Install from NuGet
|
||||
|
||||
<RefSubLayout.EducationRow>
|
||||
<RefSubLayout.Details>
|
||||
|
||||
You can install Supabase package from [nuget.org](https://www.nuget.org/packages/supabase-csharp/)
|
||||
|
||||
</RefSubLayout.Details>
|
||||
|
||||
<RefSubLayout.Examples>
|
||||
|
||||
<Tabs
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="csharp">
|
||||
<TabPanel id="csharp" label="Terminal">
|
||||
|
||||
```sh Terminal
|
||||
dotnet add package supabase-csharp
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
</RefSubLayout.Examples>
|
||||
</RefSubLayout.EducationRow>
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
id: introduction
|
||||
title: Introduction
|
||||
hideTitle: true
|
||||
---
|
||||
|
||||
<div className="flex items-start gap-6 not-prose" id="introduction">
|
||||
<img src="/docs/img/icons/menu/reference-csharp.svg" className="w-8 h-8 rounded" />
|
||||
<div className="flex flex-col gap-2">
|
||||
<h1 className="text-3xl text-scale-1200 m-0">C# Client Library</h1>
|
||||
<h2 className="text-base font-mono text-scale-1100">supabase-csharp</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="max-w-xl">
|
||||
This reference documents every object and method available in Supabase's C#
|
||||
library, [supabase-csharp](https://www.nuget.org/packages/supabase-csharp). You can
|
||||
use supabase-csharp to interact with your Postgres database, listen to database changes, invoke
|
||||
Deno Edge Functions, build login and user management functionality, and manage large files.
|
||||
</div>
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
id: release-notes
|
||||
title: Release Notes
|
||||
---
|
||||
|
||||
## dart v0 this is the release notes file.
|
||||
@@ -19,7 +19,7 @@ const Layout: FC<Props> = (props: Props) => {
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{props.meta?.title} | Supabase</title>
|
||||
<title>{props.meta?.title} | Supabase Docs</title>
|
||||
<meta name="description" content={props.meta?.description} />
|
||||
<meta content="width=device-width, initial-scale=1" name="viewport" />
|
||||
<link rel="icon" href="/docs/favicon.ico" />
|
||||
|
||||
@@ -29,7 +29,7 @@ const levelsData = {
|
||||
},
|
||||
functions: {
|
||||
icon: '/docs/img/icons/menu/functions',
|
||||
name: 'Functions',
|
||||
name: 'Edge Functions',
|
||||
},
|
||||
realtime: {
|
||||
icon: '/docs/img/icons/menu/realtime',
|
||||
@@ -39,6 +39,10 @@ const levelsData = {
|
||||
icon: '/docs/img/icons/menu/storage',
|
||||
name: 'Storage',
|
||||
},
|
||||
supabase_cli: {
|
||||
icon: '/docs/img/icons/menu/reference-cli',
|
||||
name: 'Supabase CLI',
|
||||
},
|
||||
platform: {
|
||||
icon: '/docs/img/icons/menu/platform',
|
||||
name: 'Platform',
|
||||
@@ -71,6 +75,10 @@ const levelsData = {
|
||||
icon: '/docs/img/icons/menu/reference-dart',
|
||||
name: 'Dart Reference v0.0',
|
||||
},
|
||||
reference_csharp_v0: {
|
||||
icon: '/docs/img/icons/menu/reference-csharp',
|
||||
name: 'C# Reference v0.0',
|
||||
},
|
||||
reference_python_v2: {
|
||||
icon: '/docs/img/icons/menu/reference-python',
|
||||
name: 'Python Reference v2.0',
|
||||
|
||||
@@ -8,7 +8,6 @@ import components from '~/components'
|
||||
import { highlightSelectedTocItem } from '~/components/CustomHTMLElements/CustomHTMLElements.utils'
|
||||
import GuidesTableOfContents from '~/components/GuidesTableOfContents'
|
||||
import useHash from '~/hooks/useHash'
|
||||
import { getPageType } from '~/lib/helpers'
|
||||
|
||||
interface Props {
|
||||
meta: {
|
||||
@@ -17,6 +16,8 @@ interface Props {
|
||||
hide_table_of_contents?: boolean
|
||||
breadcrumb?: string
|
||||
subtitle?: string
|
||||
video?: string
|
||||
canonical?: string
|
||||
}
|
||||
children: any
|
||||
toc?: any
|
||||
@@ -65,12 +66,28 @@ const Layout: FC<Props> = (props) => {
|
||||
return (
|
||||
<>
|
||||
<NextSeo
|
||||
title={`${props.meta?.title} | Supabase`}
|
||||
title={`${props.meta?.title} | Supabase Docs`}
|
||||
description={props.meta?.description ? props.meta?.description : props.meta?.title}
|
||||
canonical={props.meta?.canonical ?? `https://supabase.com/docs${asPath}`}
|
||||
openGraph={{
|
||||
title: props.meta?.title,
|
||||
description: props.meta?.description,
|
||||
url: `https://supabase.com/docs${asPath}`,
|
||||
type: 'article',
|
||||
videos: props.meta?.video && [
|
||||
{
|
||||
// youtube based video meta
|
||||
url: props.meta?.video,
|
||||
width: 640,
|
||||
height: 385,
|
||||
type: 'application/x-shockwave-flash',
|
||||
},
|
||||
],
|
||||
article: {
|
||||
publishedTime: new Date().toISOString(),
|
||||
modifiedTime: new Date().toISOString(),
|
||||
authors: ['Supabase'],
|
||||
},
|
||||
images: [
|
||||
{
|
||||
url: `https://obuldanrptloktxcffvn.functions.supabase.co/og-images?site=docs${
|
||||
@@ -82,7 +99,6 @@ const Layout: FC<Props> = (props) => {
|
||||
],
|
||||
}}
|
||||
/>
|
||||
|
||||
<div className={['grid grid-cols-12 relative gap-4'].join(' ')}>
|
||||
<div
|
||||
className={[
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { NextRequest } from 'next/server'
|
||||
import isbot from 'isbot'
|
||||
|
||||
export function middleware(request: NextRequest) {
|
||||
const specs = ['javascript', 'dart']
|
||||
const specs = ['javascript', 'dart', 'csharp']
|
||||
|
||||
let version = ''
|
||||
if (request.url.includes('/v1/')) {
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"lint": "next lint",
|
||||
"build:sitemap": "node ./internals/generate-sitemap.mjs",
|
||||
"postbuild": "ts-node ./scripts/build-search.ts && node ./internals/generate-sitemap.mjs",
|
||||
"generate:all": "npm-run-all --parallel gen:api gen:cli gen:gotrue gen:storage gen:supabase-dart:v0 gen:supabase-dart:v1 gen:supabase-js:v1 gen:supabase-js:v2 gen:realtime",
|
||||
"generate:all": "npm-run-all --parallel gen:api gen:cli gen:gotrue gen:storage gen:supabase-dart:v0 gen:supabase-dart:v1 gen:supabase-csharp:v0 gen:supabase-js:v1 gen:supabase-js:v2 gen:realtime",
|
||||
"gen:api": "npm-run-all gen:api:usage",
|
||||
"gen:api:usage": "ts-node ./generator/index.ts gen --type api --url https://api.supabase.com --input ../../spec/transforms/api_v0_openapi_deparsed.json --output ./docs/reference/api/generated/usage.mdx",
|
||||
"gen:cli": "npm-run-all gen:cli:commands gen:cli:config",
|
||||
@@ -25,6 +25,8 @@
|
||||
"gen:supabase-dart:v0:ref": "ts-node ./generator/index.ts gen --type legacy --input ../../spec/supabase_dart_v0.yml --output ./docs/reference/dart/v0/generated",
|
||||
"gen:supabase-dart:v1": "npm-run-all gen:supabase-dart:v1:ref",
|
||||
"gen:supabase-dart:v1:ref": "ts-node ./generator/index.ts gen --type legacy --input ../../spec/supabase_dart_v1.yml --output ./docs/reference/dart/generated",
|
||||
"gen:supabase-csharp:v0": "npm-run-all gen:supabase-csharp:v0:ref",
|
||||
"gen:supabase-csharp:v0:ref": "ts-node ./generator/index.ts gen --type legacy --input ../../spec/supabase_csharp_v0.yml --output ./docs/reference/csharp/generated",
|
||||
"gen:supabase-js:v1": "npm-run-all gen:supabase-js:v1:ref",
|
||||
"gen:supabase-js:v1:ref": "ts-node ./generator/index.ts gen --type legacy --input ../../spec/supabase_js_v1.yml --output ./docs/reference/javascript/v1/generated",
|
||||
"gen:supabase-js:v2": "npm-run-all gen:supabase-js:v2:ref",
|
||||
@@ -42,6 +44,7 @@
|
||||
"@radix-ui/react-accordion": "^1.0.1",
|
||||
"algoliasearch": "^4.14.2",
|
||||
"babel": "^6.23.0",
|
||||
"clsx": "^1.2.1",
|
||||
"common": "*",
|
||||
"config": "*",
|
||||
"gray-matter": "^4.0.3",
|
||||
@@ -63,6 +66,7 @@
|
||||
"react-intersection-observer": "^9.4.0",
|
||||
"react-markdown": "^8.0.3",
|
||||
"react-syntax-highlighter": "^15.3.1",
|
||||
"rehype-slug": "^5.1.0",
|
||||
"remark": "^14.0.2",
|
||||
"remark-admonitions": "^1.2.1",
|
||||
"remark-gfm": "^3.0.1",
|
||||
@@ -72,10 +76,10 @@
|
||||
"valtio": "^1.7.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"dotenv": "^16.0.3",
|
||||
"@types/node": "^17.0.12",
|
||||
"@types/react": "17.0.39",
|
||||
"config": "*",
|
||||
"dotenv": "^16.0.3",
|
||||
"ejs": "^3.1.8",
|
||||
"eslint": "8.9.0",
|
||||
"globby": "^12.0.2",
|
||||
|
||||
@@ -1,161 +0,0 @@
|
||||
import Layout from '~/layouts/DefaultGuideLayout'
|
||||
|
||||
export const meta = {
|
||||
title: 'Overview',
|
||||
description: 'An overview of Supabase\'s architecture and product principles.',
|
||||
}
|
||||
|
||||
Supabase is open source. We choose open source tools which are scalable and make them simple to use.
|
||||
|
||||
Supabase is not a 1-to-1 mapping of Firebase. While we are building many of the features that Firebase offers, we are not going about it the same way:
|
||||
our technological choices are quite different; everything we use is open source; and wherever possible, we use and support existing tools rather than developing from scratch.
|
||||
|
||||
Most notably, we use Postgres rather than a NoSQL store. This choice was deliberate. We believe that no other database offers the functionality required to compete with Firebase,
|
||||
while maintaining the scalability required to go beyond it.
|
||||
|
||||
## Architecture
|
||||
|
||||
Each Supabase project consists of several tools:
|
||||
|
||||

|
||||
|
||||
### PostgreSQL (Database)
|
||||
|
||||
PostgreSQL is the core of Supabase. We do not abstract the PostgreSQL database — you can access it and use it with full privileges. We simply provide tools which makes PostgreSQL as easy to use as Firebase.
|
||||
|
||||
- Official Docs: [postgresql.org/docs](https://www.postgresql.org/docs/current/index.html)
|
||||
- Source code: [github.com/postgres/postgres](https://github.com/postgres/postgres) (mirror)
|
||||
- License: [PostgreSQL Licence](https://www.postgresql.org/about/licence/)
|
||||
- Language: C
|
||||
|
||||
### Studio (Dashboard)
|
||||
|
||||
An open source Dashboard for managing your database and services.
|
||||
|
||||
- Official Docs: [Supabase docs](/docs)
|
||||
- Source code: [github.com/supabase/supabase](https://github.com/supabase/supabase/tree/master/studio)
|
||||
- License: [Apache 2](https://github.com/supabase/supabase/blob/master/LICENSE)
|
||||
- Language: TypeScript
|
||||
|
||||
### GoTrue (Auth)
|
||||
|
||||
A JWT-based API for managing users and issuing access tokens. This integrates with PostgreSQL's Row Level Security and the API servers.
|
||||
|
||||
- Official Docs: [Supabase Auth reference docs](/docs/reference/auth)
|
||||
- Source code: [github.com/supabase/gotrue](https://github.com/supabase/gotrue)
|
||||
- License: [MIT](https://github.com/supabase/gotrue/blob/master/LICENSE)
|
||||
- Language: Go
|
||||
|
||||
### PostgREST (API)
|
||||
|
||||
A standalone web server that turns your PostgreSQL database directly into a RESTful API.
|
||||
We use this with our [pg_graphql](https://github.com/supabase/pg_graphql) extension to provide a GraphQL API.
|
||||
|
||||
- Official Docs: [postgrest.org](https://postgrest.org/)
|
||||
- Source code: [github.com/PostgREST/postgrest](https://github.com/PostgREST/postgrest)
|
||||
- License: [MIT](https://github.com/PostgREST/postgrest/blob/main/LICENSE)
|
||||
- Language: Haskel
|
||||
|
||||
### Realtime (API & multiplayer)
|
||||
|
||||
A scalable websocket engine for managing user Presence, broadcasting messages, and streaming database changes.
|
||||
|
||||
- Official Docs: [Supabase Realtime docs](/docs/guides/realtime)
|
||||
- Source code: [github.com/supabase/realtime](https://github.com/supabase/realtime)
|
||||
- License: [Apache 2](https://github.com/supabase/realtime/blob/main/LICENSE)
|
||||
- Language: Elixir
|
||||
|
||||
### Storage API (large file storage)
|
||||
|
||||
An S3-compatible object storage service that stores metadata in Postgres.
|
||||
|
||||
- Official Docs: [Supabase Storage reference docs](/docs/reference/storage)
|
||||
- Source code: [github.com/supabase/storage-api](https://github.com/supabase/storage-api)
|
||||
- License: [Apache 2.0](https://github.com/supabase/storage-api/blob/master/LICENSE)
|
||||
- Language: NodeJS / TypeScript
|
||||
|
||||
### Deno (Edge Functions)
|
||||
|
||||
A modern runtime for JavaScript and TypeScript.
|
||||
|
||||
- Official Docs: [deno.land](https://deno.land/)
|
||||
- Source code: [github.com/denoland/deno](https://github.com/denoland/deno)
|
||||
- License: [MIT](https://github.com/denoland/deno/blob/main/LICENSE.md)
|
||||
- Language: TypeScript / Rust
|
||||
|
||||
### postgres-meta (Database management)
|
||||
|
||||
A RESTful API for managing your Postgres. Fetch tables, add roles, and run queries.
|
||||
|
||||
- Official Docs: [supabase.github.io/postgres-meta](https://supabase.github.io/postgres-meta/)
|
||||
- Source code: [github.com/supabase/postgres-meta](https://github.com/supabase/postgres-meta)
|
||||
- License: [Apache 2.0](https://github.com/supabase/postgres-meta/blob/master/LICENSE)
|
||||
- Language: NodeJS / TypeScript
|
||||
|
||||
### PgBouncer
|
||||
|
||||
A lightweight connection pooler for PostgreSQL. This is useful for connecting to Postgres when using Serverless functions.
|
||||
|
||||
- Official Docs: [pgbouncer.org](http://www.pgbouncer.org/)
|
||||
- Source code: [pgbouncer/pgbouncer](https://github.com/pgbouncer/pgbouncer)
|
||||
- License: [ISC](https://github.com/pgbouncer/pgbouncer/blob/master/COPYRIGHT)
|
||||
- Language: C
|
||||
|
||||
### Kong (API Gateway)
|
||||
|
||||
A cloud-native API gateway, built on top of Nginx.
|
||||
|
||||
- Official Docs: [docs.konghq.com](https://docs.konghq.com/)
|
||||
- Source code: [github.com/kong/kong](https://github.com/kong/kong)
|
||||
- License: [Apache 2.0](https://github.com/Kong/kong/blob/master/LICENSE)
|
||||
- Language: Lua
|
||||
|
||||
## Product Principles
|
||||
|
||||
It is our goal to provide an architecture that any large-scale company would design for themselves,
|
||||
and then provide tooling around that architecture that is easy-to-use for indie-developers and small teams.
|
||||
|
||||
We use a series of principles to ensure that scalability and usability are never mutually exclusive:
|
||||
|
||||
### Everything works in isolation
|
||||
|
||||
Each system must work as a standalone tool with as few moving parts as possible.
|
||||
The litmus test for this is: "Can a user run this product with nothing but a Postgres database?"
|
||||
|
||||
### Everything is integrated
|
||||
|
||||
Supabase is composable. Even though every product works in isolation, each product on the platform needs to 10x the other products.
|
||||
For integration, each tool should expose an API and Webhooks.
|
||||
|
||||
### Everything is extensible
|
||||
|
||||
We're deliberate about adding a new tool, and prefer instead to extend an existing one.
|
||||
This is the opposite of many cloud providers whose product offering expands into niche use-cases. We provide _primitives_ for developers, which allow them to achieve any goal.
|
||||
Less, but better.
|
||||
|
||||
### Everything is portable
|
||||
|
||||
To avoid lock-in, we make it easy to migrate in and out. Our cloud offering is compatible with our self-hosted product.
|
||||
We use existing standards to increase portability (like pg_dump an CSV files). If a new standard emerges which competes with a "Supabase" approach, we will deprecate the approach in favor of the standard.
|
||||
This forces us compete on experience. We aim to be the best Postgres hosting service.
|
||||
|
||||
### Play the long game
|
||||
|
||||
We sacrifice short-term wins for long-term gains. For example, it is tempting to run a fork of Postgres with additional functionality which only our customers need.
|
||||
Instead, we prefer to support efforts to upstream missing functionality so that the entire community benefits. This has the additional benefit of ensuring portability and longevity.
|
||||
|
||||
### Build for developers
|
||||
|
||||
"Developers" are a specific profile of user: they are _builders_.
|
||||
When assessing impact as a function of effort, developers have a large efficiency due to the type of products and systems they can build.
|
||||
As the profile of a developer changes over time, Supabase will continue to evolve the product to fit this evolving profile.
|
||||
|
||||
### Support existing tools
|
||||
|
||||
Supabase supports existing tools and communities wherever possible. Supabase is more like a "community of communities" - each tool typically has its own community
|
||||
which we work with.
|
||||
Open source is something we approach [collaboratively](https://supabase.com/blog/supabase-series-b#giving-back): we employ maintainers, sponsor projects, invest in businesses, and develop our own open source tools.
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} />
|
||||
|
||||
export default Page
|
||||
@@ -5,6 +5,7 @@ export const meta = {
|
||||
title: 'Serverless APIs',
|
||||
description: 'Auto-generating and Realtime APIs.',
|
||||
sidebar_label: 'Overview',
|
||||
video: 'https://www.youtube.com/v/rPAJJFdtPw0',
|
||||
}
|
||||
|
||||
Supabase auto-generates three types of API directly from your database schema.
|
||||
@@ -55,6 +56,7 @@ GraphQL in Supabase works through [pg_graphql](https://supabase.com/blog/pg-grap
|
||||
|
||||
Supabase provides a Realtime API using [Realtime](https://github.com/supabase/realtime). You can use this to listen to database changes over websockets.
|
||||
Realtime leverages PostgreSQL's built-in logical replication. You can manage your Realtime API simply by managing Postgres publications.
|
||||
Go to your project's [Replication section](https://app.supabase.com/project/_/database/replication) to get started.
|
||||
|
||||
## Getting started
|
||||
|
||||
@@ -217,7 +219,7 @@ import { createClient, useQuery } from 'urql'
|
||||
// Prepare API key and Authorization header
|
||||
const headers = {
|
||||
apikey: <SUPABASE_ANON_KEY>,
|
||||
authorization: `Bearer ${<SUPABASE_ANON_KEY}`>
|
||||
authorization: `Bearer ${<SUPABASE_ANON_KEY>}`,
|
||||
}
|
||||
|
||||
// Create GraphQL client
|
||||
|
||||
@@ -4,6 +4,7 @@ export const meta = {
|
||||
id: 'generating-types',
|
||||
title: 'Generating Types',
|
||||
description: 'How to generate types for your API and Supabase libraries.',
|
||||
video: 'https://www.youtube.com/v/7CqlTU9aOR4',
|
||||
}
|
||||
|
||||
Supabase APIs are generated from your database, which means that we can use database introspection to generate type-safe API definitions.
|
||||
|
||||
@@ -4,6 +4,7 @@ export const meta = {
|
||||
id: 'auth-captcha',
|
||||
title: 'Enable Captcha Protection',
|
||||
description: 'Add Captcha Protection to your Supabase project',
|
||||
video: 'https://www.youtube.com/v/em1cpOAXknM',
|
||||
}
|
||||
|
||||
Supabase provides you with the option of adding captcha to your sign-in, sign-up, and password reset forms. This keeps your website safe from bots and malicious scripts. Supabase authentication has support for [hCaptcha](https://www.hcaptcha.com/).
|
||||
|
||||
@@ -2,7 +2,7 @@ import Layout from '~/layouts/DefaultGuideLayout'
|
||||
|
||||
export const meta = {
|
||||
id: 'index',
|
||||
title: 'Auth Helpers',
|
||||
title: 'Auth Helpers Overview',
|
||||
description: 'A collection of framework-specific Auth utilities for working with Supabase.',
|
||||
sidebar_label: 'Overview',
|
||||
}
|
||||
|
||||
@@ -34,10 +34,7 @@ Pass `supabaseClient` from `@supabase/supabase-js` as a prop to the component.
|
||||
import { createClient } from '@supabase/supabase-js'
|
||||
import { Auth } from '@supabase/auth-ui-react'
|
||||
|
||||
const supabase = createClient(
|
||||
'<INSERT PROJECT URL>',
|
||||
'<INSERT PROJECT ANON API KEY>'
|
||||
)
|
||||
const supabase = createClient('<INSERT PROJECT URL>', '<INSERT PROJECT ANON API KEY>')
|
||||
|
||||
const App = () => <Auth supabaseClient={supabase} />
|
||||
```
|
||||
@@ -75,20 +72,29 @@ The Auth component also supports login with [offical social providers](../../aut
|
||||
import { createClient } from '@supabase/supabase-js'
|
||||
import { Auth, ThemeSupa } from '@supabase/auth-ui-react'
|
||||
|
||||
const supabase = createClient(
|
||||
'<INSERT PROJECT URL>',
|
||||
'<INSERT PROJECT ANON API KEY>'
|
||||
)
|
||||
const supabase = createClient('<INSERT PROJECT URL>', '<INSERT PROJECT ANON API KEY>')
|
||||
|
||||
const App = () => (
|
||||
<Auth
|
||||
supabaseClient={supabase}
|
||||
appearance={{ theme: ThemeSupa }}
|
||||
providers={['google', 'facebook', 'twitter']}
|
||||
providers={['google', 'facebook', 'twitter']}
|
||||
/>
|
||||
)
|
||||
```
|
||||
|
||||
### Supported Views
|
||||
|
||||
The Auth component is currently shipped with the following views:
|
||||
|
||||
- [Email Login](../auth-email)
|
||||
- [Magic Link login](../auth-magic-link)
|
||||
- [Social Login](../social-login)
|
||||
- Update password
|
||||
- Forgotten password
|
||||
|
||||
We are planning on adding more views in the future. Follow along on that [repo](https://github.com/supabase/auth-ui).
|
||||
|
||||
## Customization
|
||||
|
||||
There are several ways to customize Auth UI:
|
||||
@@ -161,10 +167,7 @@ Auth UI themes can be overridden using variable tokens. See the [list of variabl
|
||||
import { createClient } from '@supabase/supabase-js'
|
||||
import { Auth, ThemeSupa } from '@supabase/auth-ui-react'
|
||||
|
||||
const supabase = createClient(
|
||||
'<INSERT PROJECT URL>',
|
||||
'<INSERT PROJECT ANON API KEY>'
|
||||
)
|
||||
const supabase = createClient('<INSERT PROJECT URL>', '<INSERT PROJECT ANON API KEY>')
|
||||
|
||||
const App = () => (
|
||||
<Auth
|
||||
@@ -247,10 +250,7 @@ You can use custom CSS classes for the following elements:
|
||||
import { createClient } from '@supabase/supabase-js'
|
||||
import { Auth } from '@supabase/auth-ui-react'
|
||||
|
||||
const supabase = createClient(
|
||||
'<INSERT PROJECT URL>',
|
||||
'<INSERT PROJECT ANON API KEY>'
|
||||
)
|
||||
const supabase = createClient('<INSERT PROJECT URL>', '<INSERT PROJECT ANON API KEY>')
|
||||
|
||||
const App = () => (
|
||||
<Auth
|
||||
@@ -275,10 +275,7 @@ You can use custom CSS inline styles for the following elements:
|
||||
import { createClient } from '@supabase/supabase-js'
|
||||
import { Auth } from '@supabase/auth-ui-react'
|
||||
|
||||
const supabase = createClient(
|
||||
'<INSERT PROJECT URL>',
|
||||
'<INSERT PROJECT ANON API KEY>'
|
||||
)
|
||||
const supabase = createClient('<INSERT PROJECT URL>', '<INSERT PROJECT ANON API KEY>')
|
||||
|
||||
const App = () => (
|
||||
<Auth
|
||||
@@ -302,10 +299,7 @@ You can use custom labels with `localization.variables`. See the [list of labels
|
||||
import { createClient } from '@supabase/supabase-js'
|
||||
import { Auth } from '@supabase/auth-ui-react'
|
||||
|
||||
const supabase = createClient(
|
||||
'<INSERT PROJECT URL>',
|
||||
'<INSERT PROJECT ANON API KEY>'
|
||||
)
|
||||
const supabase = createClient('<INSERT PROJECT URL>', '<INSERT PROJECT ANON API KEY>')
|
||||
|
||||
const App = () => (
|
||||
<Auth
|
||||
|
||||
@@ -140,7 +140,7 @@ export async function middleware(req) {
|
||||
|
||||
<TabPanel id="ts" label="TypeScript">
|
||||
|
||||
Create a new `/app/middleware.js` file and populate with the following:
|
||||
Create a new `/app/middleware.ts` file and populate with the following:
|
||||
|
||||
```tsx title="middleware.ts"
|
||||
import { createMiddlewareSupabaseClient } from '@supabase/auth-helpers-nextjs'
|
||||
@@ -282,7 +282,7 @@ type SupabaseContext = {
|
||||
supabase: SupabaseClient<Database>
|
||||
}
|
||||
|
||||
const Context = createContext<SupabaseContext>()
|
||||
const Context = createContext<SupabaseContext | undefined>(undefined)
|
||||
|
||||
export default function SupabaseProvider({ children }: { children: React.ReactNode }) {
|
||||
const [supabase] = useState(() => createClient())
|
||||
@@ -294,7 +294,13 @@ export default function SupabaseProvider({ children }: { children: React.ReactNo
|
||||
)
|
||||
}
|
||||
|
||||
export const useSupabase = () => useContext(Context)
|
||||
export const useSupabase = () => {
|
||||
if (context === undefined) {
|
||||
throw new Error("useSupabase must be used inside SupabaseProvider");
|
||||
}
|
||||
|
||||
return useContext(Context)
|
||||
}
|
||||
```
|
||||
|
||||
> TypeScript types can be [generated with the Supabase CLI](https://supabase.com/docs/reference/javascript/typescript-support) and passed to `createBrowserSupabaseClient` to add type support to the Supabase client.
|
||||
|
||||
@@ -594,7 +594,7 @@ export const config = {
|
||||
|
||||
To make these helpers more flexible as well as more maintainable and easier to upgrade for new versions of Next.js, we're stripping them down to the most useful part which is managing the cookies and giving you an authenticated supabase-js client in any environment (client, server, middleware/edge).
|
||||
|
||||
Therefore we're marking the `withApiAuth`, `withPageAuth`, and `withMiddlewareAuth` higher order functions as deprectaed and they will be removed in the next **minor** release (v0.6.X).
|
||||
Therefore we're marking the `withApiAuth`, `withPageAuth`, and `withMiddlewareAuth` higher order functions as deprecated and they will be removed in the next **minor** release (v0.6.X).
|
||||
|
||||
Please follow the steps below to update your API routes, pages, and middleware handlers. Thanks!
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ If the trigger fails however, it could block the user sign ups - so make sure th
|
||||
For example:
|
||||
|
||||
```sql
|
||||
-- inserts a row into public.users
|
||||
-- inserts a row into public.profiles
|
||||
create function public.handle_new_user()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
|
||||
@@ -7,6 +7,7 @@ export const meta = {
|
||||
title: 'Auth',
|
||||
description: 'Use Supabase to Authenticate and Authorize your users.',
|
||||
sidebar_label: 'Overview',
|
||||
video: 'https://www.youtube.com/v/6ow_jW4epf8',
|
||||
}
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -4,6 +4,7 @@ export const meta = {
|
||||
id: 'auth-twilio',
|
||||
title: 'Phone Auth with Twilio',
|
||||
description: 'How to set up and use Mobile OTP with Twilio and Supabase.',
|
||||
video: 'https://www.youtube.com/v/akScoPO01bc',
|
||||
}
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -4,6 +4,7 @@ export const meta = {
|
||||
id: 'row-level-security',
|
||||
title: 'Row Level Security',
|
||||
description: 'Secure your data using Postgres Row Level Security.',
|
||||
video: 'https://www.youtube.com/v/Ow_Uzedfohk',
|
||||
}
|
||||
|
||||
When you need granular authorization rules, nothing beats PostgreSQL's [Row Level Security (RLS)](https://www.postgresql.org/docs/current/ddl-rowsecurity.html).
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import Layout from '~/layouts/DefaultGuideLayout'
|
||||
|
||||
export const meta = {
|
||||
id: 'cli',
|
||||
title: 'Supabase CLI',
|
||||
description:
|
||||
'The Supabase CLI provides tools to develop your project locally and deploy to the Supabase Platform.',
|
||||
@@ -111,7 +110,8 @@ brew upgrade supabase
|
||||
|
||||
## See also
|
||||
|
||||
- [Supabase CLI Reference](/docs/reference/cli/usage)
|
||||
- [Supabase CLI Reference](/docs/reference/cli/introduction)
|
||||
- [Supabase CLI Configuration](/docs/reference/cli/config)
|
||||
- [Local Development](/docs/guides/cli/local-development)
|
||||
- [Managing Environments](/docs/guides/cli/managing-environments)
|
||||
|
||||
+2
-2
@@ -4,6 +4,7 @@ export const meta = {
|
||||
id: 'local-development',
|
||||
title: 'Local Development',
|
||||
description: 'How to use Supabase on your local development machine.',
|
||||
video: 'https://www.youtube.com/v/vyHyYpvjaks',
|
||||
}
|
||||
|
||||
Learn how to use the Supabase CLI to develop your project locally and deploy to the Supabase Platform.
|
||||
@@ -34,9 +35,8 @@ supabase login
|
||||
<Admonition type="note">
|
||||
|
||||
If you installed the Supabase CLI via NPM you may have to use `npx supabase login`.
|
||||
|
||||
</Admonition>
|
||||
|
||||
</Admonition>
|
||||
|
||||
## Initialize your project
|
||||
|
||||
+1
@@ -4,6 +4,7 @@ export const meta = {
|
||||
id: 'managing-environments',
|
||||
title: 'Managing Environments',
|
||||
description: 'How to deploy Supabase schema changes with a CI / CD pipeline.',
|
||||
video: 'https://www.youtube.com/v/rOLyOsBR1Uc',
|
||||
}
|
||||
|
||||
## Overview
|
||||
@@ -2,7 +2,7 @@ import Layout from '~/layouts/DefaultGuideLayout'
|
||||
|
||||
export const meta = {
|
||||
id: 'extensions',
|
||||
title: 'Overview',
|
||||
title: 'Postgres Extensions Overview',
|
||||
description: 'Using Postgres extensions.',
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ export const meta = {
|
||||
id: 'http',
|
||||
title: 'http: RESTful Client',
|
||||
description: 'An HTTP Client for PostgreSQL Functions.',
|
||||
video: 'https://www.youtube.com/v/rARgrELRCwY',
|
||||
}
|
||||
|
||||
The `http` extension allows you to call RESTful endpoints within Postgres.
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
import Layout from '~/layouts/DefaultGuideLayout'
|
||||
|
||||
export const meta = {
|
||||
id: 'hypopg',
|
||||
title: 'HypoPG: Hypothetical indexes',
|
||||
description: 'Quickly check if an index can be used without creating it.',
|
||||
}
|
||||
|
||||
`HypoPG` is PostgreSQL extension for creating hypothetical/virtual indexes. HypoPG allows users to rapidly create hypothetical/virtual indexes that have no resource cost (CPU, disk, memory) that are visible to the PostgreSQL query planner.
|
||||
That allows users to quickly search for an index to improve a slow query without waiting for them to build.
|
||||
|
||||
## Usage
|
||||
|
||||
### Enable the extension
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="dashboard"
|
||||
>
|
||||
<TabPanel id="dashboard" label="Dashboard">
|
||||
|
||||
1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard.
|
||||
2. Click on **Extensions** in the sidebar.
|
||||
3. Search for "hypopg" and enable the extension.
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="sql" label="SQL">
|
||||
|
||||
{/* prettier-ignore */}
|
||||
```sql
|
||||
-- Enable the "hypopg" extension
|
||||
create extension hypopg with schema extensions;
|
||||
|
||||
-- Disable the "hypopg" extension
|
||||
drop extension if exists hypopg;
|
||||
```
|
||||
|
||||
Even though the SQL code is `create extension`, this is the equivalent of "enabling the extension".
|
||||
To disable an extension you can call `drop extension`.
|
||||
|
||||
It's good practice to create the extension within a separate schema (like `extensions`) to keep your database clean.
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
### Speeding up a query
|
||||
|
||||
Given the following table and a simple query to select from the table by id:
|
||||
|
||||
{/* prettier-ignore */}
|
||||
```sql
|
||||
create table account (
|
||||
id int,
|
||||
address text
|
||||
);
|
||||
|
||||
insert into account(id, address)
|
||||
select
|
||||
id,
|
||||
id || ' main street'
|
||||
from
|
||||
generate_series(1, 10000) id;
|
||||
```
|
||||
|
||||
We can generate an explain plan for a description of how the PostgreSQL query planner
|
||||
intends to execute the query.
|
||||
|
||||
{/* prettier-ignore */}
|
||||
```sql
|
||||
explain select * from account where id=1;
|
||||
|
||||
QUERY PLAN
|
||||
-------------------------------------------------------
|
||||
Seq Scan on account (cost=0.00..180.00 rows=1 width=13)
|
||||
Filter: (id = 1)
|
||||
(2 rows)
|
||||
```
|
||||
|
||||
Using HypoPG, we can create a hypothetical index on the `account(id)` column to check if it would be useful to the query planner and then re-run the explain plan.
|
||||
|
||||
Note that the virtual indexes created by HypoPG are only visible in the PostgreSQL connection that they were created in. Supabase connects to PostgreSQL through a connection pooler so the `hypopg_create_index` statement and the `explain` statement should be executed in a single query.
|
||||
|
||||
{/* prettier-ignore */}
|
||||
```sql
|
||||
select * from hypopg_create_index('create index on account(id)');
|
||||
|
||||
explain select * from account where id=1;
|
||||
|
||||
QUERY PLAN
|
||||
------------------------------------------------------------------------------------
|
||||
Index Scan using <13504>btree_account_id on hypo (cost=0.29..8.30 rows=1 width=13)
|
||||
Index Cond: (id = 1)
|
||||
(2 rows)
|
||||
```
|
||||
|
||||
The query plan has changed from a `Seq Scan` to an `Index Scan` using the newly created virtual index, so we may choose to create a real version of the index to improve performance on the target query:
|
||||
|
||||
{/* prettier-ignore */}
|
||||
```sql
|
||||
create index on account(id);
|
||||
```
|
||||
|
||||
## Functions
|
||||
|
||||
- [`hypo_create_index(text)`](https://hypopg.readthedocs.io/en/rel1_stable/usage.html#create-a-hypothetical-index): A function to create a hypothetical index.
|
||||
- [`hypopg_list_indexes`](https://hypopg.readthedocs.io/en/rel1_stable/usage.html#manipulate-hypothetical-indexes): A View that lists all hypothetical indexes that have been created.
|
||||
- [`hypopg()`](https://hypopg.readthedocs.io/en/rel1_stable/usage.html#manipulate-hypothetical-indexes): A function that lists all hypothetical indexes that have been created with the same format as pg_index.
|
||||
- [`hypopg_get_index_def(oid)`](https://hypopg.readthedocs.io/en/rel1_stable/usage.html#manipulate-hypothetical-indexes): A function to display the `create index` statement that would create the index.
|
||||
- [`hypopg_get_relation_size(oid)`](https://hypopg.readthedocs.io/en/rel1_stable/usage.html#manipulate-hypothetical-indexes): A function to estimate how large a hypothetical index would be.
|
||||
- [`hypopg_drop_index(oid)`](https://hypopg.readthedocs.io/en/rel1_stable/usage.html#manipulate-hypothetical-indexes): A function to remove a given hypothetical index by oid.
|
||||
- [`hypopg_reset()`](https://hypopg.readthedocs.io/en/rel1_stable/usage.html#manipulate-hypothetical-indexes): A function to remove all hypothetical indexes.
|
||||
|
||||
## Resources
|
||||
|
||||
- Official [HypoPG documentation](https://hypopg.readthedocs.io/en/rel1_stable/)
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} />
|
||||
|
||||
export default Page
|
||||
@@ -0,0 +1,113 @@
|
||||
import Layout from '~/layouts/DefaultGuideLayout'
|
||||
|
||||
export const meta = {
|
||||
id: 'pg_jsonschema',
|
||||
title: 'pg_jsonschema: JSON Schema Validation',
|
||||
description: 'Validate json/jsonb with JSON Schema in PostgreSQL.',
|
||||
}
|
||||
|
||||
[JSON Schema](https://json-schema.org) is a language for annotating and validating JSON documents. [`pg_jsonschema`](https://json-schema.org) is a PostgreSQL extension that adds the ability to validate PostgreSQL's built-in `json` and `jsonb` data types against a JSON Schema document.
|
||||
|
||||
## Usage
|
||||
|
||||
### Enable the extension
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="dashboard"
|
||||
>
|
||||
<TabPanel id="dashboard" label="Dashboard">
|
||||
|
||||
1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard.
|
||||
2. Click on **Extensions** in the sidebar.
|
||||
3. Search for "pg_jsonschema" and enable the extension.
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="sql" label="SQL">
|
||||
|
||||
{/* prettier-ignore */}
|
||||
```sql
|
||||
-- Enable the "pg_jsonschema" extension
|
||||
create extension pg_jsonschema with schema extensions;
|
||||
|
||||
-- Disable the "pg_jsonschema" extension
|
||||
drop extension if exists pg_jsonschema;
|
||||
```
|
||||
|
||||
Even though the SQL code is `create extension`, this is the equivalent of "enabling the extension".
|
||||
To disable an extension you can call `drop extension`.
|
||||
|
||||
It's good practice to create the extension within a separate schema (like `extensions`) to keep your database clean.
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
## Functions
|
||||
|
||||
- [`json_matches_schema(schema json, instance json)`](https://github.com/supabase/pg_jsonschema#api): Checks if a `json` _instance_ conforms to a JSON Schema _schema_.
|
||||
- [`jsonb_matches_schema(schema json, instance jsonb)`](https://github.com/supabase/pg_jsonschema#api): Checks if a `jsonb` _instance_ conforms to a JSON Schema _schema_.
|
||||
|
||||
### Validating a document
|
||||
|
||||
Since `pg_jsonschema` exposes its utilities as functions, we can test it with a simple select statement:
|
||||
|
||||
{/* prettier-ignore */}
|
||||
```sql
|
||||
select
|
||||
extensions.json_matches_schema(
|
||||
schema := '{"type": "object"}',
|
||||
instance := '{}'
|
||||
);
|
||||
```
|
||||
|
||||
`pg_jsonschema` is generally used in tandem with a [check constraint](https://www.postgresql.org/docs/current/ddl-constraints.html) as a way to constrain the contents of a json/b field to match a JSON Schema.
|
||||
|
||||
{/* prettier-ignore */}
|
||||
```sql
|
||||
create table customer(
|
||||
id serial primary key,
|
||||
...
|
||||
metadata json,
|
||||
|
||||
check (
|
||||
json_matches_schema(
|
||||
'{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"tags": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"maxLength": 16
|
||||
}
|
||||
}
|
||||
}
|
||||
}',
|
||||
metadata
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
-- Example: Valid Payload
|
||||
insert into customer(metadata)
|
||||
values ('{"tags": ["vip", "darkmode-ui"]}');
|
||||
-- Result:
|
||||
-- INSERT 0 1
|
||||
|
||||
-- Example: Invalid Payload
|
||||
insert into customer(metadata)
|
||||
values ('{"tags": [1, 3]}');
|
||||
-- Result:
|
||||
-- ERROR: new row for relation "customer" violates check constraint "customer_metadata_check"
|
||||
-- DETAIL: Failing row contains (2, {"tags": [1, 3]}).
|
||||
```
|
||||
|
||||
## Resources
|
||||
|
||||
- Official [`pg_jsonschema` documentation](https://github.com/supabase/pg_jsonschema)
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} />
|
||||
|
||||
export default Page
|
||||
@@ -0,0 +1,93 @@
|
||||
import Layout from '~/layouts/DefaultGuideLayout'
|
||||
|
||||
export const meta = {
|
||||
id: 'pg_stat_statements',
|
||||
title: 'pg_stat_statements: SQL Planning and Execution Statistics',
|
||||
description:
|
||||
'Track planning and execution statistics of all SQL statements executed on the database.',
|
||||
}
|
||||
|
||||
`pg_stat_statements` is a database extension that exposes a view, of the same name, to track statistics about SQL statements executed on the database. The following table shows some of the available statistics and metadata:
|
||||
|
||||
| Column Type | Description |
|
||||
| ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
|
||||
| userid oid (references pg_authid.oid) | OID of user who executed the statement |
|
||||
| dbid oid (references pg_database.oid) | OID of database in which the statement was executed |
|
||||
| toplevel bool | True if the query was executed as a top-level statement (always true if pg_stat_statements.track is set to top) |
|
||||
| queryid bigint | Hash code to identify identical normalized queries. |
|
||||
| query text | Text of a representative statement |
|
||||
| plans bigint | Number of times the statement was planned (if pg_stat_statements.track_planning is enabled, otherwise zero) |
|
||||
| total_plan_time double precision | Total time spent planning the statement, in milliseconds (if pg_stat_statements.track_planning is enabled, otherwise zero) |
|
||||
| min_plan_time double precision | Minimum time spent planning the statement, in milliseconds (if pg_stat_statements.track_planning is enabled, otherwise zero) |
|
||||
|
||||
A full list of statistics is available in the [pg_stat_statements docs](https://www.postgresql.org/docs/current/pgstatstatements.html).
|
||||
|
||||
## Usage
|
||||
|
||||
### Enable the extension
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="dashboard"
|
||||
>
|
||||
<TabPanel id="dashboard" label="Dashboard">
|
||||
|
||||
1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard.
|
||||
2. Click on **Extensions** in the sidebar.
|
||||
3. Search for "pg_stat_statements" and enable the extension.
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="sql" label="SQL">
|
||||
|
||||
{/* prettier-ignore */}
|
||||
```sql
|
||||
-- Enable the "pg_stat_statements" extension
|
||||
create extension pg_stat_statements with schema extensions;
|
||||
|
||||
-- Disable the "pg_stat_statements" extension
|
||||
drop extension if exists pg_stat_statements;
|
||||
```
|
||||
|
||||
Even though the SQL code is `create extension`, this is the equivalent of "enabling the extension".
|
||||
To disable an extension you can call `drop extension`.
|
||||
|
||||
It's good practice to create the extension within a separate schema (like `extensions`) to keep your database clean.
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
### Inspecting activity
|
||||
|
||||
A common use for `pg_stat_statements` is to track down expensive or slow queries. The `pg_stat_statements` view contains a row for each executed query with statistics inlined. For example, you can leverage the statistics to identify frequently executed and slow queries against a given table.
|
||||
|
||||
{/* prettier-ignore */}
|
||||
```sql
|
||||
select
|
||||
calls,
|
||||
mean_exec_time,
|
||||
max_exec_time,
|
||||
total_exec_time,
|
||||
stddev_exec_time,
|
||||
query,
|
||||
from
|
||||
pg_stat_statements
|
||||
where
|
||||
calls > 1000 -- at least 50 calls
|
||||
and mean_exec_time > 2.0 -- averaging at least 2ms/call
|
||||
and total_exec_time > 60000 -- at least one minute total server time spent
|
||||
and query ilike '%user_in_organization%' -- filter to queries that touch the user_in_organization table
|
||||
order by
|
||||
calls desc
|
||||
```
|
||||
|
||||
From the results, an informed decision about which queries to optimize, index, or adjust can be made.
|
||||
|
||||
## Resources
|
||||
|
||||
- Official [pg_stat_statements documentation](https://www.postgresql.org/docs/current/pgstatstatements.html)
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} />
|
||||
|
||||
export default Page
|
||||
@@ -0,0 +1,110 @@
|
||||
import Layout from '~/layouts/DefaultGuideLayout'
|
||||
|
||||
export const meta = {
|
||||
title: 'pg_repack: Physical storage optimization and maintenance',
|
||||
description:
|
||||
'A tool to remove bloat from tables and indexes and optimize physical data order and physical storage',
|
||||
}
|
||||
|
||||
[pg_repack](https://github.com/reorg/pg_repack) is a PostgreSQL extension to remove bloat from tables and indexes, and optionally restore the physical order of clustered indexes. Unlike CLUSTER and VACUUM FULL it works online, without holding an exclusive lock on the processed tables during processing. pg_repack is efficient to boot, with performance comparable to using CLUSTER directly.
|
||||
|
||||
pg_repack provides the following methods to optimize physical storage:
|
||||
|
||||
- Online CLUSTER: ordering table data by cluster index in a non-blocking way
|
||||
- Ordering table data by specified columns
|
||||
- Online VACUUM FULL: packing rows only in a non-blocking way
|
||||
- Rebuild or relocate only the indexes of a table
|
||||
|
||||
## Requirements
|
||||
|
||||
- Only superusers can use the utility.
|
||||
- Target table must have a PRIMARY KEY, or a UNIQUE total index on a NOT NULL column.
|
||||
- Performing a full-table repack requires free disk space about twice as large as the target table and its indexes.
|
||||
|
||||
## Usage
|
||||
|
||||
### Enable the extension
|
||||
|
||||
Get started with pg_repack by enabling the extension in the Supabase Dashboard.
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="dashboard"
|
||||
>
|
||||
<TabPanel id="dashboard" label="Dashboard">
|
||||
|
||||
1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard.
|
||||
2. Click on **Extensions** in the sidebar.
|
||||
3. Search for "pg_repack" and enable the extension.
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="sql" label="SQL">
|
||||
|
||||
```sql
|
||||
-- Example: enable the "pg_repack" extension
|
||||
create extension pg_repack with schema extensions;
|
||||
|
||||
-- Example: disable the "pg_repack" extension
|
||||
drop extension if exists pg_repack;
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
### Syntax
|
||||
|
||||
```sh
|
||||
pg_repack [OPTION]... [DBNAME]
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
It's useful for performance to support tables data ordered on disk and physically remove deleted data that remain
|
||||
otherwise.
|
||||
|
||||
Perform an online CLUSTER of all the clustered tables in the database `db`, and perform an online `VACUUM FULL` of all the non-clustered tables:
|
||||
|
||||
```sh
|
||||
pg_repack db
|
||||
```
|
||||
|
||||
Perform an online `VACUUM FULL` on the tables `table1` and `table2` in the database `db` (an eventual cluster index is ignored):
|
||||
|
||||
```sh
|
||||
pg_repack --no-order --table table1 --table table2 db
|
||||
```
|
||||
|
||||
Moving indexes to a tablespace on a faster volume increases performance of `SELECT` queries using these indexes
|
||||
drastically. `INSERT`s and `UPDATE`s of a table with indexes on a fast volume are also faster. This is very useful
|
||||
when the fast volume is small and can not accommodate all tables, as indexes are much smaller than tables.
|
||||
|
||||
Move all indexes of table `table1` to tablespace `tbs`:
|
||||
|
||||
```sh
|
||||
pg_repack -d db --table table1 --only-indexes --tablespace tbs
|
||||
```
|
||||
|
||||
Move the specified index `idx` to tablespace `tbs`:
|
||||
|
||||
```sh
|
||||
pg_repack -d db --index idx --tablespace tbs
|
||||
```
|
||||
|
||||
See the [official pg_repack documentation](https://reorg.github.io/pg_repack/) for the full list of options.
|
||||
|
||||
## Restrictions
|
||||
|
||||
- pg_repack cannot reorganize temp tables.
|
||||
- pg_repack cannot cluster tables by GiST indexes.
|
||||
- You cannot perform DDL commands of the target tables except VACUUM or ANALYZE while pg_repack is working.
|
||||
pg_repack holds an ACCESS SHARE lock on the target table to enforce this restriction.
|
||||
|
||||
## Resources
|
||||
|
||||
- [Official pg_repack documentation](https://reorg.github.io/pg_repack/)
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} />
|
||||
|
||||
export default Page
|
||||
@@ -0,0 +1,113 @@
|
||||
import Layout from '~/layouts/DefaultGuideLayout'
|
||||
|
||||
export const meta = {
|
||||
id: 'pgroonga',
|
||||
title: 'PGRoonga: Multilingual Full Text Search',
|
||||
description: 'Full Text Search for multiple languages in PostgreSQL',
|
||||
}
|
||||
|
||||
`PGroonga` is a PostgreSQL extension adding a full text search indexing method based on [Groonga](https://groonga.org). While native PostgreSQL supports full text indexing, it is limited to alphabet and digit based languages. `PGroonga` offers a wider range of character support making it viable for a superset of languages supported by PostgreSQL including Japanese, Chinese, etc.
|
||||
|
||||
## Usage
|
||||
|
||||
### Enable the extension
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="dashboard"
|
||||
>
|
||||
<TabPanel id="dashboard" label="Dashboard">
|
||||
|
||||
1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard.
|
||||
2. Click on **Extensions** in the sidebar.
|
||||
3. Search for "pgroonga" and enable the extension.
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="sql" label="SQL">
|
||||
|
||||
{/* prettier-ignore */}
|
||||
```sql
|
||||
-- Enable the "pgroonga" extension
|
||||
create extension pgroonga with schema extensions;
|
||||
|
||||
-- Disable the "pgroonga" extension
|
||||
drop extension if exists pgroonga;
|
||||
```
|
||||
|
||||
Even though the SQL code is `create extension`, this is the equivalent of "enabling the extension".
|
||||
To disable an extension you can call `drop extension`.
|
||||
|
||||
It's good practice to create the extension within a separate schema (like `extensions`) to keep your database clean.
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
### Creating a full text search index
|
||||
|
||||
Given a table with a `text` column:
|
||||
|
||||
{/* prettier-ignore */}
|
||||
```sql
|
||||
create table memos (
|
||||
id serial primary key,
|
||||
content text
|
||||
);
|
||||
```
|
||||
|
||||
We can index the column for full text search with a `pgroonga` index:
|
||||
|
||||
{/* prettier-ignore */}
|
||||
```sql
|
||||
create index ix_memos_content ON memos USING pgroonga(content);
|
||||
```
|
||||
|
||||
To test the full text index, we'll add some data.
|
||||
|
||||
{/* prettier-ignore */}
|
||||
```sql
|
||||
insert into memos(content)
|
||||
values
|
||||
('PostgreSQL is a relational database management system.'),
|
||||
('Groonga is a fast full text search engine that supports all languages.'),
|
||||
('PGroonga is a PostgreSQL extension that uses Groonga as index.'),
|
||||
('There is groonga command.');
|
||||
```
|
||||
|
||||
The PostgreSQL query planner is smart enough to know that, for extremely small tables, it's faster to scan the whole table rather than loading an index. To force the index to be used, we can disable sequential scans:
|
||||
|
||||
{/* prettier-ignore */}
|
||||
```sql
|
||||
-- For testing only. Don't do this in production
|
||||
set enable_seqscan = off;
|
||||
```
|
||||
|
||||
Now if we run an explain plan on a query filtering on `memos.content`:
|
||||
|
||||
{/* prettier-ignore */}
|
||||
```sql
|
||||
explain select * from memos where content like '%engine%';
|
||||
|
||||
QUERY PLAN
|
||||
-----------------------------------------------------------------------------
|
||||
Index Scan using ix_memos_content on memos (cost=0.00..1.11 rows=1 width=36)
|
||||
Index Cond: (content ~~ '%engine%'::text)
|
||||
(2 rows)
|
||||
```
|
||||
|
||||
The pgroonga index is used to retrive the result set:
|
||||
|
||||
```markdown
|
||||
| id | content |
|
||||
| --- | ------------------------------------------------------------------------ |
|
||||
| 2 | 'Groonga is a fast full text search engine that supports all languages.' |
|
||||
```
|
||||
|
||||
## Resources
|
||||
|
||||
- Official [PGroonga documentation](https://pgroonga.github.io/tutorial/)
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} />
|
||||
|
||||
export default Page
|
||||
@@ -0,0 +1,163 @@
|
||||
import Layout from '~/layouts/DefaultGuideLayout'
|
||||
|
||||
export const meta = {
|
||||
id: 'rum',
|
||||
title: 'RUM: improved inverted index for full-text search based on GIN index',
|
||||
description: 'A GIN-like index with additional tree-organized data for each index entry',
|
||||
}
|
||||
|
||||
[RUM](https://github.com/postgrespro/rum) is an extension which adds a RUM index to Postgresql.
|
||||
|
||||
RUM index is based on GIN that stores additional per-entry information in a posting tree. For example, positional information of lexemes or timestamps. In comparison to GIN it can use this information to make faster index-only scans for:
|
||||
|
||||
- Phrase search
|
||||
- Text search with ranking by text distance operator
|
||||
- Text `SELECT`s with ordering by some non-indexed additional column e.g. by timestamp.
|
||||
|
||||
RUM works best in scenarios when the possible keys are highly repeatable. I.e. all texts are composed of a
|
||||
limited amount of words, so per-lexeme indexing gives significant speed-up in searching texts containing word
|
||||
combinations or phrases.
|
||||
|
||||
Main operators for ordering are:
|
||||
|
||||
tsvector `<=>` tsquery | float4 | Distance between tsvector and tsquery.
|
||||
value `<=>` value | float8 | Distance between two values.
|
||||
|
||||
Where value is timestamp, timestamptz, int2, int4, int8, float4, float8, money and oid
|
||||
|
||||
## Usage
|
||||
|
||||
### Enable the extension
|
||||
|
||||
You can get started with rum by enabling the extension in your Supabase dashboard.
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="dashboard"
|
||||
>
|
||||
<TabPanel id="dashboard" label="Dashboard">
|
||||
|
||||
1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard.
|
||||
2. Click on **Extensions** in the sidebar.
|
||||
3. Search for "rum" and enable the extension.
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="sql" label="SQL">
|
||||
|
||||
```sql
|
||||
-- Example: enable the "rum" extension
|
||||
create extension rum with schema extensions;
|
||||
|
||||
-- Example: disable the "rum" extension
|
||||
drop extension if exists rum;
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
### Syntax
|
||||
|
||||
#### For type: tsvector
|
||||
|
||||
To understand the following you may need first to see [Official PostgreSQL documentation on text
|
||||
search](https://www.postgresql.org/docs/current/functions-textsearch.html)
|
||||
|
||||
`rum_tsvector_ops`
|
||||
|
||||
```sql
|
||||
CREATE TABLE test_rum(t text, a tsvector);
|
||||
|
||||
CREATE TRIGGER tsvectorupdate
|
||||
BEFORE UPDATE OR INSERT ON test_rum
|
||||
FOR EACH ROW EXECUTE PROCEDURE tsvector_update_trigger('a', 'pg_catalog.english', 't');
|
||||
|
||||
INSERT INTO test_rum(t) VALUES ('The situation is most beautiful');
|
||||
INSERT INTO test_rum(t) VALUES ('It is a beautiful');
|
||||
INSERT INTO test_rum(t) VALUES ('It looks like a beautiful place');
|
||||
|
||||
CREATE INDEX rumidx ON test_rum USING rum (a rum_tsvector_ops);
|
||||
```
|
||||
|
||||
And we can execute `tsvector` selects with ordering by text distance operator:
|
||||
|
||||
```sql
|
||||
SELECT t, a `<=>` to_tsquery('english', 'beautiful | place') AS rank
|
||||
FROM test_rum
|
||||
WHERE a @@ to_tsquery('english', 'beautiful | place')
|
||||
ORDER BY a `<=>` to_tsquery('english', 'beautiful | place');
|
||||
t | rank
|
||||
---------------------------------+---------
|
||||
It looks like a beautiful place | 8.22467
|
||||
The situation is most beautiful | 16.4493
|
||||
It is a beautiful | 16.4493
|
||||
(3 rows)
|
||||
```
|
||||
|
||||
`rum_tsvector_addon_ops`
|
||||
|
||||
```sql
|
||||
CREATE TABLE tsts (id int, t tsvector, d timestamp);
|
||||
CREATE INDEX tsts_idx ON tsts USING rum (t rum_tsvector_addon_ops, d)
|
||||
WITH (attach = 'd', to = 't');
|
||||
```
|
||||
|
||||
Now we can execute the selects with ordering distance operator on attached column:
|
||||
|
||||
```sql
|
||||
SELECT id, d, d `<=>` '2016-05-16 14:21:25' FROM tsts WHERE t @@ 'wr&qh' ORDER BY d `<=>` '2016-05-16 14:21:25' LIMIT 5;
|
||||
id | d | ?column?
|
||||
-----+---------------------------------+---------------
|
||||
355 | Mon May 16 14:21:22.326724 2016 | 2.673276
|
||||
354 | Mon May 16 13:21:22.326724 2016 | 3602.673276
|
||||
371 | Tue May 17 06:21:22.326724 2016 | 57597.326724
|
||||
406 | Wed May 18 17:21:22.326724 2016 | 183597.326724
|
||||
415 | Thu May 19 02:21:22.326724 2016 | 215997.326724
|
||||
(5 rows)
|
||||
```
|
||||
|
||||
#### For type: anyarray
|
||||
|
||||
`rum_anyarray_ops`
|
||||
|
||||
This operator class stores anyarray elements with length of the array. It supports operators `&&`, `@>`, `<@`, `=`, `%` operators. It also supports ordering by `<=>` operator.
|
||||
|
||||
```sql
|
||||
CREATE TABLE test_array (i int2[]);
|
||||
INSERT INTO test_array VALUES ('{}'), ('{0}'), ('{1,2,3,4}'), ('{1,2,3}'), ('{1,2}'), ('{1}');
|
||||
CREATE INDEX idx_array ON test_array USING rum (i rum_anyarray_ops);
|
||||
```
|
||||
|
||||
Now we can execute the query using index scan:
|
||||
|
||||
```sql
|
||||
SELECT * FROM test_array WHERE i && '{1}' ORDER BY i `<=>` '{1}' ASC;
|
||||
i
|
||||
-----------
|
||||
{1}
|
||||
{1,2}
|
||||
{1,2,3}
|
||||
{1,2,3,4}
|
||||
(4 rows)
|
||||
```
|
||||
|
||||
`rum_anyarray_addon_ops`
|
||||
|
||||
The does the same with anyarray index as `rum_tsvector_addon_ops` i.e. allows to order select results using distance
|
||||
operator by attached column.
|
||||
|
||||
## Limitations
|
||||
|
||||
`RUM` has slower build and insert times than `GIN` due to:
|
||||
|
||||
1. It is bigger due to the additional attributes stored in the index.
|
||||
2. It uses generic WAL records.
|
||||
|
||||
## Resources
|
||||
|
||||
- [Official RUM documentation](https://github.com/postgrespro/rum)
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} />
|
||||
|
||||
export default Page
|
||||
@@ -4,6 +4,7 @@ export const meta = {
|
||||
id: 'full-text-search',
|
||||
title: 'Full Text Search',
|
||||
description: 'How to use full text search in PostgreSQL.',
|
||||
video: 'https://www.youtube.com/v/b-mgca_2Oe4',
|
||||
}
|
||||
|
||||
Postgres has built-in functions to handle `Full Text Search` queries. This is like a "search engine" within Postgres.
|
||||
|
||||
@@ -4,6 +4,7 @@ export const meta = {
|
||||
id: 'functions',
|
||||
title: 'Database Functions',
|
||||
description: 'Creating and using Postgres functions.',
|
||||
video: 'https://www.youtube.com/v/MJZCCpCYEqk',
|
||||
}
|
||||
|
||||
Postgres has built-in support for [SQL functions](https://www.postgresql.org/docs/current/sql-createfunction.html).
|
||||
|
||||
@@ -4,6 +4,7 @@ export const meta = {
|
||||
id: 'tables',
|
||||
title: 'Tables and Data',
|
||||
description: 'Creating and using Postgres tables.',
|
||||
video: 'https://www.youtube.com/v/TKwF3IGij5c',
|
||||
}
|
||||
|
||||
Tables are where you store your data.
|
||||
@@ -77,7 +78,7 @@ You must define the "data type" when you create a column.
|
||||
### Data types
|
||||
|
||||
Every column is a predefined type. PostgreSQL provides many [default types](https://www.postgresql.org/docs/current/datatype.html), and you can even design your own (or use extensions)
|
||||
if the default types don't fit your needs.
|
||||
if the default types don't fit your needs. You can use any data type that Postgres supports via the SQL editor. We only support a subset of these in the Table Editor in an effort to keep the experience simple for people with less experience with databases.
|
||||
|
||||
<details>
|
||||
<summary>Show/Hide default data types</summary>
|
||||
|
||||
@@ -4,6 +4,7 @@ export const meta = {
|
||||
id: 'webhooks',
|
||||
title: 'Database Webhooks',
|
||||
description: 'Trigger external payloads on database events.',
|
||||
video: 'https://www.youtube.com/v/codAs9-NeHM',
|
||||
}
|
||||
|
||||
Database Webhooks allow you to send real-time data from your database to another system whenever a table event occurs.
|
||||
|
||||
@@ -4,6 +4,7 @@ export const meta = {
|
||||
id: 'examples',
|
||||
title: 'Examples and Resources',
|
||||
description: 'Examples you can use to get started with Supabase',
|
||||
video: 'https://www.youtube.com/v/7uKQBl9uZ00',
|
||||
}
|
||||
|
||||
{/* <!-- This page is outdated and should not be added to the sidebar until it has been reworked --> */}
|
||||
|
||||
@@ -4,6 +4,7 @@ export const meta = {
|
||||
id: 'cicd-workflow',
|
||||
title: 'CI / CD Workflow',
|
||||
description: 'How to deploy Supabase Edge Functions with a CI / CD pipeline.',
|
||||
video: 'https://www.youtube.com/v/6OMVWiiycLs',
|
||||
}
|
||||
|
||||
As described in the Supabase CLI [Environments Guide](/docs/guides/cli/managing-environments), you can use the [`setup-cli` GitHub Action](https://github.com/marketplace/actions/supabase-cli-action) to run Supabase CLI commands in your GitHub Actions, for example to deploy a Supabase Edge Function:
|
||||
|
||||
@@ -3,16 +3,16 @@ import Layout from '~/layouts/DefaultGuideLayout'
|
||||
export const meta = {
|
||||
id: 'functions-cors',
|
||||
title: 'CORS (Cross-Origin Resource Sharing)',
|
||||
description: 'Add CORS headers to invoke functions from the browser.',
|
||||
description: 'Add CORS headers to invoke Edge Functions from the browser.',
|
||||
}
|
||||
|
||||
To invoke the functions from the browser, you need to handle [CORS Preflight](https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request) requests.
|
||||
To invoke edge functions from the browser, you need to handle [CORS Preflight](https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request) requests.
|
||||
|
||||
See the [example on GitHub](https://github.com/supabase/supabase/blob/master/examples/edge-functions/supabase/functions/browser-with-cors/index.ts).
|
||||
|
||||
### Recommended setup
|
||||
|
||||
We recommend adding a `corst.ts` file within a [`_shared` folder](/docs/guides/functions/quickstart#organizing-your-edge-functions) which makes it easy to reuse the CORS headers across functions:
|
||||
We recommend adding a `cors.ts` file within a [`_shared` folder](/docs/guides/functions/quickstart#organizing-your-edge-functions) which makes it easy to reuse the CORS headers across functions:
|
||||
|
||||
```ts cors.ts
|
||||
export const corsHeaders = {
|
||||
|
||||
@@ -2,8 +2,8 @@ import Layout from '~/layouts/DefaultGuideLayout'
|
||||
|
||||
export const meta = {
|
||||
id: 'functions-debugging',
|
||||
title: 'Debugging Functions',
|
||||
description: 'Debug functions in production.',
|
||||
title: 'Debugging Edge Functions',
|
||||
description: 'Debug Edge Functions in production.',
|
||||
}
|
||||
|
||||
You can debug your deployed Edge Functions using the ["Functions" section](https://app.supabase.com/project/_/functions) of the Dashboard. There are two debugging tools available:
|
||||
|
||||
@@ -4,6 +4,7 @@ export const meta = {
|
||||
id: 'examples-cloudflare-turnstile',
|
||||
title: 'Cloudflare Turnstile',
|
||||
description: 'Protecting Forms with Cloudflare Turnstile.',
|
||||
video: 'https://www.youtube.com/v/OwW0znboh60',
|
||||
}
|
||||
|
||||
<div class="video-container">
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
import Layout from '~/layouts/DefaultGuideLayout'
|
||||
|
||||
export const meta = {
|
||||
id: 'examples-postgres-on-the-edge',
|
||||
title: 'Connect to Postgres',
|
||||
description: 'Connecting to Postgres from Edge Functions.',
|
||||
}
|
||||
|
||||
<div class="video-container">
|
||||
<iframe
|
||||
src="https://www.youtube-nocookie.com/embed/cl7EuF1-RsY"
|
||||
frameBorder="1"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||
allowFullScreen
|
||||
></iframe>
|
||||
</div>
|
||||
|
||||
Supabase Edge Functions allow you to go beyond HTTP and can connect to your Postgres Database directly!
|
||||
|
||||
```ts index.ts
|
||||
import * as postgres from 'https://deno.land/x/postgres@v0.14.2/mod.ts'
|
||||
import { serve } from 'https://deno.land/std@0.168.0/http/server.ts'
|
||||
|
||||
// Get the connection string from the environment variable "DATABASE_URL"
|
||||
const databaseUrl = Deno.env.get('DATABASE_URL')!
|
||||
|
||||
// Create a database pool with three connections that are lazily established
|
||||
const pool = new postgres.Pool(databaseUrl, 3, true)
|
||||
|
||||
serve(async (_req) => {
|
||||
try {
|
||||
// Grab a connection from the pool
|
||||
const connection = await pool.connect()
|
||||
|
||||
try {
|
||||
// Run a query
|
||||
const result = await connection.queryObject`SELECT * FROM animals`
|
||||
const animals = result.rows // [{ id: 1, name: "Lion" }, ...]
|
||||
console.log(animals)
|
||||
|
||||
// Encode the result as pretty printed JSON
|
||||
const body = JSON.stringify(
|
||||
animals,
|
||||
(key, value) => (typeof value === 'bigint' ? value.toString() : value),
|
||||
2
|
||||
)
|
||||
|
||||
// Return the response with the correct content type header
|
||||
return new Response(body, {
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8',
|
||||
},
|
||||
})
|
||||
} finally {
|
||||
// Release the connection back into the pool
|
||||
connection.release()
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
return new Response(String(err?.message ?? err), { status: 500 })
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} />
|
||||
|
||||
export default Page
|
||||
@@ -4,11 +4,12 @@ export const meta = {
|
||||
id: 'examples-github-actions',
|
||||
title: 'GitHub Actions',
|
||||
description: 'Deploying Edge Functions with GitHub Actions.',
|
||||
video: 'https://www.youtube.com/v/l2KlzGrhB6w',
|
||||
}
|
||||
|
||||
<div class="video-container">
|
||||
<iframe
|
||||
src="https://www.youtube-nocookie.com/embed/6OMVWiiycLs"
|
||||
src="https://www.youtube-nocookie.com/embed/l2KlzGrhB6w"
|
||||
frameBorder="1"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||
allowFullScreen
|
||||
@@ -31,8 +32,8 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
env:
|
||||
SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }}
|
||||
PROJECT_ID: zdtdtxajzydjqzuktnqx
|
||||
SUPABASE_ACCESS_TOKEN: YOUR_SUPABASE_ACCESS_TOKEN
|
||||
PROJECT_ID: YOUR_SUPABASE_PROJECT_ID
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
@@ -4,6 +4,7 @@ export const meta = {
|
||||
id: 'examples-og-image',
|
||||
title: 'Generating OG Images',
|
||||
description: 'Generate Open Graph images with Deno and Supabase Edge Functions.',
|
||||
video: 'https://www.youtube.com/v/jZgyOJGWayQ',
|
||||
}
|
||||
|
||||
<div class="video-container">
|
||||
|
||||
@@ -4,6 +4,7 @@ export const meta = {
|
||||
id: 'examples-storage-caching',
|
||||
title: 'Caching Images with Supabase Storage CDN',
|
||||
description: 'Integrate Edge Functions with Supabase Storage to cache images on the Edge (CDN).',
|
||||
video: 'https://www.youtube.com/v/wW6L52v9Ldo',
|
||||
}
|
||||
|
||||
<div class="video-container">
|
||||
|
||||
@@ -4,6 +4,7 @@ export const meta = {
|
||||
id: 'examples-stripe-webhooks',
|
||||
title: 'Handling Stripe Webhooks',
|
||||
description: 'Handling signed Stripe Webhooks with Edge Functions.',
|
||||
video: 'https://www.youtube.com/v/6OMVWiiycLs',
|
||||
}
|
||||
|
||||
<div class="video-container">
|
||||
|
||||
@@ -4,6 +4,7 @@ export const meta = {
|
||||
id: 'examples-telegram-bot',
|
||||
title: 'Telegram Bot',
|
||||
description: 'Building a Telegram Bot with Edge Functions.',
|
||||
video: 'https://www.youtube.com/v/AWfE3a9J_uo',
|
||||
}
|
||||
|
||||
<div class="video-container">
|
||||
|
||||
@@ -4,8 +4,18 @@ export const meta = {
|
||||
id: 'functions-import-maps',
|
||||
title: 'Import Maps',
|
||||
description: 'Using Import Maps.',
|
||||
video: 'https://www.youtube.com/v/ILr3cneZuFk',
|
||||
}
|
||||
|
||||
<div class="video-container">
|
||||
<iframe
|
||||
src="https://www.youtube-nocookie.com/embed/ILr3cneZuFk"
|
||||
frameBorder="1"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||
allowFullScreen
|
||||
></iframe>
|
||||
</div>
|
||||
|
||||
Since [Supabase CLI](https://supabase.com/docs/guides/resources/supabase-cli) version `1.33.0` [import maps](https://deno.land/manual@v1.29.2/node/import_maps) can be used with Supabase Edge Functions.
|
||||
|
||||
[Import maps](https://github.com/WICG/import-maps#the-import-map) is a web-platform standard that allows you to use bare specifiers with Deno without having to install the Node.js package locally.
|
||||
|
||||
@@ -3,7 +3,7 @@ import Layout from '~/layouts/DefaultGuideLayout'
|
||||
export const meta = {
|
||||
id: 'functions-local-development',
|
||||
title: 'Local Development',
|
||||
description: 'Run your functions locally.',
|
||||
description: 'Run your Edge Functions locally.',
|
||||
}
|
||||
|
||||
You can run your Edge Function locally using [`supabase functions serve`](/docs/reference/cli/usage#supabase-functions-serve):
|
||||
@@ -15,9 +15,9 @@ supabase functions serve hello-world # start the Function watcher
|
||||
|
||||
The `functions serve` command has hot-reloading capabilities. It will watch for any changes to your files and restart the Deno server.
|
||||
|
||||
### Invoking functions locally
|
||||
### Invoking Edge Functions locally
|
||||
|
||||
While serving your local Function, you can invoke it using curl:
|
||||
While serving your local Edge Function, you can invoke it using curl:
|
||||
|
||||
```bash
|
||||
curl --request POST 'http://localhost:54321/functions/v1/hello-world' \
|
||||
@@ -44,7 +44,7 @@ const { data, error } = await supabase.functions.invoke('hello-world', {
|
||||
|
||||
You should see the response `{ "message":"Hello Functions!" }`.
|
||||
|
||||
If you execute Function with a different payload the response will change. <br />
|
||||
If you execute the function with a different payload the response will change. <br />
|
||||
Modify the `--data '{"name":"Functions"}'` line to `--data '{"name":"World"}'` and try invoking the command again!
|
||||
|
||||
<Admonition type="note">
|
||||
|
||||
@@ -5,6 +5,7 @@ export const meta = {
|
||||
title: 'Edge Functions Quickstart',
|
||||
description: 'Globally distributed TypeScript Functions.',
|
||||
sidebar_label: 'Quickstart',
|
||||
video: 'https://www.youtube.com/v/rzglqRdZUQE',
|
||||
}
|
||||
|
||||
Learn how to build an Edge Function locally and deploy it to the Supabase Platform in less than 7 minutes.
|
||||
@@ -28,7 +29,7 @@ Follow the steps to prepare your Supabase project on your local machine.
|
||||
- Link to your Remote Project using the command `supabase link --project-ref your-project-ref`. [Docs](/docs/reference/cli/usage#supabase-link).
|
||||
- Optional: Setup your environment: Follow [this setup guide](https://deno.land/manual/getting_started/setup_your_environment) to integrate the Deno language server with your editor.
|
||||
|
||||
## Create a function
|
||||
## Create an Edge Function
|
||||
|
||||
Let's create a new Edge Function called `hello-world` inside your project:
|
||||
|
||||
|
||||
@@ -2,17 +2,17 @@ import Layout from '~/layouts/DefaultGuideLayout'
|
||||
|
||||
export const meta = {
|
||||
id: 'schedule-functions',
|
||||
title: 'Schedule Functions',
|
||||
description: 'Schedule Functions with pg_cron.',
|
||||
title: 'Schedule Edge Functions',
|
||||
description: 'Schedule Edge Functions with pg_cron.',
|
||||
}
|
||||
|
||||
The hosted Supabase Platform supports the [`pg_cron` extension](/docs/guides/database/extensions/pgcron), a simple cron-based job scheduler for PostgreSQL that runs inside the database.
|
||||
|
||||
In combination with the [`pg_net` extension](/docs/guides/database/extensions/pgnet), this allows us to invoke functions periodically on a set schedule.
|
||||
In combination with the [`pg_net` extension](/docs/guides/database/extensions/pgnet), this allows us to invoke Edge Functions periodically on a set schedule.
|
||||
|
||||
## Examples
|
||||
|
||||
### Invoke Function every minute
|
||||
### Invoke an Edge Function every minute
|
||||
|
||||
Make a POST request to a Supabase Edge Function every minute:
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ By default, Edge Functions have access to these secrets:
|
||||
|
||||
Let's create a local file for storing our secrets, and inside it we can store a secret `MY_NAME`:
|
||||
|
||||
```jsx
|
||||
```bash
|
||||
echo "MY_NAME=Yoda" >> ./supabase/.env.local
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
import Layout from '~/layouts/DefaultGuideLayout'
|
||||
import StepHikeCompact from '~/components/StepHikeCompact'
|
||||
|
||||
export const meta = {
|
||||
title: 'Use Supabase with SolidJS',
|
||||
subtitle:
|
||||
'Learn how to create a Supabase project, add some sample data to your database, and query the data from a SolidJS app.',
|
||||
breadcrumb: 'Framework Quickstarts',
|
||||
}
|
||||
|
||||
<StepHikeCompact>
|
||||
|
||||
<StepHikeCompact.Step step={1}>
|
||||
<StepHikeCompact.Details title="Set up a Supabase project with sample data">
|
||||
|
||||
[Create a new project](https://app.supabase.com) in the Supabase Dashboard.
|
||||
|
||||
After your project is ready, create a table in your Supabase database using the [SQL Editor](https://app.supabase.com/project/_/sql) in the Dashboard. Use the following SQL statement to create a `countries` table with some sample data.
|
||||
|
||||
</StepHikeCompact.Details>
|
||||
|
||||
<StepHikeCompact.Code>
|
||||
|
||||
```sql SQL_EDITOR
|
||||
-- Create the table
|
||||
CREATE TABLE countries (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL
|
||||
);
|
||||
-- Insert some sample data into the table
|
||||
INSERT INTO countries (name) VALUES ('United States');
|
||||
INSERT INTO countries (name) VALUES ('Canada');
|
||||
INSERT INTO countries (name) VALUES ('Mexico');
|
||||
````
|
||||
|
||||
</StepHikeCompact.Code>
|
||||
|
||||
</StepHikeCompact.Step>
|
||||
|
||||
<StepHikeCompact.Step step={2}>
|
||||
|
||||
<StepHikeCompact.Details title="Create a SolidJS app">
|
||||
|
||||
Create a SolidJS app using the `degit` command.
|
||||
|
||||
</StepHikeCompact.Details>
|
||||
|
||||
<StepHikeCompact.Code>
|
||||
|
||||
```bash Terminal
|
||||
npx degit solidjs/templates/js my-app
|
||||
```
|
||||
|
||||
</StepHikeCompact.Code>
|
||||
|
||||
</StepHikeCompact.Step>
|
||||
|
||||
<StepHikeCompact.Step step={3}>
|
||||
<StepHikeCompact.Details title="Install the Supabase client library">
|
||||
|
||||
The fastest way to get started is to use the `supabase-js` client library which provides a convenient interface for working with Supabase from a SolidJS app.
|
||||
|
||||
Navigate to the SolidJS app and install `supabase-js`.
|
||||
|
||||
</StepHikeCompact.Details>
|
||||
|
||||
<StepHikeCompact.Code>
|
||||
|
||||
```bash Terminal
|
||||
cd my-app && npm install @supabase/supabase-js
|
||||
```
|
||||
|
||||
</StepHikeCompact.Code>
|
||||
|
||||
</StepHikeCompact.Step>
|
||||
|
||||
<StepHikeCompact.Step step={4}>
|
||||
<StepHikeCompact.Details title="Query data from the app">
|
||||
|
||||
In `App.jsx`, create a Supabase client using your [Project URL and public API (anon) key](https://app.supabase.com/project/_/settings/api).
|
||||
|
||||
Add a `getCountries` function to fetch the data and display the query result to the page.
|
||||
|
||||
</StepHikeCompact.Details>
|
||||
<StepHikeCompact.Code>
|
||||
|
||||
|
||||
```jsx src/App.jsx
|
||||
import { createClient } from "@supabase/supabase-js";
|
||||
import { createEffect, createSignal, For } from "solid-js";
|
||||
|
||||
const supabase = createClient('https://<project>.supabase.co', '<your-anon-key>');
|
||||
|
||||
function App() {
|
||||
const [countries, setCountries] = createSignal();
|
||||
|
||||
createEffect(() => {
|
||||
getCountries();
|
||||
});
|
||||
|
||||
async function getCountries() {
|
||||
const { data } = await supabase.from("countries").select();
|
||||
setCountries(data);
|
||||
}
|
||||
|
||||
return (
|
||||
<ul>
|
||||
<For each={countries()}>{(country) => <li>{country.name}</li>}</For>
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
```
|
||||
|
||||
</StepHikeCompact.Code>
|
||||
|
||||
</StepHikeCompact.Step>
|
||||
|
||||
<StepHikeCompact.Step step={5}>
|
||||
<StepHikeCompact.Details title="Start the app">
|
||||
|
||||
Start the app and go to http://localhost:3000 in a browser and you should see the list of countries.
|
||||
|
||||
</StepHikeCompact.Details>
|
||||
|
||||
<StepHikeCompact.Code>
|
||||
|
||||
```bash Terminal
|
||||
npm run dev
|
||||
```
|
||||
|
||||
</StepHikeCompact.Code>
|
||||
|
||||
</StepHikeCompact.Step>
|
||||
</StepHikeCompact>
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} hideToc={true} />
|
||||
|
||||
export default Page
|
||||
@@ -0,0 +1,166 @@
|
||||
import Layout from '~/layouts/DefaultGuideLayout'
|
||||
import StepHikeCompact from '~/components/StepHikeCompact'
|
||||
|
||||
export const meta = {
|
||||
title: 'Use Supabase with SvelteKit',
|
||||
subtitle:
|
||||
'Learn how to create a Supabase project, add some sample data to your database, and query the data from a SvelteKit app.',
|
||||
breadcrumb: 'Framework Quickstarts',
|
||||
}
|
||||
|
||||
<StepHikeCompact>
|
||||
|
||||
<StepHikeCompact.Step step={1}>
|
||||
<StepHikeCompact.Details title="Set up a Supabase project with sample data">
|
||||
|
||||
[Create a new project](https://app.supabase.com) in the Supabase Dashboard.
|
||||
|
||||
After your project is ready, create a table in your Supabase database using the [SQL Editor](https://app.supabase.com/project/_/sql) in the Dashboard. Use the following SQL statement to create a `countries` table with some sample data.
|
||||
|
||||
</StepHikeCompact.Details>
|
||||
|
||||
<StepHikeCompact.Code>
|
||||
|
||||
```sql SQL_EDITOR
|
||||
-- Create the table
|
||||
CREATE TABLE countries (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL
|
||||
);
|
||||
-- Insert some sample data into the table
|
||||
INSERT INTO countries (name) VALUES ('United States');
|
||||
INSERT INTO countries (name) VALUES ('Canada');
|
||||
INSERT INTO countries (name) VALUES ('Mexico');
|
||||
````
|
||||
|
||||
</StepHikeCompact.Code>
|
||||
|
||||
</StepHikeCompact.Step>
|
||||
|
||||
<StepHikeCompact.Step step={2}>
|
||||
|
||||
<StepHikeCompact.Details title="Create a SvelteKit app">
|
||||
|
||||
Create a SvelteKit app using the `npm create` command.
|
||||
|
||||
</StepHikeCompact.Details>
|
||||
|
||||
<StepHikeCompact.Code>
|
||||
|
||||
```bash Terminal
|
||||
npm create svelte@latest myapp
|
||||
```
|
||||
|
||||
</StepHikeCompact.Code>
|
||||
|
||||
</StepHikeCompact.Step>
|
||||
|
||||
<StepHikeCompact.Step step={3}>
|
||||
<StepHikeCompact.Details title="Install the Supabase client library">
|
||||
|
||||
The fastest way to get started is to use the `supabase-js` client library which provides a convenient interface for working with Supabase from a SvelteKit app.
|
||||
|
||||
Navigate to the SvelteKit app and install `supabase-js`.
|
||||
|
||||
</StepHikeCompact.Details>
|
||||
|
||||
<StepHikeCompact.Code>
|
||||
|
||||
```bash Terminal
|
||||
cd my-app && npm install @supabase/supabase-js
|
||||
```
|
||||
|
||||
</StepHikeCompact.Code>
|
||||
|
||||
</StepHikeCompact.Step>
|
||||
|
||||
<StepHikeCompact.Step step={4}>
|
||||
<StepHikeCompact.Details title="Create the Supabase client">
|
||||
|
||||
Create a `/src/lib` directory in your SvelteKit app, create a file called `supabaseClient.js` and add the following code to initialize the Supabase client with your [project URL and public API (anon) key](https://app.supabase.com/project/_/settings/api).
|
||||
|
||||
</StepHikeCompact.Details>
|
||||
|
||||
<StepHikeCompact.Code>
|
||||
|
||||
```js src/lib/supabaseClient.js
|
||||
import { createClient } from '@supabase/supabase-js'
|
||||
|
||||
export const supabase = createClient('https://<project>.supabase.co', '<your-anon-key>')
|
||||
```
|
||||
|
||||
</StepHikeCompact.Code>
|
||||
|
||||
</StepHikeCompact.Step>
|
||||
|
||||
<StepHikeCompact.Step step={5}>
|
||||
<StepHikeCompact.Details title="Query data from the app">
|
||||
|
||||
Use `load` method to fetch the data server-side and display the query results as a simple list.
|
||||
|
||||
Create `+page.server.js` file in the `routes` directory with the following code.
|
||||
|
||||
</StepHikeCompact.Details>
|
||||
<StepHikeCompact.Code>
|
||||
|
||||
|
||||
```js src/routes/+page.server.js
|
||||
import { supabase } from "$lib/supabaseClient";
|
||||
|
||||
export async function load() {
|
||||
const { data } = await supabase.from("countries").select();
|
||||
return {
|
||||
countries: data ?? [],
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
</StepHikeCompact.Code>
|
||||
|
||||
<StepHikeCompact.Details title="">
|
||||
|
||||
Replace the existing content in your `+page.svelte` file in the `routes` directory with the following code.
|
||||
|
||||
</StepHikeCompact.Details>
|
||||
<StepHikeCompact.Code>
|
||||
|
||||
|
||||
```svelte src/routes/+page.svelte
|
||||
<script>
|
||||
export let data;
|
||||
let { countries } = data;
|
||||
$: ({ countries } = data);
|
||||
</script>
|
||||
|
||||
<ul>
|
||||
{#each countries as country}
|
||||
<li>{country.name}</li>
|
||||
{/each}
|
||||
</ul>
|
||||
```
|
||||
|
||||
</StepHikeCompact.Code>
|
||||
|
||||
</StepHikeCompact.Step>
|
||||
|
||||
<StepHikeCompact.Step step={6}>
|
||||
<StepHikeCompact.Details title="Start the app">
|
||||
|
||||
Start the app and go to http://localhost:5173 in a browser and you should see the list of countries.
|
||||
|
||||
</StepHikeCompact.Details>
|
||||
|
||||
<StepHikeCompact.Code>
|
||||
|
||||
```bash Terminal
|
||||
npm run dev
|
||||
```
|
||||
|
||||
</StepHikeCompact.Code>
|
||||
|
||||
</StepHikeCompact.Step>
|
||||
</StepHikeCompact>
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} hideToc={true} />
|
||||
|
||||
export default Page
|
||||
@@ -0,0 +1,153 @@
|
||||
import Layout from '~/layouts/DefaultGuideLayout'
|
||||
import StepHikeCompact from '~/components/StepHikeCompact'
|
||||
|
||||
export const meta = {
|
||||
title: 'Use Supabase with Vue',
|
||||
subtitle:
|
||||
'Learn how to create a Supabase project, add some sample data to your database, and query the data from a Vue app.',
|
||||
breadcrumb: 'Framework Quickstarts',
|
||||
}
|
||||
|
||||
<StepHikeCompact>
|
||||
|
||||
<StepHikeCompact.Step step={1}>
|
||||
<StepHikeCompact.Details title="Set up a Supabase project with sample data">
|
||||
|
||||
[Create a new project](https://app.supabase.com) in the Supabase Dashboard.
|
||||
|
||||
After your project is ready, create a table in your Supabase database using the [SQL Editor](https://app.supabase.com/project/_/sql) in the Dashboard. Use the following SQL statement to create a `countries` table with some sample data.
|
||||
|
||||
</StepHikeCompact.Details>
|
||||
|
||||
<StepHikeCompact.Code>
|
||||
|
||||
```sql SQL_EDITOR
|
||||
-- Create the table
|
||||
CREATE TABLE countries (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL
|
||||
);
|
||||
-- Insert some sample data into the table
|
||||
INSERT INTO countries (name) VALUES ('United States');
|
||||
INSERT INTO countries (name) VALUES ('Canada');
|
||||
INSERT INTO countries (name) VALUES ('Mexico');
|
||||
````
|
||||
|
||||
</StepHikeCompact.Code>
|
||||
|
||||
</StepHikeCompact.Step>
|
||||
|
||||
<StepHikeCompact.Step step={2}>
|
||||
|
||||
<StepHikeCompact.Details title="Create a Vue app">
|
||||
|
||||
Create a Vue app using the `npm init` command.
|
||||
|
||||
</StepHikeCompact.Details>
|
||||
|
||||
<StepHikeCompact.Code>
|
||||
|
||||
```sh Terminal
|
||||
npm init vue@latest my-app
|
||||
```
|
||||
|
||||
</StepHikeCompact.Code>
|
||||
|
||||
</StepHikeCompact.Step>
|
||||
|
||||
<StepHikeCompact.Step step={3}>
|
||||
<StepHikeCompact.Details title="Install the Supabase client library">
|
||||
|
||||
The fastest way to get started is to use the `supabase-js` client library which provides a convenient interface for working with Supabase from a Vue app.
|
||||
|
||||
Navigate to the Vue app and install `supabase-js`.
|
||||
|
||||
</StepHikeCompact.Details>
|
||||
|
||||
<StepHikeCompact.Code>
|
||||
|
||||
```bash Terminal
|
||||
cd my-app && npm install @supabase/supabase-js
|
||||
```
|
||||
|
||||
</StepHikeCompact.Code>
|
||||
|
||||
</StepHikeCompact.Step>
|
||||
|
||||
<StepHikeCompact.Step step={4}>
|
||||
<StepHikeCompact.Details title="Create the Supabase client">
|
||||
|
||||
Create a `/src/lib` directory in your Vue app, create a file called `supabaseClient.js` and add the following code to initialize the Supabase client with your [project URL and public API (anon) key](https://app.supabase.com/project/_/settings/api).
|
||||
|
||||
</StepHikeCompact.Details>
|
||||
|
||||
<StepHikeCompact.Code>
|
||||
|
||||
```js src/lib/supabaseClient.js
|
||||
import { createClient } from '@supabase/supabase-js'
|
||||
|
||||
export const supabase = createClient('https://<project>.supabase.co', '<your-anon-key>')
|
||||
```
|
||||
|
||||
</StepHikeCompact.Code>
|
||||
|
||||
</StepHikeCompact.Step>
|
||||
|
||||
<StepHikeCompact.Step step={5}>
|
||||
<StepHikeCompact.Details title="Query data from the app">
|
||||
|
||||
Replace the existing content in your `App.vue` file with the following code.
|
||||
|
||||
</StepHikeCompact.Details>
|
||||
<StepHikeCompact.Code>
|
||||
|
||||
|
||||
```vue src/App.vue
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { supabase } from './lib/supabaseClient'
|
||||
|
||||
const countries = ref([])
|
||||
|
||||
async function getCountries() {
|
||||
const { data } = await supabase.from('countries').select()
|
||||
countries.value = data
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getCountries()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ul>
|
||||
<li v-for="country in countries" :key="country.id">{{ country.name }}</li>
|
||||
</ul>
|
||||
</template>
|
||||
```
|
||||
|
||||
</StepHikeCompact.Code>
|
||||
|
||||
</StepHikeCompact.Step>
|
||||
|
||||
<StepHikeCompact.Step step={6}>
|
||||
<StepHikeCompact.Details title="Start the app">
|
||||
|
||||
Start the app and go to http://localhost:5173 in a browser and you should see the list of countries.
|
||||
|
||||
</StepHikeCompact.Details>
|
||||
|
||||
<StepHikeCompact.Code>
|
||||
|
||||
```bash Terminal
|
||||
npm run dev
|
||||
```
|
||||
|
||||
</StepHikeCompact.Code>
|
||||
|
||||
</StepHikeCompact.Step>
|
||||
</StepHikeCompact>
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} hideToc={true} />
|
||||
|
||||
export default Page
|
||||
@@ -530,6 +530,10 @@ export class AccountComponent implements OnInit {
|
||||
}
|
||||
```
|
||||
|
||||
### Storage management
|
||||
|
||||
<StorageManagement />
|
||||
|
||||
## Next steps
|
||||
|
||||
At this stage you have a fully functional application!
|
||||
|
||||
@@ -500,6 +500,10 @@ Now you will need to run the prebuild command to get the application working on
|
||||
expo prebuild
|
||||
```
|
||||
|
||||
### Storage management
|
||||
|
||||
<StorageManagement />
|
||||
|
||||
## Next steps
|
||||
|
||||
At this stage you have a fully functional application!
|
||||
|
||||
@@ -793,6 +793,10 @@ class _AccountPageState extends State<AccountPage> {
|
||||
}
|
||||
```
|
||||
|
||||
### Storage management
|
||||
|
||||
<StorageManagement />
|
||||
|
||||
Congratulations, that is it! You have now built a fully functional user management app using Flutter and Supabase!
|
||||
|
||||
## See also
|
||||
|
||||
@@ -522,6 +522,10 @@ template: `
|
||||
`
|
||||
```
|
||||
|
||||
### Storage management
|
||||
|
||||
<StorageManagement />
|
||||
|
||||
## Next steps
|
||||
|
||||
At this stage you have a fully functional application!
|
||||
|
||||
@@ -495,6 +495,10 @@ return (
|
||||
<Avatar url={profile.avatar_url} onUpload={updateProfile}></Avatar>
|
||||
```
|
||||
|
||||
### Storage management
|
||||
|
||||
<StorageManagement />
|
||||
|
||||
## Next steps
|
||||
|
||||
At this stage you have a fully functional application!
|
||||
|
||||
@@ -555,6 +555,10 @@ export default defineComponent({
|
||||
</script>
|
||||
```
|
||||
|
||||
### Storage management
|
||||
|
||||
<StorageManagement />
|
||||
|
||||
## Next steps
|
||||
|
||||
At this stage you have a fully functional application!
|
||||
|
||||
@@ -5,6 +5,7 @@ export const meta = {
|
||||
title: 'Quickstart: Next.js',
|
||||
description: 'Learn how to use Supabase in your Next App.',
|
||||
sidebar_label: 'Next.js',
|
||||
video: 'https://www.youtube.com/v/0Fs96oZ4se0',
|
||||
}
|
||||
|
||||
<QuickstartIntro />
|
||||
@@ -718,6 +719,10 @@ return (
|
||||
)
|
||||
```
|
||||
|
||||
### Storage management
|
||||
|
||||
<StorageManagement />
|
||||
|
||||
## Next steps
|
||||
|
||||
At this stage you have a fully functional application!
|
||||
|
||||
@@ -420,6 +420,10 @@ And then we can add the widget to the Account page:
|
||||
|
||||
That is it! You should now be able to upload a profile photo to Supabase Storage.
|
||||
|
||||
### Storage management
|
||||
|
||||
<StorageManagement />
|
||||
|
||||
## Next steps
|
||||
|
||||
At this stage you have a fully functional application!
|
||||
|
||||
@@ -397,6 +397,10 @@ return (
|
||||
)
|
||||
```
|
||||
|
||||
### Storage management
|
||||
|
||||
<StorageManagement />
|
||||
|
||||
## Next steps
|
||||
|
||||
At this stage you have a fully functional application!
|
||||
|
||||
@@ -771,6 +771,10 @@ return (
|
||||
)
|
||||
```
|
||||
|
||||
### Storage management
|
||||
|
||||
<StorageManagement />
|
||||
|
||||
## Next steps
|
||||
|
||||
At this stage you have a fully functional application!
|
||||
|
||||
@@ -415,6 +415,10 @@ return (
|
||||
)
|
||||
```
|
||||
|
||||
### Storage management
|
||||
|
||||
<StorageManagement />
|
||||
|
||||
## Next steps
|
||||
|
||||
At this stage you have a fully functional application!
|
||||
|
||||
@@ -368,6 +368,10 @@ And then we can add the widget to the Account page:
|
||||
</form>
|
||||
```
|
||||
|
||||
### Storage management
|
||||
|
||||
<StorageManagement />
|
||||
|
||||
## Next steps
|
||||
|
||||
At this stage you have a fully functional application!
|
||||
|
||||
@@ -24,7 +24,7 @@ Let's start building the Svelte app from scratch.
|
||||
### Initialize a Svelte app
|
||||
|
||||
We can use the [SvelteKit Skeleton Project](https://kit.svelte.dev/docs) to initialize
|
||||
an app called `supabase-sveltekit` (for this tutorial you do not need TypeScript, ESLint, Prettier, or Playwright):
|
||||
an app called `supabase-sveltekit` (for this tutorial we will be using TypeScript):
|
||||
|
||||
```bash
|
||||
npm init svelte@next supabase-sveltekit
|
||||
@@ -434,6 +434,10 @@ And then we can add the widget to the Account page:
|
||||
</form>
|
||||
```
|
||||
|
||||
### Storage management
|
||||
|
||||
<StorageManagement />
|
||||
|
||||
## Next steps
|
||||
|
||||
At this stage you have a fully functional application!
|
||||
|
||||
@@ -50,7 +50,7 @@ VITE_SUPABASE_URL=YOUR_SUPABASE_URL
|
||||
VITE_SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY
|
||||
```
|
||||
|
||||
Now that we have the API credentials in place, let's create a helper file to initialize the Supabase client. These variables will be exposed
|
||||
With the API credentials in place, create an `src/supabase.js` helper file to initialize the Supabase client. These variables are exposed
|
||||
on the browser, and that's completely fine since we have [Row Level Security](/docs/guides/auth#row-level-security) enabled on our Database.
|
||||
|
||||
```js title=src/supabase.js
|
||||
@@ -66,7 +66,7 @@ Optionally, update [src/style.css](https://raw.githubusercontent.com/supabase/su
|
||||
|
||||
### Set up a Login component
|
||||
|
||||
Let's set up a Vue component to manage logins and sign ups. We'll use Magic Links, so users can sign in with their email without using passwords.
|
||||
Set up an `src/components/Auth.vue` component to manage logins and sign ups. We'll use Magic Links, so users can sign in with their email without using passwords.
|
||||
|
||||
```html title=/src/components/Auth.vue
|
||||
<script setup>
|
||||
@@ -118,7 +118,7 @@ Let's set up a Vue component to manage logins and sign ups. We'll use Magic Link
|
||||
### Account page
|
||||
|
||||
After a user is signed in we can allow them to edit their profile details and manage their account.
|
||||
Let's create a new component for that called `Account.vue`.
|
||||
Create a new `src/components/Account.vue` component to handle this.
|
||||
|
||||
```html title=src/components/Account.vue
|
||||
<script setup>
|
||||
@@ -277,7 +277,7 @@ Every Supabase project is configured with [Storage](/docs/guides/storage) for ma
|
||||
|
||||
### Create an upload widget
|
||||
|
||||
Let's create an avatar for the user so that they can upload a profile photo. We can start by creating a new component:
|
||||
Create a new `src/components/Avatar.vue` component that allows users to upload profile photos:
|
||||
|
||||
```html title=src/components/Avatar.vue
|
||||
<script setup>
|
||||
@@ -361,7 +361,7 @@ Let's create an avatar for the user so that they can upload a profile photo. We
|
||||
|
||||
### Add the new widget
|
||||
|
||||
And then we can add the widget to the Account page:
|
||||
And then we can add the widget to the Account page in `src/components/Account.vue`:
|
||||
|
||||
```html title=src/components/Account.vue
|
||||
<script>
|
||||
@@ -379,6 +379,10 @@ And then we can add the widget to the Account page:
|
||||
</template>
|
||||
```
|
||||
|
||||
### Storage management
|
||||
|
||||
<StorageManagement />
|
||||
|
||||
## Next steps
|
||||
|
||||
At this stage you have a fully functional application!
|
||||
|
||||
@@ -18,7 +18,7 @@ Once created, the pipeline backfills all your historical data from Firestore and
|
||||
|
||||
Before you begin, you'll need:
|
||||
|
||||
- An [Estuary account](https://go.estuary.dev/sign-up).
|
||||
- An Estuary account. [Head to the web app to start for free](https://dashboard.estuary.dev).
|
||||
|
||||
- For your Firestore database:
|
||||
|
||||
@@ -31,36 +31,35 @@ Before you begin, you'll need:
|
||||
|
||||
You'll start by creating a **capture**, a task in Flow that connects to your data source system: in this case, Firestore. This process will create one or more data **collections**, backed by a real-time data lake.
|
||||
|
||||
1. Go to the Flow web application at [dashboard.estuary.dev](http://dashboard.estuary.dev) and sign in using the credentials provided by your Estuary account manager.
|
||||
1. Go to the [**Captures** tab](https://dashboard.estuary.dev/captures) of the Flow web app and choose **New Capture**.
|
||||
|
||||
2. Click the **Captures** tab and choose **New Capture**.
|
||||
|
||||
3. Locate and select the **Google Firestore** card.
|
||||
2. Locate and select the **Google Firestore** card.
|
||||
|
||||
A form appears with the properties required for a Firestore capture.
|
||||
|
||||
4. Set a name for your capture.
|
||||
3. Set a name for your capture.
|
||||
|
||||
Click inside the **Name** field to generate a drop-down menu of available **prefixes** and select one (likely, this will be the name of your organization). Append a unique capture name after the `/` to create the full name, for example, `acmeCo/myFirestoreCapture`.
|
||||
|
||||
5. Fill out the required properties for Firestore.
|
||||
4. Fill out the required properties for Firestore.
|
||||
|
||||
- **Database**: Flow can autodetect the database name, but you may optionally specify it here. This is helpful if the service account used has access to multiple Firebase projects. Your database name usually follows the format `projects/$PROJECTID/databases/(default)`.
|
||||
- **Credentials**: The JSON service account key created per the prerequisites.
|
||||
**Database**: Flow can autodetect the database name, but you may optionally specify it here. This is helpful if the service account used has access to multiple Firebase projects. Your database name usually follows the format `projects/$PROJECTID/databases/(default)`.
|
||||
|
||||
**Credentials**: The JSON service account key created per the prerequisites.
|
||||
|
||||
6. Click **Discover Endpoint**.
|
||||
5. Click **Next**.
|
||||
|
||||
Flow uses the provided configuration to initiate a connection with Firestore. It generates a capture specification and details of the collections that it will create, once published.
|
||||
Flow uses the provided configuration to initiate a connection with Firestore. It maps each collection in the Firestore database to a Flow collection.
|
||||
|
||||
7. Use the **Specification Editor** to view the [JSON schemas](https://docs.estuary.dev/concepts/schemas/) for each collection and make sure they are formatted correctly for your needs. If they're not, you can edit them.
|
||||
6. Optionally, use the **Collection Selector** to remove any collections you don't need to migrate to Supabase.
|
||||
|
||||
8. Click **Save and publish**.
|
||||
7. Click **Save and Publish**.
|
||||
|
||||
You'll see a notification when the capture publishes successfully.
|
||||
You'll see a notification when the capture publishes successfully.
|
||||
|
||||
The data currently in your Firestore database has been captured, and future updates to it will be captured continuously.
|
||||
The data currently in your Firestore database has been captured to Flow, and future updates to it will be captured continuously.
|
||||
|
||||
Click **Materialize Collections** to continue.
|
||||
Click **Materialize Collections** to continue.
|
||||
|
||||
## Step 2: Materialize your collections to Postgres
|
||||
|
||||
@@ -72,23 +71,31 @@ A form appears with the properties required for a Postgres materialization.
|
||||
|
||||
2. Choose a unique name for your materialization like you did when naming your capture; for example, `acmeCo/mySupabaseMaterialization`.
|
||||
|
||||
3. Fill out the required properties for PostgreSQL. You can find most of these in Supabase by going to the **Settings** section and clicking **Database**.
|
||||
3. Fill out the required properties for Postgres. You can find most of these in Supabase by going to the **Settings** section and clicking **Database**.
|
||||
|
||||
- **Address**: Format at `<host>:<port>`.
|
||||
- **User**: Usually, this is `postgres`.
|
||||
- **Password**: The password you set when you created your Supabase project.
|
||||
**Address**: Format at `<host>:<port>`.
|
||||
|
||||
**User**: Usually, this is `postgres`.
|
||||
|
||||
**Password**: The password you set when you created your Supabase project.
|
||||
|
||||
4. Scroll down to view the **Collection Selector** and fill in the **Table** field for each collection.
|
||||
4. Click **Next**.
|
||||
|
||||
The collections you just created have already been selected, but you must provide names for the tables to which they'll be materialized.
|
||||
Flow initiates a connection with the database and the **Collection Selector** expands.
|
||||
It's populated with your collections from Firestore, each mapped to a Postgres table.
|
||||
|
||||
5. Click **Discover Endpoint**.
|
||||
5. For each collection, apply a stricter JSON schema.
|
||||
This ensure that the less-structured Firestore data will be written to a Postgres table in the correct shape.
|
||||
|
||||
Flow uses the provided configuration to initiate a connection to your Supabase Postgres database and generate a specification.
|
||||
In the Collection Selector, choose a collection and click its **Specification** tab.
|
||||
|
||||
Click **Schema Inference**. Flow scans the data in your collection and infers a new schema to use for materialization.
|
||||
|
||||
Review the new schema and click **Apply Inferred Schema**.
|
||||
|
||||
6. Click **Save and Publish**. You'll see a notification when the full materialization publishes successfully.
|
||||
6. Click **Save and Publish**. You'll see a notification when the materialization publishes successfully.
|
||||
|
||||
Your Firestore collections are copied to tables in Supabase. As long as you leave the capture and materialation running, any changes to the Firestore data will be reflected in Supabase in milliseconds.
|
||||
Your Firestore collections are copied to tables in Supabase. As long as you leave the capture and materialation running, any changes to the Firestore data will be reflected in Supabase in milliseconds.
|
||||
|
||||
## Resources
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ export const meta = {
|
||||
id: 'fezto',
|
||||
title: 'Frontend Zero to One',
|
||||
description: 'Create an app automatically from your Supabase Postgres using OpenAPI',
|
||||
video: 'https://www.youtube.com/v/GOC6a0_AlgI',
|
||||
}
|
||||
|
||||
[Frontend Zero to One is](https://www.fezto.xyz) is a service which creates an app for your Supabase Postgres database on-the-fly without any drag and drop, using the OpenAPI spec provided by PostgREST.
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
import Layout from '~/layouts/DefaultGuideLayout'
|
||||
|
||||
export const meta = {
|
||||
id: 'flutterflow',
|
||||
title: 'FlutterFlow',
|
||||
description:
|
||||
'FlutterFlow is a low-code tool that allows you to build Flutter apps incredibly fast.',
|
||||
canonical: 'https://docs.flutterflow.io/actions/actions/backend-database/supabase',
|
||||
}
|
||||
|
||||
[FlutterFlow](https://flutterflow.io/) is a low-code builder for developing native mobile applications using Flutter. You can use the simple drag-and-drop interface to build your app faster than traditional development.
|
||||
|
||||
<div className="video-container">
|
||||
<iframe
|
||||
src="https://www.youtube-nocookie.com/embed/hw9Q-NjASbU"
|
||||
frameBorder="1"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||
allowFullScreen
|
||||
></iframe>
|
||||
</div>
|
||||
|
||||
This guide gives you a quick overview of implementing basic CRUD operations using FlutterFlow and Supabase. You can find the full docs on FlutterFlow and Supabase [here](https://docs.flutterflow.io/actions/actions/backend-database/supabase).
|
||||
|
||||
## Step 1: Connect FlutterFlow to Supabase
|
||||
|
||||
Before we dive into the code, this guide assumes that you have the following ready:
|
||||
|
||||
- [Supabase](https://database.new/) project created
|
||||
- Have setup tables in your Supabase project
|
||||
- [FlutterFlow](https://app.flutterflow.io/) project created
|
||||
|
||||
You can then connect your Supabase project to your FlutterFlow project with the following steps:
|
||||
|
||||
1. In your Supabase project, navigate to Project Settings > API. Copy the Project URL.
|
||||
2. Return to FlutterFlow, navigate to Settings and Integrations > Integrations > Supabase. Turn on the toggle (i.e., enable Supabase) and paste the API URL.
|
||||
3. Similarly, from the Supabase API section, copy the anon key (under Project API keys) and paste it inside the FlutterFlow > Settings and Integrations > Integrations > Supabase > Anon Key.
|
||||
4. Click on the Get Schema button. This will show the list of all tables with their schema (structure) created in Supabase.
|
||||
5. (Optional) If you have defined an Array for any Column Data Type in Supabase, you must set its type here. To do so, tap the "Click to set Array type" and choose the right one.
|
||||
|
||||
<video width="99%" muted playsInline controls={true}>
|
||||
<source
|
||||
src="/docs/videos/guides/integrations/flutterflow/connect-flutterflow-to-supabase.mp4"
|
||||
type="video/mp4"
|
||||
muted
|
||||
playsInline
|
||||
/>
|
||||
</video>
|
||||
|
||||
## Step 2: Inserting rows
|
||||
|
||||
Go to your project page on FlutterFlow and follow the steps below to define the Action to any widget.
|
||||
|
||||
1. Select the Widget (e.g., Button) on which you want to define the action.
|
||||
2. Select Actions from the Properties panel (the right menu), and click Open. This will open an Action flow Editor in a new popup window.
|
||||
1. Click on + Add Action.
|
||||
2. On the right side, search and select the Supabase > Insert Row action.
|
||||
3. Set the Table to your table name (e.g., assignments).
|
||||
4. Under the Set Fields section, click on the + Add Field button.
|
||||
5. Click on the Field name and scroll down to find the Value Source dropdown and change it to From Variable.
|
||||
6. Click on UNSET and select Widget State > Name of the TextField.
|
||||
7. Similarly, add the field for the other UI elements.
|
||||
|
||||
<video width="99%" muted playsInline controls={true}>
|
||||
<source
|
||||
src="/docs/videos/guides/integrations/flutterflow/insert.mp4"
|
||||
type="video/mp4"
|
||||
muted
|
||||
playsInline
|
||||
/>
|
||||
</video>
|
||||
|
||||
## Step 3: Selecting and displaying rows
|
||||
|
||||
To query a Supabase table on a ListView:
|
||||
|
||||
1. Select the ListView widget. Make sure you choose the ListView widget, not the ListTile.
|
||||
2. Select Backend Query from the properties panel (the right menu), and click Add Backend Query.
|
||||
3. Set the Query Type to Supabase Query.
|
||||
4. Select your Table from the dropdown list
|
||||
5. Set the Query Type to List of Rows.
|
||||
6. Optional: If you want to display the limited result, say, for example, you have thousands of entries, but you want to display only 100, you can specify the limit.
|
||||
7. Click Confirm.
|
||||
|
||||
<video width="99%" muted playsInline controls={true}>
|
||||
<source
|
||||
src="/docs/videos/guides/integrations/flutterflow/select.mp4"
|
||||
type="video/mp4"
|
||||
muted
|
||||
playsInline
|
||||
/>
|
||||
</video>
|
||||
|
||||
## Step 4: Updating rows
|
||||
|
||||
Go to your project page on FlutterFlow and follow the steps below to define the Action to any widget.
|
||||
|
||||
1. Select the Widget (e.g., Button) on which you want to define the action.
|
||||
2. Select Actions from the Properties panel (the right menu), and click Open. This will open an Action flow Editor in a new popup window.
|
||||
1. Click on + Add Action.
|
||||
2. On the right side, search and select the Supabase > Update Row action.
|
||||
3. Set the Table to your table name (e.g., assignments).
|
||||
4. Optional: If you want to get the rows after the update is finished, enable the Return Matching Rows option.
|
||||
5. Now, you must set the row you want to update. Usually, this is done by finding a row in a table that matches the current row ID. To do so, click + Add Filter button inside the Matching Rows section.
|
||||
1. Set the Field Name to the field that contains the IDs. Typically, this is the id column.
|
||||
2. Set the Relation to Equal To because you want to find a row with the exact id.
|
||||
3. Into the Value Source, you can select the From Variable and provide the id of the row for which you just updated values in the UI.
|
||||
6. Under the Set Fields section, click on the + Add Field button.
|
||||
7. Click on the field name.
|
||||
8. Scroll down to find the Value Source dropdown and change it to From Variable.
|
||||
9. Click on UNSET and select Widget State > Name of the TextField.
|
||||
10. Similarly, add the field for the other UI elements.
|
||||
|
||||
## Step 5: Deleting rows
|
||||
|
||||
Go to your project page on FlutterFlow and follow the steps below to define the Action to any widget.
|
||||
|
||||
1. Select the Widget (e.g., Button) on which you want to define the action.
|
||||
2. Select Actions from the Properties panel (the right menu), and click Open. This will open an Action flow Editor in a new popup window.
|
||||
1. Click on + Add Action.
|
||||
2. On the right side, search and select the Supabase -> Delete Row action.
|
||||
3. Set the Table to your table name (e.g., assignments).
|
||||
4. Optional: Later, if you want to know which rows were deleted from a table, enable the Return Matching Rows option.
|
||||
5. Now, you must set the row you want to delete. Usually, this is done by finding a row in a table that matches the current row ID. To do so, click + Add Filter button inside the Matching Rows section.
|
||||
1. Set the Field Name to the field that contains the IDs. Typically, this is the id column.
|
||||
2. Set the Relation to Equal To because you want to find a row with the exact id.
|
||||
3. Into the Value Source, you can select the From Variable and provide the id of the row you want to delete.
|
||||
|
||||
<video width="99%" muted playsInline controls={true}>
|
||||
<source
|
||||
src="/docs/videos/guides/integrations/flutterflow/delete.mp4"
|
||||
type="video/mp4"
|
||||
muted
|
||||
playsInline
|
||||
/>
|
||||
</video>
|
||||
|
||||
## Resources
|
||||
|
||||
You can find more detailed guides on FlutterFlow’s docs.
|
||||
|
||||
- [FlutterFlow Supabase available actions](https://docs.flutterflow.io/actions/actions/backend-database/supabase)
|
||||
- [Retrieving Data from Supabase on FlutterFlow](https://docs.flutterflow.io/data-and-backend/supabase/supabase-database/retrieving-data)
|
||||
- [Adding data to Supabase DB from FlutterFlow](https://docs.flutterflow.io/data-and-backend/supabase/supabase-database/adding-data)
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} />
|
||||
|
||||
export default Page
|
||||
@@ -5,6 +5,7 @@ export const meta = {
|
||||
title: 'Keyri',
|
||||
description:
|
||||
'QR authentication for an easy and flexible biometric solution across all platforms.',
|
||||
video: 'https://www.youtube.com/v/jrjrcpc2PFQ',
|
||||
}
|
||||
|
||||
Keyri can be used to incorporate sign-in-with-QR functionality into your Supabase app, allowing users to scan a QR code on your web app with your mobile app and be instantly logged into the web app without having to input any credentials.
|
||||
|
||||
@@ -5,6 +5,7 @@ export const meta = {
|
||||
title: 'OneSignal',
|
||||
description:
|
||||
'OneSignal allows you to send cloud messages to your users. Combine OneSignal with your Supabase apps and you can reach out to your users whenever there is a change in your database.',
|
||||
video: 'https://www.youtube.com/v/mw0DLwItue4',
|
||||
}
|
||||
|
||||
[OneSignal](https://onesignal.com/) is a tool that allows you to send messages across different channels such as the following to keep your users engaged.
|
||||
@@ -14,7 +15,18 @@ export const meta = {
|
||||
- Emails
|
||||
- In-app notifications
|
||||
|
||||
This guide steps you through how you can integrate OneSignal with Supabase to create a seamless cloud messaging experience for your users using Database webhooks and edge functions through a simple Next.js application.
|
||||
Here is William giving us the overview of how OneSignal can work with Supabase to send notifications to your users.
|
||||
|
||||
<div className="video-container">
|
||||
<iframe
|
||||
src="https://www.youtube-nocookie.com/embed/mw0DLwItue4"
|
||||
frameBorder="1"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||
allowFullScreen
|
||||
></iframe>
|
||||
</div>
|
||||
|
||||
In this guide, we will build a similar app and steps you through how you can integrate OneSignal with Supabase to create a seamless cloud messaging experience for your users using Database webhooks and edge functions through a simple Next.js application.
|
||||
|
||||

|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ export const meta = {
|
||||
id: 'custom-domains',
|
||||
title: 'Custom Domains',
|
||||
description: 'Configuring a Custom Domain for your Supabase project.',
|
||||
video: 'https://www.youtube.com/v/6rcGnW_Mh-0',
|
||||
}
|
||||
|
||||
<Admonition type="note">
|
||||
@@ -14,6 +15,15 @@ Custom Domains are currently in beta, and are being slowly made available to pro
|
||||
|
||||
Custom domains allow you to present a branded experience to your users. Custom domains are available as a [add-on for projects on a paid tier](https://app.supabase.com/project/_/settings/billing/update). Setting up a custom domain requires [Owner or Admin permissions](/docs/guides/platform/access-control#manage-team-members) for the project. Currently, you must use a subdomain (e.g., `api.example.com`, rather than `example.com`) for the purposes of this guide.
|
||||
|
||||
<div className="video-container">
|
||||
<iframe
|
||||
src="https://www.youtube-nocookie.com/embed/6rcGnW_Mh-0"
|
||||
frameBorder="1"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||
allowFullScreen
|
||||
></iframe>
|
||||
</div>
|
||||
|
||||
## Configure a Custom Domain using the Supabase Dashboard
|
||||
|
||||
Follow the **Custom Domains** steps in the [General Settings](https://app.supabase.com/project/_/settings/general) page in the Dashboard to set up a custom domain for your project.
|
||||
|
||||
@@ -19,15 +19,11 @@ This value is also reported in the [database settings page](https://app.supabase
|
||||
|
||||
## Database storage management
|
||||
|
||||
Your project will be changed to read-only mode once it reaches 95% of your subscription quota, preventing further writes to your project.
|
||||
In read-only mode, clients will encounter errors such as `cannot execute INSERT in a read-only transaction`.
|
||||
Supabase uses network-attached storage to balance performance with scalability. For Pro and Enterprise projects, database storage expands ~1.5x automatically (e.g., 8GB -> 12GB) when you reach 90% of the database storage quota. Automatic database storage expansion can only occur once every six hours. Pro projects can store up to 1024TB.
|
||||
|
||||
Regular operation (read-write mode) is automatically re-enabled once database storage usage falls under 95%.
|
||||
All projects enter read-only mode when you reach 95% of the database storage quota. In read-only mode, clients will encounter errors such as `cannot execute INSERT in a read-only transaction`. Regular operation (read-write mode) is automatically re-enabled once usage is below 95% of the database storage quota.
|
||||
|
||||
If you are on Pro or Enterprise, database storage expands automatically, however you may also encounter this error if fill the database faster than it can expand.
|
||||
|
||||
Supabase uses network-attached storage, which balances performance with scalability. Pro projects can store up to 1024TB.
|
||||
If you intend to grow beyond this, reach out to our team to learn more about our Enterprise tier.
|
||||
If you need more than 1024TB of database storage or require multiple storage expansions in a short period of time, [contact us](https://app.supabase.com/support/new) to learn more about the Enterprise plan.
|
||||
|
||||
### Increasing available database storage
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ export const meta = {
|
||||
title: 'Realtime Quickstart',
|
||||
description: "Getting started with Realtime's Features",
|
||||
sidebar_label: 'Quickstart',
|
||||
video: 'https://www.youtube.com/v/BelYEMJ2N00',
|
||||
}
|
||||
|
||||
Learn how to build [multiplayer.dev](https://multiplayer.dev), a collaborative app that demonstrates Broadcast, Presence, and Postgres Changes using [Realtime](/docs/guides/realtime).
|
||||
@@ -121,7 +122,7 @@ channel.subscribe(async (status) => {
|
||||
})
|
||||
```
|
||||
|
||||
## Track and display which users are online
|
||||
## 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.
|
||||
|
||||
|
||||
Loaded 100 of 345 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user