mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 17:35:10 +03:00
Merge branch 'master' into fs/docs-homepage
This commit is contained in:
188 files changed
+5757
-1723
No files matched your search
@@ -11,9 +11,16 @@ jobs:
|
||||
steps:
|
||||
- name: Check out repo
|
||||
uses: actions/checkout@v3
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'npm'
|
||||
- name: Download dependencies
|
||||
run: npm ci
|
||||
- name: Run prettier
|
||||
run: |-
|
||||
npx prettier -c 'apps/**/*.{js,jsx,ts,tsx,css,md,json}'
|
||||
npx prettier -c 'apps/**/*.{js,jsx,ts,tsx,css,md,mdx,json}'
|
||||
# [Joshen] Temp disable the following due to an issue
|
||||
# https://github.com/EPMatt/reviewdog-action-prettier/issues/34
|
||||
# uses: EPMatt/reviewdog-action-prettier@v1
|
||||
@@ -42,9 +49,16 @@ jobs:
|
||||
steps:
|
||||
- name: Check out repo
|
||||
uses: actions/checkout@v3
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'npm'
|
||||
- name: Download dependencies
|
||||
run: npm ci
|
||||
- name: Run prettier
|
||||
run: |-
|
||||
npx prettier -c 'i18n/**/*.{js,jsx,ts,tsx,css,md,json}'
|
||||
npx prettier -c 'i18n/**/*.{js,jsx,ts,tsx,css,md,mdx,json}'
|
||||
# uses: EPMatt/reviewdog-action-prettier@v1
|
||||
# with:
|
||||
# github_token: ${{ secrets.github_token }}
|
||||
|
||||
+3
-1
@@ -5,5 +5,7 @@
|
||||
"singleQuote": true,
|
||||
"printWidth": 100,
|
||||
"endOfLine": "lf",
|
||||
"sqlKeywordCase": "lower"
|
||||
"sqlKeywordCase": "lower",
|
||||
"pluginSearchDirs": false,
|
||||
"plugins": ["prettier-plugin-sql-cst"]
|
||||
}
|
||||
@@ -149,8 +149,8 @@ Our approach for client libraries is modular. Each sub-library is a standalone i
|
||||
<tr>
|
||||
<td>Kotlin</td>
|
||||
<td><a href="https://github.com/supabase-community/supabase-kt" target="_blank" rel="noopener noreferrer">supabase-kt</a></td>
|
||||
<td><a href="https://github.com/supabase-community/postgrest-kt" target="_blank" rel="noopener noreferrer">postgrest-kt</a></td>
|
||||
<td><a href="https://github.com/supabase-community/gotrue-kt" target="_blank" rel="noopener noreferrer">gotrue-kt</a></td>
|
||||
<td><a href="https://github.com/supabase-community/supabase-kt/tree/master/Postgrest" target="_blank" rel="noopener noreferrer">postgrest-kt</a></td>
|
||||
<td><a href="https://github.com/supabase-community/supabase-kt/tree/master/GoTrue" target="_blank" rel="noopener noreferrer">gotrue-kt</a></td>
|
||||
<td><a href="https://github.com/supabase-community/supabase-kt/tree/master/Realtime" target="_blank" rel="noopener noreferrer">realtime-kt</a></td>
|
||||
<td><a href="https://github.com/supabase-community/supabase-kt/tree/master/Storage" target="_blank" rel="noopener noreferrer">storage-kt</a></td>
|
||||
<td><a href="https://github.com/supabase-community/supabase-kt/tree/master/Functions" target="_blank" rel="noopener noreferrer">functions-kt</a></td>
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
- Click on [`Providers`](https://app.supabase.com/project/_/auth/providers) under the Configuration section
|
||||
- Click on **{props.provider}** from the accordion list to expand and turn **{props.provider} Enabled** to ON
|
||||
- Enter your **{props.provider} Client ID** and **{props.provider} Client Secret** saved in the previous step
|
||||
- Click `Save`
|
||||
- Click `Save`
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
IconMenuStorage,
|
||||
IconMenuSwift,
|
||||
IconMenuStatus,
|
||||
IconMenuKotlin,
|
||||
} from './HomeMenuIcons'
|
||||
|
||||
function getMenuIcon(menuKey: string, width: number = 16, height: number = 16) {
|
||||
@@ -58,6 +59,8 @@ function getMenuIcon(menuKey: string, width: number = 16, height: number = 16) {
|
||||
return <IconMenuCsharp width={width} height={height} />
|
||||
case 'reference-swift':
|
||||
return <IconMenuSwift width={width} height={height} />
|
||||
case 'reference-kotlin':
|
||||
return <IconMenuKotlin width={width} height={height} />
|
||||
case 'reference-api':
|
||||
return <IconMenuApi width={width} height={height} />
|
||||
case 'reference-cli':
|
||||
|
||||
@@ -382,6 +382,25 @@ export function IconMenuSwift({ width = 16, height = 16 }: HomeMenuIcon) {
|
||||
)
|
||||
}
|
||||
|
||||
export function IconMenuKotlin({ width = 16, height = 16 }: HomeMenuIcon) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 16 16"
|
||||
width={width}
|
||||
height={height}
|
||||
fill="none"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M14 14H2V2H14L8 8L14 14Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function IconMenuStatus({ width = 16, height = 16 }: HomeMenuIcon) {
|
||||
return (
|
||||
<svg
|
||||
|
||||
@@ -117,6 +117,13 @@ export const HOMEPAGE_MENU_ITEMS: HomepageMenuItems = [
|
||||
level: 'reference_swift',
|
||||
community: true,
|
||||
},
|
||||
{
|
||||
label: 'Kotlin',
|
||||
icon: 'reference-kotlin',
|
||||
href: '/reference/kotlin/introduction',
|
||||
level: 'reference_kotlin',
|
||||
community: true,
|
||||
},
|
||||
{
|
||||
label: 'Tools reference',
|
||||
},
|
||||
@@ -167,6 +174,12 @@ export const REFERENCES: References = {
|
||||
versions: ['v0'],
|
||||
icon: '/docs/img/libraries/swift-icon.svg',
|
||||
},
|
||||
kotlin: {
|
||||
name: 'Kotlin',
|
||||
library: 'supabase-kt',
|
||||
versions: ['v0'],
|
||||
icon: '/docs/img/libraries/kotlin-icon.svg',
|
||||
},
|
||||
cli: {
|
||||
name: 'CLI',
|
||||
library: undefined,
|
||||
@@ -284,6 +297,10 @@ export const cli = {
|
||||
items: [
|
||||
{ name: 'Overview', url: '/guides/cli' },
|
||||
{ name: 'Managing Environments', url: '/guides/cli/managing-environments' },
|
||||
{
|
||||
name: 'Using environment variables in config.toml',
|
||||
url: '/guides/cli/using-environment-variables-in-config',
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
@@ -845,6 +862,10 @@ export const supabase_cli = {
|
||||
items: [
|
||||
{ name: 'Overview', url: '/guides/cli' },
|
||||
{ name: 'Managing Environments', url: '/guides/cli/managing-environments' },
|
||||
{
|
||||
name: 'Using environment variables in config.toml',
|
||||
url: '/guides/cli/using-environment-variables-in-config',
|
||||
},
|
||||
{
|
||||
name: 'Reference',
|
||||
url: undefined,
|
||||
@@ -923,6 +944,10 @@ export const platform = {
|
||||
name: 'HTTP and Project Issues',
|
||||
url: '/guides/platform/troubleshooting',
|
||||
},
|
||||
{
|
||||
name: 'High CPU Usage',
|
||||
url: '/guides/platform/exhaust-cpu',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -1128,6 +1153,13 @@ export const reference = {
|
||||
items: [],
|
||||
icon: '/img/icons/menu/reference-swift',
|
||||
},
|
||||
{
|
||||
name: 'supabase-kt',
|
||||
url: '/reference/kotlin/start',
|
||||
level: 'reference_kotlin',
|
||||
items: [],
|
||||
icon: '/img/icons/menu/reference-kotlin',
|
||||
},
|
||||
// {
|
||||
// name: 'supabase-python',
|
||||
// url: '/reference/python/start',
|
||||
@@ -1204,6 +1236,13 @@ export const reference_swift_v0 = {
|
||||
parent: '/reference',
|
||||
}
|
||||
|
||||
export const reference_kotlin_v0 = {
|
||||
icon: 'reference-kotlin',
|
||||
title: 'kotlin',
|
||||
url: 'guides/reference/kotlin',
|
||||
parent: '/reference',
|
||||
}
|
||||
|
||||
export const reference_cli = {
|
||||
icon: 'reference-cli',
|
||||
title: 'Supabase CLI',
|
||||
@@ -1325,6 +1364,13 @@ export const references = [
|
||||
icon: '/docs/img/icons/swift-icon.svg',
|
||||
url: '/reference/swift/start',
|
||||
},
|
||||
{
|
||||
label: 'supabase-kt',
|
||||
versions: ['v0'],
|
||||
description: 'something about the reference',
|
||||
icon: '/docs/img/icons/kotlin-icon.svg',
|
||||
url: '/reference/kotlin/start',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -143,6 +143,13 @@ const menus: Menu[] = [
|
||||
specFile: 'supabase_swift_v0.yml',
|
||||
type: 'reference',
|
||||
},
|
||||
{
|
||||
id: 'reference_kotlin_v0',
|
||||
path: '/reference/kotlin',
|
||||
commonSectionsFile: 'common-client-libs-sections.json',
|
||||
specFile: 'supabase_kt_v0.yml',
|
||||
type: 'reference',
|
||||
},
|
||||
{
|
||||
id: 'reference_cli',
|
||||
path: '/reference/cli',
|
||||
|
||||
@@ -56,7 +56,7 @@ const TopNavBar: FC = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<nav className="h-[60px] border-b px-4 backdrop-blur backdrop-filter bg-white-1200 dark:bg-blackA-300">
|
||||
<nav className="h-[60px] border-b px-4 backdrop-blur backdrop-filter bg-white-1200 bg-scale-200/90">
|
||||
<div className="max-w-[1400px] grid grid-cols-12 mx-auto gap-4 px-5 h-full">
|
||||
<div className="col-span-3 flex items-center">
|
||||
<button className="mr-4 block stroke-2 lg:hidden" onClick={toggleMobileMenu}>
|
||||
|
||||
@@ -56,7 +56,7 @@ const TopNavBarRef: FC = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<nav className="h-[60px] border-b backdrop-blur backdrop-filter bg-white-1200 dark:bg-blackA-300">
|
||||
<nav className="h-[60px] border-b backdrop-blur backdrop-filter bg-white-1200 dark:bg-scale-200/90">
|
||||
<div className="px-5 max-w-7xl mx-auto flex gap-3 justify-between items-center h-full">
|
||||
<div className={['lg:hidden'].join(' ')}>
|
||||
<Link href="/">
|
||||
|
||||
@@ -48,6 +48,7 @@ import {
|
||||
IconMenuPython,
|
||||
IconMenuCsharp,
|
||||
IconMenuSwift,
|
||||
IconMenuKotlin,
|
||||
IconMenuApi,
|
||||
IconMenuCli,
|
||||
} from './Navigation/NavigationMenu/HomeMenuIcons'
|
||||
@@ -110,6 +111,7 @@ const components = {
|
||||
IconMenuFlutter,
|
||||
IconMenuPython,
|
||||
IconMenuCsharp,
|
||||
IconMenuKotlin,
|
||||
IconMenuSwift,
|
||||
IconMenuApi,
|
||||
IconMenuCli,
|
||||
|
||||
@@ -123,6 +123,8 @@ const RefFunctionSection: React.FC<IRefFunctionSection> = (props) => {
|
||||
? 'dart'
|
||||
: example?.code?.startsWith('```c#')
|
||||
? 'csharp'
|
||||
: example?.code?.startsWith('```kotlin')
|
||||
? 'kotlin'
|
||||
: 'js'
|
||||
// `
|
||||
// import { createClient } from '@supabase/supabase-js'
|
||||
@@ -156,7 +158,8 @@ const RefFunctionSection: React.FC<IRefFunctionSection> = (props) => {
|
||||
.replace('js', '')
|
||||
.replace('ts', '')
|
||||
.replace('dart', '')
|
||||
.replace('c#', ''))}
|
||||
.replace('c#', '')
|
||||
.replace('kotlin', ''))}
|
||||
</CodeBlock>
|
||||
|
||||
{((tables && tables.length > 0) || sql) && (
|
||||
|
||||
@@ -16,6 +16,8 @@ const nonGeneratedReferencePages = [
|
||||
'docs/reference/csharp',
|
||||
'docs/reference/csharp/installing',
|
||||
'docs/reference/csharp/initializing',
|
||||
'docs/reference/kotlin/installing',
|
||||
'docs/reference/kotlin/initializing',
|
||||
'docs/reference/api',
|
||||
'docs/reference/api/release-notes',
|
||||
'docs/reference/cli',
|
||||
|
||||
@@ -89,7 +89,6 @@ The signIn() method has been deprecated in favor of more explicit method signatu
|
||||
defaultActiveId="1.0x"
|
||||
>
|
||||
<TabPanel id="1.0x" label="Before">
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
const { user, error } = await supabase
|
||||
.auth
|
||||
@@ -98,7 +97,6 @@ const { user, error } = await supabase
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="2.0x" label="After">
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
const {
|
||||
data: { user },
|
||||
@@ -128,7 +126,6 @@ const {
|
||||
defaultActiveId="1.0x"
|
||||
>
|
||||
<TabPanel id="1.0x" label="Before">
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
const { error } = await supabase
|
||||
.auth
|
||||
@@ -137,7 +134,6 @@ const { error } = await supabase
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="2.0x" label="After">
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
const { error } = await supabase
|
||||
.auth
|
||||
@@ -165,7 +161,6 @@ const { error } = await supabase
|
||||
defaultActiveId="1.0x"
|
||||
>
|
||||
<TabPanel id="1.0x" label="Before">
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
const { error } = await supabase
|
||||
.auth
|
||||
@@ -174,7 +169,6 @@ const { error } = await supabase
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="2.0x" label="After">
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
const { error } = await supabase
|
||||
.auth
|
||||
@@ -201,7 +195,6 @@ const { error } = await supabase
|
||||
defaultActiveId="1.0x"
|
||||
>
|
||||
<TabPanel id="1.0x" label="Before">
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
const { error } = await supabase
|
||||
.auth
|
||||
@@ -210,7 +203,6 @@ const { error } = await supabase
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="2.0x" label="After">
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
const { error } = await supabase
|
||||
.auth
|
||||
@@ -238,7 +230,6 @@ const { error } = await supabase
|
||||
defaultActiveId="1.0x"
|
||||
>
|
||||
<TabPanel id="1.0x" label="Before">
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
const { error } = await supabase
|
||||
.auth
|
||||
@@ -248,16 +239,15 @@ const { error } = await supabase
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="2.0x" label="After">
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
const { data, error } = await supabase
|
||||
.auth
|
||||
.signInWithOtp({ phone })
|
||||
|
||||
|
||||
// After receiving a SMS with a OTP.
|
||||
const { data, error } = await supabase
|
||||
.auth
|
||||
.verifyOtp({ phone, token })
|
||||
.auth
|
||||
.verifyOtp({ phone, token })
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
@@ -281,7 +271,6 @@ const { data, error } = await supabase
|
||||
defaultActiveId="1.0x"
|
||||
>
|
||||
<TabPanel id="1.0x" label="Before">
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
const { data, error } = await supabase
|
||||
.auth
|
||||
@@ -291,7 +280,6 @@ const { data, error } = await supabase
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="2.0x" label="After">
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
const { data, error } = await supabase
|
||||
.auth
|
||||
@@ -390,7 +378,6 @@ const { user } = session
|
||||
defaultActiveId="1.0x"
|
||||
>
|
||||
<TabPanel id="1.0x" label="Before">
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
const { user, error } = await supabase
|
||||
.auth
|
||||
@@ -435,7 +422,6 @@ const { user, error } = supabase.auth.setAuth(access_token)
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="2.0x" label="After">
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
const supabase = createClient(
|
||||
SUPABASE_URL,
|
||||
@@ -486,7 +472,6 @@ Previously, these methods return inserted/updated/deleted rows by default (which
|
||||
defaultActiveId="1.0x"
|
||||
>
|
||||
<TabPanel id="1.0x" label="Before">
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
const { data, error } = await supabase
|
||||
.from('my_table')
|
||||
@@ -495,7 +480,6 @@ const { data, error } = await supabase
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="2.0x" label="After">
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
const { data, error } = await supabase
|
||||
.from('my_table')
|
||||
@@ -524,7 +508,6 @@ const { data, error } = await supabase
|
||||
defaultActiveId="1.0x"
|
||||
>
|
||||
<TabPanel id="1.0x" label="Before">
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
const { data, error } = await supabase
|
||||
.from('my_table')
|
||||
@@ -534,7 +517,6 @@ const { data, error } = await supabase
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="2.0x" label="After">
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
const { data, error } = await supabase
|
||||
.from('my_table')
|
||||
@@ -564,7 +546,6 @@ const { data, error } = await supabase
|
||||
defaultActiveId="1.0x"
|
||||
>
|
||||
<TabPanel id="1.0x" label="Before">
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
const { data, error } = await supabase
|
||||
.from('my_table')
|
||||
@@ -573,7 +554,6 @@ const { data, error } = await supabase
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="2.0x" label="After">
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
const { data, error } = await supabase
|
||||
.from('my_table')
|
||||
@@ -602,7 +582,6 @@ const { data, error } = await supabase
|
||||
defaultActiveId="1.0x"
|
||||
>
|
||||
<TabPanel id="1.0x" label="Before">
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
const { data, error } = await supabase
|
||||
.from('my_table')
|
||||
@@ -612,7 +591,6 @@ const { data, error } = await supabase
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="2.0x" label="After">
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
const { data, error } = await supabase
|
||||
.from('my_table')
|
||||
|
||||
@@ -0,0 +1,268 @@
|
||||
---
|
||||
id: installing
|
||||
title: 'Installing & Initialization'
|
||||
slug: installing
|
||||
custom_edit_url: https://github.com/supabase/supabase/edit/master/web/spec/supabase.yml
|
||||
---
|
||||
|
||||
### Add one or more modules to your project
|
||||
|
||||
<RefSubLayout.EducationRow>
|
||||
<RefSubLayout.Examples>
|
||||
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center' }}>
|
||||
<a href="https://github.com/supabase-community/supabase-kt/releases" style={{ marginRight: '8px' }}>
|
||||
<img src="https://img.shields.io/github/release/supabase-community/supabase-kt?label=stable" alt="latest stable supabase-kt version"/>
|
||||
</a>
|
||||
<a href="https://github.com/supabase-community/supabase-kt/releases">
|
||||
<img src="https://img.shields.io/maven-central/v/io.github.jan-tennert.supabase/supabase-kt?label=experimental" alt="latest supabase-kt version"/>
|
||||
</a>
|
||||
</div>
|
||||
Add dependency to your build file:
|
||||
<Tabs
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="kotlin">
|
||||
<TabPanel id="kotlin" label="build.gradle.kts">
|
||||
|
||||
```kotlin
|
||||
implementation("io.github.jan-tennert.supabase:[module]:VERSION")
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="groovy" label="build.gradle">
|
||||
|
||||
```groovy
|
||||
implementation 'io.github.jan-tennert.supabase:[module]:VERSION'
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="xml" label="pom.xml">
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>io.github.jan-tennert.supabase</groupId>
|
||||
<artifactId>[module]</artifactId>
|
||||
<version>VERSION</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
The available modules are: **gotrue-kt**, **realtime-kt**, **storage-kt**, **functions-kt**, **postgrest-kt** and **apollo-graphql**
|
||||
|
||||
When using multiple modules, you can also use the BOM dependency to ensure that all modules use the same version:
|
||||
|
||||
<Tabs
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="kotlin">
|
||||
<TabPanel id="kotlin" label="build.gradle.kts">
|
||||
|
||||
```kotlin
|
||||
implementation(platform("io.github.jan-tennert.supabase:bom:VERSION"))
|
||||
implementation("io.github.jan-tennert.supabase:postgrest-kt")
|
||||
implementation("io.github.jan-tennert.supabase:realtime-kt")
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="groovy" label="build.gradle">
|
||||
|
||||
```groovy
|
||||
implementation platform("io.github.jan-tennert.supabase:bom:VERSION")
|
||||
implementation 'io.github.jan-tennert.supabase:postgrest-kt'
|
||||
implementation 'io.github.jan-tennert.supabase:realtime-kt'
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="xml" label="pom.xml">
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>io.github.jan-tennert.supabase</groupId>
|
||||
<artifactId>bom</artifactId>
|
||||
<version>VERSION</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.github.jan-tennert.supabase</groupId>
|
||||
<artifactId>postgrest-kt</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.github.jan-tennert.supabase</groupId>
|
||||
<artifactId>realtime-kt</artifactId>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
</RefSubLayout.Examples>
|
||||
|
||||
</RefSubLayout.EducationRow>
|
||||
|
||||
### Add Ktor Client Engine to each of your Kotlin targets (required)
|
||||
|
||||
<RefSubLayout.EducationRow>
|
||||
<RefSubLayout.Examples>
|
||||
|
||||
You can find a list of engines [here](https://ktor.io/docs/http-client-engines.html)
|
||||
|
||||
<Tabs
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="kotlin">
|
||||
<TabPanel id="kotlin" label="build.gradle.kts">
|
||||
|
||||
```kotlin
|
||||
implementation("io.ktor:ktor-client-[engine]:KTOR_VERSION")
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="groovy" label="build.gradle">
|
||||
|
||||
```groovy
|
||||
implementation 'io.ktor:ktor-client-[engine]:KTOR_VERSION'
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="xml" label="pom.xml">
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>io.ktor</groupId>
|
||||
<artifactId>ktor-client-[engine]</artifactId>
|
||||
<version>KTOR_VERSION</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
Multiplatform example:
|
||||
|
||||
<Tabs
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="kotlin">
|
||||
<TabPanel id="kotlin" label="build.gradle.kts">
|
||||
|
||||
```kotlin
|
||||
val commonMain by getting {
|
||||
dependencies {
|
||||
//supabase modules
|
||||
}
|
||||
}
|
||||
val jvmMain by getting {
|
||||
dependencies {
|
||||
implementation("io.ktor:ktor-client-cio:KTOR_VERSION")
|
||||
}
|
||||
}
|
||||
val androidMain by getting {
|
||||
dependsOn(jvmMain)
|
||||
}
|
||||
val jsMain by getting {
|
||||
dependencies {
|
||||
implementation("io.ktor:ktor-client-js:KTOR_VERSION")
|
||||
}
|
||||
}
|
||||
val iosMain by getting {
|
||||
dependencies {
|
||||
implementation("io.ktor:ktor-client-darwin:KTOR_VERSION")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
</RefSubLayout.Examples>
|
||||
|
||||
</RefSubLayout.EducationRow>
|
||||
|
||||
### Add KotlinX Serialization (required for PostgREST and Realtime serialization)
|
||||
|
||||
<RefSubLayout.EducationRow>
|
||||
<RefSubLayout.Examples>
|
||||
|
||||
Add the serialization plugin
|
||||
|
||||
<Tabs
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="kotlin">
|
||||
<TabPanel id="kotlin" label="build.gradle.kts">
|
||||
|
||||
```kotlin
|
||||
plugins {
|
||||
kotlin("plugin.serialization") version "KOTLIN_VERSION"
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="groovy" label="build.gradle">
|
||||
|
||||
```groovy
|
||||
plugins {
|
||||
id 'org.jetbrains.kotlin.plugin.serialization' version 'KOTLIN_VERSION'
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="xml" label="pom.xml">
|
||||
|
||||
```xml
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-maven-plugin</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>compile</id>
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<compilerPlugins>
|
||||
<plugin>kotlinx-serialization</plugin>
|
||||
</compilerPlugins>
|
||||
</configuration>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-maven-serialization</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
Now you can create serializable classes in your project:
|
||||
```kotlin
|
||||
@Serializable
|
||||
data class Message(
|
||||
val id: Int,
|
||||
val content: String,
|
||||
@SerialName("author_id")
|
||||
val authorId: String
|
||||
)
|
||||
```
|
||||
And use them in your Supabase queries:
|
||||
```kotlin
|
||||
val messages = client.postgrest["messages"].select().decodeList<Message>()
|
||||
```
|
||||
|
||||
</RefSubLayout.Examples>
|
||||
|
||||
</RefSubLayout.EducationRow>
|
||||
@@ -0,0 +1,33 @@
|
||||
---
|
||||
id: introduction
|
||||
title: Introduction
|
||||
hideTitle: true
|
||||
---
|
||||
|
||||
<div className="flex items-start gap-6 not-prose" id="introduction">
|
||||
<IconMenuKotlin width={35} height={35} />
|
||||
<div className="flex flex-col gap-2">
|
||||
<h1 className="text-3xl text-scale-1200 m-0">Kotlin Client Library</h1>
|
||||
<h2 className="text-base font-mono text-scale-1100">@supabase-community/supabase-kt</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="max-w-xl">
|
||||
|
||||
This reference documents every object and method available in Supabase's Kotlin Multiplatform library, [supabase-kt](https://github.com/supabase-community/supabase-kt). You can use supabase-kt to interact with your Postgres database, listen to database changes, invoke Deno Edge Functions, build login and user management functionality, and manage large files.
|
||||
|
||||
Supported Kotlin targets:
|
||||
|
||||
- JVM
|
||||
- Android
|
||||
- iOS (Experimental)
|
||||
- JS (Browser)
|
||||
|
||||
</div>
|
||||
|
||||
<div className="max-w-xl bg-slate-300 px-4 py-2 rounded-md">
|
||||
<p>The Kotlin 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, [jan-tennert](https://github.com/jan-tennert).</p>
|
||||
|
||||
</div>
|
||||
@@ -14,9 +14,9 @@ hideTitle: true
|
||||
<RefSubLayout.EducationRow>
|
||||
<RefSubLayout.Details>
|
||||
|
||||
The Supabase Auth Server (GoTrue) is a JSON Web Token (JWT)-based API for managing users and issuing access tokens.
|
||||
The Supabase Auth Server (GoTrue) is a JSON Web Token (JWT)-based API for managing users and issuing access tokens.
|
||||
|
||||
GoTrue is an open-source API written in Golang, that acts as a self-standing API service for handling user registration and authentication for JAM projects. It's based on OAuth2 and JWT and handles user signup, authentication, and custom user data.
|
||||
GoTrue is an open-source API written in Golang, that acts as a self-standing API service for handling user registration and authentication for JAM projects. It's based on OAuth2 and JWT and handles user signup, authentication, and custom user data.
|
||||
|
||||
</RefSubLayout.Details>
|
||||
|
||||
|
||||
@@ -14,12 +14,12 @@ hideTitle: true
|
||||
<RefSubLayout.EducationRow>
|
||||
<RefSubLayout.Details>
|
||||
|
||||
An S3 compatible object storage service that integrates with Postgres.
|
||||
An S3 compatible object storage service that integrates with Postgres.
|
||||
|
||||
- Uses Postgres as it's datastore for storing metadata
|
||||
- Authorization rules are written as Postgres Row Level Security policies
|
||||
- Integrates with S3 as the storage backend (with more in the pipeline!)
|
||||
- Extremely lightweight and performant
|
||||
- Uses Postgres as it's datastore for storing metadata
|
||||
- Authorization rules are written as Postgres Row Level Security policies
|
||||
- Integrates with S3 as the storage backend (with more in the pipeline!)
|
||||
- Extremely lightweight and performant
|
||||
|
||||
</RefSubLayout.Details>
|
||||
|
||||
|
||||
@@ -13,9 +13,9 @@ hideTitle: true
|
||||
</div>
|
||||
|
||||
<div className="max-w-xl">
|
||||
This reference documents every object and method available in Supabase's Swift library, [supabase-swift](https://github.com/supabase-community/supabase-swift). You can use supabase-swift to interact with your Postgres database, listen to database changes, invoke Deno Edge Functions, build login and user management functionality, and manage large files.
|
||||
This reference documents every object and method available in Supabase's Swift library, [supabase-swift](https://github.com/supabase-community/supabase-swift). You can use supabase-swift to interact with your Postgres database, listen to database changes, invoke Deno Edge Functions, build login and user management functionality, and manage large files.
|
||||
|
||||
We also provide a [supabase](https://pub.dev/packages/supabase) package for non-Swift projects.
|
||||
We also provide a [supabase](https://pub.dev/packages/supabase) package for non-Swift projects.
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@@ -4,9 +4,6 @@ title: 'Initializing'
|
||||
slug: initializing
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs'
|
||||
import TabItem from '@theme/TabItem'
|
||||
|
||||
## Flutter
|
||||
|
||||
For `supabase-flutter`, you will be using the static `initialize()` method on `Supabase` class.
|
||||
@@ -30,8 +27,9 @@ final supabase = Supabase.instance.client;
|
||||
|
||||
### Call `initialize()` with custom headers
|
||||
|
||||
You can pass `headers` to initialize your Supabase client with customer headers.
|
||||
You can pass `headers` to initialize your Supabase client with customer headers.
|
||||
Here is an example of passing a custom auth header to Supabase client.
|
||||
|
||||
```dart title=main.dart
|
||||
Future<void> main() async {
|
||||
await Supabase.initialize(
|
||||
|
||||
@@ -4,9 +4,6 @@ title: 'Installing'
|
||||
slug: installing
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs'
|
||||
import TabItem from '@theme/TabItem'
|
||||
|
||||
## Flutter
|
||||
|
||||
For Flutter project, you can use [supabase_flutter](https://pub.dev/packages/supabase_flutter) package.
|
||||
|
||||
@@ -5,9 +5,6 @@ slug: generating-types
|
||||
custom_edit_url: https://github.com/supabase/supabase/edit/master/spec/supabase_js_v1.yml
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs'
|
||||
import TabItem from '@theme/TabItem'
|
||||
|
||||
Supabase will soon release native type generators that dump your database types for various languages. For now, we support TypeScript [through third-party tools](/docs/guides/database/api/generating-types).
|
||||
|
||||
## Usage with TypeScript
|
||||
|
||||
@@ -5,9 +5,6 @@ slug: initializing
|
||||
custom_edit_url: https://github.com/supabase/supabase/edit/master/spec/supabase_js_v1.yml
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs'
|
||||
import TabItem from '@theme/TabItem'
|
||||
|
||||
You can initialize a new Supabase client using the `createClient()` method.
|
||||
|
||||
The Supabase client is your entrypoint to the rest of the Supabase functionality
|
||||
@@ -117,7 +114,7 @@ const supabase = createClient('https://xyzcompany.supabase.co', 'public-anon-key
|
||||
```
|
||||
|
||||
By default the API server points to the `public` schema. You can enable other database schemas within the Dashboard.
|
||||
Go to `Settings > API > Schema` and add the schema which you want to expose to the API. You also need to grant `USAGE` on your new schema with the grants you desire, such as `SELECT, INSERT, UPDATE, DELETE`.
|
||||
Go to `Settings > API > Schema` and add the schema which you want to expose to the API. You also need to grant `USAGE` on your new schema with the grants you desire, such as `SELECT, INSERT, UPDATE, DELETE`.
|
||||
|
||||
Note: each client connection can only access a single schema, so the code above can access the `other_schema` schema but cannot access the `public` schema.
|
||||
|
||||
|
||||
@@ -5,9 +5,6 @@ slug: installing
|
||||
custom_edit_url: https://github.com/supabase/supabase/edit/master/spec/supabase_js_v1.yml
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs'
|
||||
import TabItem from '@theme/TabItem'
|
||||
|
||||
All JavaScript libraries are built directly by the Supabase team.
|
||||
|
||||
Other languages are built by the community and supported by Supabase.
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
---
|
||||
id: changing-timezones
|
||||
title: "Changing Timezones"
|
||||
title: 'Changing Timezones'
|
||||
slug: changing-timezones
|
||||
custom_edit_url: https://github.com/supabase/supabase/edit/master/web/spec/postgres.yml
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabsPanel from '@theme/TabsPanel';
|
||||
|
||||
Data types.
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -19,38 +15,22 @@ Data types.
|
||||
<TabsPanel id="sql" label="sql">
|
||||
|
||||
```sql
|
||||
alter database postgres
|
||||
alter database postgres
|
||||
set timezone to 'America/New_York';
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Notes
|
||||
|
||||
- View a full list of timezones on [Wikipedia](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones).
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
### Change timezone
|
||||
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -59,11 +39,10 @@ set timezone to 'America/New_York';
|
||||
<TabsPanel id="sql" label="sql">
|
||||
|
||||
```sql
|
||||
alter database postgres
|
||||
alter database postgres
|
||||
set timezone to 'America/New_York';
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
@@ -77,7 +56,6 @@ Get a full list of timezones supported by your database. This will return the fo
|
||||
- `utc_offset`: Offset from UTC (positive means east of Greenwich)
|
||||
- `is_dst`: True if currently observing daylight savings
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -87,11 +65,10 @@ Get a full list of timezones supported by your database. This will return the fo
|
||||
|
||||
```sql
|
||||
select name, abbrev, utc_offset, is_dst
|
||||
from pg_timezone_names()
|
||||
from pg_timezone_names()
|
||||
order by name;
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
@@ -108,12 +85,11 @@ Use `ilike` (case insensitive search) to find specific timezones.
|
||||
<TabsPanel id="sql" label="sql">
|
||||
|
||||
```sql
|
||||
select *
|
||||
from pg_timezone_names()
|
||||
select *
|
||||
from pg_timezone_names()
|
||||
where name ilike '%york%';
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
</Tabs>
|
||||
@@ -1,16 +1,12 @@
|
||||
---
|
||||
id: columns
|
||||
title: "Columns"
|
||||
title: 'Columns'
|
||||
slug: columns
|
||||
custom_edit_url: https://github.com/supabase/supabase/edit/master/web/spec/postgres.yml
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabsPanel from '@theme/TabsPanel';
|
||||
|
||||
Creating columns.
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -26,33 +22,14 @@ create table table_name (
|
||||
);
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
### During table creation
|
||||
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -68,15 +45,12 @@ create table table_name (
|
||||
);
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
|
||||
### Create column
|
||||
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -89,7 +63,6 @@ alter table new_table
|
||||
add new_column text;
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
</Tabs>
|
||||
@@ -1,16 +1,12 @@
|
||||
---
|
||||
id: connection-strings
|
||||
title: "Connection Strings"
|
||||
title: 'Connection Strings'
|
||||
slug: connection-strings
|
||||
custom_edit_url: https://github.com/supabase/supabase/edit/master/web/spec/postgres.yml
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabsPanel from '@theme/TabsPanel';
|
||||
|
||||
There are various ways to connect to your database, depending on the configuration of your Postgres instance and the tool which you are connecting with.
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -20,42 +16,27 @@ There are various ways to connect to your database, depending on the configurati
|
||||
|
||||
```bash
|
||||
postgres://postgres:postgres@localhost:5432/postgres
|
||||
# or
|
||||
# or
|
||||
postgresql://postgres:postgres@localhost:5432/postgres
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Notes
|
||||
|
||||
- [Official Documentation](https://www.postgresql.org/docs/current/libpq-connect.html).
|
||||
- Avoid using special characters usernames and passwords. If you use special characters in a connection URL, you'll need to URL encode any special characters.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
### Basic connection string
|
||||
|
||||
If you're using a default setup, your postgres connection string will likely be in the format:
|
||||
If you're using a default setup, your postgres connection string will likely be in the format:
|
||||
|
||||
`postgres://{user}:{password}@{host}:{port}/{database_name}`
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -65,11 +46,10 @@ If you're using a default setup, your postgres connection string will likely be
|
||||
|
||||
```bash
|
||||
postgres://postgres:postgres@localhost:5432/postgres
|
||||
# or
|
||||
# or
|
||||
postgresql://postgres:postgres@localhost:5432/postgres
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
@@ -89,7 +69,6 @@ See full [documentation](http://jdbc.postgresql.org/documentation/head/connect.h
|
||||
jdbc:postgresql://{host}:{port}/{database_name}
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
@@ -109,7 +88,6 @@ See full [documentation](http://npgsql.projects.postgresql.org/docs/manual/UserM
|
||||
Server=host;Port=5432;User Id=username;Password=secret;Database=database_name;
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
@@ -129,7 +107,6 @@ See full [documentation](http://php.net/manual/en/book.pgsql.php).
|
||||
host=hostname port=5432 dbname=databasename user=username password=secret
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
</Tabs>
|
||||
@@ -1,40 +1,16 @@
|
||||
---
|
||||
id: database-passwords
|
||||
title: "Database Passwords"
|
||||
title: 'Database Passwords'
|
||||
slug: database-passwords
|
||||
custom_edit_url: https://github.com/supabase/supabase/edit/master/web/spec/postgres.yml
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabsPanel from '@theme/TabsPanel';
|
||||
|
||||
Manage the passwords of your database users using any super user.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
### Password reset
|
||||
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -43,11 +19,10 @@ Manage the passwords of your database users using any super user.
|
||||
<TabsPanel id="sql" label="sql">
|
||||
|
||||
```sql
|
||||
alter user postgres
|
||||
alter user postgres
|
||||
with password 'new_password';
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
</Tabs>
|
||||
@@ -1,40 +1,16 @@
|
||||
---
|
||||
id: database-users
|
||||
title: "Database Users"
|
||||
title: 'Database Users'
|
||||
slug: database-users
|
||||
custom_edit_url: https://github.com/supabase/supabase/edit/master/web/spec/postgres.yml
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabsPanel from '@theme/TabsPanel';
|
||||
|
||||
Users and Roles are almost interchangeable.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
### Create New User
|
||||
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -47,7 +23,6 @@ create user new_user
|
||||
with password 'hello';
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
</Tabs>
|
||||
@@ -1,15 +1,11 @@
|
||||
---
|
||||
id: index
|
||||
title: "Getting started"
|
||||
title: 'Getting started'
|
||||
slug: getting-started
|
||||
custom_edit_url: https://github.com/supabase/supabase/edit/master/web/spec/postgres.yml
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabsPanel from '@theme/TabsPanel';
|
||||
|
||||
|
||||
PostgreSQL, also known as Postgres, is a free and open-source relational
|
||||
database management system emphasizing extensibility and SQL compliance.
|
||||
It was originally named POSTGRES, referring to its origins as a successor
|
||||
to the Ingres database developed at the University of California, Berkeley.
|
||||
PostgreSQL, also known as Postgres, is a free and open-source relational
|
||||
database management system emphasizing extensibility and SQL compliance.
|
||||
It was originally named POSTGRES, referring to its origins as a successor
|
||||
to the Ingres database developed at the University of California, Berkeley.
|
||||
@@ -1,42 +1,19 @@
|
||||
---
|
||||
id: publications
|
||||
title: "Publications"
|
||||
title: 'Publications'
|
||||
slug: publications
|
||||
custom_edit_url: https://github.com/supabase/supabase/edit/master/web/spec/postgres.yml
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabsPanel from '@theme/TabsPanel';
|
||||
|
||||
Publications are a way of grouping changes generated from a table or a group of tables.
|
||||
Publications are a way of grouping changes generated from a table or a group of tables.
|
||||
These changes can then be sent to other systems (usually another Postgres database).
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
### Create a Publication
|
||||
|
||||
This publication will contain all changes to all tables.
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -45,19 +22,16 @@ This publication will contain all changes to all tables.
|
||||
<TabsPanel id="sql" label="sql">
|
||||
|
||||
```sql
|
||||
create publication publication_name
|
||||
create publication publication_name
|
||||
for all tables;
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
|
||||
### Create a Publication which listens to individual tables
|
||||
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -66,19 +40,16 @@ for all tables;
|
||||
<TabsPanel id="sql" label="sql">
|
||||
|
||||
```sql
|
||||
create publication publication_name
|
||||
create publication publication_name
|
||||
for table table_one, table_two;
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
|
||||
### Add tables to an existing publication
|
||||
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -87,19 +58,16 @@ for table table_one, table_two;
|
||||
<TabsPanel id="sql" label="sql">
|
||||
|
||||
```sql
|
||||
alter publication publication_name
|
||||
alter publication publication_name
|
||||
add table table_name;
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
|
||||
### Listens to inserts only
|
||||
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -108,20 +76,17 @@ add table table_name;
|
||||
<TabsPanel id="sql" label="sql">
|
||||
|
||||
```sql
|
||||
create publication publication_name
|
||||
create publication publication_name
|
||||
for all tables
|
||||
with (publish = 'insert');
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
|
||||
### Listens to updates only
|
||||
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -130,20 +95,17 @@ with (publish = 'insert');
|
||||
<TabsPanel id="sql" label="sql">
|
||||
|
||||
```sql
|
||||
create publication publication_name
|
||||
create publication publication_name
|
||||
for all tables
|
||||
with (publish = 'update');
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
|
||||
### Listens to deletions only
|
||||
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -152,20 +114,17 @@ with (publish = 'update');
|
||||
<TabsPanel id="sql" label="sql">
|
||||
|
||||
```sql
|
||||
create publication publication_name
|
||||
create publication publication_name
|
||||
for all tables
|
||||
with (publish = 'delete');
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
|
||||
### Remove a Publication
|
||||
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -177,7 +136,6 @@ with (publish = 'delete');
|
||||
drop publication if exists publication_name;
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
@@ -186,7 +144,6 @@ drop publication if exists publication_name;
|
||||
|
||||
If you are planning to re-create a publication, it's best to do it in a transaction to ensure the operation succeeds.
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -195,16 +152,15 @@ If you are planning to re-create a publication, it's best to do it in a transact
|
||||
<TabsPanel id="sql" label="sql">
|
||||
|
||||
```sql
|
||||
begin;
|
||||
begin;
|
||||
-- remove the realtime publication
|
||||
drop publication if exists publication_name;
|
||||
drop publication if exists publication_name;
|
||||
|
||||
-- re-create the publication but don't enable it for any tables
|
||||
create publication publication_name;
|
||||
create publication publication_name;
|
||||
commit;
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
</Tabs>
|
||||
@@ -1,19 +1,15 @@
|
||||
---
|
||||
id: schemas
|
||||
title: "Schemas"
|
||||
title: 'Schemas'
|
||||
slug: schemas
|
||||
custom_edit_url: https://github.com/supabase/supabase/edit/master/web/spec/postgres.yml
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabsPanel from '@theme/TabsPanel';
|
||||
Schemas are like "folders". They help to keep your database organized.
|
||||
|
||||
Schemas are like "folders". They help to keep your database organized.
|
||||
|
||||
Schemas are particularly useful for security. You can set different permissions on each schema.
|
||||
Schemas are particularly useful for security. You can set different permissions on each schema.
|
||||
For example, you might want to use a `public` schema for user-facing data, and an `auth` schema for all logins and secured data.
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -25,36 +21,20 @@ For example, you might want to use a `public` schema for user-facing data, and a
|
||||
create schema schema_name;
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Notes
|
||||
|
||||
- Schemas contain [tables](/docs/reference/postgres/tables), columns, triggers, functions, etc.
|
||||
- Postgres comes with a `public` schema set up by default.
|
||||
- It is best practice to use lowercase and underscores when naming schemas. For example: `schema_name`, not `Schema Name`.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
### Creating a schema
|
||||
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -66,15 +46,12 @@ create schema schema_name;
|
||||
create schema schema_name;
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
|
||||
### Removing a schema
|
||||
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -86,7 +63,6 @@ create schema schema_name;
|
||||
drop schema if exists schema_name;
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
@@ -96,7 +72,6 @@ drop schema if exists schema_name;
|
||||
Although it's not recommended, you can use uppercase and spaces when naming your schema by wrapping the name with double-quotes.
|
||||
As a result, you will always need to use double-quotes when referencing your schema.
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -108,7 +83,6 @@ As a result, you will always need to use double-quotes when referencing your sch
|
||||
create schema "Schema Name";
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
</Tabs>
|
||||
@@ -1,19 +1,15 @@
|
||||
---
|
||||
id: tables
|
||||
title: "Tables"
|
||||
title: 'Tables'
|
||||
slug: tables
|
||||
custom_edit_url: https://github.com/supabase/supabase/edit/master/web/spec/postgres.yml
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabsPanel from '@theme/TabsPanel';
|
||||
|
||||
Tables are similar to excel spreadsheets. They contain columns & rows of data. There are a few key differences from a spreadsheet however:
|
||||
|
||||
- Every column is a strict type of data. When you set up a column, you must define what "data type" it is.
|
||||
- Tables can be joined together through relationships. For example you can have a "users" table, which is joined to a "teams" table.
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -28,7 +24,7 @@ create table table_name (
|
||||
name text
|
||||
);
|
||||
|
||||
# with schema
|
||||
# with schema
|
||||
create table schema_name.table_name (
|
||||
id integer primary key,
|
||||
data jsonb,
|
||||
@@ -36,36 +32,20 @@ create table schema_name.table_name (
|
||||
);
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Notes
|
||||
|
||||
- Tables contain columns, rows, triggers, comments,
|
||||
- Tables contain columns, rows, triggers, comments,
|
||||
- It is best practice to use lowercase and underscores when naming tables. For example: `table_name`, not `Table Name`.
|
||||
- Tables belong to [schemas](/docs/reference/postgres/schemas). If you don't explicitly pass the schema, Postgres will assume that you want to create the table in the `public` schema.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
### Create table
|
||||
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -80,7 +60,7 @@ create table table_name (
|
||||
name text
|
||||
);
|
||||
|
||||
# with schema
|
||||
# with schema
|
||||
create table schema_name.table_name (
|
||||
id integer primary key,
|
||||
data jsonb,
|
||||
@@ -88,15 +68,12 @@ create table schema_name.table_name (
|
||||
);
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
|
||||
### Primary keys using multiple columns
|
||||
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -113,15 +90,12 @@ create table table_name (
|
||||
);
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
|
||||
### Multiple foreign keys to the same table
|
||||
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -135,15 +109,12 @@ alter table table_name
|
||||
add constraint constraint_name_2 foreign key (column_2) references foreign_table(id);
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
|
||||
### Delete a table
|
||||
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -155,7 +126,6 @@ alter table table_name
|
||||
delete table if exists table_name;
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
</Tabs>
|
||||
@@ -12,6 +12,7 @@ const clientLibFiles = [
|
||||
{ fileName: 'supabase_py_v2', label: 'python', version: 'v2', versionSlug: false },
|
||||
{ fileName: 'supabase_csharp_v0', label: 'csharp', version: 'v0', versionSlug: false },
|
||||
{ fileName: 'supabase_swift_v0', label: 'swift', version: 'v0', versionSlug: false },
|
||||
{ fileName: 'supabase_kt_v0', label: 'kotlin', version: 'v0', versionSlug: false },
|
||||
]
|
||||
|
||||
export function generateReferencePages() {
|
||||
|
||||
@@ -12,6 +12,7 @@ interface Props {
|
||||
description?: string
|
||||
hide_table_of_contents?: boolean
|
||||
video?: string
|
||||
tocVideo?: string
|
||||
canonical?: string
|
||||
}
|
||||
children: any
|
||||
|
||||
@@ -94,6 +94,10 @@ const levelsData = {
|
||||
icon: '/docs/img/icons/menu/reference-swift',
|
||||
name: 'Swift Reference v1.0',
|
||||
},
|
||||
reference_kotlin_v0: {
|
||||
icon: '/docs/img/icons/menu/reference-kotlin',
|
||||
name: 'Kotlin Reference v0.0',
|
||||
},
|
||||
reference_cli: {
|
||||
icon: '/docs/img/icons/menu/reference-cli',
|
||||
name: 'CLI Reference',
|
||||
@@ -197,7 +201,7 @@ const MobileMenuBackdrop = memo(function MobileMenuBackdrop() {
|
||||
'left-0',
|
||||
'right-0',
|
||||
'z-10',
|
||||
'backdrop-blur-sm backdrop-filter bg-white-1200 dark:bg-blackA-600',
|
||||
'backdrop-blur-sm backdrop-filter bg-white-1200 dark:bg-scale-200/90',
|
||||
mobileMenuOpen ? 'absolute h-full w-full top-0 left-0' : 'hidden h-0',
|
||||
// always hide on desktop
|
||||
'lg:hidden',
|
||||
@@ -272,7 +276,7 @@ const NavContainer = memo(function NavContainer() {
|
||||
'relative',
|
||||
'w-auto',
|
||||
'border-r overflow-auto h-screen',
|
||||
'backdrop-blur backdrop-filter bg-white-1200 dark:bg-blackA-300',
|
||||
'backdrop-blur backdrop-filter bg-white-1200 dark:bg-scale-200',
|
||||
'flex flex-col',
|
||||
].join(' ')}
|
||||
>
|
||||
@@ -328,7 +332,7 @@ const SiteLayout = ({ children }: PropsWithChildren<{}>) => {
|
||||
className={[
|
||||
'sticky transition-all top-0',
|
||||
'z-10',
|
||||
'backdrop-blur backdrop-filter bg-white-1200 dark:bg-blackA-300',
|
||||
'backdrop-blur backdrop-filter bg-white-1200 dark:bg-scale-200',
|
||||
].join(' ')}
|
||||
>
|
||||
<div className={['lg:hidden', 'px-5 ', 'border-b z-10'].join(' ')}>
|
||||
|
||||
@@ -5,6 +5,7 @@ import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { FC, useEffect, useRef, useState } from 'react'
|
||||
import { IconExternalLink } from 'ui'
|
||||
import ExpandableVideo from 'ui/src/components/ExpandableVideo/ExpandableVideo'
|
||||
import components from '~/components'
|
||||
import { highlightSelectedTocItem } from '~/components/CustomHTMLElements/CustomHTMLElements.utils'
|
||||
import FooterHelpCallout, { FooterHelpCalloutType } from '~/components/FooterHelpCallout'
|
||||
@@ -21,6 +22,7 @@ interface Props {
|
||||
subtitle?: string
|
||||
footerHelpType?: FooterHelpCalloutType
|
||||
video?: string
|
||||
tocVideo?: string
|
||||
canonical?: string
|
||||
}
|
||||
children: any
|
||||
@@ -36,7 +38,6 @@ const Layout: FC<Props> = (props) => {
|
||||
const [tocList, setTocList] = useState([])
|
||||
|
||||
const { asPath } = useRouter()
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
const EDIT_BUTTON_EXCLUDE_LIST = ['/404']
|
||||
@@ -64,6 +65,7 @@ const Layout: FC<Props> = (props) => {
|
||||
}, [])
|
||||
|
||||
const hasTableOfContents = tocList.length > 0
|
||||
const tocVideoPreview = `http://img.youtube.com/vi/${props.meta.tocVideo}/0.jpg`
|
||||
|
||||
// page type, ie, Auth, Database, Storage etc
|
||||
const ogPageType = asPath.split('/')[2]
|
||||
|
||||
@@ -6,7 +6,13 @@ import SideBar from '~/components/Navigation/SideBar'
|
||||
import TableOfContents from '~/components/TableOfContents'
|
||||
|
||||
interface Props {
|
||||
meta: { title: string; description?: string; hide_table_of_contents?: boolean; video?: string }
|
||||
meta: {
|
||||
title: string
|
||||
description?: string
|
||||
hide_table_of_contents?: boolean
|
||||
video?: string
|
||||
tocVideo?: string
|
||||
}
|
||||
children: any
|
||||
toc?: any
|
||||
menuItems: any
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
import { ICommonSection } from '~/components/reference/Reference.types'
|
||||
|
||||
async function handleRefGetStaticPaths(sections: ICommonSection[], libraryPath: string) {
|
||||
async function handleRefGetStaticPaths(sections: ICommonSection[]) {
|
||||
return {
|
||||
paths: sections.map((section) => {
|
||||
return {
|
||||
params: {
|
||||
slug: libraryPath
|
||||
.split('/')
|
||||
.filter((dir) => !!dir)
|
||||
.concat(section.slug),
|
||||
slug: [section.slug],
|
||||
},
|
||||
}
|
||||
}),
|
||||
|
||||
@@ -60,6 +60,7 @@ const nextConfig = {
|
||||
'user-images.githubusercontent.com',
|
||||
'raw.githubusercontent.com',
|
||||
'weweb-changelog.ghost.io',
|
||||
'img.youtube.com',
|
||||
],
|
||||
},
|
||||
experimental: {
|
||||
|
||||
@@ -32,6 +32,7 @@ Supabase provides a RESTful API using [PostgREST](https://postgrest.org/). This
|
||||
It provides everything you need from a CRUD API at the URL `https://<project_ref>.supabase.co/rest/v1/`.
|
||||
|
||||
The REST interface is automatically reflected from your database's schema and supports:
|
||||
|
||||
- Basic CRUD operations (Create/Read/Update/Delete)
|
||||
- Arbitrarily deep relationships among tables/views, functions that return table types can also nest related tables/views.
|
||||
- Works with Postgres Views, Materialized Views and Foreign Tables
|
||||
@@ -42,6 +43,7 @@ The REST interface is automatically reflected from your database's schema and su
|
||||
The REST API resolves all requests to a single SQL statement leading to fast response times and high throughput.
|
||||
|
||||
Reference:
|
||||
|
||||
- [Docs](https://postgrest.org/)
|
||||
- [Source Code](https://github.com/PostgREST/postgrest)
|
||||
|
||||
@@ -52,6 +54,7 @@ You can introspect and query the GraphQL API of an existing Supabase project wit
|
||||
or navigate there manually at `API Docs > GraphQL > GraphiQL`.
|
||||
|
||||
The GraphQL interface is automatically reflected from your database's schema and supports:
|
||||
|
||||
- Basic CRUD operations (Create/Read/Update/Delete)
|
||||
- Support for Tables, Views, Materialized Views, and Foreign Tables
|
||||
- Arbitrarily deep relationships among tables/views
|
||||
@@ -61,6 +64,7 @@ The GraphQL interface is automatically reflected from your database's schema and
|
||||
The GraphQL API resolves all requests in a single round-trip leading to fast response times and high throughput.
|
||||
|
||||
Reference:
|
||||
|
||||
- [Docs](https://supabase.github.io/pg_graphql/)
|
||||
- [Source Code](https://github.com/supabase/pg_graphql)
|
||||
|
||||
|
||||
@@ -40,24 +40,25 @@ Let's use an example database that stores `countries` and `cities`:
|
||||
<TabPanel id="SQL" label="SQL">
|
||||
|
||||
```sql
|
||||
create table
|
||||
countries ("id" serial primary key, "name" text);
|
||||
create table countries (
|
||||
"id" serial primary key,
|
||||
"name" text
|
||||
);
|
||||
|
||||
insert into
|
||||
countries (id, name)
|
||||
insert into countries
|
||||
(id, name)
|
||||
values
|
||||
(1, 'United Kingdom'),
|
||||
(2, 'United States');
|
||||
|
||||
create table
|
||||
cities (
|
||||
"id" serial primary key,
|
||||
"name" text,
|
||||
"country_id" int references "countries"
|
||||
);
|
||||
create table cities (
|
||||
"id" serial primary key,
|
||||
"name" text,
|
||||
"country_id" int references "countries"
|
||||
);
|
||||
|
||||
insert into
|
||||
cities (name, country_id)
|
||||
insert into cities
|
||||
(name, country_id)
|
||||
values
|
||||
('London', 1),
|
||||
('Manchester', 1),
|
||||
@@ -130,18 +131,21 @@ GET https://[REF].supabase.co/rest/v1/countries?select=id,name,cities(id,name)
|
||||
The Serverless APIs will detect many-to-many joins. For example, if you have a database which stored teams of users (where each user could belong to many teams):
|
||||
|
||||
```sql
|
||||
create table
|
||||
users ("id" serial primary key, "name" text);
|
||||
create table users (
|
||||
"id" serial primary key,
|
||||
"name" text
|
||||
);
|
||||
|
||||
create table
|
||||
teams ("id" serial primary key, "team_name" text);
|
||||
create table teams (
|
||||
"id" serial primary key,
|
||||
"team_name" text
|
||||
);
|
||||
|
||||
create table
|
||||
members (
|
||||
"user_id" int references users,
|
||||
"team_id" int references teams,
|
||||
primary key(user_id, team_id)
|
||||
);
|
||||
create table members (
|
||||
"user_id" int references users,
|
||||
"team_id" int references teams,
|
||||
primary key (user_id, team_id)
|
||||
);
|
||||
```
|
||||
|
||||
In these cases you don't need to explicitly define the joining table (members). If we wanted to fetch all the teams and the members in each team:
|
||||
|
||||
@@ -49,10 +49,8 @@ To confirm that safeupdate is enabled for API queries, run the following query:
|
||||
select
|
||||
usename,
|
||||
useconfig
|
||||
from
|
||||
pg_shadow
|
||||
where
|
||||
usename = 'authenticator';
|
||||
from pg_shadow
|
||||
where usename = 'authenticator';
|
||||
```
|
||||
|
||||
The expected value for `useconfig` should be:
|
||||
|
||||
@@ -7,7 +7,7 @@ export const meta = {
|
||||
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/).
|
||||
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/) and [Cloudflare Turnstile](https://www.cloudflare.com/products/turnstile/).
|
||||
|
||||
<div className="video-container">
|
||||
<iframe
|
||||
@@ -18,8 +18,15 @@ Supabase provides you with the option of adding captcha to your sign-in, sign-up
|
||||
></iframe>
|
||||
</div>
|
||||
|
||||
## Sign up for hCaptcha
|
||||
## Sign up for Captcha
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="hcaptcha-1"
|
||||
>
|
||||
<TabPanel id="hcaptcha-1" label="HCaptcha">
|
||||
Go to the [hCaptcha](https://www.hcaptcha.com/) website and sign up for an account. On the welcome page, copy the **Sitekey** and **Secret key**.
|
||||
|
||||
If you have already signed up and didn't copy this information from the welcome page, you can get the **Secret key** from the settings page.
|
||||
@@ -34,17 +41,34 @@ In the Settings page, look for the **Sitekey** section and copy the key.
|
||||
|
||||

|
||||
|
||||
## Enable hCaptcha protection for your Supabase project
|
||||
</TabPanel>
|
||||
|
||||
Navigate to the **[Authentication](https://app.supabase.com/project/_/settings/auth)** page in the Supabase Dashboard and find the **Enable hCaptcha protection** toggle under the **Security and Protection** section.
|
||||
<TabPanel id="turnstile-1" label="Turnstile">
|
||||
Go to the [Cloudflare website](https://dash.cloudflare.com/login) and sign up for an account. On the welcome page, head to the Turnstile section and add a new site. Create a site and take note of the **Sitekey** and **Secret Key** as shown below
|
||||

|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
## Enable Captcha protection for your Supabase project
|
||||
|
||||
Navigate to the **[Authentication](https://app.supabase.com/project/_/settings/auth)** page in the Supabase Dashboard and find the **Enable Captcha protection** toggle under the **Security and Protection** section.
|
||||
|
||||

|
||||
|
||||
Enter your hCaptcha **Secret key** and click **Save**.
|
||||
Enter your Captcha **Secret key** and click **Save**.
|
||||
|
||||
## Add the hCaptcha frontend component
|
||||
## Add the Captcha frontend component
|
||||
|
||||
The frontend requires some changes to provide the captcha on-screen for the user. This example uses React and the hCaptcha React component, but hCaptcha can be used with any JavaScript framework.
|
||||
The frontend requires some changes to provide the captcha on-screen for the user. This example uses React and the corresponding Captcha React component, but both Captcha providers can be used with any JavaScript framework.
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="hcaptcha-2"
|
||||
>
|
||||
|
||||
<TabPanel id="hcaptcha-2" label="HCaptcha">
|
||||
|
||||
Install `@hcaptcha/react-hcaptcha` in your project as a dependency.
|
||||
|
||||
@@ -58,7 +82,7 @@ Now import the `HCaptcha` component from the `@hcaptcha/react-hcaptcha` library.
|
||||
import HCaptcha from '@hcaptcha/react-hcaptcha'
|
||||
```
|
||||
|
||||
Let’s create a empty state to store our `captchaToken`
|
||||
Let's create a empty state to store our `captchaToken`
|
||||
|
||||
```jsx
|
||||
const [captchaToken, setCaptchaToken] = useState()
|
||||
@@ -75,7 +99,7 @@ We will pass it the sitekey we copied from the hCaptcha website as a property al
|
||||
```jsx
|
||||
<HCaptcha
|
||||
sitekey="your-sitekey"
|
||||
onVerify={(token) => { setCaptchaToken(token) }
|
||||
onVerify={(token) => { setCaptchaToken(token) }
|
||||
/>
|
||||
```
|
||||
|
||||
@@ -117,6 +141,60 @@ captcha.current.resetCaptcha()
|
||||
|
||||
In order to test that this works locally we will need to use something like [ngrok](https://ngrok.com/) or add an entry to your hosts file. You can read more about this in the [hCaptcha docs](https://docs.hcaptcha.com/#local-development).
|
||||
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel id="turnstile-2" label="Turnstile">
|
||||
|
||||
The frontend requires some changes to provide the captcha on-screen for the user. This example uses React and the Turnstile React component, but Turnstile can be used with any JavaScript framework.
|
||||
|
||||
Install @marsidev/react-turnstile in your project as a dependency.
|
||||
|
||||
```bash
|
||||
npm install @marsidev/react-turnstile
|
||||
```
|
||||
|
||||
Now import the Turnstile component from the @marsidev/react-turnstile library.
|
||||
|
||||
```jsx
|
||||
import { Turnstile } from '@marsidev/react-turnstile'
|
||||
```
|
||||
|
||||
Let's create an empty state to store our `captchaToken`
|
||||
|
||||
```jsx
|
||||
const [captchaToken, setCaptchaToken] = useState()
|
||||
```
|
||||
|
||||
Now lets add the Cloudflare Turnstile component to the JSX section of our code
|
||||
|
||||
```html
|
||||
<Turnstile />
|
||||
```
|
||||
|
||||
We will pass it the sitekey we copied from the Cloudflare website as a property along with a `onVerify` property which takes a callback function. This callback function will have a token as one of its properties. Let's set the token in the state using `setCaptchaToken`
|
||||
|
||||
```jsx
|
||||
<Turnstile
|
||||
sitekey="your-sitekey"
|
||||
onVerify={(token) => { setCaptchaToken(token) }
|
||||
/>
|
||||
```
|
||||
|
||||
We can now use the captchaToken we receive in our Supabase `signUp` function.
|
||||
|
||||
```jsx
|
||||
await supabase.auth.signUp({
|
||||
email,
|
||||
password,
|
||||
options: { captchaToken },
|
||||
})
|
||||
```
|
||||
|
||||
To test locally, you will need to add localhost to the domain whitelist as per the [Cloudflare docs](https://developers.cloudflare.com/turnstile/reference/testing/)
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
Run the application and you should now be provided with a captcha challenge.
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} />
|
||||
|
||||
@@ -94,7 +94,7 @@ The Auth component is currently shipped with the following views:
|
||||
- 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).
|
||||
We are planning on adding more views in the future. Follow along on that [repo](https://github.com/supabase/auth-ui).
|
||||
|
||||
## Customization
|
||||
|
||||
@@ -202,10 +202,7 @@ See the list of [tokens within a theme](https://github.com/supabase/auth-ui/blob
|
||||
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 customTheme = {
|
||||
default: {
|
||||
@@ -239,7 +236,7 @@ const App = () => (
|
||||
<Auth
|
||||
supabaseClient={supabase}
|
||||
theme="default" // can also be "dark" or "evenDarker"
|
||||
appearance={{ theme: customTheme}}
|
||||
appearance={{ theme: customTheme }}
|
||||
/>
|
||||
)
|
||||
```
|
||||
|
||||
@@ -138,7 +138,7 @@ import { useRouter } from 'next/navigation'
|
||||
|
||||
const Context = createContext(undefined)
|
||||
|
||||
export default function SupabaseProvider({ children }) {
|
||||
export default function SupabaseProvider({ children, session }) {
|
||||
const [supabase] = useState(() => createBrowserSupabaseClient())
|
||||
const router = useRouter()
|
||||
|
||||
@@ -155,7 +155,7 @@ export default function SupabaseProvider({ children }) {
|
||||
}, [router, supabase])
|
||||
|
||||
return (
|
||||
<Context.Provider value={{ supabase }}>
|
||||
<Context.Provider value={{ supabase, session }}>
|
||||
<>{children}</>
|
||||
</Context.Provider>
|
||||
)
|
||||
@@ -188,13 +188,22 @@ import { useRouter } from 'next/navigation'
|
||||
import type { SupabaseClient } from '@supabase/auth-helpers-nextjs'
|
||||
import type { Database } from '@/lib/database.types'
|
||||
|
||||
type MaybeSession = Session | null
|
||||
|
||||
type SupabaseContext = {
|
||||
supabase: SupabaseClient<Database>
|
||||
session: MaybeSession
|
||||
}
|
||||
|
||||
const Context = createContext<SupabaseContext | undefined>(undefined)
|
||||
|
||||
export default function SupabaseProvider({ children }: { children: React.ReactNode }) {
|
||||
export default function SupabaseProvider({
|
||||
children,
|
||||
session,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
session: MaybeSession
|
||||
}) {
|
||||
const [supabase] = useState(() => createBrowserSupabaseClient())
|
||||
const router = useRouter()
|
||||
|
||||
@@ -211,7 +220,7 @@ export default function SupabaseProvider({ children }: { children: React.ReactNo
|
||||
}, [router, supabase])
|
||||
|
||||
return (
|
||||
<Context.Provider value={{ supabase }}>
|
||||
<Context.Provider value={{ supabase, session }}>
|
||||
<>{children}</>
|
||||
</Context.Provider>
|
||||
)
|
||||
@@ -256,7 +265,10 @@ export default function RootLayout({ children }) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body>
|
||||
<SupabaseProvider>{children}</SupabaseProvider>
|
||||
<SupabaseProvider session={session}>
|
||||
<SupabaseListener serverAccessToken={session?.access_token} />
|
||||
{children}
|
||||
</SupabaseProvider>
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
@@ -282,7 +294,10 @@ export default function RootLayout({ children }: { children: React.ReactNode })
|
||||
return (
|
||||
<html lang="en">
|
||||
<body>
|
||||
<SupabaseProvider>{children}</SupabaseProvider>
|
||||
<SupabaseProvider session={session}>
|
||||
<SupabaseListener serverAccessToken={session?.access_token} />
|
||||
{children}
|
||||
</SupabaseProvider>
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
|
||||
@@ -424,9 +424,7 @@ And then use the revalidator, inside the `onAuthStateChange` hook.
|
||||
const { env, session } = useLoaderData()
|
||||
const { revalidate } = useRevalidator()
|
||||
|
||||
const [supabase] = useState(() =>
|
||||
createBrowserClient(env.SUPABASE_URL, env.SUPABASE_ANON_KEY)
|
||||
)
|
||||
const [supabase] = useState(() => createBrowserClient(env.SUPABASE_URL, env.SUPABASE_ANON_KEY))
|
||||
|
||||
const serverAccessToken = session?.access_token
|
||||
|
||||
@@ -464,7 +462,7 @@ useEffect(() => {
|
||||
const {
|
||||
data: { subscription },
|
||||
} = supabase.auth.onAuthStateChange((event, session) => {
|
||||
if (event !== "INITIAL_SESSION" && session?.access_token !== serverAccessToken) {
|
||||
if (event !== 'INITIAL_SESSION' && session?.access_token !== serverAccessToken) {
|
||||
// server and client are out of sync.
|
||||
revalidate()
|
||||
}
|
||||
|
||||
@@ -35,19 +35,16 @@ Create a server supabase client in a handle hook:
|
||||
|
||||
```ts title=src/hooks.server.ts
|
||||
// src/hooks.server.ts
|
||||
import {
|
||||
PUBLIC_SUPABASE_URL,
|
||||
PUBLIC_SUPABASE_ANON_KEY
|
||||
} from '$env/static/public';
|
||||
import { createSupabaseServerClient } from '@supabase/auth-helpers-sveltekit';
|
||||
import type { Handle } from '@sveltejs/kit';
|
||||
import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY } from '$env/static/public'
|
||||
import { createSupabaseServerClient } from '@supabase/auth-helpers-sveltekit'
|
||||
import type { Handle } from '@sveltejs/kit'
|
||||
|
||||
export const handle: Handle = async ({ event, resolve }) => {
|
||||
event.locals.supabase = createSupabaseServerClient({
|
||||
supabaseUrl: PUBLIC_SUPABASE_URL,
|
||||
supabaseKey: PUBLIC_SUPABASE_ANON_KEY,
|
||||
event
|
||||
});
|
||||
event,
|
||||
})
|
||||
|
||||
/**
|
||||
* a little helper that is written for convenience so that instead
|
||||
@@ -56,10 +53,10 @@ export const handle: Handle = async ({ event, resolve }) => {
|
||||
*/
|
||||
event.locals.getSession = async () => {
|
||||
const {
|
||||
data: { session }
|
||||
} = await event.locals.supabase.auth.getSession();
|
||||
return session;
|
||||
};
|
||||
data: { session },
|
||||
} = await event.locals.supabase.auth.getSession()
|
||||
return session
|
||||
}
|
||||
|
||||
return resolve(event, {
|
||||
/**
|
||||
@@ -68,10 +65,10 @@ export const handle: Handle = async ({ event, resolve }) => {
|
||||
* https://github.com/sveltejs/kit/issues/8061
|
||||
*/
|
||||
filterSerializedResponseHeaders(name) {
|
||||
return name === 'content-range';
|
||||
}
|
||||
});
|
||||
};
|
||||
return name === 'content-range'
|
||||
},
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
> Note that we are specifying filterSerializedResponseHeaders here. We need to tell SvelteKit that supabase needs the content-range header.
|
||||
@@ -82,13 +79,13 @@ In order to make the session available to the UI (pages, layouts) we need to pas
|
||||
|
||||
```ts title=src/routes/+layout.server.ts
|
||||
// src/routes/+layout.server.ts
|
||||
import type { LayoutServerLoad } from './$types';
|
||||
import type { LayoutServerLoad } from './$types'
|
||||
|
||||
export const load: LayoutServerLoad = async ({ locals: { getSession } }) => {
|
||||
return {
|
||||
session: await getSession()
|
||||
};
|
||||
};
|
||||
session: await getSession(),
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Shared Load functions and pages
|
||||
@@ -97,30 +94,27 @@ To be able to use Supabase in shared load functions and inside pages you need to
|
||||
|
||||
```ts
|
||||
// src/routes/+layout.ts
|
||||
import {
|
||||
PUBLIC_SUPABASE_ANON_KEY,
|
||||
PUBLIC_SUPABASE_URL
|
||||
} from '$env/static/public';
|
||||
import { createSupabaseLoadClient } from '@supabase/auth-helpers-sveltekit';
|
||||
import type { LayoutLoad } from './$types';
|
||||
import type { Database } from '../DatabaseDefinitions';
|
||||
import { PUBLIC_SUPABASE_ANON_KEY, PUBLIC_SUPABASE_URL } from '$env/static/public'
|
||||
import { createSupabaseLoadClient } from '@supabase/auth-helpers-sveltekit'
|
||||
import type { LayoutLoad } from './$types'
|
||||
import type { Database } from '../DatabaseDefinitions'
|
||||
|
||||
export const load: LayoutLoad = async ({ fetch, data, depends }) => {
|
||||
depends('supabase:auth');
|
||||
depends('supabase:auth')
|
||||
|
||||
const supabase = createSupabaseLoadClient<Database>({
|
||||
supabaseUrl: PUBLIC_SUPABASE_URL,
|
||||
supabaseKey: PUBLIC_SUPABASE_ANON_KEY,
|
||||
event: { fetch },
|
||||
serverSession: data.session
|
||||
});
|
||||
serverSession: data.session,
|
||||
})
|
||||
|
||||
const {
|
||||
data: { session }
|
||||
} = await supabase.auth.getSession();
|
||||
data: { session },
|
||||
} = await supabase.auth.getSession()
|
||||
|
||||
return { supabase, session };
|
||||
};
|
||||
return { supabase, session }
|
||||
}
|
||||
```
|
||||
|
||||
Access the client inside pages by `$page.data.supabase` or `data.supabase` when using `export let data: PageData`.
|
||||
@@ -169,17 +163,17 @@ In order to get the most out of TypeScript and it's intellisense, you should imp
|
||||
```ts
|
||||
// src/app.d.ts
|
||||
|
||||
import { SupabaseClient, Session } from '@supabase/supabase-js';
|
||||
import { Database } from './DatabaseDefinitions';
|
||||
import { SupabaseClient, Session } from '@supabase/supabase-js'
|
||||
import { Database } from './DatabaseDefinitions'
|
||||
|
||||
declare global {
|
||||
namespace App {
|
||||
interface Locals {
|
||||
supabase: SupabaseClient<Database>;
|
||||
getSession(): Promise<Session | null>;
|
||||
supabase: SupabaseClient<Database>
|
||||
getSession(): Promise<Session | null>
|
||||
}
|
||||
interface PageData {
|
||||
session: Session | null;
|
||||
session: Session | null
|
||||
}
|
||||
// interface Error {}
|
||||
// interface Platform {}
|
||||
@@ -193,24 +187,24 @@ For [row level security](https://supabase.com/docs/guides/auth/row-level-securit
|
||||
|
||||
```html
|
||||
<script lang="ts">
|
||||
import type { PageData } from './$types';
|
||||
import type { PageData } from './$types'
|
||||
|
||||
export let data: PageData;
|
||||
export let data: PageData
|
||||
|
||||
let loadedData = [];
|
||||
let loadedData = []
|
||||
async function loadData() {
|
||||
const { data: result } = await data.supabase.from('test').select('*').limit(20);
|
||||
loadedData = result;
|
||||
const { data: result } = await data.supabase.from('test').select('*').limit(20)
|
||||
loadedData = result
|
||||
}
|
||||
|
||||
$: if (data.session) {
|
||||
loadData();
|
||||
loadData()
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if data.session}
|
||||
<p>client-side data fetching with RLS</p>
|
||||
<pre>{JSON.stringify(loadedData, null, 2)}</pre>
|
||||
<p>client-side data fetching with RLS</p>
|
||||
<pre>{JSON.stringify(loadedData, null, 2)}</pre>
|
||||
{/if}
|
||||
```
|
||||
|
||||
@@ -219,10 +213,10 @@ For [row level security](https://supabase.com/docs/guides/auth/row-level-securit
|
||||
```html
|
||||
<!-- src/routes/profile/+page.svelte -->
|
||||
<script lang="ts">
|
||||
import type { PageData } from './$types';
|
||||
import type { PageData } from './$types'
|
||||
|
||||
export let data: PageData;
|
||||
$: ({ user, tableData } = data);
|
||||
export let data: PageData
|
||||
$: ({ user, tableData } = data)
|
||||
</script>
|
||||
|
||||
<div>Protected content for {user.email}</div>
|
||||
@@ -232,21 +226,21 @@ For [row level security](https://supabase.com/docs/guides/auth/row-level-securit
|
||||
|
||||
```ts
|
||||
// src/routes/profile/+page.ts
|
||||
import type { PageLoad } from './$types';
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import type { PageLoad } from './$types'
|
||||
import { redirect } from '@sveltejs/kit'
|
||||
|
||||
export const load: PageLoad = async ({ parent }) => {
|
||||
const { supabase, session } = await parent();
|
||||
const { supabase, session } = await parent()
|
||||
if (!session) {
|
||||
throw redirect(303, '/');
|
||||
throw redirect(303, '/')
|
||||
}
|
||||
const { data: tableData } = await supabase.from('test').select('*');
|
||||
const { data: tableData } = await supabase.from('test').select('*')
|
||||
|
||||
return {
|
||||
user: session.user,
|
||||
tableData
|
||||
};
|
||||
};
|
||||
tableData,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Protecting API routes
|
||||
@@ -255,21 +249,19 @@ Wrap an API Route to check that the user has a valid session. If they're not log
|
||||
|
||||
```ts
|
||||
// src/routes/api/protected-route/+server.ts
|
||||
import type { RequestHandler } from './$types';
|
||||
import { json, error } from '@sveltejs/kit';
|
||||
import type { RequestHandler } from './$types'
|
||||
import { json, error } from '@sveltejs/kit'
|
||||
|
||||
export const GET: RequestHandler = async ({
|
||||
locals: { supabase, getSession }
|
||||
}) => {
|
||||
const session = await getSession();
|
||||
export const GET: RequestHandler = async ({ locals: { supabase, getSession } }) => {
|
||||
const session = await getSession()
|
||||
if (!session) {
|
||||
// the user is not signed in
|
||||
throw error(401, { message: 'Unauthorized' });
|
||||
throw error(401, { message: 'Unauthorized' })
|
||||
}
|
||||
const { data } = await supabase.from('test').select('*');
|
||||
const { data } = await supabase.from('test').select('*')
|
||||
|
||||
return json({ data });
|
||||
};
|
||||
return json({ data })
|
||||
}
|
||||
```
|
||||
|
||||
If you visit `/api/protected-route` without a valid session cookie, you will get a 401 response.
|
||||
@@ -280,35 +272,35 @@ Wrap an Action to check that the user has a valid session. If they're not logged
|
||||
|
||||
```ts
|
||||
// src/routes/posts/+page.server.ts
|
||||
import type { Actions } from './$types';
|
||||
import { error, fail } from '@sveltejs/kit';
|
||||
import type { Actions } from './$types'
|
||||
import { error, fail } from '@sveltejs/kit'
|
||||
|
||||
export const actions: Actions = {
|
||||
createPost: async ({ request, locals: { supabase, getSession } }) => {
|
||||
const session = await getSession();
|
||||
const session = await getSession()
|
||||
|
||||
if (!session) {
|
||||
// the user is not signed in
|
||||
throw error(401, { message: 'Unauthorized' });
|
||||
throw error(401, { message: 'Unauthorized' })
|
||||
}
|
||||
// we are save, let the user create the post
|
||||
const formData = await request.formData();
|
||||
const content = formData.get('content');
|
||||
const formData = await request.formData()
|
||||
const content = formData.get('content')
|
||||
|
||||
const { error: createPostError, data: newPost } = await supabase
|
||||
.from('posts')
|
||||
.insert({ content });
|
||||
.insert({ content })
|
||||
|
||||
if (createPostError) {
|
||||
return fail(500, {
|
||||
supabaseErrorMessage: createPostError.message
|
||||
});
|
||||
supabaseErrorMessage: createPostError.message,
|
||||
})
|
||||
}
|
||||
return {
|
||||
newPost
|
||||
};
|
||||
}
|
||||
};
|
||||
newPost,
|
||||
}
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
If you try to submit a form with the action `?/createPost` without a valid session cookie, you will get a 401 error response.
|
||||
@@ -316,47 +308,47 @@ If you try to submit a form with the action `?/createPost` without a valid sessi
|
||||
## Saving and deleting the session
|
||||
|
||||
```ts
|
||||
import type { Actions } from './$types';
|
||||
import { fail, redirect } from '@sveltejs/kit';
|
||||
import { AuthApiError } from '@supabase/supabase-js';
|
||||
import type { Actions } from './$types'
|
||||
import { fail, redirect } from '@sveltejs/kit'
|
||||
import { AuthApiError } from '@supabase/supabase-js'
|
||||
|
||||
export const actions: Actions = {
|
||||
signin: async ({ request, locals: { supabase } }) => {
|
||||
const formData = await request.formData();
|
||||
const formData = await request.formData()
|
||||
|
||||
const email = formData.get('email') as string;
|
||||
const password = formData.get('password') as string;
|
||||
const email = formData.get('email') as string
|
||||
const password = formData.get('password') as string
|
||||
|
||||
const { error } = await supabase.auth.signInWithPassword({
|
||||
email,
|
||||
password
|
||||
});
|
||||
password,
|
||||
})
|
||||
|
||||
if (error) {
|
||||
if (error instanceof AuthApiError && error.status === 400) {
|
||||
return fail(400, {
|
||||
error: 'Invalid credentials.',
|
||||
values: {
|
||||
email
|
||||
}
|
||||
});
|
||||
email,
|
||||
},
|
||||
})
|
||||
}
|
||||
return fail(500, {
|
||||
error: 'Server error. Try again later.',
|
||||
values: {
|
||||
email
|
||||
}
|
||||
});
|
||||
email,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
throw redirect(303, '/dashboard');
|
||||
throw redirect(303, '/dashboard')
|
||||
},
|
||||
|
||||
signout: async ({ locals: { supabase } }) => {
|
||||
await supabase.auth.signOut();
|
||||
throw redirect(303, '/');
|
||||
}
|
||||
};
|
||||
await supabase.auth.signOut()
|
||||
throw redirect(303, '/')
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## Protecting multiple routes
|
||||
@@ -366,33 +358,30 @@ protect multiple routes at once.
|
||||
|
||||
```ts
|
||||
// src/hooks.server.ts
|
||||
import type { RequestHandler } from './$types';
|
||||
import { redirect, error } from '@sveltejs/kit';
|
||||
import type { RequestHandler } from './$types'
|
||||
import { redirect, error } from '@sveltejs/kit'
|
||||
|
||||
export const handle: Handle = async ({ event, resolve }) => {
|
||||
// protect requests to all routes that start with /protected-routes
|
||||
if (event.url.pathname.startsWith('/protected-routes')) {
|
||||
const session = await event.locals.getSession();
|
||||
const session = await event.locals.getSession()
|
||||
if (!session) {
|
||||
// the user is not signed in
|
||||
throw redirect(303, '/');
|
||||
throw redirect(303, '/')
|
||||
}
|
||||
}
|
||||
|
||||
// protect POST requests to all routes that start with /protected-posts
|
||||
if (
|
||||
event.url.pathname.startsWith('/protected-posts') &&
|
||||
event.request.method === 'POST'
|
||||
) {
|
||||
const session = await event.locals.getSession();
|
||||
if (event.url.pathname.startsWith('/protected-posts') && event.request.method === 'POST') {
|
||||
const session = await event.locals.getSession()
|
||||
if (!session) {
|
||||
// the user is not signed in
|
||||
throw error(303, '/');
|
||||
throw error(303, '/')
|
||||
}
|
||||
}
|
||||
|
||||
return resolve(event);
|
||||
};
|
||||
return resolve(event)
|
||||
}
|
||||
```
|
||||
|
||||
## Migrate from 0.8.x to 0.9 [#migration]
|
||||
@@ -425,19 +414,16 @@ export const supabaseClient = createClient(env.PUBLIC_SUPABASE_URL, env.PUBLIC_S
|
||||
|
||||
```js title=src/hooks.server.ts
|
||||
// src/hooks.server.ts
|
||||
import {
|
||||
PUBLIC_SUPABASE_URL,
|
||||
PUBLIC_SUPABASE_ANON_KEY
|
||||
} from '$env/static/public';
|
||||
import { createSupabaseServerClient } from '@supabase/auth-helpers-sveltekit';
|
||||
import type { Handle } from '@sveltejs/kit';
|
||||
import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY } from '$env/static/public'
|
||||
import { createSupabaseServerClient } from '@supabase/auth-helpers-sveltekit'
|
||||
import type { Handle } from '@sveltejs/kit'
|
||||
|
||||
export const handle: Handle = async ({ event, resolve }) => {
|
||||
event.locals.supabase = createSupabaseServerClient({
|
||||
supabaseUrl: PUBLIC_SUPABASE_URL,
|
||||
supabaseKey: PUBLIC_SUPABASE_ANON_KEY,
|
||||
event
|
||||
});
|
||||
event,
|
||||
})
|
||||
|
||||
/**
|
||||
* a little helper that is written for convenience so that instead
|
||||
@@ -446,10 +432,10 @@ export const handle: Handle = async ({ event, resolve }) => {
|
||||
*/
|
||||
event.locals.getSession = async () => {
|
||||
const {
|
||||
data: { session }
|
||||
} = await event.locals.supabase.auth.getSession();
|
||||
return session;
|
||||
};
|
||||
data: { session },
|
||||
} = await event.locals.supabase.auth.getSession()
|
||||
return session
|
||||
}
|
||||
|
||||
return resolve(event, {
|
||||
/**
|
||||
@@ -458,10 +444,10 @@ export const handle: Handle = async ({ event, resolve }) => {
|
||||
* https://github.com/sveltejs/kit/issues/8061
|
||||
*/
|
||||
filterSerializedResponseHeaders(name) {
|
||||
return name === 'content-range';
|
||||
}
|
||||
});
|
||||
};
|
||||
return name === 'content-range'
|
||||
},
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
@@ -469,7 +455,7 @@ export const handle: Handle = async ({ event, resolve }) => {
|
||||
|
||||
### Initialize the client [#migration-initialize-client]
|
||||
|
||||
In order to use the Supabase library in your client code you will need to setup a shared load function inside the root `+layout.ts` and create a `+layout.svelte` to handle our event listening for Auth events.
|
||||
In order to use the Supabase library in your client code you will need to setup a shared load function inside the root `+layout.ts` and create a `+layout.svelte` to handle our event listening for Auth events.
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
@@ -507,31 +493,28 @@ In order to use the Supabase library in your client code you will need to setup
|
||||
|
||||
```ts title=src/routes/+layout.ts
|
||||
// src/routes/+layout.ts
|
||||
import { invalidate } from '$app/navigation';
|
||||
import {
|
||||
PUBLIC_SUPABASE_ANON_KEY,
|
||||
PUBLIC_SUPABASE_URL
|
||||
} from '$env/static/public';
|
||||
import { createSupabaseLoadClient } from '@supabase/auth-helpers-sveltekit';
|
||||
import type { LayoutLoad } from './$types';
|
||||
import type { Database } from '../DatabaseDefinitions';
|
||||
import { invalidate } from '$app/navigation'
|
||||
import { PUBLIC_SUPABASE_ANON_KEY, PUBLIC_SUPABASE_URL } from '$env/static/public'
|
||||
import { createSupabaseLoadClient } from '@supabase/auth-helpers-sveltekit'
|
||||
import type { LayoutLoad } from './$types'
|
||||
import type { Database } from '../DatabaseDefinitions'
|
||||
|
||||
export const load: LayoutLoad = async ({ fetch, data, depends }) => {
|
||||
depends('supabase:auth');
|
||||
depends('supabase:auth')
|
||||
|
||||
const supabase = createSupabaseLoadClient<Database>({
|
||||
supabaseUrl: PUBLIC_SUPABASE_URL,
|
||||
supabaseKey: PUBLIC_SUPABASE_ANON_KEY,
|
||||
event: { fetch },
|
||||
serverSession: data.session
|
||||
});
|
||||
serverSession: data.session,
|
||||
})
|
||||
|
||||
const {
|
||||
data: { session }
|
||||
} = await supabase.auth.getSession();
|
||||
data: { session },
|
||||
} = await supabase.auth.getSession()
|
||||
|
||||
return { supabase, session };
|
||||
};
|
||||
return { supabase, session }
|
||||
}
|
||||
```
|
||||
|
||||
```svelte title=src/routes/+layout.svelte
|
||||
@@ -605,17 +588,17 @@ declare namespace App {
|
||||
|
||||
```ts title=src/app.d.ts
|
||||
// src/app.d.ts
|
||||
import { SupabaseClient, Session } from '@supabase/supabase-js';
|
||||
import { Database } from './DatabaseDefinitions';
|
||||
import { SupabaseClient, Session } from '@supabase/supabase-js'
|
||||
import { Database } from './DatabaseDefinitions'
|
||||
|
||||
declare global {
|
||||
namespace App {
|
||||
interface Locals {
|
||||
supabase: SupabaseClient<Database>;
|
||||
getSession(): Promise<Session | null>;
|
||||
supabase: SupabaseClient<Database>
|
||||
getSession(): Promise<Session | null>
|
||||
}
|
||||
interface PageData {
|
||||
session: Session | null;
|
||||
session: Session | null
|
||||
}
|
||||
// interface Error {}
|
||||
// interface Platform {}
|
||||
@@ -675,10 +658,10 @@ export const load: PageLoad = async (event) => {
|
||||
```html title=src/routes/profile/+page.svelte
|
||||
<!-- src/routes/profile/+page.svelte -->
|
||||
<script lang="ts">
|
||||
import type { PageData } from './$types';
|
||||
import type { PageData } from './$types'
|
||||
|
||||
export let data: PageData;
|
||||
$: ({ user, tableData } = data);
|
||||
export let data: PageData
|
||||
$: ({ user, tableData } = data)
|
||||
</script>
|
||||
|
||||
<div>Protected content for {user.email}</div>
|
||||
@@ -688,21 +671,21 @@ export const load: PageLoad = async (event) => {
|
||||
|
||||
```ts title=src/routes/profile/+page.ts
|
||||
// src/routes/profile/+page.ts
|
||||
import type { PageLoad } from './$types';
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import type { PageLoad } from './$types'
|
||||
import { redirect } from '@sveltejs/kit'
|
||||
|
||||
export const load: PageLoad = async ({ parent }) => {
|
||||
const { supabase, session } = await parent();
|
||||
const { supabase, session } = await parent()
|
||||
if (!session) {
|
||||
throw redirect(303, '/');
|
||||
throw redirect(303, '/')
|
||||
}
|
||||
const { data: tableData } = await supabase.from('test').select('*');
|
||||
const { data: tableData } = await supabase.from('test').select('*')
|
||||
|
||||
return {
|
||||
user: session.user,
|
||||
tableData
|
||||
};
|
||||
};
|
||||
tableData,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
@@ -740,27 +723,24 @@ export const GET: RequestHandler = async (event) => {
|
||||
|
||||
```ts title=src/routes/api/protected-route/+server.ts
|
||||
// src/routes/api/protected-route/+server.ts
|
||||
import type { RequestHandler } from './$types';
|
||||
import { json, error } from '@sveltejs/kit';
|
||||
import type { RequestHandler } from './$types'
|
||||
import { json, error } from '@sveltejs/kit'
|
||||
|
||||
export const GET: RequestHandler = async ({
|
||||
locals: { supabase, getSession }
|
||||
}) => {
|
||||
const session = await getSession();
|
||||
export const GET: RequestHandler = async ({ locals: { supabase, getSession } }) => {
|
||||
const session = await getSession()
|
||||
if (!session) {
|
||||
// the user is not signed in
|
||||
throw error(401, { message: 'Unauthorized' });
|
||||
throw error(401, { message: 'Unauthorized' })
|
||||
}
|
||||
const { data } = await supabase.from('test').select('*');
|
||||
const { data } = await supabase.from('test').select('*')
|
||||
|
||||
return json({ data });
|
||||
};
|
||||
return json({ data })
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
|
||||
## Migrate from 0.7.x to 0.8 [#migration-0-8]
|
||||
|
||||
### Set up the Supabase client [#migration-set-up-supabase-client-0-8]
|
||||
|
||||
@@ -98,7 +98,7 @@ export const meta = {
|
||||
} = supabase.auth.onAuthStateChange((_event, session) => {
|
||||
setSession(session)
|
||||
})
|
||||
|
||||
|
||||
return () => subscription.unsubscribe()
|
||||
}, [])
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ Go to the [GitHub Developer Settings](https://github.com/settings/developers) pa
|
||||
|
||||
<SocialProviderSetup provider="GitHub" />
|
||||
|
||||
## Register a new OAuth application on Github.
|
||||
## Register a new OAuth application on Github.
|
||||
|
||||
- Navigate to `Settings`/`Developer settings`/`OAuth Apps`
|
||||
- Click `Register a new application`. If you've created an app before, click `New OAuth App` here.
|
||||
|
||||
@@ -105,17 +105,16 @@ A common use case with SSO is to use the UUID of the identity provider as the id
|
||||
For example, let's say you have a table like:
|
||||
|
||||
```sql
|
||||
CREATE TABLE
|
||||
organization_settings (
|
||||
-- the organization's unique ID
|
||||
id UUID NOT NULL PRIMARY KEY,
|
||||
-- the organization's SSO identity provider
|
||||
sso_provider_id UUID UNIQUE,
|
||||
-- name of the organization
|
||||
name TEXT,
|
||||
-- billing plan (paid, free, enterprise)
|
||||
billing_plan TEXT
|
||||
);
|
||||
create table organization_settings (
|
||||
-- the organization's unique ID
|
||||
id uuid not null primary key,
|
||||
-- the organization's SSO identity provider
|
||||
sso_provider_id uuid unique,
|
||||
-- name of the organization
|
||||
name text,
|
||||
-- billing plan (paid, free, enterprise)
|
||||
billing_plan text
|
||||
);
|
||||
```
|
||||
|
||||
You can use the information present in the user's JWT to scope down which rows from this table the user can see, without doing any additional user management:
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import Layout from '~/layouts/DefaultGuideLayout'
|
||||
|
||||
export const meta = {
|
||||
id: 'using-environment-variables-in-config',
|
||||
title: 'Using environment variables in config.toml',
|
||||
description: 'How to use environment variables in config.toml with the Supabase CLI.',
|
||||
}
|
||||
|
||||
The Supabase CLI is capable of utilizing environment variables stored in our project's root directory's `.env` file within the `config.toml` file.
|
||||
|
||||
We can reference the environment variable by using the `env()` function.
|
||||
|
||||
Inside of our `.env` file we add the environment variable as we normally would
|
||||
|
||||
```env
|
||||
GITHUB_CLIENT_ID=""
|
||||
GITHUB_SECRET=""
|
||||
```
|
||||
|
||||
And then reference them inside of our `config.toml`
|
||||
|
||||
```toml
|
||||
[auth.external.github]
|
||||
enabled = true
|
||||
client_id = "env(GITHUB_CLIENT_ID)"
|
||||
secret = "env(GITHUB_SECRET)"
|
||||
# Overrides the default auth redirectUrl.
|
||||
redirect_uri = ""
|
||||
```
|
||||
|
||||
These same environment variables will be referenced by the `supabase start` command from the Supabase CLI.
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} />
|
||||
|
||||
export default Page
|
||||
@@ -12,13 +12,13 @@ extension which provides SQL access to
|
||||
algorithms.
|
||||
|
||||
libsodium is a modern, easy-to-use software library for encryption,
|
||||
decryption, signatures, password hashing, and more. It is a portable,
|
||||
decryption, signatures, password hashing, and more. It is a portable,
|
||||
cross-compilable, installable, and packageable fork of the
|
||||
[NaCl](http://nacl.cr.yp.to/) library, with a compatible but extended
|
||||
API to improve usability even further.
|
||||
|
||||
Its goal is to provide all of the core operations needed to build
|
||||
higher-level cryptographic tools. Sodium is cross-platform and
|
||||
higher-level cryptographic tools. Sodium is cross-platform and
|
||||
cross-language. It runs on many compilers and operating systems,
|
||||
including Windows (with MinGW or Visual Studio, x86 and x86_64), iOS,
|
||||
and Android. JavaScript and WebAssembly versions are also available
|
||||
@@ -33,12 +33,12 @@ pgsodium exposes the following libsodium APIs to SQL:
|
||||
|
||||
- [Generating Random Data](https://github.com/michelp/pgsodium/#generating-random-data)
|
||||
- [Secret key cryptography](https://github.com/michelp/pgsodium/#secret-key-cryptography)
|
||||
- [Authenticated encryption](https://github.com/michelp/pgsodium/#authenticated-encryption)
|
||||
- [Authentication](https://github.com/michelp/pgsodium/#authentication)
|
||||
- [Authenticated encryption](https://github.com/michelp/pgsodium/#authenticated-encryption)
|
||||
- [Authentication](https://github.com/michelp/pgsodium/#authentication)
|
||||
- [Public key cryptography](https://github.com/michelp/pgsodium/#public-key-cryptography)
|
||||
- [Authenticated encryption](https://github.com/michelp/pgsodium/#authenticated-encryption-1)
|
||||
- [Public key signatures](https://github.com/michelp/pgsodium/#public-key-signatures)
|
||||
- [Sealed boxes](https://github.com/michelp/pgsodium/#sealed-boxes)
|
||||
- [Authenticated encryption](https://github.com/michelp/pgsodium/#authenticated-encryption-1)
|
||||
- [Public key signatures](https://github.com/michelp/pgsodium/#public-key-signatures)
|
||||
- [Sealed boxes](https://github.com/michelp/pgsodium/#sealed-boxes)
|
||||
- [Hashing](https://github.com/michelp/pgsodium/#hashing)
|
||||
- [Password hashing](https://github.com/michelp/pgsodium/#password-hashing)
|
||||
- [Key Derivation](https://github.com/michelp/pgsodium/#key-derivation)
|
||||
|
||||
@@ -15,6 +15,7 @@ Even though indexes improve query performance, the Postgres query planner may no
|
||||
## Create an index
|
||||
|
||||
Let's take an example table:
|
||||
|
||||
```sql
|
||||
create table persons (
|
||||
id bigint generated by default as identity primary key,
|
||||
@@ -25,6 +26,7 @@ create table persons (
|
||||
deceased boolean
|
||||
);
|
||||
```
|
||||
|
||||
<Admonition>
|
||||
|
||||
All the queries in this guide can be run using the [SQL Editor](https://app.supabase.com/project/_/sql) in the Supabase Dashboard, or via `psql` if you're [connecting directly to the database](/docs/guides/database/connecting-to-postgres#direct-connections).
|
||||
@@ -32,6 +34,7 @@ All the queries in this guide can be run using the [SQL Editor](https://app.supa
|
||||
</Admonition>
|
||||
|
||||
We might want to frequently query users based on their age:
|
||||
|
||||
```sql
|
||||
select name from persons where age = 32;
|
||||
```
|
||||
@@ -39,19 +42,22 @@ select name from persons where age = 32;
|
||||
Without an index, Postgres will scan every row in the table to find equality matches on age.
|
||||
|
||||
You can verify this by doing an explain on the query:
|
||||
|
||||
```sql
|
||||
explain select name from persons where age = 32;
|
||||
```
|
||||
|
||||
Outputs:
|
||||
|
||||
```
|
||||
Seq Scan on persons (cost=0.00..22.75 rows=x width=y)
|
||||
Filter: (age = 32)
|
||||
```
|
||||
|
||||
To add a simple B-Tree index you can run:
|
||||
|
||||
```sql
|
||||
create index idx_persons_age on persons(age);
|
||||
create index idx_persons_age on persons (age);
|
||||
```
|
||||
|
||||
<Admonition type="caution">
|
||||
@@ -73,7 +79,8 @@ You can see that in any large data set, traversing the index to locate a given v
|
||||
If you are frequently querying a subset of rows then it may be more efficient to build a partial index. In our example, perhaps we only want to match on `age` where `deceased is false`. We could build a partial index:
|
||||
|
||||
```sql
|
||||
create index idx_living_persons_age ON persons(age) where deceased is false;
|
||||
create index idx_living_persons_age on persons (age)
|
||||
where deceased is false;
|
||||
```
|
||||
|
||||
## Ordering indexes
|
||||
@@ -81,14 +88,13 @@ create index idx_living_persons_age ON persons(age) where deceased is false;
|
||||
By default B-Tree indexes are sorted in ascending order, but sometimes you may want to provide a different ordering. Perhaps our application has a page featuring the top 10 oldest people. Here we would want to sort in descending order, and include `NULL` values last. For this we can use:
|
||||
|
||||
```sql
|
||||
create index idx_persons_age_desc on persons(age desc nulls last);
|
||||
create index idx_persons_age_desc on persons (age desc nulls last);
|
||||
```
|
||||
|
||||
## Reindexing
|
||||
|
||||
After a while indexes can become stale and may need rebuilding. Postgres provides a `reindex` command for this, but due to Postgres locks being placed on the index during this process, you may want to make use of the `concurrent` keyword.
|
||||
|
||||
|
||||
```sql
|
||||
reindex index concurrently idx_persons_age;
|
||||
```
|
||||
@@ -103,4 +109,4 @@ Take note that `reindex` can be used inside a transaction, but `reindex [index/t
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} />
|
||||
|
||||
export default Page
|
||||
export default Page
|
||||
@@ -4,6 +4,7 @@ export const meta = {
|
||||
title: 'Build a User Management App with NextJS',
|
||||
description: 'Learn how to use Supabase in your Next App.',
|
||||
video: 'https://www.youtube.com/v/0Fs96oZ4se0',
|
||||
tocVideo: '0Fs96oZ4se0',
|
||||
}
|
||||
|
||||
<QuickstartIntro />
|
||||
@@ -126,7 +127,7 @@ export default MyApp
|
||||
Wrap your `pages/_app.tsx` component with the `SessionContextProvider` component:
|
||||
|
||||
```jsx lines=2,8 title=pages/_app.tsx
|
||||
import '../styles/globals.css';
|
||||
import '../styles/globals.css'
|
||||
import { useState } from 'react'
|
||||
import { createBrowserSupabaseClient } from '@supabase/auth-helpers-nextjs'
|
||||
import { SessionContextProvider, Session } from '@supabase/auth-helpers-react'
|
||||
|
||||
@@ -122,13 +122,13 @@ After a user is signed in we can allow them to edit their profile details and ma
|
||||
|
||||
Let's create a new component for that called `Account.tsx`.
|
||||
|
||||
```jsx title=src/Account.tsx
|
||||
```tsx title=src/Account.tsx
|
||||
import { AuthSession } from '@supabase/supabase-js'
|
||||
import { Component, createEffect, createSignal } from 'solid-js'
|
||||
import { supabase } from './supabaseClient'
|
||||
|
||||
interface Props {
|
||||
session: AuthSession;
|
||||
session: AuthSession
|
||||
}
|
||||
|
||||
const Account: Component<Props> = ({ session }) => {
|
||||
|
||||
@@ -63,29 +63,26 @@ Add the code below to your `src/hooks.server.ts` to initialize the client on the
|
||||
|
||||
```ts title=src/hooks.server.ts
|
||||
// src/hooks.server.ts
|
||||
import {
|
||||
PUBLIC_SUPABASE_URL,
|
||||
PUBLIC_SUPABASE_ANON_KEY
|
||||
} from '$env/static/public';
|
||||
import { createSupabaseServerClient } from '@supabase/auth-helpers-sveltekit';
|
||||
import type { Handle } from '@sveltejs/kit';
|
||||
import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY } from '$env/static/public'
|
||||
import { createSupabaseServerClient } from '@supabase/auth-helpers-sveltekit'
|
||||
import type { Handle } from '@sveltejs/kit'
|
||||
|
||||
export const handle: Handle = async ({ event, resolve }) => {
|
||||
event.locals.supabase = createSupabaseServerClient({
|
||||
supabaseUrl: PUBLIC_SUPABASE_URL,
|
||||
supabaseKey: PUBLIC_SUPABASE_ANON_KEY,
|
||||
event
|
||||
});
|
||||
event,
|
||||
})
|
||||
|
||||
/**
|
||||
* A convenience helper so we can just call await getSession() instead const { data: { session } } = await supabase.auth.getSession()
|
||||
*/
|
||||
event.locals.getSession = async () => {
|
||||
const {
|
||||
data: { session }
|
||||
} = await event.locals.supabase.auth.getSession();
|
||||
return session;
|
||||
};
|
||||
data: { session },
|
||||
} = await event.locals.supabase.auth.getSession()
|
||||
return session
|
||||
}
|
||||
|
||||
return resolve(event, {
|
||||
/**
|
||||
@@ -94,10 +91,10 @@ export const handle: Handle = async ({ event, resolve }) => {
|
||||
* https://github.com/sveltejs/kit/issues/8061
|
||||
*/
|
||||
filterSerializedResponseHeaders(name) {
|
||||
return name === 'content-range';
|
||||
}
|
||||
});
|
||||
};
|
||||
return name === 'content-range'
|
||||
},
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
If you are using TypeScript the compiler might complain about `event.locals.supabase` and `event.locals.getSession`, this can be fixed by updating your `src/app.d.ts` with the content below:
|
||||
@@ -105,16 +102,16 @@ If you are using TypeScript the compiler might complain about `event.locals.supa
|
||||
```ts title=src/app.d.ts
|
||||
// src/app.d.ts
|
||||
|
||||
import { SupabaseClient, Session } from '@supabase/supabase-js';
|
||||
import { SupabaseClient, Session } from '@supabase/supabase-js'
|
||||
|
||||
declare global {
|
||||
namespace App {
|
||||
interface Locals {
|
||||
supabase: SupabaseClient;
|
||||
getSession(): Promise<Session | null>;
|
||||
supabase: SupabaseClient
|
||||
getSession(): Promise<Session | null>
|
||||
}
|
||||
interface PageData {
|
||||
session: Session | null;
|
||||
session: Session | null
|
||||
}
|
||||
// interface Error {}
|
||||
// interface Platform {}
|
||||
@@ -126,11 +123,13 @@ Create a new `src/routes/+layout.server.ts` file to handle the session on the se
|
||||
|
||||
```ts title=src/routes/+layout.server.ts
|
||||
// src/routes/+layout.server.ts
|
||||
export const load = async ({ locals: { getSession } }) => {
|
||||
import type { LayoutServerLoad } from './$types'
|
||||
|
||||
export const load: LayoutServerLoad = async ({ locals: { getSession } }) => {
|
||||
return {
|
||||
session: await getSession()
|
||||
};
|
||||
};
|
||||
session: await getSession(),
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> Start your dev server (`npm run dev`) in order to generate the `./$types` files we are referencing in our project.
|
||||
@@ -139,29 +138,27 @@ Create a new `src/routes/+layout.ts` file to handle the session and the supabase
|
||||
|
||||
```ts title=src/routes/+layout.ts
|
||||
// src/routes/+layout.ts
|
||||
import { invalidate } from '$app/navigation';
|
||||
import {
|
||||
PUBLIC_SUPABASE_ANON_KEY,
|
||||
PUBLIC_SUPABASE_URL
|
||||
} from '$env/static/public';
|
||||
import { createSupabaseLoadClient } from '@supabase/auth-helpers-sveltekit';
|
||||
import { invalidate } from '$app/navigation'
|
||||
import { PUBLIC_SUPABASE_ANON_KEY, PUBLIC_SUPABASE_URL } from '$env/static/public'
|
||||
import { createSupabaseLoadClient } from '@supabase/auth-helpers-sveltekit'
|
||||
import type { LayoutLoad } from './$types'
|
||||
|
||||
export const load = async ({ fetch, data, depends }) => {
|
||||
depends('supabase:auth');
|
||||
export const load: LayoutLoad = async ({ fetch, data, depends }) => {
|
||||
depends('supabase:auth')
|
||||
|
||||
const supabase = createSupabaseLoadClient({
|
||||
supabaseUrl: PUBLIC_SUPABASE_URL,
|
||||
supabaseKey: PUBLIC_SUPABASE_ANON_KEY,
|
||||
event: { fetch },
|
||||
serverSession: data.session
|
||||
});
|
||||
serverSession: data.session,
|
||||
})
|
||||
|
||||
const {
|
||||
data: { session }
|
||||
} = await supabase.auth.getSession();
|
||||
data: { session },
|
||||
} = await supabase.auth.getSession()
|
||||
|
||||
return { supabase, session };
|
||||
};
|
||||
return { supabase, session }
|
||||
}
|
||||
```
|
||||
|
||||
Update your `src/routes/+layout.svelte`:
|
||||
@@ -239,24 +236,26 @@ Add the `Auth` component to your home page
|
||||
|
||||
Create a `src/routes/+page.server.ts` file that will return our website url to be used in our `redirectTo` above.
|
||||
|
||||
> This is necessary because the current Supabase auth flow uses implicit grant which returns the tokens as part of the url fragment (#).
|
||||
The redirect to a non server protected page will make sure that the client captures the url fragment as we cannot read these on the server
|
||||
and send it over to the server. This all happens behind the scenes and isn't something you will need to do manually.
|
||||
> This is necessary because the current Supabase auth flow uses implicit grant which returns the tokens as part of the url fragment (#).
|
||||
|
||||
> The redirect to a non server protected page will make sure that the client captures the url fragment as we cannot read these on the server
|
||||
> and send it over to the server. This all happens behind the scenes and isn't something you will need to do manually.
|
||||
|
||||
```ts
|
||||
// src/routes/+page.server.ts
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import { redirect } from '@sveltejs/kit'
|
||||
import type { PageServerLoad } from './$types'
|
||||
|
||||
export const load = async ({ url, locals: { getSession } }) => {
|
||||
const session = await getSession();
|
||||
export const load: PageServerLoad = async ({ url, locals: { getSession } }) => {
|
||||
const session = await getSession()
|
||||
|
||||
// if the user is already logged in return them to the account page
|
||||
if (session) {
|
||||
throw redirect(303, '/account');
|
||||
}
|
||||
// if the user is already logged in return them to the account page
|
||||
if (session) {
|
||||
throw redirect(303, '/account')
|
||||
}
|
||||
|
||||
return { url: url.origin };
|
||||
};
|
||||
return { url: url.origin }
|
||||
}
|
||||
```
|
||||
|
||||
### Loading page
|
||||
@@ -378,67 +377,68 @@ Now create the associated `src/routes/account/+page.server.ts` file that will ha
|
||||
and handle all our form actions through the `actions` object.
|
||||
|
||||
```ts
|
||||
import { fail, redirect } from '@sveltejs/kit';
|
||||
import { fail, redirect } from '@sveltejs/kit'
|
||||
import type { Actions, PageServerLoad } from './$types'
|
||||
|
||||
export const load = async ({ locals: { supabase, getSession } }) => {
|
||||
const session = await getSession();
|
||||
export const load = (async ({ locals: { supabase, getSession } }) => {
|
||||
const session = await getSession()
|
||||
|
||||
if (!session) {
|
||||
throw redirect(303, '/');
|
||||
}
|
||||
if (!session) {
|
||||
throw redirect(303, '/')
|
||||
}
|
||||
|
||||
const { data: profile } = await supabase
|
||||
.from('profiles')
|
||||
.select(`username, full_name, website, avatar_url`)
|
||||
.eq('id', session.user.id)
|
||||
.single();
|
||||
const { data: profile } = await supabase
|
||||
.from('profiles')
|
||||
.select(`username, full_name, website, avatar_url`)
|
||||
.eq('id', session.user.id)
|
||||
.single()
|
||||
|
||||
return { session, profile };
|
||||
};
|
||||
return { session, profile }
|
||||
}) satisfies PageServerLoad
|
||||
|
||||
export const actions = {
|
||||
update: async ({ request, locals: { supabase, getSession } }) => {
|
||||
const formData = await request.formData();
|
||||
const fullName = formData.get('fullName') as string;
|
||||
const username = formData.get('username') as string;
|
||||
const website = formData.get('website') as string;
|
||||
const avatarUrl = formData.get('avatarUrl') as string;
|
||||
update: async ({ request, locals: { supabase, getSession } }) => {
|
||||
const formData = await request.formData()
|
||||
const fullName = formData.get('fullName') as string
|
||||
const username = formData.get('username') as string
|
||||
const website = formData.get('website') as string
|
||||
const avatarUrl = formData.get('avatarUrl') as string
|
||||
|
||||
const session = await getSession();
|
||||
const session = await getSession()
|
||||
|
||||
const { error } = await supabase.from('profiles').upsert({
|
||||
id: session?.user.id,
|
||||
full_name: fullName,
|
||||
username,
|
||||
website,
|
||||
avatar_url: avatarUrl,
|
||||
updated_at: new Date()
|
||||
});
|
||||
const { error } = await supabase.from('profiles').upsert({
|
||||
id: session?.user.id,
|
||||
full_name: fullName,
|
||||
username,
|
||||
website,
|
||||
avatar_url: avatarUrl,
|
||||
updated_at: new Date(),
|
||||
})
|
||||
|
||||
if (error) {
|
||||
return fail(500, {
|
||||
fullName,
|
||||
username,
|
||||
website,
|
||||
avatarUrl
|
||||
});
|
||||
}
|
||||
if (error) {
|
||||
return fail(500, {
|
||||
fullName,
|
||||
username,
|
||||
website,
|
||||
avatarUrl,
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
fullName,
|
||||
username,
|
||||
website,
|
||||
avatarUrl
|
||||
};
|
||||
},
|
||||
signout: async ({ locals: { supabase, getSession } }) => {
|
||||
const session = await getSession();
|
||||
if (session) {
|
||||
await supabase.auth.signOut();
|
||||
throw redirect(303, '/');
|
||||
}
|
||||
}
|
||||
};
|
||||
return {
|
||||
fullName,
|
||||
username,
|
||||
website,
|
||||
avatarUrl,
|
||||
}
|
||||
},
|
||||
signout: async ({ locals: { supabase, getSession } }) => {
|
||||
const session = await getSession()
|
||||
if (session) {
|
||||
await supabase.auth.signOut()
|
||||
throw redirect(303, '/')
|
||||
}
|
||||
},
|
||||
} satisfies Actions
|
||||
```
|
||||
|
||||
### Launch!
|
||||
|
||||
@@ -65,7 +65,7 @@ Optionally, update [src/style.css](https://raw.githubusercontent.com/supabase/su
|
||||
|
||||
### Set up a Login component
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
```vue title=/src/components/Auth.vue
|
||||
<script setup>
|
||||
|
||||
@@ -24,7 +24,9 @@ Daily Backups and PITR are mutually exclusive. If your project opts into using P
|
||||
All Pro and Enterprise tier Supabase projects are backed up automatically on a daily basis. In terms of Recovery Point Objective (RPO), Daily Backups would be suitable for projects willing to lose up to 24 hours worth of data if disaster hits at the most inopportune time. If a lower RPO is required, enabling Point-in-Time Recovery should be considered.
|
||||
|
||||
<Admonition type="note">
|
||||
For security purposes, passwords for custom roles are not stored in daily backups, and will not be found in downloadable files. As such, if you are restoring from a daily backup and are using custom roles, you will need to set their passwords once more following a completed restoration.
|
||||
For security purposes, passwords for custom roles are not stored in daily backups, and will not be
|
||||
found in downloadable files. As such, if you are restoring from a daily backup and are using
|
||||
custom roles, you will need to set their passwords once more following a completed restoration.
|
||||
</Admonition>
|
||||
|
||||
### Backup Process [#daily-backups-process]
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
import Layout from '~/layouts/DefaultGuideLayout'
|
||||
|
||||
export const meta = {
|
||||
id: 'exhaust-cpu',
|
||||
title: 'High CPU Usage',
|
||||
description:
|
||||
'Learn what high CPU usage could mean for your Supabase instance and what could have caused it.',
|
||||
}
|
||||
|
||||
Learn what high CPU usage could mean for your Supabase instance and what could have caused it.
|
||||
|
||||
## The danger of high CPU usage
|
||||
|
||||
Every Supabase project runs in its dedicated virtual machine. Your instance will have a different set of hardware provisioned depending on your [compute add-on](https://supabase.com/docs/guides/platform/compute-add-ons). Your hardware may not be suitable for the intended workload and may experience high CPU usage.
|
||||
|
||||
High CPU usage could come with a range of issues:
|
||||
|
||||
- slower queries
|
||||
- disruption of daily backup routines
|
||||
- in rare cases, your instance may become unresponsive
|
||||
|
||||
Moreover, your instance might not be able to handle future traffic spikes if it already has a high CPU usage.
|
||||
|
||||
## Monitor your CPU
|
||||
|
||||
You can check your CPU usage directly on the Supabase Platform. For this go to database health in the reports section or [click here](https://app.supabase.com/project/_/reports/database) and select your project.
|
||||
|
||||

|
||||
|
||||
It is also possible to monitor your resources and set up alerts using Prometheus/Grafana. You can find a guide for this [here](https://supabase.com/docs/guides/platform/metrics).
|
||||
|
||||
## Common reasons for high CPU usage
|
||||
|
||||
Everything you do with your Supabase project requires compute. Hence, there can be many reasons for high CPU usage. Here are some common ones:
|
||||
|
||||
- **Query performance:** Queries that take a long time to complete (>1 second) as well as excessive amounts of querying can put a strain on the CPU. Check our guide on [examining query performance](https://supabase.com/docs/guides/platform/performance#examining-query-performance).
|
||||
- **Missing indexes:** Your database might have to scan through a large amount of data to find the information it needs. Creating indexes helps your database find data faster. Learn more about indexes [here](https://supabase.com/docs/guides/database/postgres/indexes).
|
||||
- **Unsuitable compute:** The instance size of your Supabase project might not be suitable for your application as you might have more traffic or run resource-intensive operations.
|
||||
|
||||
## Solving high CPU usage
|
||||
|
||||
There are two ways to solve high CPU:
|
||||
|
||||
1. **Optimize performance:** Get more out of your instance's resources by optimizing your usage. Have a look at our [performance tuning guide](https://supabase.com/docs/guides/platform/performance#examining-query-performance) and our [production readiness guide](https://supabase.com/docs/guides/platform/going-into-prod#performance).
|
||||
2. **Upgrade your compute:** You can get a Compute Add-on for your project. Follow [this link](https://app.supabase.com/project/_/settings/billing/update/pro) and select your project to see your upgrade options.
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} />
|
||||
|
||||
export default Page
|
||||
@@ -112,7 +112,7 @@ To _permanently_ set a logging configuration (beyond a single session), execute
|
||||
alter role postgres set pgaudit.log to 'function, write, ddl';
|
||||
```
|
||||
|
||||
For user-traffic flowing through the [HTTP APIs](../../guides/database/api#rest-api-overview) powered by PostgREST, set your configuration values for the roles `anon` and `authenticated`. For traffic using the `service_role` key, set the configuration values for the role `service_role`.
|
||||
For user-traffic flowing through the [HTTP APIs](../../guides/database/api#rest-api-overview) powered by PostgREST, set your configuration values for the roles `anon` and `authenticated`. For traffic using the `service_role` key, set the configuration values for the role `service_role`.
|
||||
|
||||
To reset system-wide settings, execute the following, then perform a fast reboot:
|
||||
|
||||
|
||||
@@ -12,7 +12,10 @@ In some cases, access to new features require upgrading or migrating your Supaba
|
||||
|
||||
## Upgrade your project
|
||||
|
||||
<Admonition type="note">This is only available for projects on the Free plan. For projects on the Pro plan, please contact support for assistance with upgrading.</Admonition>
|
||||
<Admonition type="note">
|
||||
This is only available for projects on the Free plan. For projects on the Pro plan, please contact
|
||||
support for assistance with upgrading.
|
||||
</Admonition>
|
||||
|
||||
When you pause and restore a project, the restored database includes the latest features. This method _does_ include downtime, so be aware that your project will be inaccessible for a short period of time.
|
||||
|
||||
@@ -80,7 +83,7 @@ const NEW_PROJECT_SERVICE_KEY = 'new-project-service-key-yyy'
|
||||
;(async () => {
|
||||
const oldSupabaseRestClient = createClient(OLD_PROJECT_URL, OLD_PROJECT_SERVICE_KEY, {
|
||||
db: {
|
||||
schema: 'storage'
|
||||
schema: 'storage',
|
||||
},
|
||||
})
|
||||
const oldSupabaseClient = createClient(OLD_PROJECT_URL, OLD_PROJECT_SERVICE_KEY)
|
||||
|
||||
@@ -45,8 +45,8 @@ Your project will continue to operate after using all the free quota allowance f
|
||||
This additional usage will be charged based on the line item's cost per unit outlined on the [pricing page](/pricing).
|
||||
|
||||
<Admonition type="note">
|
||||
When your Spend Cap is **off**, we recommend monitoring your consumption and costs using the [Usage
|
||||
section of your project](https://app.supabase.com/project/_/settings/billing/usage) in the
|
||||
When your Spend Cap is **off**, we recommend monitoring your consumption and costs using the
|
||||
[Usage section of your project](https://app.supabase.com/project/_/settings/billing/usage) in the
|
||||
dashboard.
|
||||
</Admonition>
|
||||
|
||||
|
||||
@@ -25,8 +25,7 @@ const supabase = createClient(process.env.SUPABASE_URL, process.env.SUPABASE_KEY
|
||||
|
||||
const channel = supabase.channel('test')
|
||||
|
||||
channel.on('broadcast', { event: 'supa' }, (payload) => console.log(payload))
|
||||
.subscribe()
|
||||
channel.on('broadcast', { event: 'supa' }, (payload) => console.log(payload)).subscribe()
|
||||
```
|
||||
|
||||
## Send Messages
|
||||
@@ -69,7 +68,8 @@ const channel = supabase.channel('test', {
|
||||
},
|
||||
})
|
||||
|
||||
channel.on('broadcast', { event: 'supa' }, (payload) => console.log(payload))
|
||||
channel
|
||||
.on('broadcast', { event: 'supa' }, (payload) => console.log(payload))
|
||||
.subscribe((status) => {
|
||||
if (status === 'SUBSCRIBED') {
|
||||
channel.send({
|
||||
|
||||
@@ -139,9 +139,7 @@ const channel = supabase
|
||||
.subscribe()
|
||||
```
|
||||
|
||||
<Admonition type="note">
|
||||
This filter uses Postgres' `=`.
|
||||
</Admonition>
|
||||
<Admonition type="note">This filter uses Postgres' `=`.</Admonition>
|
||||
|
||||
### neq
|
||||
|
||||
@@ -165,9 +163,7 @@ const channel = supabase
|
||||
.subscribe()
|
||||
```
|
||||
|
||||
<Admonition type="note">
|
||||
This filter uses Postgres' `!=`.
|
||||
</Admonition>
|
||||
<Admonition type="note">This filter uses Postgres' `!=`.</Admonition>
|
||||
|
||||
### lt
|
||||
|
||||
@@ -244,7 +240,8 @@ const channel = supabase
|
||||
```
|
||||
|
||||
<Admonition type="note">
|
||||
This filter uses Postgres' `>` so it works for non-numeric types but make sure to check the expected behavior of the compared data's type.
|
||||
This filter uses Postgres' `>` so it works for non-numeric types but make sure to check the
|
||||
expected behavior of the compared data's type.
|
||||
</Admonition>
|
||||
|
||||
### gte
|
||||
@@ -270,7 +267,8 @@ const channel = supabase
|
||||
```
|
||||
|
||||
<Admonition type="note">
|
||||
This filter uses Postgres' `>=` so it works for non-numeric types but make sure to check the expected behavior of the compared data's type.
|
||||
This filter uses Postgres' `>=` so it works for non-numeric types but make sure to check the
|
||||
expected behavior of the compared data's type.
|
||||
</Admonition>
|
||||
|
||||
### in
|
||||
|
||||
@@ -39,7 +39,8 @@ In the initial message, the client sends a message specifying the features they
|
||||
```
|
||||
|
||||
<Admonition type="note">
|
||||
The `in` filter has the format `COLUMN_NAME=in.(value1,value2,value3)`. However, other filters use the format `COLUMN_NAME=FILTER_NAME.value`.
|
||||
The `in` filter has the format `COLUMN_NAME=in.(value1,value2,value3)`. However, other filters use
|
||||
the format `COLUMN_NAME=FILTER_NAME.value`.
|
||||
</Admonition>
|
||||
|
||||
In response, the server sends the Postgres configuration with a unique ID. With this ID, the client should route incoming changes to the appropriate callback.
|
||||
|
||||
@@ -88,7 +88,7 @@ Build a basic Todo List with Supabase and your favorite frontend framework:
|
||||
- Flutter (in development): [GitHub](https://github.com/supabase/supabase-flutter)
|
||||
- Python (in development): [GitHub](https://github.com/supabase/supabase-py)
|
||||
- C# (in development): [GitHub](https://github.com/supabase/supabase-csharp)
|
||||
- Kotlin (in development): [GitHub](https://github.com/supabase-community/postgrest-kt)
|
||||
- Kotlin (in development): [GitHub](https://github.com/supabase-community/supabase-kt)
|
||||
- `useSupabase`: Supabase React Hooks. [GitHub](https://github.com/gbibeaul/use-supabase)
|
||||
- `vue-supabase`: Supabase Vue wrapper. [GitHub](https://github.com/supabase/vue-supabase)
|
||||
- `vue-3-supabase`: Supabase Vue 3 wrapper. [GitHub](https://github.com/DidoMarchet/vue-3-supabase)
|
||||
|
||||
@@ -118,7 +118,6 @@ Single-sign on allows enterprises to centrally manage accounts and access to app
|
||||
|
||||
A time-based one-time password is a one-time password generated at regular time intervals from a secret, usually from an application in a mobile device (e.g., Google Authenticator, 1Password).
|
||||
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} />
|
||||
|
||||
export default Page
|
||||
@@ -5,7 +5,7 @@ export const meta = {
|
||||
description: 'Migrate your Amazon RDS MySQL or MS SQL database to Supabase Postgres database.',
|
||||
}
|
||||
|
||||
This guide aims to exhibit the process of transferring your Amazon RDS database from any of these engines Postgres, MySQL or MS SQL to Supabase's Postgres database. Although Amazon RDS is a favored managed database service provided by AWS, it may not suffice for all use cases. Supabase, on the other hand, provides an excellent free and open source option that encompasses all the necessary backend features to develop a product: a Postgres database, authentication, instant APIs, edge functions, real-time subscriptions, and storage.
|
||||
This guide aims to exhibit the process of transferring your Amazon RDS database from any of these engines Postgres, MySQL or MS SQL to Supabase's Postgres database. Although Amazon RDS is a favored managed database service provided by AWS, it may not suffice for all use cases. Supabase, on the other hand, provides an excellent free and open source option that encompasses all the necessary backend features to develop a product: a Postgres database, authentication, instant APIs, edge functions, real-time subscriptions, and storage.
|
||||
|
||||
Supabase's core is Postgres, enabling the use of row-level security and providing access to over 40 Postgres extensions. By migrating from Amazon RDS to Supabase, you can leverage Postgres to its fullest potential and acquire all the features you need to complete your project.
|
||||
|
||||
@@ -33,7 +33,7 @@ Supabase's core is Postgres, enabling the use of row-level security and providin
|
||||
## Migrate the database
|
||||
|
||||
The fastest way to migrate your database is with the Supabase migration tool on
|
||||
[Google Colab](https://colab.research.google.com/github/mansueli/Supa-Migrate/blob/main/Amazon_RDS_to_Supabase.ipynb).
|
||||
[Google Colab](https://colab.research.google.com/github/mansueli/Supa-Migrate/blob/main/Amazon_RDS_to_Supabase.ipynb).
|
||||
|
||||
Alternatively, you can use [pgloader](https://github.com/dimitri/pgloader), a flexible and powerful data migration tool that supports a wide range of source database engines, including MySQL and MS SQL, and migrates the data to a Postgres database. For databases using the Postgres engine, we recommend using the pg_dump and psql command line tools, which are included in a full PostgreSQL installation.
|
||||
|
||||
@@ -47,8 +47,8 @@ Alternatively, you can use [pgloader](https://github.com/dimitri/pgloader), a fl
|
||||
|
||||
1. Select the Dabase Engine from the Source database in the dropdown
|
||||
1. Set the environment variables (`HOST`, `USER`, `SOURCE_DB`,`PASSWORD`, `SUPABASE_URL`, and `SUPABASE_PASSWORD`) in the Colab notebook.
|
||||
2. Run the first two steps in [the notebook](https://colab.research.google.com/github/mansueli/Supa-Migrate/blob/main/Amazon_RDS_to_Supabase.ipynb) in order. The first sets engine and installs the necessary files.
|
||||
3. Run the third step to start the migration. This will take a few minutes.
|
||||
1. Run the first two steps in [the notebook](https://colab.research.google.com/github/mansueli/Supa-Migrate/blob/main/Amazon_RDS_to_Supabase.ipynb) in order. The first sets engine and installs the necessary files.
|
||||
1. Run the third step to start the migration. This will take a few minutes.
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="MySQL" label="Migrate from MySQL with pgloader">
|
||||
@@ -57,18 +57,19 @@ Alternatively, you can use [pgloader](https://github.com/dimitri/pgloader), a fl
|
||||
Create a configuration file (e.g., config.load).
|
||||
|
||||
```sql
|
||||
LOAD DATABASE
|
||||
FROM mysql://USER:PASSWORD@HOST/SOURCE_DB
|
||||
INTO postgres://postgres:password@db.xxxx.supabase.co:6543/postgres
|
||||
ALTER SCHEMA 'public' OWNER TO 'postgres';
|
||||
load database
|
||||
from mysql://user:password@host/source_db
|
||||
into postgres://postgres:password@db.xxxx.supabase.co:6543/postgres
|
||||
alter schema 'public' owner to 'postgres';
|
||||
set wal_buffers = '64MB', max_wal_senders = 0, statement_timeout = 0, work_mem to '2GB';
|
||||
````
|
||||
```
|
||||
|
||||
##Run the migration with pgloader
|
||||
|
||||
```bash
|
||||
pgloader config.load
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="MS SQL" label="Migrate from MSSQL">
|
||||
|
||||
@@ -81,13 +82,14 @@ LOAD DATABASE
|
||||
INTO postgres://postgres:password@db.xxxx.supabase.co:6543/postgres
|
||||
ALTER SCHEMA 'public' OWNER TO 'postgres';
|
||||
set wal_buffers = '64MB', max_wal_senders = 0, statement_timeout = 0, work_mem to '2GB';
|
||||
````
|
||||
```
|
||||
|
||||
##Run the migration with pgloader
|
||||
|
||||
```bash
|
||||
pgloader config.load
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
|
||||
@@ -42,7 +42,6 @@ Note that CDNs might still evict your object from their cache if it has not been
|
||||
|
||||
The cache status of a particular request is sent in the `cf-cache-status` header. A cache status of `MISS` indicates that the CDN node did not have the object in its cache and had to ping the origin to get it. A cache status of `HIT` indicates that the object was sent directly from the CDN.
|
||||
|
||||
|
||||
## Smart CDN Caching
|
||||
|
||||
<Admonition type="note">
|
||||
|
||||
@@ -77,7 +77,7 @@ For instance, if you use Chrome when viewing a jpeg image and using transformati
|
||||
As a result, this will lower the bandwidth that you send to your users and your application will load much faster.
|
||||
|
||||
<Admonition type="note">
|
||||
We currently only support WebP. AVIF support will come in the near future
|
||||
We currently only support WebP. AVIF support will come in the near future
|
||||
</Admonition>
|
||||
|
||||
**Disabling automatic optimisation:**
|
||||
@@ -87,11 +87,11 @@ this is also supported in the JavaScript SDK starting from v2.2.0
|
||||
|
||||
```ts
|
||||
await storage.from('bucket').download('image.jpeg', {
|
||||
transform: {
|
||||
width: 200,
|
||||
height: 200,
|
||||
format: 'origin',
|
||||
},
|
||||
transform: {
|
||||
width: 200,
|
||||
height: 200,
|
||||
format: 'origin',
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
@@ -105,7 +105,9 @@ To get started, create a `supabase-image-loader.js` file in your NextJS project
|
||||
const projectId = '' // your supabase project id
|
||||
|
||||
export default function supabaseLoader({ src, width, quality }) {
|
||||
return `https://${projectId}.supabase.co/storage/v1/render/image/public/${src}?width=${width}&quality=${quality || 75}`
|
||||
return `https://${projectId}.supabase.co/storage/v1/render/image/public/${src}?width=${width}&quality=${
|
||||
quality || 75
|
||||
}`
|
||||
}
|
||||
```
|
||||
|
||||
@@ -193,11 +195,11 @@ Under the hood we use the awesome [Imgproxy](https://imgproxy.net/)
|
||||
Simply deploy an imgproxy container with the following configuration:
|
||||
|
||||
```yaml
|
||||
imgproxy:
|
||||
image: darthsim/imgproxy
|
||||
environment:
|
||||
- IMGPROXY_ENABLE_WEBP_DETECTION=true
|
||||
- IMGPROXY_JPEG_PROGRESSIVE=true
|
||||
imgproxy:
|
||||
image: darthsim/imgproxy
|
||||
environment:
|
||||
- IMGPROXY_ENABLE_WEBP_DETECTION=true
|
||||
- IMGPROXY_JPEG_PROGRESSIVE=true
|
||||
```
|
||||
|
||||
Note: make sure that this service can only be reachable within an internal network and not exposed to the public internet
|
||||
|
||||
@@ -20,5 +20,5 @@ export async function getStaticProps() {
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return handleRefGetStaticPaths(sections, libraryPath)
|
||||
return handleRefGetStaticPaths(sections)
|
||||
}
|
||||
@@ -19,5 +19,5 @@ export async function getStaticProps() {
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return handleRefGetStaticPaths(sections, libraryPath)
|
||||
return handleRefGetStaticPaths(sections)
|
||||
}
|
||||
@@ -17,5 +17,5 @@ export async function getStaticProps() {
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return handleRefGetStaticPaths(sections, libraryPath)
|
||||
return handleRefGetStaticPaths(sections)
|
||||
}
|
||||
@@ -40,5 +40,5 @@ export async function getStaticProps() {
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return handleRefGetStaticPaths(sections, libraryPath)
|
||||
return handleRefGetStaticPaths(sections)
|
||||
}
|
||||
@@ -17,5 +17,5 @@ export async function getStaticProps() {
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return handleRefGetStaticPaths(sections, libraryPath)
|
||||
return handleRefGetStaticPaths(sections)
|
||||
}
|
||||
@@ -39,5 +39,5 @@ export async function getStaticProps() {
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return handleRefGetStaticPaths(sections, libraryPath)
|
||||
return handleRefGetStaticPaths(sections)
|
||||
}
|
||||
@@ -17,5 +17,5 @@ export async function getStaticProps() {
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return handleRefGetStaticPaths(sections, libraryPath)
|
||||
return handleRefGetStaticPaths(sections)
|
||||
}
|
||||
@@ -40,5 +40,5 @@ export async function getStaticProps() {
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return handleRefGetStaticPaths(sections, libraryPath)
|
||||
return handleRefGetStaticPaths(sections)
|
||||
}
|
||||
@@ -17,5 +17,5 @@ export async function getStaticProps() {
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return handleRefGetStaticPaths(sections, libraryPath)
|
||||
return handleRefGetStaticPaths(sections)
|
||||
}
|
||||
@@ -39,5 +39,5 @@ export async function getStaticProps() {
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return handleRefGetStaticPaths(sections, libraryPath)
|
||||
return handleRefGetStaticPaths(sections)
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
import { inspect } from 'util'
|
||||
import clientLibsCommonSections from '~/../../spec/common-client-libs-sections.json'
|
||||
import typeSpec from '~/../../spec/enrichments/tsdoc_v2/combined.json'
|
||||
import spec from '~/../../spec/supabase_js_v2.yml' assert { type: 'yml' }
|
||||
@@ -27,5 +26,5 @@ export async function getStaticProps() {
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return handleRefGetStaticPaths(sections, libraryPath)
|
||||
return handleRefGetStaticPaths(sections)
|
||||
}
|
||||
@@ -40,5 +40,5 @@ export async function getStaticProps() {
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return handleRefGetStaticPaths(sections, libraryPath)
|
||||
return handleRefGetStaticPaths(sections)
|
||||
}
|
||||
@@ -27,5 +27,5 @@ export async function getStaticProps() {
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return handleRefGetStaticPaths(sections, libraryPath)
|
||||
return handleRefGetStaticPaths(sections)
|
||||
}
|
||||
@@ -40,5 +40,5 @@ export async function getStaticProps() {
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return handleRefGetStaticPaths(sections, libraryPath)
|
||||
return handleRefGetStaticPaths(sections)
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import clientLibsCommonSections from '~/../../spec/common-client-libs-sections.json'
|
||||
import spec from '~/../../spec/supabase_kt_v0.yml' assert { type: 'yml' }
|
||||
import RefSectionHandler from '~/components/reference/RefSectionHandler'
|
||||
import { flattenSections } from '~/lib/helpers'
|
||||
import handleRefGetStaticPaths from '~/lib/mdx/handleRefStaticPaths'
|
||||
import handleRefStaticProps from '~/lib/mdx/handleRefStaticProps'
|
||||
|
||||
const sections = flattenSections(clientLibsCommonSections)
|
||||
const libraryPath = '/kotlin'
|
||||
|
||||
export default function KotlinReference(props) {
|
||||
return <RefSectionHandler sections={sections} spec={spec} pageProps={props} type="client-lib" />
|
||||
}
|
||||
|
||||
export async function getStaticProps() {
|
||||
return handleRefStaticProps(sections, libraryPath)
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return handleRefGetStaticPaths(sections)
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import clientLibsCommonSections from '~/../../spec/common-client-libs-sections.json'
|
||||
import typeSpec from '~/../../spec/enrichments/tsdoc_v2/combined.json'
|
||||
import spec from '~/../../spec/supabase_kt_v0.yml' assert { type: 'yml' }
|
||||
import RefSectionHandler from '~/components/reference/RefSectionHandler'
|
||||
import { flattenSections } from '~/lib/helpers'
|
||||
import handleRefGetStaticPaths from '~/lib/mdx/handleRefStaticPaths'
|
||||
import handleRefStaticProps from '~/lib/mdx/handleRefStaticProps'
|
||||
import { useRouter } from 'next/router'
|
||||
import RefSEO from '~/components/reference/RefSEO'
|
||||
|
||||
const sections = flattenSections(clientLibsCommonSections)
|
||||
const libraryPath = '/kotlin'
|
||||
|
||||
export default function KotlinReference(props) {
|
||||
const router = useRouter()
|
||||
const slug = router.query.slug[0]
|
||||
const filteredSection = sections.filter((section) => section.id === slug)
|
||||
|
||||
const pageTitle = filteredSection[0]?.title
|
||||
? `${filteredSection[0]?.title} | Supabase`
|
||||
: 'Supabase'
|
||||
|
||||
return (
|
||||
<>
|
||||
<RefSEO title={pageTitle} />
|
||||
|
||||
<RefSectionHandler
|
||||
sections={filteredSection}
|
||||
spec={spec}
|
||||
typeSpec={typeSpec}
|
||||
pageProps={props}
|
||||
type="client-lib"
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export async function getStaticProps() {
|
||||
return handleRefStaticProps(sections, libraryPath)
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return handleRefGetStaticPaths(sections)
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import clientLibsCommonSections from '~/../../spec/common-client-libs-sections.json'
|
||||
import spec from '~/../../spec/supabase_kt_v0.yml' assert { type: 'yml' }
|
||||
import RefSectionHandler from '~/components/reference/RefSectionHandler'
|
||||
import { flattenSections } from '~/lib/helpers'
|
||||
import handleRefGetStaticPaths from '~/lib/mdx/handleRefStaticPaths'
|
||||
import handleRefStaticProps from '~/lib/mdx/handleRefStaticProps'
|
||||
|
||||
const sections = flattenSections(clientLibsCommonSections)
|
||||
const libraryPath = '/kotlin/v0'
|
||||
|
||||
export default function KotlinReference(props) {
|
||||
return <RefSectionHandler sections={sections} spec={spec} pageProps={props} type="client-lib" />
|
||||
}
|
||||
|
||||
export async function getStaticProps() {
|
||||
return handleRefStaticProps(sections, libraryPath)
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return handleRefGetStaticPaths(sections)
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import clientLibsCommonSections from '~/../../spec/common-client-libs-sections.json'
|
||||
import typeSpec from '~/../../spec/enrichments/tsdoc_v2/combined.json'
|
||||
import spec from '~/../../spec/supabase_kt_v0.yml' assert { type: 'yml' }
|
||||
import RefSectionHandler from '~/components/reference/RefSectionHandler'
|
||||
import { flattenSections } from '~/lib/helpers'
|
||||
import handleRefGetStaticPaths from '~/lib/mdx/handleRefStaticPaths'
|
||||
import handleRefStaticProps from '~/lib/mdx/handleRefStaticProps'
|
||||
import { useRouter } from 'next/router'
|
||||
import RefSEO from '~/components/reference/RefSEO'
|
||||
|
||||
const sections = flattenSections(clientLibsCommonSections)
|
||||
const libraryPath = '/kotlin/v0'
|
||||
|
||||
export default function KotlinReference(props) {
|
||||
const router = useRouter()
|
||||
const slug = router.query.slug[0]
|
||||
const filteredSection = sections.filter((section) => section.id === slug)
|
||||
|
||||
const pageTitle = filteredSection[0]?.title
|
||||
? `${filteredSection[0]?.title} | Supabase`
|
||||
: 'Supabase'
|
||||
return (
|
||||
<>
|
||||
<RefSEO title={pageTitle} />
|
||||
|
||||
<RefSectionHandler
|
||||
sections={filteredSection}
|
||||
spec={spec}
|
||||
typeSpec={typeSpec}
|
||||
pageProps={props}
|
||||
type="client-lib"
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export async function getStaticProps() {
|
||||
return handleRefStaticProps(sections, libraryPath)
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return handleRefGetStaticPaths(sections)
|
||||
}
|
||||
@@ -26,5 +26,5 @@ export async function getStaticProps() {
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return handleRefGetStaticPaths(sections, libraryPath)
|
||||
return handleRefGetStaticPaths(sections)
|
||||
}
|
||||
@@ -40,5 +40,5 @@ export async function getStaticProps() {
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return handleRefGetStaticPaths(sections, libraryPath)
|
||||
return handleRefGetStaticPaths(sections)
|
||||
}
|
||||
@@ -21,5 +21,5 @@ export async function getStaticProps() {
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return handleRefGetStaticPaths(sections, libraryPath)
|
||||
return handleRefGetStaticPaths(sections)
|
||||
}
|
||||
@@ -21,5 +21,5 @@ export async function getStaticProps() {
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return handleRefGetStaticPaths(sections, libraryPath)
|
||||
return handleRefGetStaticPaths(sections)
|
||||
}
|
||||
@@ -18,5 +18,5 @@ export async function getStaticProps() {
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return handleRefGetStaticPaths(sections, libraryPath)
|
||||
return handleRefGetStaticPaths(sections)
|
||||
}
|
||||
@@ -17,5 +17,5 @@ export async function getStaticProps() {
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return handleRefGetStaticPaths(sections, libraryPath)
|
||||
return handleRefGetStaticPaths(sections)
|
||||
}
|
||||
@@ -21,5 +21,5 @@ export async function getStaticProps() {
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return handleRefGetStaticPaths(sections, libraryPath)
|
||||
return handleRefGetStaticPaths(sections)
|
||||
}
|
||||
@@ -17,5 +17,5 @@ export async function getStaticProps() {
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return handleRefGetStaticPaths(sections, libraryPath)
|
||||
return handleRefGetStaticPaths(sections)
|
||||
}
|
||||
@@ -40,5 +40,5 @@ export async function getStaticProps() {
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return handleRefGetStaticPaths(sections, libraryPath)
|
||||
return handleRefGetStaticPaths(sections)
|
||||
}
|
||||
@@ -17,5 +17,5 @@ export async function getStaticProps() {
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return handleRefGetStaticPaths(sections, libraryPath)
|
||||
return handleRefGetStaticPaths(sections)
|
||||
}
|
||||
@@ -39,5 +39,5 @@ export async function getStaticProps() {
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return handleRefGetStaticPaths(sections, libraryPath)
|
||||
return handleRefGetStaticPaths(sections)
|
||||
}
|
||||
Loaded 100 of 188 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user