diff --git a/.dockerignore b/.dockerignore
index b5924c32836..ff561a782b4 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -1,7 +1,20 @@
-node_modules/
+# Root folder of this turbo monorepo
+# Doesn't send useless context to Docker daemon
+.git
+.github
+about
+apps
+docker
+examples
+i18n
+**/node_modules
+spec
+supabase
+tests
+
+**/.next
.vercel
-.next
-.env.local
-.env.production
-.env.dev
-.env.*
\ No newline at end of file
+.env.*
+
+.dockerignore
+studio/Dockerfile
diff --git a/.github/workflows/avoid-typos.yml b/.github/workflows/avoid-typos.yml
new file mode 100644
index 00000000000..cd8d982f4aa
--- /dev/null
+++ b/.github/workflows/avoid-typos.yml
@@ -0,0 +1,20 @@
+name: reviewdog
+on:
+ pull_request:
+ types: [opened, synchronize, reopened, edited]
+
+jobs:
+ misspell:
+ name: runner / misspell
+ runs-on: ubuntu-latest
+ steps:
+ - name: Check out code.
+ uses: actions/checkout@v1
+ - name: misspell
+ uses: reviewdog/action-misspell@v1
+ with:
+ github_token: ${{ secrets.github_token }}
+ locale: "US"
+ reporter: github-pr-review
+ level: error
+ exclude: "*.css"
diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml
deleted file mode 100644
index 0c9a3c46ebd..00000000000
--- a/.github/workflows/integration-tests.yml
+++ /dev/null
@@ -1,92 +0,0 @@
-name: Tests
-
-# Controls when the workflow will run
-on:
- schedule:
- - cron: '0 4/6 * * *'
- workflow_dispatch:
-
-# A workflow run is made up of one or more jobs that can run sequentially or in parallel
-jobs:
- autotests:
- name: Run tests and generate Allure Report
- strategy:
- matrix:
- node: ['14']
-
- runs-on: ubuntu-latest
- defaults:
- run:
- working-directory: ./tests
-
- steps:
- - uses: actions/checkout@v2
-
- - name: Set up JDK
- uses: actions/setup-java@v1
- with:
- java-version: 1.8
-
- - name: Set up Node
- uses: actions/setup-node@v1
- with:
- node-version: ${{ matrix.node }}
-
- - name: Install dependencies
- run: npm ci
-
- - uses: supabase/setup-cli@v1
- - run: supabase start
-
- - name: Run Test
- run: npm run test:local
- env:
- SUPABASE_DB_PORT: 54322
- SUPABASE_DB_PASS: postgres
- SUPABASE_DB_HOST: localhost
- SUPABASE_GOTRUE: http://localhost:54321
- SUPABASE_URL: http://localhost:54321
- SUPABASE_KEY_ANON: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24ifQ.625_WdcF3KHqz5amU0x2X5WWHP-OEs_4qj0ssLNHzTs
- SUPABASE_KEY_ADMIN: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6InNlcnZpY2Vfcm9sZSJ9.vI9obAHOGyVVKa3pD--kJlyxp-Z2zV9UUMAhKpNLAcU
-
- - name: Stop infrastructure
- if: always()
- run: supabase stop
-
- - name: Get Allure history
- uses: actions/checkout@v2
- if: always()
- continue-on-error: true
- with:
- repository: supabase/test-reports
- ref: gh-pages
- path: gh-pages
-
- - name: Allure Report action
- uses: simple-elf/allure-report-action@master
- if: always()
- with:
- github_repo: test-reports
- allure_results: tests/allure-results
- allure_history: allure-history
- subfolder: supabase-integration
- keep_reports: 50
-
- - name: Deploy report to Github Pages
- if: always()
- uses: peaceiris/actions-gh-pages@v2
- env:
- EXTERNAL_REPOSITORY: supabase/test-reports
- ACTIONS_DEPLOY_KEY: ${{ secrets.DEPLOY_TO_TEST_REPORTS_KEY }}
- PUBLISH_BRANCH: gh-pages
- PUBLISH_DIR: allure-history
-
- - name: Post the link to the report
- if: always()
- uses: Sibz/github-status-action@v1
- with:
- authToken: ${{ secrets.GITHUB_TOKEN }}
- context: 'Test report'
- state: 'success'
- sha: ${{ github.event.pull_request.head.sha || github.sha }}
- target_url: https://supabase.github.io/test-reports/supabase-integration/${{ github.run_number }}
diff --git a/.github/workflows/mirror.yml b/.github/workflows/mirror.yml
index 0a4d1b1fd57..47563fba1ef 100644
--- a/.github/workflows/mirror.yml
+++ b/.github/workflows/mirror.yml
@@ -19,12 +19,16 @@ jobs:
permissions:
contents: read
packages: write
+ id-token: write
steps:
+ - name: configure aws credentials
+ uses: aws-actions/configure-aws-credentials@v1
+ with:
+ role-to-assume: ${{ secrets.PROD_AWS_ROLE }}
+ aws-region: us-east-1
- uses: docker/login-action@v2
with:
registry: public.ecr.aws
- username: ${{ secrets.PROD_ACCESS_KEY_ID }}
- password: ${{ secrets.PROD_SECRET_ACCESS_KEY }}
- uses: docker/login-action@v2
with:
registry: ghcr.io
diff --git a/.github/workflows/og_images.yml b/.github/workflows/og_images.yml
new file mode 100644
index 00000000000..2e1461461d2
--- /dev/null
+++ b/.github/workflows/og_images.yml
@@ -0,0 +1,28 @@
+name: Deploy OG Images
+
+on:
+ push:
+ branches:
+ - main
+ paths:
+ - 'supabase/functions/og-images'
+ workflow_dispatch:
+
+jobs:
+ deploy:
+ runs-on: ubuntu-latest
+
+ env:
+ SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }}
+ PROJECT_ID: ${{ secrets.PROJECT_ID }}
+
+ steps:
+ - name: Check out repo
+ - uses: actions/checkout@v3
+
+ - name: Setup the Supabase CLI
+ - uses: supabase/setup-cli@v1
+ with:
+ version: 1.0.0
+
+ - run: supabase functions deploy og-images --project-ref $PROJECT_ID
diff --git a/.github/workflows/studio-build.yml b/.github/workflows/studio-build.yml
index 570baa1aa61..35d1f97b8b1 100644
--- a/.github/workflows/studio-build.yml
+++ b/.github/workflows/studio-build.yml
@@ -7,6 +7,7 @@ on:
- studio
paths:
- 'studio/**'
+ - 'package-lock.json'
jobs:
build:
runs-on: ubuntu-latest
diff --git a/.github/workflows/studio-tests.yml b/.github/workflows/studio-tests.yml
index 1a99a50182d..72a84549568 100644
--- a/.github/workflows/studio-tests.yml
+++ b/.github/workflows/studio-tests.yml
@@ -8,10 +8,12 @@ on:
branches: [master]
paths:
- 'studio/**'
+ - 'package-lock.json'
pull_request:
branches: [master]
paths:
- 'studio/**'
+ - 'package-lock.json'
jobs:
build:
runs-on: ubuntu-latest
diff --git a/.gitignore b/.gitignore
index f536bae18d7..c67a911193a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,7 @@ node_modules
out
.docz
tmp
+.swp
coverage
allure-results
@@ -119,4 +120,4 @@ typings/
**/supabase/.branches
**/supabase/.temp
-apps/new-docs/*
\ No newline at end of file
+apps/new-docs/*
diff --git a/.prettierignore b/.prettierignore
index 9ab0849d504..db287264935 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -3,4 +3,5 @@
node_modules
package-lock.json
docker*
-apps/**/out
\ No newline at end of file
+apps/**/out
+**/**.mdx
\ No newline at end of file
diff --git a/DEVELOPERS.md b/DEVELOPERS.md
index 0281b1b0bd7..1845aa29ca5 100644
--- a/DEVELOPERS.md
+++ b/DEVELOPERS.md
@@ -57,7 +57,7 @@ To contribute code to [Supabase](https://supabase.com), you must fork the [Supab
[Supabase](https://supabase.com) uses [Turborepo](https://turborepo.org/docs) to manage and run this monorepo.
-1. Install the dependences in the root of the repo.
+1. Install the dependencies in the root of the repo.
```sh
npm install # install dependencies
@@ -113,7 +113,7 @@ Following the changes to the [Supabase docs](https://supabase.com/blog/new-supab
- Inside of `apps/docs` create a `.env.local` file with the following: `NEXT_PUBLIC_NEW_DOCS=true`
-Now when you run a local devlopment docs server you will see the new docs site.
+Now when you run a local development docs server you will see the new docs site.
---
diff --git a/LICENSE b/LICENSE
index 438f239eb5d..e029233d99c 100644
--- a/LICENSE
+++ b/LICENSE
@@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
- Copyright 2022 Supabase
+ Copyright 2023 Supabase
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/README.md b/README.md
index 304eceed468..d6498236c88 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
-
-
+
+
---
@@ -12,9 +12,9 @@
- [x] Hosted Postgres Database. [Docs](https://supabase.com/docs/guides/database)
- [x] Authentication and Authorization. [Docs](https://supabase.com/docs/guides/auth)
- [x] Auto-generated APIs.
- - [x] REST. [Docs](https://supabase.com/docs/guides/api#rest-api)
- - [x] Realtime subscriptions. [Docs](https://supabase.com/docs/guides/api#realtime-api)
- - [x] GraphQL (Beta). [Docs](https://supabase.com/docs/guides/api#graphql-api)
+ - [x] REST. [Docs](https://supabase.com/docs/guides/database/api#rest-api)
+ - [x] Realtime subscriptions. [Docs](https://supabase.com/docs/guides/database/api#realtime-api)
+ - [x] GraphQL (Beta). [Docs](https://supabase.com/docs/guides/database/api#graphql-api)
- [x] Functions.
- [x] Database Functions. [Docs](https://supabase.com/docs/guides/database/functions)
- [x] Edge Functions [Docs](https://supabase.com/docs/guides/functions)
diff --git a/apps/docs/.env.example b/apps/docs/.env.example
new file mode 100644
index 00000000000..24f1544c0e9
--- /dev/null
+++ b/apps/docs/.env.example
@@ -0,0 +1,10 @@
+# PUBLIC
+NEXT_PUBLIC_SUPABASE_URL=http://localhost:54321
+NEXT_PUBLIC_SUPABASE_ANON_KEY=
+
+# PRIVATE
+OPENAI_KEY=
+SUPABASE_SERVICE_ROLE_KEY=
+
+# Set IS_PLATFORM to true if you have the api keys and urls
+NEXT_PUBLIC_IS_PLATFORM=false
diff --git a/apps/docs/DEVELOPERS.md b/apps/docs/DEVELOPERS.md
new file mode 100644
index 00000000000..3935d5e0ce0
--- /dev/null
+++ b/apps/docs/DEVELOPERS.md
@@ -0,0 +1,99 @@
+# Developing Supabase Docs
+
+## Getting started
+
+Thanks for your interest in [Supabase docs](https://supabase.com/docs) and for wanting to contribute! Before you begin, read the
+[code of conduct](https://github.com/supabase/.github/blob/main/CODE_OF_CONDUCT.md) and check out the
+[existing issues](https://github.com/supabase/supabase/issues).
+This document describes how to set up your development environment to contribute to [Supabase docs](https://supabase.com/docs).
+
+For a complete run-down on how all of our tools work together, see the main DEVELOPERS.md. That readme describes how to get set up locally in lots of detail, including minimum requirements, our Turborepo setup, installing packages, sharing components across projects, and more. This readme deals specifically with the docs site.
+
+## Local setup
+
+[supabase.com/docs](https://supabase.com/docs) is a Next.JS site. You can get setup by following the same steps for all of our other Next.JS projects:
+
+1. Follow the steps outlined in the Local Development section of the main [DEVELOPERS.md](https://github.com/supabase/supabase/blob/master/DEVELOPERS.md)
+2. If you work at Supabase, run `dev:secrets:pull` to pull down the internal environment variables. If you're a community member, create a `.env` file and add this line to it: `NEXT_PUBLIC_IS_PLATFORM=false`
+3. Start the local docs site by navigating to `/apps/docs` and running `npm run dev`
+4. Visit http://localhost:3001/docs in your browser - don't forget to append the `/docs` to the end
+5. Your local site should look exactly like [https://supabase.com/docs](https://supabase.com/docs)
+
+## Types of documentation
+
+[https://supabase.com/docs](https://supabase.com/docs) has several different kinds of documentation, all coming from different sources.
+
+### Guides
+
+The primary, instructional type of content. Basically anything that lives on the `https://supabase.com/docs/guides` route. This includes Guides for Auth, Database, Storage, Realtime, Edge Functions, as well as general resources, self-hosting instructions, and integrations. These are all [`.mdx`](https://mdxjs.com/) files — a combination of Markdown and Javascript.
+
+#### Things to know
+
+Here's a simple [example](https://supabase.com/docs/guides/functions) `.mdx` Guide, and here is [the source on Github](https://raw.githubusercontent.com/supabase/supabase/master/apps/docs/pages/guides/functions.mdx).
+
+Some things to note:
+
+1. The files need to import a Layout at the top
+2. The files need to export a `Page` at the bottom with the `` component
+3. The files frontmatter is stored in `const meta = {}`. You should always include `title` and `description`.
+4. You can write Markdown as you normally would, but you can also write regular Javascript and JSX. Note the `examples` array that we iterate over.
+5. Any Javascript variables you use in these files need to be exported in order to be used (i.e., `export const examples = []`).
+
+##### Using components
+
+You can use any standard React components in these `.mdx` files without having to explicitly import them in each file. All components get imported in a [common components](https://github.com/supabase/supabase/blob/master/apps/docs/components/index.tsx) file and can be used in any `.mdx` file. Components can also be "intercepted" and modified via this file. Note how we're intercepting the `h2`, `h3` and `code` tags and modifying them before converting the `mdx` to `html`.
+
+### Reference docs for client libraries
+
+We maintain client libraries for [Javascript](https://supabase.com/docs/reference/javascript) and [Flutter/Dart](https://supabase.com/docs/reference/dart) (with more to come). These reference docs document every object and method available for developers to use. The are assembled from different sources and work much differently than the `.mdx` Guides we just looked at.
+
+The client libraries are essentially wrappers around the clients for the various tools we use — GoTrue, PostgREST, Storage, Functions, and Realtime. The easiest way to describe how the things fit together is to look at an example and trace where the various pieces of information are coming from.
+
+#### Example
+
+Let's look at the `updateUser()` function in the `supabase-js` library.
+
+#### Common file
+
+Several pieces of information for this function come from a [common file](https://github.com/supabase/supabase/blob/3d774b3b7bcdcb410e25726d832467584ebea686/spec/common-client-libs-sections.json#L548) where we store information shared by all libraries.
+
+1. id — used to identify this function
+2. title - the human-readable title
+3. slug — the url slug
+4. product - the Supabase tool or product that "owns" this function. Since `updateUser()` is an auth function, its product is `auth`
+5. type — `updateUser()` is a function and marked as such, but we can also have sections of markdown interspersed with these function definitions.
+
+When a new function is added, this info would need to be manually added to the common file.
+
+#### Function Parameters
+
+The `updateUser()` function takes one parameter: `attributes`. The details for this parameter live in the GoTrue client library, referenced via a `$ref` property in the `supabase-js` [spec file](https://github.com/supabase/supabase/blob/cb04d85262db6a371539dda7df9b00ba5a901e87/spec/supabase_js_v2.yml#L357). Here, the `$ref` property is pointing to the [actual function definition](https://github.com/supabase/gotrue-js/blob/2d60e79073b96ae8c97a6ce18e2601ed1e2a2712/src/GoTrueClient.ts#L590) in the `gotrue-js` library. The accepted values for the `attributes` parameter come from the [type definition](https://github.com/supabase/gotrue-js/blob/16d3deb822097e8640a3a15b94a5690b3beaf11b/src/lib/types.ts#L233).
+
+These individual library spec files are fetched via this [Makefile](https://github.com/supabase/supabase/blob/master/spec/Makefile), and get [transformed](https://github.com/supabase/supabase/blob/master/spec/enrichments/tsdoc_v2/supabase_dereferenced.json) to combine the information we need (params, types, etc). Unless you're a library maintainer, you shouldn't need to worry about this part of the process.
+
+If you are a library maintainer, the last important note about these library files is that the [Makefile](https://github.com/supabase/supabase/blob/master/spec/Makefile) pulls from the `gh-pages` branch of the client library repo. Here's an example of the [`realtime-js` spec file](https://github.com/supabase/realtime-js/blob/gh-pages/v2/spec.json). Updating something like function params or returns, the process is:
+
+1. Get your changes merged to `master` in your library
+2. This will kick off an action that automatically updates the spec file in the library's `gh-pages` branch
+3. Run `make` in `/spec` of the `supabase/supabase` repo. This will regenerate all of the `tsdoc` files that the docs site uses
+4. You should now see the changes you've made in the docs site locally
+
+#### Function Examples
+
+The `updateUser()` function has three examples listed with it. The examples are stored along with the `$ref` property in the [supabase_js_v2 spec file](https://github.com/supabase/supabase/blob/master/spec/supabase_js_v2.yml).
+
+#### Rendering in Next.JS
+
+These reference docs are rendered by Next.JS via a dynamic route using a [`[...slug.tsx]`](https://github.com/supabase/supabase/blob/master/apps/docs/pages/reference/javascript/%5B...slug%5D.tsx). Here, we use the library [spec file](https://github.com/supabase/supabase/blob/bd0514553c627db8f1e8d0b3ae440ccb6759d228/apps/docs/pages/reference/javascript/%5B...slug%5D.tsx#L4) and the [common file](https://github.com/supabase/supabase/blob/bd0514553c627db8f1e8d0b3ae440ccb6759d228/apps/docs/pages/reference/javascript/%5B...slug%5D.tsx#L1) to output the info you see on the page.
+
+### Other reference docs
+
+The reference docs for the [Supabase Management API](https://supabase.com/docs/reference/api) and the [Supabase CLI](https://supabase.com/docs/reference/cli) are a little more straightforward than the client libraries. Both files also have a [common file](https://github.com/supabase/supabase/blob/master/spec/common-cli-sections.json) which handles things like `title`, `id` and `slug`. Both also have a spec file detailing things like parameters, descriptions, and responses ([Management API](https://github.com/supabase/supabase/blob/master/spec/api_v0_openapi.json) / [CLI](https://github.com/supabase/supabase/blob/master/spec/cli_v1_commands.yaml))
+
+On the Next.JS side of things, these work almost exactly the same as the client libaries with a dynamic [`[...slug.tsx]`](https://github.com/supabase/supabase/blob/master/apps/docs/pages/reference/cli/%5B...slug%5D.tsx).
+
+### Misc
+
+#### Search
+
+Search is handled through Algolia. When the site is built, a [search script](https://github.com/supabase/supabase/blob/master/apps/docs/scripts/build-search.ts) runs through all of the types of content, generating search objects that are sent to Algolia to index.
diff --git a/apps/docs/components/AuthProviders.tsx b/apps/docs/components/AuthProviders.tsx
index a1903636904..82a57180a4b 100644
--- a/apps/docs/components/AuthProviders.tsx
+++ b/apps/docs/components/AuthProviders.tsx
@@ -1,53 +1,21 @@
import providers from '../data/authProviders'
-import ButtonCard from './ButtonCard'
+import { IconPanel } from 'ui'
+import Link from 'next/link'
+
+export default function AuthProviders({ type }: { type: string }) {
+ const filterProviders = providers.filter((item) => item.authType === type)
-export default function AuthProviders() {
return (
-
- This reference documents every object and method available in Supabase's isomorphic JavaScript
- library, supabase-js. You can use supabase-js to interact with your Postgres database, listen to
- database changes, invoke Deno Edge Functions, build login and user management functionality, and
- manage large files.
+
+
+ You're viewing the docs for an older version of the `supabase-js` library. Learn how to [upgrade
+ to the latest version](/docs/reference/javascript/v1/upgrade-guide).
+
+
+This reference documents every object and method available in Supabase's isomorphic JavaScript
+library, supabase-js. You can use supabase-js to interact with your Postgres database, listen to
+database changes, invoke Deno Edge Functions, build login and user management functionality, and
+manage large files.
+
diff --git a/apps/docs/docs/ref/javascript/upgrade-guide.mdx b/apps/docs/docs/ref/javascript/v1/upgrade-guide.mdx
similarity index 100%
rename from apps/docs/docs/ref/javascript/upgrade-guide.mdx
rename to apps/docs/docs/ref/javascript/v1/upgrade-guide.mdx
diff --git a/apps/docs/docs/ref/python/introduction.mdx b/apps/docs/docs/ref/python/introduction.mdx
new file mode 100644
index 00000000000..cf0a9f0ee4f
--- /dev/null
+++ b/apps/docs/docs/ref/python/introduction.mdx
@@ -0,0 +1,21 @@
+---
+id: introduction
+title: Introduction
+hideTitle: true
+---
+
+
+
+
+
Python Client Library
+
@supabase-community/supabase-py
+
+
+
+
+ This reference documents every object and method available in the
+ [supabase-py](https://github.com/supabase-community/supabase-py) library from the Supabase
+ community. You can use `supabase-py` to interact with your Postgres database, listen to database
+ changes, invoke Deno Edge Functions, build login and user management functionality, and manage
+ large files.
+
diff --git a/apps/docs/docs/ref/python/release-notes.mdx b/apps/docs/docs/ref/python/release-notes.mdx
new file mode 100644
index 00000000000..1deb6705f7e
--- /dev/null
+++ b/apps/docs/docs/ref/python/release-notes.mdx
@@ -0,0 +1,10 @@
+---
+id: release-notes
+title: Release Notes
+---
+
+The community is actively working on the library and we will be upgrading the Authentication library, `gotrue-py`, to mirror the Supabase-js v2 lib.
+
+## Storage Transformations
+
+We currently support [image transformations](https://supabase.com/docs/guides/storage/image-transformations) in our storage library.
diff --git a/apps/docs/pages/ref-pages.mdx b/apps/docs/docs/reference/csharp/generated/.gitkeep
similarity index 100%
rename from apps/docs/pages/ref-pages.mdx
rename to apps/docs/docs/reference/csharp/generated/.gitkeep
diff --git a/apps/docs/docs/reference/csharp/initializing.mdx b/apps/docs/docs/reference/csharp/initializing.mdx
new file mode 100644
index 00000000000..ca29a5605a3
--- /dev/null
+++ b/apps/docs/docs/reference/csharp/initializing.mdx
@@ -0,0 +1,67 @@
+---
+id: initializing
+title: 'Initializing'
+slug: initializing
+custom_edit_url: https://github.com/supabase/supabase/edit/master/web/spec/supabase.yml
+---
+
+### Initializing a new Client
+
+
+
+
+Initializing a new client is pretty straightforward. Find your project url and public key from the
+admin panel and pass it into your client initialization function.
+
+
+
+
+
+
+
+
+ ```csharp
+ var url = Environment.GetEnvironmentVariable("SUPABASE_URL");
+ var key = Environment.GetEnvironmentVariable("SUPABASE_KEY");
+
+ var options = new Supabase.SupabaseOptions
+ {
+ AutoConnectRealtime = true
+ };
+
+ var supabase = new Supabase.Client(url, key, options);
+ await supabase.InitializeAsync();
+ ```
+
+
+
+
+
+ ```csharp
+ public static MauiApp CreateMauiApp()
+ {
+ // ...
+ var builder = MauiApp.CreateBuilder();
+
+ var url = Environment.GetEnvironmentVariable("SUPABASE_URL");
+ var key = Environment.GetEnvironmentVariable("SUPABASE_KEY");
+ var options = new SupabaseOptions
+ {
+ AutoRefreshToken = true,
+ AutoConnectRealtime = true,
+ SessionHandler = new SupabaseSessionHandler()
+ };
+
+ // Note the creation as a singleton.
+ builder.Services.AddSingleton(provider => new Supabase.Client(url, key, options));
+ }
+ ```
+
+
+
+
+
+
diff --git a/apps/docs/docs/reference/csharp/installing.mdx b/apps/docs/docs/reference/csharp/installing.mdx
new file mode 100644
index 00000000000..e274af8e280
--- /dev/null
+++ b/apps/docs/docs/reference/csharp/installing.mdx
@@ -0,0 +1,33 @@
+---
+id: installing
+title: 'Installing & Initialization'
+slug: installing
+custom_edit_url: https://github.com/supabase/supabase/edit/master/web/spec/supabase.yml
+---
+
+### Install from NuGet
+
+
+
+
+ You can install Supabase package from [nuget.org](https://www.nuget.org/packages/supabase-csharp/)
+
+
+
+
+
+
+
+
+ ```sh Terminal
+ dotnet add package supabase-csharp
+ ```
+
+
+
+
+
+
diff --git a/apps/docs/docs/reference/csharp/introduction.mdx b/apps/docs/docs/reference/csharp/introduction.mdx
new file mode 100644
index 00000000000..6c699314937
--- /dev/null
+++ b/apps/docs/docs/reference/csharp/introduction.mdx
@@ -0,0 +1,20 @@
+---
+id: introduction
+title: Introduction
+hideTitle: true
+---
+
+
+
+
+
C# Client Library
+
supabase-csharp
+
+
+
+
+ This reference documents every object and method available in Supabase's C#
+ library, [supabase-csharp](https://www.nuget.org/packages/supabase-csharp). You can
+ use supabase-csharp to interact with your Postgres database, listen to database changes, invoke
+ Deno Edge Functions, build login and user management functionality, and manage large files.
+
diff --git a/apps/docs/docs/reference/csharp/v0/release-notes.mdx b/apps/docs/docs/reference/csharp/v0/release-notes.mdx
new file mode 100644
index 00000000000..2ecb0ef3990
--- /dev/null
+++ b/apps/docs/docs/reference/csharp/v0/release-notes.mdx
@@ -0,0 +1,6 @@
+---
+id: release-notes
+title: Release Notes
+---
+
+## dart v0 this is the release notes file.
diff --git a/apps/docs/docs/reference/javascript/upgrade-guide.mdx b/apps/docs/docs/reference/javascript/upgrade-guide.mdx
deleted file mode 100644
index 282d66d4966..00000000000
--- a/apps/docs/docs/reference/javascript/upgrade-guide.mdx
+++ /dev/null
@@ -1,439 +0,0 @@
----
-id: upgrade-guide
-title: Upgrade to supabase-js v2
-description: 'Learn how to upgrade to supabase-js v2.'
----
-
-import Tabs from '@theme/Tabs'
-import TabItem from '@theme/TabItem'
-
-supabase-js v2 focuses on "quality-of-life" improvements for developers and addresses some of the largest pain points in v1. v2 includes type support, a rebuilt Auth library with async methods, improved errors, and more.
-
-No new features will be added to supabase-js v1 , but we'll continuing merging security fixes to v1, with maintenance patches for the next 3 months.
-
-## Upgrade the client library
-
-```sh
-npm install @supabase/supabase-js@2
-```
-
-_Optionally_ if you are using custom configuration with `createClient` then follow below:
-
-
-
-
-```ts title=src/supabaseClient.ts
-const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
- schema: 'custom',
- persistSession: false,
-})
-```
-
-
-
-
-```ts title=src/supabaseClient.ts
-const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
- db: {
- schema: 'custom',
- },
- auth: {
- persistSession: true,
- },
-})
-```
-
-
-
-
-Read more about the [constructor options](/docs/reference/javascript/release-notes#explicit-constructor-options).
-
-### Auth methods
-
-The signIn() method has been deprecated in favor of more explicit method signatures to help with type hinting. Previously it was difficult for developers to know what they were missing (e.g., a lot of developers didn't realize they could use passwordless magic links).
-
-#### Sign in with email and password
-
-
-
-
-```ts
-const { user, error } = await supabase.auth.signIn({ email, password })
-```
-
-
-
-
-```ts
-const {
- data: { user },
- error,
-} = await supabase.auth.signInWithPassword({ email, password })
-```
-
-
-
-
-#### Sign in with magic link
-
-
-
-
-```ts
-const { error } = await supabase.auth.signIn({ email })
-```
-
-
-
-
-```ts
-const { error } = await supabase.auth.signInWithOtp({ email })
-```
-
-
-
-
-#### Sign in with a third-party provider
-
-
-
-
-```ts
-const { error } = await supabase.auth.signIn({ provider })
-```
-
-
-
-
-```ts
-const { error } = await supabase.auth.signInWithOAuth({ provider })
-```
-
-
-
-
-#### Sign in with phone
-
-
-
-
-```ts
-const { error } = await supabase.auth.signIn({ phone, password })
-```
-
-
-
-
-```ts
-const { error } = await supabase.auth.signInWithPassword({ phone, password })
-```
-
-
-
-
-#### Sign in with phone using OTP
-
-
-
-
-```ts
-const { error } = await supabase.auth.api.sendMobileOTP(phone)
-```
-
-
-
-
-```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 })
-```
-
-
-
-
-#### Reset password for email
-
-
-
-
-```ts
-const { data, error } = await supabase.auth.api.resetPasswordForEmail(email)
-```
-
-
-
-
-```ts
-const { data, error } = await supabase.auth.resetPasswordForEmail(email)
-```
-
-
-
-
-#### Get the user's current session
-
-
-
-
-```ts
-const session = supabase.auth.session()
-```
-
-
-
-
-```ts
-const {
- data: { session },
-} = await supabase.auth.getSession()
-```
-
-
-
-
-#### Get the logged-in user
-
-
-
-
-```ts
-const user = supabase.auth.user()
-```
-
-
-
-
-```ts
-const {
- data: { session },
-} = await supabase.auth.getSession()
-const { user } = session
-```
-
-
-
-
-#### Update user data for a logged-in user
-
-
-
-
-```ts
-const { user, error } = await supabase.auth.update({ attributes })
-```
-
-
-
-
-```ts
-const {
- data: { user },
- error,
-} = await supabase.auth.updateUser({ attributes })
-```
-
-
-
-
-#### Use a custom `access_token` JWT with Supabase
-
-
-
-
-```ts
-const { user, error } = supabase.auth.setAuth(access_token)
-```
-
-
-
-
-```ts
-const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
- global: {
- headers: {
- Authorization: `Bearer ${access_token}`,
- },
- },
-})
-```
-
-
-
-
-#### Cookie methods
-
-The cookie-related methods like `setAuthCookie` and `getUserByCookie` have been removed.
-
-For Next.js you can use the [Auth Helpers](https://supabase.com/docs/guides/auth/auth-helpers/nextjs) to help you manage cookies.
-If you can't use the Auth Helpers, you can use [server-side rendering](https://supabase.com/docs/guides/auth/server-side-rendering).
-
-Some the [PR](https://github.com/supabase/gotrue-js/pull/340) for additional background information.
-
-### Data methods
-
-`.insert()` / `.upsert()` / `.update()` / `.delete()` don't return rows by default: [PR](https://github.com/supabase/postgrest-js/pull/276).
-
-Previously, these methods return inserted/updated/deleted rows by default (which caused [some confusion](https://github.com/supabase/supabase/discussions/1548)), and you can opt to not return it by specifying `returning: 'minimal'`. Now the default behavior is to not return rows. To return inserted/updated/deleted rows, add a `.select()` call at the end.
-
-#### Insert and return data
-
-
-
-
-```ts
-const { data, error } = await supabase.auth.insert({ new_data })
-```
-
-
-
-
-```ts
-const { data, error } = await supabase.auth.insert({ new_data }).select()
-```
-
-
-
-
-#### Update and return data
-
-
-
-
-```ts
-const { data, error } = await supabase.auth.update({ new_data }).eq('id', id)
-```
-
-
-
-
-```ts
-const { data, error } = await supabase.auth.update({ new_data }).eq('id', id).select()
-```
-
-
-
-
-### Realtime methods
-
-#### Subscribe
-
-
-
-
-```ts
-const userListener = supabase
- .from('users')
- .on('*', (payload) => handleAllEventsPayload(payload.new))
- .subscribe()
-```
-
-
-
-
-```ts
-const userListener = supabase
- .channel('public:user')
- .on('postgres_changes', { event: '*', schema: 'public', table: 'user' }, (payload) =>
- handleAllEventsPayload()
- )
- .subscribe()
-```
-
-
-
-
-#### Unsubscribe
-
-
-
-
-```ts
-userListener.unsubscribe()
-```
-
-
-
-
-```ts
-supabase.removeChannel(userListener)
-```
-
-
-
diff --git a/apps/docs/docs/reference/javascript/v1/generating-types.mdx b/apps/docs/docs/reference/javascript/v1/generating-types.mdx
index 033cfdb9165..a46fd7ba70d 100644
--- a/apps/docs/docs/reference/javascript/v1/generating-types.mdx
+++ b/apps/docs/docs/reference/javascript/v1/generating-types.mdx
@@ -8,7 +8,7 @@ custom_edit_url: https://github.com/supabase/supabase/edit/master/spec/supabase_
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/api/generating-types).
+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
diff --git a/apps/docs/hooks/useFlag.ts b/apps/docs/hooks/useFlag.ts
new file mode 100644
index 00000000000..3e990f6b3a8
--- /dev/null
+++ b/apps/docs/hooks/useFlag.ts
@@ -0,0 +1,7 @@
+import { useContext } from 'react'
+import FlagContext from 'components/Flag/FlagContext'
+
+export function useFlag(name: string) {
+ const store: any = useContext(FlagContext)
+ return store[name]
+}
diff --git a/apps/docs/internals/getSecrets.js b/apps/docs/internals/getSecrets.js
new file mode 100644
index 00000000000..4782a3f1ed5
--- /dev/null
+++ b/apps/docs/internals/getSecrets.js
@@ -0,0 +1,35 @@
+// for internal supabase use only
+const fs = require('fs/promises')
+const AWS = require('aws-sdk')
+
+const secretName = 'local/docs'
+const region = 'ap-southeast-2'
+
+const getSecrets = async (name, region) => {
+ try {
+ AWS.config.update({ region })
+ const secretsmanager = new AWS.SecretsManager()
+
+ const data = await secretsmanager
+ .getSecretValue({
+ SecretId: name,
+ })
+ .promise()
+
+ if (!data.SecretString) {
+ throw new Error('Secrets not found')
+ }
+ return JSON.parse(data.SecretString)
+ } catch (err) {
+ console.log('Error getting secrets', err)
+ }
+}
+
+// gets secrets from secrets manager and writes it to .env.local file
+getSecrets(secretName, region).then(async (secrets) => {
+ let secretContent = ''
+ for (const [secretKey, secretValue] of Object.entries(secrets)) {
+ secretContent += `${secretKey}="${secretValue}"\n`
+ }
+ await fs.writeFile('.env.local', secretContent.trim())
+})
diff --git a/apps/docs/layouts/DefaultGuideLayout.tsx b/apps/docs/layouts/DefaultGuideLayout.tsx
index ea39047c4dd..d2c01704763 100644
--- a/apps/docs/layouts/DefaultGuideLayout.tsx
+++ b/apps/docs/layouts/DefaultGuideLayout.tsx
@@ -1,8 +1,15 @@
import { FC } from 'react'
+import { FooterHelpCalloutType } from '~/components/FooterHelpCallout'
import GuideLayout from './guides'
interface Props {
- meta: { title: string; description?: string; hide_table_of_contents?: boolean; video?: string }
+ meta: {
+ title: string
+ description?: string
+ hide_table_of_contents?: boolean
+ video?: string
+ footerHelpType?: FooterHelpCalloutType
+ }
children: any
toc?: any
currentPage?: string
diff --git a/apps/docs/layouts/DefaultLayout.tsx b/apps/docs/layouts/DefaultLayout.tsx
index db490d4c9e7..3e610896b8b 100644
--- a/apps/docs/layouts/DefaultLayout.tsx
+++ b/apps/docs/layouts/DefaultLayout.tsx
@@ -19,7 +19,7 @@ const Layout: FC = (props: Props) => {
return (
<>
- {props.meta?.title} | Supabase
+ {props.meta?.title} | Supabase Docs
diff --git a/apps/docs/layouts/SiteLayout.tsx b/apps/docs/layouts/SiteLayout.tsx
index eb017378efd..54f19ce5350 100644
--- a/apps/docs/layouts/SiteLayout.tsx
+++ b/apps/docs/layouts/SiteLayout.tsx
@@ -7,8 +7,10 @@ import TopNavBarRef from '~/components/Navigation/NavigationMenu/TopNavBarRef'
import FooterHelpCallout from '~/components/FooterHelpCallout'
import { memo, useEffect } from 'react'
+import Clippy from '~/components/Clippy/Clippy'
import Footer from '~/components/Navigation/Footer'
import { menuState, useMenuLevelId, useMenuMobileOpen } from '~/hooks/useMenuState'
+import { IS_PLATFORM } from '~/lib/constants'
const levelsData = {
home: {
@@ -29,7 +31,7 @@ const levelsData = {
},
functions: {
icon: '/docs/img/icons/menu/functions',
- name: 'Functions',
+ name: 'Edge Functions',
},
realtime: {
icon: '/docs/img/icons/menu/realtime',
@@ -39,6 +41,10 @@ const levelsData = {
icon: '/docs/img/icons/menu/storage',
name: 'Storage',
},
+ supabase_cli: {
+ icon: '/docs/img/icons/menu/reference-cli',
+ name: 'Supabase CLI',
+ },
platform: {
icon: '/docs/img/icons/menu/platform',
name: 'Platform',
@@ -71,6 +77,14 @@ const levelsData = {
icon: '/docs/img/icons/menu/reference-dart',
name: 'Dart Reference v0.0',
},
+ reference_csharp_v0: {
+ icon: '/docs/img/icons/menu/reference-csharp',
+ name: 'C# Reference v0.0',
+ },
+ reference_python_v2: {
+ icon: '/docs/img/icons/menu/reference-python',
+ name: 'Python Reference v2.0',
+ },
reference_cli: {
icon: '/docs/img/icons/menu/reference-cli',
name: 'CLI Reference',
@@ -322,11 +336,13 @@ const SiteLayout = ({ children }) => {
{children}
-
+
+ {IS_PLATFORM && }
+
)
diff --git a/apps/docs/layouts/guides/index.tsx b/apps/docs/layouts/guides/index.tsx
index 07ec7929195..5899d282fc7 100644
--- a/apps/docs/layouts/guides/index.tsx
+++ b/apps/docs/layouts/guides/index.tsx
@@ -6,9 +6,9 @@ import { FC, useEffect, useRef, useState } from 'react'
import { IconExternalLink } from 'ui'
import components from '~/components'
import { highlightSelectedTocItem } from '~/components/CustomHTMLElements/CustomHTMLElements.utils'
+import FooterHelpCallout, { FooterHelpCalloutType } from '~/components/FooterHelpCallout'
import GuidesTableOfContents from '~/components/GuidesTableOfContents'
import useHash from '~/hooks/useHash'
-import { getPageType } from '~/lib/helpers'
interface Props {
meta: {
@@ -17,6 +17,9 @@ interface Props {
hide_table_of_contents?: boolean
breadcrumb?: string
subtitle?: string
+ footerHelpType?: FooterHelpCalloutType
+ video?: string
+ canonical?: string
}
children: any
toc?: any
@@ -65,12 +68,28 @@ const Layout: FC = (props) => {
return (
<>
= (props) => {
],
}}
/>
-
= (props) => {
)}
+
>
)
}
diff --git a/apps/docs/lib/constants.ts b/apps/docs/lib/constants.ts
new file mode 100644
index 00000000000..7fff3fa4410
--- /dev/null
+++ b/apps/docs/lib/constants.ts
@@ -0,0 +1 @@
+export const IS_PLATFORM = process.env.NEXT_PUBLIC_IS_PLATFORM === 'true'
diff --git a/apps/docs/lib/mdx/handleRefStaticProps.tsx b/apps/docs/lib/mdx/handleRefStaticProps.tsx
index c9c47b4496d..c97245387f9 100644
--- a/apps/docs/lib/mdx/handleRefStaticProps.tsx
+++ b/apps/docs/lib/mdx/handleRefStaticProps.tsx
@@ -1,33 +1,12 @@
-import generateOldRefMarkdown from '~/lib/mdx/generateOldRefMarkdown'
import generateRefMarkdown from '~/lib/mdx/generateRefMarkdown'
-async function handleRefStaticProps(sections, params, librarypath, urlPath) {
+async function handleRefStaticProps(sections, librarypath) {
let markdownContent = await generateRefMarkdown(sections, librarypath)
- /*
- * old content generation
- * this is for grabbing to old markdown files
- */
-
- let slug
- if (params.slug.length > 1) {
- slug = `docs/reference${urlPath}/${params.slug.join('/')}`
- } else {
- slug = `docs/reference${urlPath}/${params.slug[0]}`
- }
-
- /*
- * handle old ref pages
- */
- if (process.env.NEXT_PUBLIC_NEW_DOCS === 'false') {
- const oldMarkdown = await generateOldRefMarkdown(slug)
- return oldMarkdown
- } else {
- return {
- props: {
- docs: markdownContent,
- },
- }
+ return {
+ props: {
+ docs: markdownContent,
+ },
}
}
diff --git a/apps/docs/lib/refGenerator/helpers.ts b/apps/docs/lib/refGenerator/helpers.ts
index 9c14a80167f..83864fa5540 100644
--- a/apps/docs/lib/refGenerator/helpers.ts
+++ b/apps/docs/lib/refGenerator/helpers.ts
@@ -218,6 +218,12 @@ export type enrichedOperation = OpenAPIV3.OperationObject & {
operationId: string
operation: string
responseList: []
+ description?: string
+ parameters?: []
+ responses?: {}
+ security?: []
+ summary?: string
+ tags?: []
}
export function gen_v3(spec: OpenAPIV3.Document, dest: string, { apiUrl }: { apiUrl: string }) {
diff --git a/apps/docs/middleware.ts b/apps/docs/middleware.ts
index d13ce4a442f..254486ccf06 100644
--- a/apps/docs/middleware.ts
+++ b/apps/docs/middleware.ts
@@ -3,7 +3,7 @@ import type { NextRequest } from 'next/server'
import isbot from 'isbot'
export function middleware(request: NextRequest) {
- const specs = ['javascript', 'dart']
+ const specs = ['javascript', 'dart', 'csharp']
let version = ''
if (request.url.includes('/v1/')) {
diff --git a/apps/docs/next.config.mjs b/apps/docs/next.config.mjs
index bf98d6d34fd..777b2e2224a 100644
--- a/apps/docs/next.config.mjs
+++ b/apps/docs/next.config.mjs
@@ -53,6 +53,7 @@ const nextConfig = {
'github.com',
'user-images.githubusercontent.com',
'raw.githubusercontent.com',
+ 'weweb-changelog.ghost.io',
],
},
experimental: {
@@ -84,6 +85,6 @@ const nextConfig = {
// next.config.js
export default () => {
// const plugins = [withMDX]/
- const plugins = [withTM(['ui', 'common']), withMDX, withYaml]
+ const plugins = [withTM(['ui', 'common', '@supabase/auth-helpers-nextjs']), withMDX, withYaml]
return plugins.reduce((acc, next) => next(acc), nextConfig)
}
diff --git a/apps/docs/package.json b/apps/docs/package.json
index 2abc71adb53..717e8d05d28 100644
--- a/apps/docs/package.json
+++ b/apps/docs/package.json
@@ -4,12 +4,14 @@
"private": true,
"scripts": {
"dev": "next dev --port 3001",
+ "dev:secrets:pull": "AWS_PROFILE=supabase-dev node internals/getSecrets.js",
"build": "npm run generate:all && next build",
"start": "next start",
"lint": "next lint",
"build:sitemap": "node ./internals/generate-sitemap.mjs",
+ "build:embeddings": "tsx scripts/generate-embeddings.ts",
"postbuild": "ts-node ./scripts/build-search.ts && node ./internals/generate-sitemap.mjs",
- "generate:all": "npm-run-all --parallel gen:api gen:cli gen:gotrue gen:storage gen:supabase-dart:v0 gen:supabase-dart:v1 gen:supabase-js:v1 gen:supabase-js:v2 gen:realtime",
+ "generate:all": "npm-run-all --parallel gen:api gen:cli gen:gotrue gen:storage gen:supabase-dart:v0 gen:supabase-dart:v1 gen:supabase-csharp:v0 gen:supabase-js:v1 gen:supabase-js:v2 gen:realtime",
"gen:api": "npm-run-all gen:api:usage",
"gen:api:usage": "ts-node ./generator/index.ts gen --type api --url https://api.supabase.com --input ../../spec/transforms/api_v0_openapi_deparsed.json --output ./docs/reference/api/generated/usage.mdx",
"gen:cli": "npm-run-all gen:cli:commands gen:cli:config",
@@ -25,6 +27,8 @@
"gen:supabase-dart:v0:ref": "ts-node ./generator/index.ts gen --type legacy --input ../../spec/supabase_dart_v0.yml --output ./docs/reference/dart/v0/generated",
"gen:supabase-dart:v1": "npm-run-all gen:supabase-dart:v1:ref",
"gen:supabase-dart:v1:ref": "ts-node ./generator/index.ts gen --type legacy --input ../../spec/supabase_dart_v1.yml --output ./docs/reference/dart/generated",
+ "gen:supabase-csharp:v0": "npm-run-all gen:supabase-csharp:v0:ref",
+ "gen:supabase-csharp:v0:ref": "ts-node ./generator/index.ts gen --type legacy --input ../../spec/supabase_csharp_v0.yml --output ./docs/reference/csharp/generated",
"gen:supabase-js:v1": "npm-run-all gen:supabase-js:v1:ref",
"gen:supabase-js:v1:ref": "ts-node ./generator/index.ts gen --type legacy --input ../../spec/supabase_js_v1.yml --output ./docs/reference/javascript/v1/generated",
"gen:supabase-js:v2": "npm-run-all gen:supabase-js:v2:ref",
@@ -40,8 +44,12 @@
"@mdx-js/react": "^1.6.22",
"@next/mdx": "^12.0.4",
"@radix-ui/react-accordion": "^1.0.1",
+ "@supabase/auth-helpers-nextjs": "^0.5.4",
+ "@supabase/auth-helpers-react": "^0.3.1",
+ "@supabase/supabase-js": "^2.4.1",
"algoliasearch": "^4.14.2",
"babel": "^6.23.0",
+ "clsx": "^1.2.1",
"common": "*",
"config": "*",
"gray-matter": "^4.0.3",
@@ -49,7 +57,12 @@
"jsrsasign": "^10.5.26",
"lodash": "^4.17.21",
"markdown-toc": "^1.2.0",
+ "mdast": "^3.0.0",
+ "mdast-util-from-markdown": "^1.2.0",
+ "mdast-util-mdx": "^2.0.0",
+ "mdast-util-to-markdown": "^1.5.0",
"mdx-mermaid": "2.0.0-rc3",
+ "micromark-extension-mdxjs": "^1.0.0",
"next": "12.3.2",
"next-compose-plugins": "^2.2.1",
"next-mdx-remote": "^4.1.0",
@@ -57,25 +70,30 @@
"next-plugin-yaml": "^1.0.1",
"next-seo": "^5.14.1",
"next-transpile-modules": "^9.0.0",
+ "openai": "^3.1.0",
"react": "17.0.2",
"react-copy-to-clipboard": "^5.0.2",
"react-dom": "17.0.2",
"react-intersection-observer": "^9.4.0",
"react-markdown": "^8.0.3",
"react-syntax-highlighter": "^15.3.1",
+ "rehype-slug": "^5.1.0",
"remark": "^14.0.2",
"remark-admonitions": "^1.2.1",
"remark-gfm": "^3.0.1",
"remark-slug": "^7.0.1",
"shiki": "^0.11.1",
+ "sse.js": "^0.6.1",
"ui": "*",
+ "unist-builder": "^3.0.1",
+ "unist-util-filter": "^4.0.1",
"valtio": "^1.7.6"
},
"devDependencies": {
- "dotenv": "^16.0.3",
"@types/node": "^17.0.12",
"@types/react": "17.0.39",
"config": "*",
+ "dotenv": "^16.0.3",
"ejs": "^3.1.8",
"eslint": "8.9.0",
"globby": "^12.0.2",
@@ -87,6 +105,7 @@
"sass": "^1.55.0",
"ts-node": "^10.9.1",
"tsconfig": "*",
+ "tsx": "^3.12.2",
"typescript": "^4.5.3"
}
}
diff --git a/apps/docs/pages/_app.tsx b/apps/docs/pages/_app.tsx
index 0736cff9c2b..4a3f65a5680 100644
--- a/apps/docs/pages/_app.tsx
+++ b/apps/docs/pages/_app.tsx
@@ -1,22 +1,28 @@
+import { createBrowserSupabaseClient } from '@supabase/auth-helpers-nextjs'
+import { SessionContextProvider } from '@supabase/auth-helpers-react'
+import { ThemeProvider } from 'common/Providers'
import { DefaultSeo } from 'next-seo'
import { useRouter } from 'next/router'
-import { useEffect } from 'react'
+import { useEffect, useState } from 'react'
import { AppPropsWithLayout } from 'types'
+import ClippyProvider from '~/components/Clippy/ClippyProvider'
import { SearchProvider } from '~/components/DocSearch'
import Favicons from '~/components/Favicons'
-import { ThemeProvider } from 'common/Providers'
import SiteLayout from '~/layouts/SiteLayout'
+import { post } from '~/lib/fetchWrappers'
+import { IS_PLATFORM } from '~/lib/constants'
import '../styles/algolia-search.scss'
import '../styles/ch.scss'
import '../styles/docsearch.scss'
import '../styles/main.scss?v=1.0.0'
import '../styles/new-docs.scss'
import '../styles/prism-okaidia.scss'
-import { post } from '~/lib/fetchWrappers'
function MyApp({ Component, pageProps }: AppPropsWithLayout) {
const router = useRouter()
+ const [supabase] = useState(() => (IS_PLATFORM ? createBrowserSupabaseClient() : undefined))
+
function telemetry(route: string) {
return post(`https://api.supabase.io/platform/telemetry/page`, {
referrer: document.referrer,
@@ -81,13 +87,29 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) {
cardType: 'summary_large_image',
}}
/>
-
-
-
-
-
-
-
+ {IS_PLATFORM ? (
+
+
+
+
+
+
+
+
+
+
+
+ ) : (
+
+
+
+
+
+
+
+
+
+ )}
>
)
}
diff --git a/apps/docs/pages/getting-started.mdx b/apps/docs/pages/getting-started.mdx
deleted file mode 100644
index ccb7790f15e..00000000000
--- a/apps/docs/pages/getting-started.mdx
+++ /dev/null
@@ -1,161 +0,0 @@
-import Layout from '~/layouts/DefaultGuideLayout'
-
-export const meta = {
- title: 'Overview',
- description: 'An overview of Supabase\'s architecture and product principles.',
-}
-
-Supabase is open source. We choose open source tools which are scalable and make them simple to use.
-
-Supabase is not a 1-to-1 mapping of Firebase. While we are building many of the features that Firebase offers, we are not going about it the same way:
-our technological choices are quite different; everything we use is open source; and wherever possible, we use and support existing tools rather than developing from scratch.
-
-Most notably, we use Postgres rather than a NoSQL store. This choice was deliberate. We believe that no other database offers the functionality required to compete with Firebase,
-while maintaining the scalability required to go beyond it.
-
-## Architecture
-
-Each Supabase project consists of several tools:
-
-
-
-### PostgreSQL (Database)
-
-PostgreSQL is the core of Supabase. We do not abstract the PostgreSQL database — you can access it and use it with full privileges. We simply provide tools which makes PostgreSQL as easy to use as Firebase.
-
-- Official Docs: [postgresql.org/docs](https://www.postgresql.org/docs/current/index.html)
-- Source code: [github.com/postgres/postgres](https://github.com/postgres/postgres) (mirror)
-- License: [PostgreSQL Licence](https://www.postgresql.org/about/licence/)
-- Language: C
-
-### Studio (Dashboard)
-
-An open source Dashboard for managing your database and services.
-
-- Official Docs: [Supabase docs](/docs)
-- Source code: [github.com/supabase/supabase](https://github.com/supabase/supabase/tree/master/studio)
-- License: [Apache 2](https://github.com/supabase/supabase/blob/master/LICENSE)
-- Language: TypeScript
-
-### GoTrue (Auth)
-
-A JWT-based API for managing users and issuing access tokens. This integrates with PostgreSQL's Row Level Security and the API servers.
-
-- Official Docs: [Supabase Auth reference docs](/docs/reference/auth)
-- Source code: [github.com/supabase/gotrue](https://github.com/supabase/gotrue)
-- License: [MIT](https://github.com/supabase/gotrue/blob/master/LICENSE)
-- Language: Go
-
-### PostgREST (API)
-
-A standalone web server that turns your PostgreSQL database directly into a RESTful API.
-We use this with our [pg_graphql](https://github.com/supabase/pg_graphql) extension to provide a GraphQL API.
-
-- Official Docs: [postgrest.org](https://postgrest.org/)
-- Source code: [github.com/PostgREST/postgrest](https://github.com/PostgREST/postgrest)
-- License: [MIT](https://github.com/PostgREST/postgrest/blob/main/LICENSE)
-- Language: Haskel
-
-### Realtime (API & multiplayer)
-
-A scalable websocket engine for managing user Presence, broadcasting messages, and streaming database changes.
-
-- Official Docs: [Supabase Realtime docs](/docs/guides/realtime)
-- Source code: [github.com/supabase/realtime](https://github.com/supabase/realtime)
-- License: [Apache 2](https://github.com/supabase/realtime/blob/main/LICENSE)
-- Language: Elixir
-
-### Storage API (large file storage)
-
-An S3-compatible object storage service that stores metadata in Postgres.
-
-- Official Docs: [Supabase Storage reference docs](/docs/reference/storage)
-- Source code: [github.com/supabase/storage-api](https://github.com/supabase/storage-api)
-- License: [Apache 2.0](https://github.com/supabase/storage-api/blob/master/LICENSE)
-- Language: NodeJS / TypeScript
-
-### Deno (Edge Functions)
-
-A modern runtime for JavaScript and TypeScript.
-
-- Official Docs: [deno.land](https://deno.land/)
-- Source code: [github.com/denoland/deno](https://github.com/denoland/deno)
-- License: [MIT](https://github.com/denoland/deno/blob/main/LICENSE.md)
-- Language: TypeScript / Rust
-
-### postgres-meta (Database management)
-
-A RESTful API for managing your Postgres. Fetch tables, add roles, and run queries.
-
-- Official Docs: [supabase.github.io/postgres-meta](https://supabase.github.io/postgres-meta/)
-- Source code: [github.com/supabase/postgres-meta](https://github.com/supabase/postgres-meta)
-- License: [Apache 2.0](https://github.com/supabase/postgres-meta/blob/master/LICENSE)
-- Language: NodeJS / TypeScript
-
-### PgBouncer
-
-A lightweight connection pooler for PostgreSQL. This is useful for connecting to Postgres when using Serverless functions.
-
-- Official Docs: [pgbouncer.org](http://www.pgbouncer.org/)
-- Source code: [pgbouncer/pgbouncer](https://github.com/pgbouncer/pgbouncer)
-- License: [ISC](https://github.com/pgbouncer/pgbouncer/blob/master/COPYRIGHT)
-- Language: C
-
-### Kong (API Gateway)
-
-A cloud-native API gateway, built on top of Nginx.
-
-- Official Docs: [docs.konghq.com](https://docs.konghq.com/)
-- Source code: [github.com/kong/kong](https://github.com/kong/kong)
-- License: [Apache 2.0](https://github.com/Kong/kong/blob/master/LICENSE)
-- Language: Lua
-
-## Product Principles
-
-It is our goal to provide an architecture that any large-scale company would design for themselves,
-and then provide tooling around that architecture that is easy-to-use for indie-developers and small teams.
-
-We use a series of principles to ensure that scalability and usability are never mutually exclusive:
-
-### Everything works in isolation
-
-Each system must work as a standalone tool with as few moving parts as possible.
-The litmus test for this is: "Can a user run this product with nothing but a Postgres database?"
-
-### Everything is integrated
-
-Supabase is composable. Even though every product works in isolation, each product on the platform needs to 10x the other products.
-For integration, each tool should expose an API and Webhooks.
-
-### Everything is extensible
-
-We're deliberate about adding a new tool, and prefer instead to extend an existing one.
-This is the opposite of many cloud providers whose product offering expands into niche use-cases. We provide _primitives_ for developers, which allow them to achieve any goal.
-Less, but better.
-
-### Everything is portable
-
-To avoid lock-in, we make it easy to migrate in and out. Our cloud offering is compatible with our self-hosted product.
-We use existing standards to increase portability (like pg_dump an CSV files). If a new standard emerges which competes with a "Supabase" approach, we will deprecate the approach in favor of the standard.
-This forces us compete on experience. We aim to be the best Postgres hosting service.
-
-### Play the long game
-
-We sacrifice short-term wins for long-term gains. For example, it is tempting to run a fork of Postgres with additional functionality which only our customers need.
-Instead, we prefer to support efforts to upstream missing functionality so that the entire community benefits. This has the additional benefit of ensuring portability and longevity.
-
-### Build for developers
-
-"Developers" are a specific profile of user: they are _builders_.
-When assessing impact as a function of effort, developers have a large efficiency due to the type of products and systems they can build.
-As the profile of a developer changes over time, Supabase will continue to evolve the product to fit this evolving profile.
-
-### Support existing tools
-
-Supabase supports existing tools and communities wherever possible. Supabase is more like a "community of communities" - each tool typically has its own community
-which we work with.
-Open source is something we approach [collaboratively](https://supabase.com/blog/supabase-series-b#giving-back): we employ maintainers, sponsor projects, invest in businesses, and develop our own open source tools.
-
-export const Page = ({ children }) =>
-
-export default Page
diff --git a/apps/docs/pages/guides/auth/auth-captcha.mdx b/apps/docs/pages/guides/auth/auth-captcha.mdx
index 32945d8071d..6aaee2f4388 100644
--- a/apps/docs/pages/guides/auth/auth-captcha.mdx
+++ b/apps/docs/pages/guides/auth/auth-captcha.mdx
@@ -4,6 +4,7 @@ export const meta = {
id: 'auth-captcha',
title: 'Enable Captcha Protection',
description: 'Add Captcha Protection to your Supabase project',
+ video: 'https://www.youtube.com/v/em1cpOAXknM',
}
Supabase provides you with the option of adding captcha to your sign-in, sign-up, and password reset forms. This keeps your website safe from bots and malicious scripts. Supabase authentication has support for [hCaptcha](https://www.hcaptcha.com/).
diff --git a/apps/docs/pages/guides/auth/auth-helpers.mdx b/apps/docs/pages/guides/auth/auth-helpers.mdx
index 56115933b9a..b9573fd1609 100644
--- a/apps/docs/pages/guides/auth/auth-helpers.mdx
+++ b/apps/docs/pages/guides/auth/auth-helpers.mdx
@@ -2,7 +2,7 @@ import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'index',
- title: 'Auth Helpers',
+ title: 'Auth Helpers Overview',
description: 'A collection of framework-specific Auth utilities for working with Supabase.',
sidebar_label: 'Overview',
}
diff --git a/apps/docs/pages/guides/auth/auth-helpers/auth-ui.mdx b/apps/docs/pages/guides/auth/auth-helpers/auth-ui.mdx
index 1d07fb9db94..273b108db5b 100644
--- a/apps/docs/pages/guides/auth/auth-helpers/auth-ui.mdx
+++ b/apps/docs/pages/guides/auth/auth-helpers/auth-ui.mdx
@@ -34,10 +34,7 @@ Pass `supabaseClient` from `@supabase/supabase-js` as a prop to the component.
import { createClient } from '@supabase/supabase-js'
import { Auth } from '@supabase/auth-ui-react'
-const supabase = createClient(
- '',
- ''
-)
+const supabase = createClient('', '')
const App = () =>
```
@@ -75,20 +72,29 @@ The Auth component also supports login with [offical social providers](../../aut
import { createClient } from '@supabase/supabase-js'
import { Auth, ThemeSupa } from '@supabase/auth-ui-react'
-const supabase = createClient(
- '',
- ''
-)
+const supabase = createClient('', '')
const App = () => (
)
```
+### Supported Views
+
+The Auth component is currently shipped with the following views:
+
+- [Email Login](../auth-email)
+- [Magic Link login](../auth-magic-link)
+- [Social Login](../social-login)
+- Update password
+- Forgotten password
+
+We are planning on adding more views in the future. Follow along on that [repo](https://github.com/supabase/auth-ui).
+
## Customization
There are several ways to customize Auth UI:
@@ -161,10 +167,7 @@ Auth UI themes can be overridden using variable tokens. See the [list of variabl
import { createClient } from '@supabase/supabase-js'
import { Auth, ThemeSupa } from '@supabase/auth-ui-react'
-const supabase = createClient(
- '',
- ''
-)
+const supabase = createClient('', '')
const App = () => (
',
- ''
-)
+const supabase = createClient('', '')
const App = () => (
',
- ''
-)
+const supabase = createClient('', '')
const App = () => (
',
- ''
-)
+const supabase = createClient('', '')
const App = () => (
For a complete implementation example, check out [this repo](https://github.com/supabase/auth-helpers/tree/main/examples/nextjs-server-components).
+
> To learn more about fetching and caching Supabase data with Next.js 13 Server Components, check out our [blog](/blog/fetching-and-caching-supabase-data-in-next-js-server-components) or [live stream](https://www.youtube.com/watch?v=QH0P5xZt5wY).
## Install the Next.js helper library
@@ -52,6 +54,8 @@ NEXT_PUBLIC_SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY
## Creating a Supabase Client
+### Server-side
+
-Create a new file at `/utils/supabase-browser.js` and populate with the following:
-
-```js title="/utils/supabase-browser.js"
-import { createBrowserSupabaseClient } from '@supabase/auth-helpers-nextjs'
-
-export default createBrowserSupabaseClient()
-```
-
-This will be used any time we need to create a Supabase client _client-side_ - in `useEffect`, for example.
-
Create a new file at `/utils/supabase-server.js` and populate with the following:
```js title="/utils/supabase-server.js"
import { headers, cookies } from 'next/headers'
import { createServerComponentSupabaseClient } from '@supabase/auth-helpers-nextjs'
-export default () =>
+export const createClient = () =>
createServerComponentSupabaseClient({
headers,
cookies,
@@ -85,33 +79,19 @@ export default () =>
> This needs to export a function, as the headers and cookies are not populated with values until the Server Component is requesting data.
-This will be used any time we need to create a Supabase client _server-side_ - in a Server Component, for example.
-
-Create a new file at `/utils/supabase-browser.ts` and populate with the following:
-
-```ts
-import { createBrowserSupabaseClient } from '@supabase/auth-helpers-nextjs'
-import { Database } from '../lib/database.types'
-
-export default createBrowserSupabaseClient()
-```
-
-> TypeScript types can be [generated with the Supabase CLI](https://supabase.com/docs/reference/javascript/typescript-support) and passed to `createBrowserSupabaseClient` to add type support to the Supabase client.
-
-This will be used any time we need to create a Supabase client _client-side_ - in `useEffect`, for example.
-
Create a new file at `/utils/supabase-server.ts` and populate with the following:
```ts title="/utils/supabase-server.ts"
import { headers, cookies } from 'next/headers'
import { createServerComponentSupabaseClient } from '@supabase/auth-helpers-nextjs'
-import { Database } from '../lib/database.types'
-export default () =>
+import type { Database } from '../lib/database.types'
+
+export const createClient = () =>
createServerComponentSupabaseClient({
headers,
cookies,
@@ -122,14 +102,14 @@ export default () =>
> This needs to export a function, as the headers and cookies are not populated with values until the Server Component is requesting data.
-This will be used any time we need to create a Supabase client _server-side_ - in a Server Component, for example.
-
-## Middleware
+This will be used any time we need to create a Supabase client _server-side_ - in a Server Component, for example.
-Middleware runs before every route declared in the `matcher` array. Since we don't have access to set cookies or headers from Server Components, we need to create a Middleware Supabase client and refresh the user's session by calling `getSession()`.
+Next, we need a middleware file to refresh the user's session on navigation.
+
+> If you were using Middleware prior to 12.2, see the [upgrade guide](https://nextjs.org/docs/messages/middleware-upgrade-guide).
-Create a new file at `/app/middleware.js` and populate with the following:
+Create a new `middleware.js` file at the same level as your `app` (in the root or `src` directory) and populate with the following:
```jsx title="middleware.js"
import { createMiddlewareSupabaseClient } from '@supabase/auth-helpers-nextjs'
@@ -156,27 +136,25 @@ export async function middleware(req) {
return res
}
-
-export const config = {
- matcher: ['/optional-session', '/required-session', '/realtime'],
-}
```
-Create a new file at `/app/middleware.ts` and populate with the following:
+Create a new `middleware.ts` file at the same level as your `app` (in the root or `src` directory) and populate with the following:
```tsx title="middleware.ts"
import { createMiddlewareSupabaseClient } from '@supabase/auth-helpers-nextjs'
import { NextResponse } from 'next/server'
+
import type { NextRequest } from 'next/server'
+import type { Database } from '../lib/database.types'
export async function middleware(req: NextRequest) {
const res = NextResponse.next()
- const supabase = createMiddlewareSupabaseClient({ req, res })
+ const supabase = createMiddlewareSupabaseClient({ req, res })
const {
data: { session },
@@ -184,20 +162,34 @@ export async function middleware(req: NextRequest) {
return res
}
-
-export const config = {
- matcher: ['/optional-session', '/required-session', '/realtime'],
-}
```
+> TypeScript types can be [generated with the Supabase CLI](https://supabase.com/docs/reference/javascript/typescript-support) and passed to `createMiddlewareSupabaseClient` to add type support to the Supabase client.
+
-> Any Server Component route that uses a Supabase client must be added to this middleware's `matcher` array. Without this, the Server Component may try to make a request to Supabase with an expired `access_token`.
+We can now use our server-side Supabase client to fetch data in Server Components.
-## Supabase Listener
+```jsx
+import 'server-only'
-We need to set up a listener to fetch fresh data whenever our user logs in or out.
+import { createClient } from '../../utils/supabase-server'
+
+// do not cache this page
+export const revalidate = 0
+
+export default async function ServerComponent() {
+ const supabase = createClient()
+ const { data } = await supabase.from('posts').select('*')
+
+ return
{JSON.stringify({ data }, null, 2)}
+}
+```
+
+### Client-side
+
+We still need a Supabase instance client-side for authentication and realtime subscriptions. It is important, when using Supabase client-side, to have a single instance of a client. We can share this singleton instance across our components using providers and React context.
-Create a `/components/supabase-listener.jsx` file and add the following:
+Create a new file at `/utils/supabase-browser.js` and populate with the following:
+
+```js title="/utils/supabase-browser.js"
+import { createBrowserSupabaseClient } from '@supabase/auth-helpers-nextjs'
+export const createClient = () => createBrowserSupabaseClient()
+```
+
+
+
+
+
+Create a new file at `/utils/supabase-browser.ts` and populate with the following:
+
+```ts
+import { createBrowserSupabaseClient } from '@supabase/auth-helpers-nextjs'
+import { Database } from '../lib/database.types'
+
+export const createClient = () => createBrowserSupabaseClient()
+```
+
+> TypeScript types can be [generated with the Supabase CLI](https://supabase.com/docs/reference/javascript/typescript-support) and passed to `createBrowserSupabaseClient` to add type support to the Supabase client.
+
+
+
+
+
+
+
+Next, we need to create a single instance of Supabase to use client-side. Let's create a new Provider for Supabase at `/components/supabase-provider.jsx` and populate with the following:
+
+```jsx title=components/supabase-provider.jsx
+'use client'
+
+import { createContext, useContext, useState } from 'react'
+import { createClient } from '../utils/supabase-browser'
+
+const Context = createContext()
+
+export default function SupabaseProvider({ children }) {
+ const [supabase] = useState(() => createClient())
+
+ useEffect(() => {
+ const { data: { subscription } } = supabase.auth.onAuthStateChange((event, session) => {
+ if (session?.access_token !== accessToken) {
+ router.refresh()
+ }
+ })
+
+ return () => subscription.unsubscribe()
+ }, [accessToken])
+
+ return (
+
+ <>{children}>
+
+ )
+}
+
+export const useSupabase = () => useContext(Context)
+```
+
+
+
+
+
+Next, we need to create a single instance of Supabase to use client-side. Let's create a new Provider for Supabase at `/components/supabase-provider.tsx` and populate with the following:
+
+```tsx title=components/supabase-provider.tsx
+'use client'
+
+import { createContext, useContext, useState } from 'react'
+import { createClient } from '../utils/supabase-browser'
+
+import type { SupabaseClient } from '@supabase/auth-helpers-nextjs'
+import type { Database } from '../lib/database.types'
+
+type SupabaseContext = {
+ supabase: SupabaseClient
+}
+
+const Context = createContext(undefined)
+
+export default function SupabaseProvider({ children }: { children: React.ReactNode }) {
+ const [supabase] = useState(() => createClient())
+
+ return (
+
+ <>{children}>
+
+ )
+}
+
+export const useSupabase = () => {
+ let context = useContext(Context);
+ if (context === undefined) {
+ throw new Error("useSupabase must be used inside SupabaseProvider");
+ } else {
+ return context;
+ }
+}
+```
+
+> TypeScript types can be [generated with the Supabase CLI](https://supabase.com/docs/reference/javascript/typescript-support) and passed to `createBrowserSupabaseClient` to add type support to the Supabase client.
+
+
+
+
+We need to set up a listener to fetch fresh data whenever our user logs in or out. For this we need to check whether our client and server sessions match. Let's start by installing the `server-only` package.
+
+```bash
+npm install server-only
+```
+
+This will ensure that any component that imports this package will be a Server Component, and excluded from the browser bundle.
+
+Next, let's modify our root layout to fetch the user's session, wrap our application in our Supabase Provider, and pass the server access token as a prop to the `` component (we will create this next).
+
+
+
+
+```jsx title=app/layout.jsx
+import 'server-only'
+
+import SupabaseListener from '../components/supabase-listener'
+import SupabaseProvider from '../components/supabase-provider'
+import './globals.css'
+import { createClient } from '../utils/supabase-server'
+
+// do not cache this layout
+export const revalidate = 0
+
+export default async function RootLayout({ children }) {
+ const supabase = createClient()
+
+ const {
+ data: { session },
+ } = await supabase.auth.getSession()
+
+ return (
+
+ {/*
+ will contain the components returned by the nearest parent
+ head.tsx. Find out more at https://beta.nextjs.org/docs/api-reference/file-conventions/head
+ */}
+
+
+
+
+ {children}
+
+
+
+ )
+}
+```
+
+
+
+
+
+```tsx title=app/layout.tsx
+import 'server-only'
+
+import SupabaseListener from '../components/supabase-listener'
+import SupabaseProvider from '../components/supabase-provider'
+import './globals.css'
+import { createClient } from '../utils/supabase-server'
+
+// do not cache this layout
+export const revalidate = 0
+
+export default async function RootLayout({ children }: { children: React.ReactNode }) {
+ const supabase = createClient()
+
+ const {
+ data: { session },
+ } = await supabase.auth.getSession()
+
+ return (
+
+ {/*
+ will contain the components returned by the nearest parent
+ head.tsx. Find out more at https://beta.nextjs.org/docs/api-reference/file-conventions/head
+ */}
+
+
+
+
+ {children}
+
+
+
+ )
+}
+```
+
+
+
+
+And now create our Supabase listener component that uses the singleton Supabase instance to listen for auth changes.
+
+
+
```jsx title="/components/supabase-listener.jsx"
'use client'
import { useRouter } from 'next/navigation'
import { useEffect } from 'react'
-import supabase from '../utils/supabase-browser'
+import { useSupabase } from './supabase-provider'
-export default function SupabaseListener({ accessToken }) {
+export default function SupabaseListener({ serverAccessToken }) {
+ const { supabase } = useSupabase()
const router = useRouter()
useEffect(() => {
- supabase.auth.onAuthStateChange((event, session) => {
- if (session?.access_token !== accessToken) {
+ const {
+ data: { subscription },
+ } = supabase.auth.onAuthStateChange((event, session) => {
+ if (session?.access_token !== serverAccessToken) {
router.refresh()
}
})
- }, [accessToken])
+
+ return () => {
+ subscription.unsubscribe()
+ }
+ }, [serverAccessToken, router, supabase])
return null
}
```
-> `use client` tells Next.js that this is a [Client Component](https://beta.nextjs.org/docs/rendering/server-and-client-components#client-components). Only Client Components can use hooks like `useEffect` and `useRouter`.
-
-The function we pass to `onAuthStateChange` is automatically called by Supabase whenever a user's session changes. This component takes an `accessToken` prop, which will be the server's state for our user (we'll set this up next). If the `accessToken` from the server and the new `access_token` do not match then the client and server are out of sync, therefore, we want to reload the active route.
-
-Lastly, fetch the server-side session in the `RootLayout` and pass it to our new `` component.
-
-```jsx title="/app/layout.jsx"
-import SupabaseListener from '../components/supabase-listener'
-import createClient from '../utils/supabase-server'
-
-export default async function RootLayout({ children }) {
- const supabase = createClient()
-
- const {
- data: { session },
- } = await supabase.auth.getSession()
-
- return (
- // html and head section omitted
-
-
- {children}
-
- )
-}
-```
-
-We don't want Next.js to cache this `session` value, so we need to export a `revalidate` value of `0` in the layout file.
-
-```jsx
-export const revalidate = 0
-```
-
-We also want to tell Next.js to explicitly exclude this component's code from the client bundle by installing the `server-only` package:
-
-```bash
-npm install server-only
-```
-
-And importing it at the top of our component.
-
-```jsx
-import 'server-only'
-```
-
-The entire Layout component should look something like this:
-
-```jsx title="/app/layout.jsx"
-import 'server-only'
-
-import SupabaseListener from '../components/supabase-listener'
-import './globals.css'
-import createClient from '../utils/supabase-server'
-
-// do not cache this layout
-export const revalidate = 0
-
-export default async function RootLayout({ children }) {
- const supabase = createClient()
-
- const {
- data: { session },
- } = await supabase.auth.getSession()
-
- return (
-
- {/*
- will contain the components returned by the nearest parent
- head.jsx. Find out more at https://beta.nextjs.org/docs/api-reference/file-conventions/head
- */}
-
-
-
- {children}
-
-
- )
-}
-```
-
-Create a `/components/supabase-listener.tsx` file and add the following:
-
```tsx title="/components/supabase-listener.tsx"
'use client'
import { useRouter } from 'next/navigation'
import { useEffect } from 'react'
-import supabase from '../utils/supabase-browser'
+import { useSupabase } from './supabase-provider'
-export default function SupabaseListener({ accessToken }: { accessToken?: string }) {
+export default function SupabaseListener({ serverAccessToken }: { serverAccessToken?: string }) {
+ const { supabase } = useSupabase()
const router = useRouter()
useEffect(() => {
- supabase.auth.onAuthStateChange((event, session) => {
- if (session?.access_token !== accessToken) {
+ const {
+ data: { subscription },
+ } = supabase.auth.onAuthStateChange((event, session) => {
+ if (session?.access_token !== serverAccessToken) {
router.refresh()
}
})
- }, [accessToken])
+
+ return () => {
+ subscription.unsubscribe()
+ }
+ }, [serverAccessToken, router, supabase])
return null
}
```
+
+
+
> `use client` tells Next.js that this is a [Client Component](https://beta.nextjs.org/docs/rendering/server-and-client-components#client-components). Only Client Components can use hooks like `useEffect` and `useRouter`.
-The function we pass to `onAuthStateChange` is automatically called by Supabase whenever a user's session changes. This component takes an `accessToken` prop, which will be the server's state for our user (we'll set this up next). If the `accessToken` from the server and the new `access_token` do not match then the client and server are out of sync, therefore, we want to reload the active route.
+The function we pass to `onAuthStateChange` is automatically called by Supabase whenever a user's session changes. This component takes an `serverAccessToken` prop, which is the server's state for our user. If the `serverAccessToken` and the new session's `access_token` do not match then the client and server are out of sync, therefore, we want to reload the active route.
-Lastly, fetch the server-side session in the `RootLayout` and pass it to our new `` component.
-
-```tsx title="/app/layout.tsx"
-import SupabaseListener from '../components/supabase-listener'
-import createClient from '../utils/supabase-server'
-
-export default async function RootLayout({ children }: { children: React.ReactNode }) {
- const supabase = createClient()
-
- const {
- data: { session },
- } = await supabase.auth.getSession()
-
- return (
- // html and head section omitted
-
-
- {children}
-
- )
-}
-```
-
-> Again, TypeScript types can be [generated with the Supabase CLI](https://supabase.com/docs/reference/javascript/typescript-support) and passed to `createServerComponentSupabaseClient` to add type support to the Supabase client.
-
-We don't want Next.js to cache this `session` value, so we need to export a `revalidate` value of `0`.
-
-```tsx
-export const revalidate = 0
-```
-
-We also want to tell Next.js to explicitly exclude this component's code from the client bundle by installing the `server-only` package:
-
-```bash
-npm install server-only
-```
-
-And importing it at the top of our component.
-
-```tsx
-import 'server-only'
-```
-
-The entire Layout component should look something like this:
-
-```tsx title="/app/layout.tsx"
-import 'server-only'
-
-import SupabaseListener from '../components/supabase-listener'
-import './globals.css'
-import createClient from '../utils/supabase-server'
-
-// do not cache this layout
-export const revalidate = 0
-
-export default async function RootLayout({ children }: { children: React.ReactNode }) {
- const supabase = createClient()
-
- const {
- data: { session },
- } = await supabase.auth.getSession()
-
- return (
-
- {/*
- will contain the components returned by the nearest parent
- head.tsx. Find out more at https://beta.nextjs.org/docs/api-reference/file-conventions/head
- */}
-
-
-
- {children}
-
-
- )
-}
-```
-
-
-
-
-Supabase can now be used in any Client or Server component.
-
-## Server Components
-
-Next.js recommends fetching data in Server Components whenever possible.
-
-
-
-
-```jsx
-import 'server-only'
-
-import createClient from '../../utils/supabase-server'
-
-// do not cache this page
-export const revalidate = 0
-
-export default async function ServerComponent() {
- const supabase = createClient()
- const { data } = await supabase.from('posts').select('*')
-
- return
{JSON.stringify({ data }, null, 2)}
-}
-```
-
-
-
-
-
-```tsx
-import 'server-only'
-
-import createClient from '../../utils/supabase-server'
-
-// do not cache this page
-export const revalidate = 0
-
-export default async function ServerComponent() {
- const supabase = createClient()
- const { data } = await supabase.from('posts').select('*')
-
- return
{JSON.stringify({ data }, null, 2)}
-}
-```
-
-
-
-
-## Client Components
-
-While Next.js recommend doing all data fetching in Server Components, we still need Supabase client-side for things like authentication and subscribing to realtime updates.
+Now we can use our `useSupabase` hook throughout our client-side components.
### Authentication
-We can call any of [Supabase's authentication methods](https://supabase.com/docs/reference/javascript/v1/auth-signin) - such as `supabase.auth.signInWithOAuth` - from a client component.
-
{
- const { error } = await supabase.auth.signInWithOAuth({
+ const { supabase, session } = useSupabase()
+
+ const handleEmailLogin = async () => {
+ await supabase.auth.signInWithPassword({
+ email: 'jon@supabase.com',
+ password: 'password',
+ })
+ }
+
+ const handleGitHubLogin = async () => {
+ await supabase.auth.signInWithOAuth({
provider: 'github',
})
-
- if (error) {
- console.log({ error })
- }
}
const handleLogout = async () => {
- const { error } = await supabase.auth.signOut()
-
- if (error) {
- console.log({ error })
- }
+ await supabase.auth.signOut()
}
return (
<>
- Login
+ Email Login
+ GitHub LoginLogout
>
)
@@ -533,30 +542,33 @@ export default function Login() {
```tsx title="/components/login.tsx"
'use client'
-import supabase from '../utils/supabase-browser'
+import { useSupabase } from './supabase-provider'
+// Supabase auth needs to be triggered client-side
export default function Login() {
- const handleLogin = async () => {
- const { error } = await supabase.auth.signInWithOAuth({
+ const { supabase, session } = useSupabase()
+
+ const handleEmailLogin = async () => {
+ await supabase.auth.signInWithPassword({
+ email: 'jon@supabase.com',
+ password: 'password',
+ })
+ }
+
+ const handleGitHubLogin = async () => {
+ await supabase.auth.signInWithOAuth({
provider: 'github',
})
-
- if (error) {
- console.log({ error })
- }
}
const handleLogout = async () => {
- const { error } = await supabase.auth.signOut()
-
- if (error) {
- console.log({ error })
- }
+ await supabase.auth.signOut()
}
return (
<>
- Login
+ Email Login
+ GitHub LoginLogout
>
)
@@ -586,10 +598,11 @@ Create a new file at `/app/realtime/posts.jsx` and populate with the following:
'use client'
import { useEffect, useState } from 'react'
-import supabase from '../../utils/supabase-browser'
+import { useSupabase } from '../../components/supabase-provider'
export default function Posts({ serverPosts }) {
const [posts, setPosts] = useState(serverPosts)
+ const { supabase } = useSupabase()
useEffect(() => {
setPosts(serverPosts)
@@ -606,14 +619,12 @@ export default function Posts({ serverPosts }) {
return () => {
supabase.removeChannel(channel)
}
- }, [serverPosts])
+ }, [supabase, setPosts, posts])
return
{JSON.stringify(posts, null, 2)}
}
```
-> The first `useEffect` is required to overwrite posts if the `serverPosts` prop changes.
-
This can now be used in a Server Component to subscribe to realtime updates.
Create a new file at `/app/realtime/page.jsx` and populate with the following:
@@ -621,7 +632,7 @@ Create a new file at `/app/realtime/page.jsx` and populate with the following:
```jsx title="/app/realtime/page.jsx"
import 'server-only'
-import createClient from '../../utils/supabase-server'
+import { createClient } from '../../utils/supabase-server'
import Posts from './posts'
// do not cache this page
@@ -645,13 +656,15 @@ Create a new file at `/app/realtime/posts.tsx` and populate with the following:
'use client'
import { useEffect, useState } from 'react'
-import { Database } from '../lib/database.types'
-import supabase from '../../utils/supabase-browser'
+import { useSupabase } from '../../components/supabase-provider'
+
+import type { Database } from '../../lib/database.types'
type Post = Database['public']['Tables']['posts']['Row']
export default function Posts({ serverPosts }: { serverPosts: Post[] }) {
const [posts, setPosts] = useState(serverPosts)
+ const { supabase } = useSupabase()
useEffect(() => {
setPosts(serverPosts)
@@ -668,13 +681,13 @@ export default function Posts({ serverPosts }: { serverPosts: Post[] }) {
return () => {
supabase.removeChannel(channel)
}
- }, [serverPosts])
+ }, [supabase, setPosts, posts])
return
{JSON.stringify(posts, null, 2)}
}
```
-> The first `useEffect` is required to overwrite posts if the `serverPosts` prop changes.
+> TypeScript types can be [generated with the Supabase CLI](https://supabase.com/docs/reference/javascript/typescript-support) and passed to `createServerSupabaseClient` to add type support to the Supabase client.
This can now be used in a Server Component to subscribe to realtime updates.
@@ -683,7 +696,7 @@ Create a new file at `/app/realtime/page.tsx` and populate with the following:
```tsx title="/app/realtime/page.tsx"
import 'server-only'
-import createClient from '../../utils/supabase-server'
+import { createClient } from '../../utils/supabase-server'
import Posts from './posts'
// do not cache this page
diff --git a/apps/docs/pages/guides/auth/auth-helpers/nextjs.mdx b/apps/docs/pages/guides/auth/auth-helpers/nextjs.mdx
index 76e169c2271..3b093bd91b4 100644
--- a/apps/docs/pages/guides/auth/auth-helpers/nextjs.mdx
+++ b/apps/docs/pages/guides/auth/auth-helpers/nextjs.mdx
@@ -584,7 +584,7 @@ export async function middleware(req: NextRequest) {
}
export const config = {
- matcher: '/middleware-protected',
+ matcher: '/middleware-protected/:path*',
}
```
@@ -594,7 +594,7 @@ export const config = {
To make these helpers more flexible as well as more maintainable and easier to upgrade for new versions of Next.js, we're stripping them down to the most useful part which is managing the cookies and giving you an authenticated supabase-js client in any environment (client, server, middleware/edge).
-Therefore we're marking the `withApiAuth`, `withPageAuth`, and `withMiddlewareAuth` higher order functions as deprectaed and they will be removed in the next **minor** release (v0.6.X).
+Therefore we're marking the `withApiAuth`, `withPageAuth`, and `withMiddlewareAuth` higher order functions as deprecated and they will be removed in the next **minor** release (v0.6.X).
Please follow the steps below to update your API routes, pages, and middleware handlers. Thanks!
@@ -782,7 +782,7 @@ export const config = {
### Migrating to v0.4.X and supabase-js v2
-With the update to `supabase-js` v2 the `auth` API routes are no longer required, therefore you can go ahead and delete your `auth` directory under the `/pages/api/` directory. Please refer to the [v2 migration guide](/docs/reference/javascript/upgrade-guide) for the full set of changes within supabase-js.
+With the update to `supabase-js` v2 the `auth` API routes are no longer required, therefore you can go ahead and delete your `auth` directory under the `/pages/api/` directory. Please refer to the [v2 migration guide](/docs/reference/javascript/v1/upgrade-guide) for the full set of changes within supabase-js.
The `/api/auth/logout` API route has been removed, please use the `signout` method instead:
diff --git a/apps/docs/pages/guides/auth/auth-helpers/sveltekit.mdx b/apps/docs/pages/guides/auth/auth-helpers/sveltekit.mdx
index e6d1d141100..7598037b2d8 100644
--- a/apps/docs/pages/guides/auth/auth-helpers/sveltekit.mdx
+++ b/apps/docs/pages/guides/auth/auth-helpers/sveltekit.mdx
@@ -39,7 +39,7 @@ yarn add @supabase/auth-helpers-sveltekit
### Configuration
-Set up the fillowing env vars. For local development you can set them in a `.env` file. See an [example](https://github.com/supabase/auth-helpers/blob/main/examples/sveltekit/.env.example).
+Set up the following env vars. For local development you can set them in a `.env` file. See an [example](https://github.com/supabase/auth-helpers/blob/main/examples/sveltekit/.env.example).
```bash
# Find these in your Supabase project settings > API
@@ -257,7 +257,7 @@ Wrap an Action to check that the user has a valid session. If they're not logged
```ts title=src/routes/posts/+page.server.ts
import type { Actions } from './$types'
import { getSupabase } from '@supabase/auth-helpers-sveltekit'
-import { error, invalid } from '@sveltejs/kit'
+import { error, fail } from '@sveltejs/kit'
export const actions: Actions = {
createPost: async (event) => {
@@ -276,7 +276,7 @@ export const actions: Actions = {
.insert({ content })
if (createPostError) {
- return invalid(500, {
+ return fail(500, {
supabaseErrorMessage: createPostError.message,
})
}
@@ -293,7 +293,7 @@ If you try to submit a form with the action `?/createPost` without a valid sessi
```ts
import type { Actions } from './$types'
-import { invalid, redirect } from '@sveltejs/kit'
+import { fail, redirect } from '@sveltejs/kit'
import { getSupabase } from '@supabase/auth-helpers-sveltekit'
import { AuthApiError } from '@supabase/supabase-js'
@@ -313,14 +313,14 @@ export const actions: Actions = {
if (error) {
if (error instanceof AuthApiError && error.status === 400) {
- return invalid(400, {
+ return fail(400, {
error: 'Invalid credentials.',
values: {
email,
},
})
}
- return invalid(500, {
+ return fail(500, {
error: 'Server error. Try again later.',
values: {
email,
diff --git a/apps/docs/pages/guides/auth/managing-user-data.mdx b/apps/docs/pages/guides/auth/managing-user-data.mdx
index 8d6e414eb05..bd5ae4f7db7 100644
--- a/apps/docs/pages/guides/auth/managing-user-data.mdx
+++ b/apps/docs/pages/guides/auth/managing-user-data.mdx
@@ -12,9 +12,7 @@ Even though Supabase provides an `auth.users` table, it can be helpful to create
## Creating user tables
-When you create tables to store user data, it's helpful to reference the `auth.users` table in the primary key. This ensures data integrity.
-
-For example, a `public.profiles` table might look like this:
+When you create tables to store user data, it's helpful to reference the `auth.users` table in the primary key to ensure data integrity. Also specify the `on delete cascade` clause when referencing `auth.users`. Omitting it may cause problems when deleting users. For example, a `public.profiles` table might look like this:
```sql
create table public.profiles (
@@ -28,9 +26,11 @@ create table public.profiles (
alter table public.profiles enable row level security;
```
-
+
-Make sure to specify the `on delete cascade` clause when referencing `auth.users`. Omitting it may cause problems when deleting users.
+Only use primary keys as [foreign key references](https://www.postgresql.org/docs/current/tutorial-fk.html) for schemas and tables like `auth.users` which are managed by Supabase. PostgreSQL lets you specify a foreign key reference for columns backed by a unique index (not necessarily primary keys).
+
+Primary keys are **guaranteed not to change**. Columns, indices, constraints or other database objects managed by Supabase **may change at any time** and you should be careful when referencing them directly.
@@ -97,7 +97,7 @@ If the trigger fails however, it could block the user sign ups - so make sure th
For example:
```sql
--- inserts a row into public.users
+-- inserts a row into public.profiles
create function public.handle_new_user()
returns trigger
language plpgsql
diff --git a/apps/docs/pages/guides/auth/overview.mdx b/apps/docs/pages/guides/auth/overview.mdx
index 97044096e13..2e6760545b4 100644
--- a/apps/docs/pages/guides/auth/overview.mdx
+++ b/apps/docs/pages/guides/auth/overview.mdx
@@ -1,5 +1,5 @@
import Layout from '~/layouts/DefaultGuideLayout'
-import NewContent from '~/pages/new/auth.mdx'
+
import { useRouter } from 'next/router'
export const meta = {
@@ -7,6 +7,7 @@ export const meta = {
title: 'Auth',
description: 'Use Supabase to Authenticate and Authorize your users.',
sidebar_label: 'Overview',
+ video: 'https://www.youtube.com/v/6ow_jW4epf8',
}
## Overview
@@ -43,13 +44,21 @@ You can authenticate your users in several ways:
We provide a suite of Providers and login methods, as well as [Auth helpers](/docs/guides/auth/auth-helpers/).
+#### Social Auth
+
-
+
+
+
+#### Phone Auth
+
+
+
### Configure third-party providers
-You can enable third-party providers with the click of a button by navigating to Authentication > Settings > Auth Providers and inputting your `Client ID` and `Secret` for each.
+You can enable third-party providers with the click of a button by navigating to Authentication > Providers > Auth Providers and inputting your `Client ID` and `Secret` for each.

@@ -115,7 +124,12 @@ Get started with our [Row Level Security Guides](/docs/guides/auth/row-level-sec
Authentication only gets you so far. When you need granular authorization rules, nothing beats PostgreSQL's [Row Level Security (RLS)](https://www.postgresql.org/docs/current/ddl-rowsecurity.html). Supabase makes it simple to turn RLS on and off.
### Policies
@@ -123,7 +137,12 @@ Authentication only gets you so far. When you need granular authorization rules,
[Policies](https://www.postgresql.org/docs/current/sql-createpolicy.html) are PostgreSQL's rule engine. They are incredibly powerful and flexible, allowing you to write complex SQL rules which fit your unique business needs.
With policies, your database becomes the rules engine. Instead of repetitively filtering your queries, like this ...
@@ -171,15 +190,14 @@ Supabase provides multiple endpoints to authenticate and manage your users:
When users sign up, Supabase assigns them a unique ID. You can reference this ID anywhere in your database. For example, you might create a `profiles` table referencing `id` in the `auth.users` table using a `user_id` field.
-export const Page = ({ children }) => {
- const router = useRouter()
- if (process.env.NEXT_PUBLIC_NEW_DOCS === 'true' && !router.asPath.includes('overview')) {
- return NewContent()
- }
- return
-}
+export const Page = ({ children }) =>
export default Page
diff --git a/apps/docs/pages/guides/auth/phone-login/twilio.mdx b/apps/docs/pages/guides/auth/phone-login/twilio.mdx
index 4bdc9ac11e4..be9da960411 100644
--- a/apps/docs/pages/guides/auth/phone-login/twilio.mdx
+++ b/apps/docs/pages/guides/auth/phone-login/twilio.mdx
@@ -4,6 +4,7 @@ export const meta = {
id: 'auth-twilio',
title: 'Phone Auth with Twilio',
description: 'How to set up and use Mobile OTP with Twilio and Supabase.',
+ video: 'https://www.youtube.com/v/akScoPO01bc',
}
## Overview
diff --git a/apps/docs/pages/guides/auth/row-level-security.mdx b/apps/docs/pages/guides/auth/row-level-security.mdx
index eca412cd214..1be866869e0 100644
--- a/apps/docs/pages/guides/auth/row-level-security.mdx
+++ b/apps/docs/pages/guides/auth/row-level-security.mdx
@@ -4,6 +4,7 @@ export const meta = {
id: 'row-level-security',
title: 'Row Level Security',
description: 'Secure your data using Postgres Row Level Security.',
+ video: 'https://www.youtube.com/v/Ow_Uzedfohk',
}
When you need granular authorization rules, nothing beats PostgreSQL's [Row Level Security (RLS)](https://www.postgresql.org/docs/current/ddl-rowsecurity.html).
diff --git a/apps/docs/pages/guides/auth/social-login/auth-azure.mdx b/apps/docs/pages/guides/auth/social-login/auth-azure.mdx
index d75a365b1d5..57f398456b0 100644
--- a/apps/docs/pages/guides/auth/social-login/auth-azure.mdx
+++ b/apps/docs/pages/guides/auth/social-login/auth-azure.mdx
@@ -74,14 +74,12 @@ When your user signs in, call [signInWithOAuth()](/docs/reference/javascript/aut
```js
async function signInWithAzure() {
- const { data, error } = await supabase.auth.signInWithOAuth(
- {
- provider: 'azure',
- },
- {
+ const { data, error } = await supabase.auth.signInWithOAuth({
+ provider: 'azure',
+ options: {
scopes: 'email',
- }
- )
+ },
+ })
}
```
diff --git a/apps/docs/pages/guides/resources/supabase-cli.mdx b/apps/docs/pages/guides/cli.mdx
similarity index 95%
rename from apps/docs/pages/guides/resources/supabase-cli.mdx
rename to apps/docs/pages/guides/cli.mdx
index ef861654950..a17186d3d76 100644
--- a/apps/docs/pages/guides/resources/supabase-cli.mdx
+++ b/apps/docs/pages/guides/cli.mdx
@@ -1,12 +1,9 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
- id: 'cli',
title: 'Supabase CLI',
description:
'The Supabase CLI provides tools to develop your project locally and deploy to the Supabase Platform.',
- sidebar_label: 'Overview',
- toc_max_heading_level: 2,
}
The Supabase CLI provides tools to develop your project locally and deploy to the Supabase Platform.
@@ -113,7 +110,8 @@ brew upgrade supabase
## See also
-- [Supabase CLI Reference](/docs/reference/cli/usage)
+- [Supabase CLI Reference](/docs/reference/cli/introduction)
+- [Supabase CLI Configuration](/docs/reference/cli/config)
- [Local Development](/docs/guides/cli/local-development)
- [Managing Environments](/docs/guides/cli/managing-environments)
diff --git a/apps/docs/pages/guides/resources/supabase-cli/local-development.mdx b/apps/docs/pages/guides/cli/local-development.mdx
similarity index 93%
rename from apps/docs/pages/guides/resources/supabase-cli/local-development.mdx
rename to apps/docs/pages/guides/cli/local-development.mdx
index eb4d4572a8b..1c8f21a9b05 100644
--- a/apps/docs/pages/guides/resources/supabase-cli/local-development.mdx
+++ b/apps/docs/pages/guides/cli/local-development.mdx
@@ -4,6 +4,7 @@ export const meta = {
id: 'local-development',
title: 'Local Development',
description: 'How to use Supabase on your local development machine.',
+ video: 'https://www.youtube.com/v/vyHyYpvjaks',
}
Learn how to use the Supabase CLI to develop your project locally and deploy to the Supabase Platform.
@@ -31,6 +32,12 @@ Make sure you have these installed on your local machine:
supabase login
```
+
+
+If you installed the Supabase CLI via NPM you may have to use `npx supabase login`.
+
+
+
## Initialize your project
Create a new folder for your project and start a new git repository:
@@ -145,10 +152,11 @@ Database changes are managed through "migrations." Database migrations are a com
For this guide, create a table called `employees`. In Supabase Studio, navigate to the **SQL Editor** page and run the following SQL command:
```sql
-create table employees (
- id integer primary key generated always as identity,
- name text
-);
+create table
+ employees (
+ id integer primary key generated always as identity,
+ name text
+ );
```
@@ -170,8 +178,9 @@ This creates a new migration named `supabase/migrations/_create_emplo
Use the seed script in `supabase/seed.sql` (created with [`supabase init`](/docs/reference/cli/usage#supabase-init)) to add sample data to the table.
```sql
--- in supabase/seed.sql
-insert into public.employees (name)
+ -- in supabase/seed.sql
+insert into
+ public.employees (name)
values
('Erlich Bachman'),
('Richard Hendricks'),
@@ -191,9 +200,9 @@ You should now see the contents of `employees` in Studio.
Use the [`reset`](/docs/reference/cli/usage#supabase-db-reset) command to revert any changes to the local database.
```sql
--- run on local database to make a change
-alter table employees
- add department text default 'Hooli';
+ -- run on local database to make a change
+alter table
+ employees add department text default 'Hooli';
```
Run the following command to reset the local database:
@@ -251,10 +260,6 @@ The local development environment is not as feature-complete as the Supabase Pla
- Logs are not supported through the interface (however you can access them through the Docker containers).
- You cannot update your project settings in the Dashboard—this must be done using the CLI.
-```
-
-```
-
export const Page = ({ children }) =>
export default Page
diff --git a/apps/docs/pages/guides/resources/supabase-cli/managing-environments.mdx b/apps/docs/pages/guides/cli/managing-environments.mdx
similarity index 99%
rename from apps/docs/pages/guides/resources/supabase-cli/managing-environments.mdx
rename to apps/docs/pages/guides/cli/managing-environments.mdx
index 5523819a3a9..25f91ce22ff 100644
--- a/apps/docs/pages/guides/resources/supabase-cli/managing-environments.mdx
+++ b/apps/docs/pages/guides/cli/managing-environments.mdx
@@ -4,6 +4,7 @@ export const meta = {
id: 'managing-environments',
title: 'Managing Environments',
description: 'How to deploy Supabase schema changes with a CI / CD pipeline.',
+ video: 'https://www.youtube.com/v/rOLyOsBR1Uc',
}
## Overview
@@ -215,8 +216,6 @@ jobs:
- uses: actions/checkout@v3
- uses: supabase/setup-cli@v1
- with:
- version: 1.0.0
- name: Start Supabase local development setup
run: supabase start
@@ -256,8 +255,6 @@ jobs:
- uses: actions/checkout@v3
- uses: supabase/setup-cli@v1
- with:
- version: 1.0.0
- run: |
supabase link --project-ref $STAGING_PROJECT_ID
@@ -289,8 +286,6 @@ jobs:
- uses: actions/checkout@v3
- uses: supabase/setup-cli@v1
- with:
- version: 1.0.0
- run: |
supabase link --project-ref $PRODUCTION_PROJECT_ID
diff --git a/apps/docs/pages/guides/database.mdx b/apps/docs/pages/guides/database.mdx
index 7efa2ad7513..d4667ed82a3 100644
--- a/apps/docs/pages/guides/database.mdx
+++ b/apps/docs/pages/guides/database.mdx
@@ -34,7 +34,10 @@ You don't have to be a database expert to start using Supabase. Our table view m
Dig into the relationships within your data.
### Clone tables
@@ -42,7 +45,12 @@ Dig into the relationships within your data.
You can duplicate your tables, just like you would inside a spreadsheet.
### The SQL Editor
@@ -50,7 +58,12 @@ You can duplicate your tables, just like you would inside a spreadsheet.
Supabase comes with a SQL Editor. You can also save your favorite queries to run later!
### Additional features
@@ -74,7 +87,12 @@ To expand the functionality of your Postgres database, you can use extensions.
You can enable Postgres extensions with the click of a button within the Supabase dashboard.
[Learn more](/docs/guides/database/extensions) about all the extensions provided on Supabase.
diff --git a/apps/docs/pages/guides/api.mdx b/apps/docs/pages/guides/database/api.mdx
similarity index 91%
rename from apps/docs/pages/guides/api.mdx
rename to apps/docs/pages/guides/database/api.mdx
index a6b8a018fd6..fc8f86a18ae 100644
--- a/apps/docs/pages/guides/api.mdx
+++ b/apps/docs/pages/guides/database/api.mdx
@@ -5,6 +5,7 @@ export const meta = {
title: 'Serverless APIs',
description: 'Auto-generating and Realtime APIs.',
sidebar_label: 'Overview',
+ video: 'https://www.youtube.com/v/rPAJJFdtPw0',
}
Supabase auto-generates three types of API directly from your database schema.
@@ -55,6 +56,7 @@ GraphQL in Supabase works through [pg_graphql](https://supabase.com/blog/pg-grap
Supabase provides a Realtime API using [Realtime](https://github.com/supabase/realtime). You can use this to listen to database changes over websockets.
Realtime leverages PostgreSQL's built-in logical replication. You can manage your Realtime API simply by managing Postgres publications.
+Go to your project's [Replication section](https://app.supabase.com/project/_/database/replication) to get started.
## Getting started
@@ -83,7 +85,12 @@ This creates a corresponding route `todos` which can accept `GET`, `POST`, `PATC
1. Click **Save**.
@@ -111,7 +118,12 @@ Every Supabase project has a unique API URL. Your API is secured behind an API g
3. Find your API `URL`, `anon`, and `service_role` keys on this page.
The REST API and the GraphQL API are both accessible through this URL:
@@ -140,7 +152,12 @@ Let's view the documentation for a `countries` table which we created in our dat
3. Switch between the JavaScript and the cURL docs using the tabs.
#### GraphQL
@@ -192,12 +209,12 @@ curl '/rest/v1/todos' \
-JS Reference: [select()](../reference/javascript/select),
-[insert()](../reference/javascript/insert),
-[update()](../reference/javascript/update),
-[upsert()](../reference/javascript/upsert),
-[delete()](../reference/javascript/delete),
-[rpc()](../reference/javascript/rpc) (call Postgres functions).
+JS Reference: [select()](/docs/reference/javascript/select),
+[insert()](/docs/reference/javascript/insert),
+[update()](/docs/reference/javascript/update),
+[upsert()](/docs/reference/javascript/upsert),
+[delete()](/docs/reference/javascript/delete),
+[rpc()](/docs/reference/javascript/rpc) (call Postgres functions).
### GraphQL API
@@ -217,7 +234,7 @@ import { createClient, useQuery } from 'urql'
// Prepare API key and Authorization header
const headers = {
apikey: ,
- authorization: `Bearer ${
+ authorization: `Bearer ${}`,
}
// Create GraphQL client
@@ -285,7 +302,12 @@ By default Realtime is disabled on your database. Let's turn on Realtime for the
4. Control which tables broadcast changes by selecting **Source** and toggling each table.
diff --git a/apps/docs/pages/guides/api/generating-types.mdx b/apps/docs/pages/guides/database/api/generating-types.mdx
similarity index 92%
rename from apps/docs/pages/guides/api/generating-types.mdx
rename to apps/docs/pages/guides/database/api/generating-types.mdx
index a50399287b6..1adba154fcc 100644
--- a/apps/docs/pages/guides/api/generating-types.mdx
+++ b/apps/docs/pages/guides/database/api/generating-types.mdx
@@ -4,10 +4,20 @@ export const meta = {
id: 'generating-types',
title: 'Generating Types',
description: 'How to generate types for your API and Supabase libraries.',
+ video: 'https://www.youtube.com/v/7CqlTU9aOR4',
}
Supabase APIs are generated from your database, which means that we can use database introspection to generate type-safe API definitions.
+
+
+
+
## Generating types using Supabase CLI
The Supabase CLI is a single binary Go application that provides everything you need to setup a local development environment.
diff --git a/apps/docs/pages/guides/database/connecting-to-postgres.mdx b/apps/docs/pages/guides/database/connecting-to-postgres.mdx
index 13bade73e20..a92a18dfe09 100644
--- a/apps/docs/pages/guides/database/connecting-to-postgres.mdx
+++ b/apps/docs/pages/guides/database/connecting-to-postgres.mdx
@@ -28,15 +28,15 @@ You can use these simple questions to determine which connection method to use:
## API
-Supabase provides an auto-updating [API](/docs/guides/api). This is the easiest way to get started if you are managing data (fetching, inserting, updating).
+Supabase provides an auto-updating [API](/docs/guides/database/api). This is the easiest way to get started if you are managing data (fetching, inserting, updating).
### Interfaces
We provides several types of API to suit your preferences and use-case:
-- [REST](/docs/guides/api#rest-api): interact with your database through a REST interface.
-- [GraphQL](/docs/guides/api#graphql-api): interact with your database through a GraphQL interface.
-- [Realtime](/docs/guides/api#realtime-api): listen to database changes over websockets.
+- [REST](/docs/guides/database/api#rest-api): interact with your database through a REST interface.
+- [GraphQL](/docs/guides/database/api#graphql-api): interact with your database through a GraphQL interface.
+- [Realtime](/docs/guides/database/api#realtime-api): listen to database changes over websockets.
You cannot manage the database schema via the API (for security reasons). To do that you can use the dashboard or connect directly to your database.
@@ -45,7 +45,12 @@ You cannot manage the database schema via the API (for security reasons). To do
You can find the API URL and Keys in the [Dashboard](https://app.supabase.com/project/_/settings/api).
## Direct connections
@@ -59,7 +64,12 @@ Every Supabase project provides a full Postgres database. You can connect to the
3. Find your Connection Info and Connection String. Direct connections are on port `5432`.
## Connection Pool
@@ -104,7 +114,12 @@ This is the most granular option. Connections are returned to the pool after eve
3. Find your Connection Info and Connection String. Connection pooling is on port `6543`.
## Connecting with SSL
diff --git a/apps/docs/pages/guides/database/extensions.mdx b/apps/docs/pages/guides/database/extensions.mdx
index a09ca6ca56e..eff1165b8fd 100644
--- a/apps/docs/pages/guides/database/extensions.mdx
+++ b/apps/docs/pages/guides/database/extensions.mdx
@@ -2,7 +2,7 @@ import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'extensions',
- title: 'Overview',
+ title: 'Postgres Extensions Overview',
description: 'Using Postgres extensions.',
}
@@ -23,19 +23,18 @@ Supabase has pre-installed some of the most useful open source extensions.
2. Click **Extensions** in the sidebar.
3. Enable or disable an extension.
-
-
```sql
--- Example: enable the "pgtap" extension and ensure it is installed
-create extension pgtap with schema extensions;
+ -- Example: enable the "pgtap" extension and ensure it is installed
+create extension pgtap
+with
+ schema extensions;
-- Example: disable the "pgtap" extension
-drop extension pgtap;
+drop
+ extension pgtap;
```
Even though the SQL code is `create extension`, this is the equivalent of "enabling the extension".
diff --git a/apps/docs/pages/guides/database/extensions/http.mdx b/apps/docs/pages/guides/database/extensions/http.mdx
index 39d11b19c14..7f686ed8e7b 100644
--- a/apps/docs/pages/guides/database/extensions/http.mdx
+++ b/apps/docs/pages/guides/database/extensions/http.mdx
@@ -4,6 +4,7 @@ export const meta = {
id: 'http',
title: 'http: RESTful Client',
description: 'An HTTP Client for PostgreSQL Functions.',
+ video: 'https://www.youtube.com/v/rARgrELRCwY',
}
The `http` extension allows you to call RESTful endpoints within Postgres.
@@ -62,7 +63,7 @@ drop extension if exists http;
Even though the SQL code is `create extension`, this is the equivalent of "enabling the extension".
To disable an extension, call `drop extension`.
-It's good practice to create the extension within a separate schema (like `extensions`) to keep your database clean.
+It's good practice to create the extension within a separate schema (like `extensions`) to keep the `public` schema clean.
diff --git a/apps/docs/pages/guides/database/extensions/hypopg.mdx b/apps/docs/pages/guides/database/extensions/hypopg.mdx
new file mode 100644
index 00000000000..18688e276e4
--- /dev/null
+++ b/apps/docs/pages/guides/database/extensions/hypopg.mdx
@@ -0,0 +1,120 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'hypopg',
+ title: 'HypoPG: Hypothetical indexes',
+ description: 'Quickly check if an index can be used without creating it.',
+}
+
+`HypoPG` is PostgreSQL extension for creating hypothetical/virtual indexes. HypoPG allows users to rapidly create hypothetical/virtual indexes that have no resource cost (CPU, disk, memory) that are visible to the PostgreSQL query planner.
+
+The motivation for HypoPG is to allow users to quickly search for an index to improve a slow query without consuming server resources or waiting for them to build.
+
+## Enable the extension
+
+
+
+
+1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard.
+2. Click on **Extensions** in the sidebar.
+3. Search for "hypopg" and enable the extension.
+
+
+
+
+{/* prettier-ignore */}
+```sql
+-- Enable the "hypopg" extension
+create extension hypopg with schema extensions;
+
+-- Disable the "hypopg" extension
+drop extension if exists hypopg;
+```
+
+Even though the SQL code is `create extension`, this is the equivalent of "enabling the extension".
+To disable an extension you can call `drop extension`.
+
+It's good practice to create the extension within a separate schema (like `extensions`) to keep the `public` schema clean.
+
+
+
+
+### Speeding up a query
+
+Given the following table and a simple query to select from the table by `id`:
+
+{/* prettier-ignore */}
+```sql
+create table account (
+ id int,
+ address text
+);
+
+insert into account(id, address)
+select
+ id,
+ id || ' main street'
+from
+ generate_series(1, 10000) id;
+```
+
+We can generate an explain plan for a description of how the PostgreSQL query planner
+intends to execute the query.
+
+{/* prettier-ignore */}
+```sql
+explain select * from account where id=1;
+
+ QUERY PLAN
+-------------------------------------------------------
+ Seq Scan on account (cost=0.00..180.00 rows=1 width=13)
+ Filter: (id = 1)
+(2 rows)
+```
+
+Using HypoPG, we can create a hypothetical index on the `account(id)` column to check if it would be useful to the query planner and then re-run the explain plan.
+
+Note that the virtual indexes created by HypoPG are only visible in the PostgreSQL connection that they were created in. Supabase connects to PostgreSQL through a connection pooler so the `hypopg_create_index` statement and the `explain` statement should be executed in a single query.
+
+{/* prettier-ignore */}
+```sql
+select * from hypopg_create_index('create index on account(id)');
+
+explain select * from account where id=1;
+
+ QUERY PLAN
+------------------------------------------------------------------------------------
+ Index Scan using <13504>btree_account_id on hypo (cost=0.29..8.30 rows=1 width=13)
+ Index Cond: (id = 1)
+(2 rows)
+```
+
+The query plan has changed from a `Seq Scan` to an `Index Scan` using the newly created virtual index, so we may choose to create a real version of the index to improve performance on the target query:
+
+{/* prettier-ignore */}
+```sql
+create index on account(id);
+```
+
+## Functions
+
+- [`hypo_create_index(text)`](https://hypopg.readthedocs.io/en/rel1_stable/usage.html#create-a-hypothetical-index): A function to create a hypothetical index.
+- [`hypopg_list_indexes`](https://hypopg.readthedocs.io/en/rel1_stable/usage.html#manipulate-hypothetical-indexes): A View that lists all hypothetical indexes that have been created.
+- [`hypopg()`](https://hypopg.readthedocs.io/en/rel1_stable/usage.html#manipulate-hypothetical-indexes): A function that lists all hypothetical indexes that have been created with the same format as pg_index.
+- [`hypopg_get_index_def(oid)`](https://hypopg.readthedocs.io/en/rel1_stable/usage.html#manipulate-hypothetical-indexes): A function to display the `create index` statement that would create the index.
+- [`hypopg_get_relation_size(oid)`](https://hypopg.readthedocs.io/en/rel1_stable/usage.html#manipulate-hypothetical-indexes): A function to estimate how large a hypothetical index would be.
+- [`hypopg_drop_index(oid)`](https://hypopg.readthedocs.io/en/rel1_stable/usage.html#manipulate-hypothetical-indexes): A function to remove a given hypothetical index by oid.
+- [`hypopg_reset()`](https://hypopg.readthedocs.io/en/rel1_stable/usage.html#manipulate-hypothetical-indexes): A function to remove all hypothetical indexes.
+
+## Resources
+
+- Official [HypoPG documentation](https://hypopg.readthedocs.io/en/rel1_stable/)
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/database/extensions/pg-safeupdate.mdx b/apps/docs/pages/guides/database/extensions/pg-safeupdate.mdx
new file mode 100644
index 00000000000..33773977b55
--- /dev/null
+++ b/apps/docs/pages/guides/database/extensions/pg-safeupdate.mdx
@@ -0,0 +1,60 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'pg-safeupdate',
+ title: 'pg-safeupdate: Required Where Clauses',
+ description: 'Requires a where clause for update and delete',
+}
+
+[pg-safeupdate](https://github.com/eradman/pg-safeupdate) is a PostgreSQL extension designed to prevent users from accidentally updating or deleting too many records in a single statement by requiring a "where" clause in all update and delete statements.
+
+The `pg-safeupdate` extension is a useful tool for protecting data integrity and preventing accidental data loss. Without it, a user could accidentally execute an update or delete statement that affects all records in a table. With `pg-safeupdate`, users are required to be more deliberate in their update and delete statements, which reduces the risk of significant error.
+
+## Enable the extension
+
+`pg-safeupdate` can be enabled on a per connection basis:
+
+{/* prettier-ignore */}
+```sql
+load 'safeupdate';
+```
+
+or for all connections:
+
+{/* prettier-ignore */}
+```sql
+alter database some_db set session_preload_libraries = 'safeupdate';
+```
+
+## Usage
+
+Let's take a look at an example to see how pg-safeupdate works. Suppose we have a table called `employees` with the following columns: `id`, `name`, `department`, and `date_of_birth`. We want to update the `date_of_birth` for a specific employee with the `id` of 12345. Here is what the query would look like if we forgot to add a "where" clause:
+
+{/* prettier-ignore */}
+```sql
+load 'safeupdate';
+
+update employees set date_of_birth = '1987-01-28';
+```
+
+This query updates the `date_of_birth` for all employees to 1987-01-28, which is not what we intended. With `pg-safeupdate` enabled, we receive an error message prompting us to add a "where" clause to the query:
+
+{/* prettier-ignore */}
+```sql
+ERROR: UPDATE requires a WHERE clause
+```
+
+We would then update our query to include a "where" clause specifying the employee with the `id` of 12345:
+
+{/* prettier-ignore */}
+```sql
+update employees set date_of_birth = '1987-01-28' where id = 12345;
+```
+
+## Resources
+
+- Official [pg-safeupdate documentation](https://github.com/eradman/pg-safeupdate)
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/database/extensions/pg_graphql.mdx b/apps/docs/pages/guides/database/extensions/pg_graphql.mdx
new file mode 100644
index 00000000000..a63be68c164
--- /dev/null
+++ b/apps/docs/pages/guides/database/extensions/pg_graphql.mdx
@@ -0,0 +1,116 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'pg_graphql',
+ title: 'pg_graphql: GraphQL for PostgreSQL',
+ description: 'A GraphQL Interface for PostgreSQL',
+}
+
+[pg_graphql](https://supabase.github.io/pg_graphql/) is PostgreSQL extension for interacting with the database using [GraphQL](https://graphql.org) instead of SQL.
+
+The extension reflects a GraphQL schema from the existing SQL schema and exposes it through a SQL function, `graphql.resolve(...)`. This enables any programming language that can connect to PostgreSQL to query the database via GraphQL with no additional servers, processes, or libraries.
+
+The `pg_graphql` resolve method is designed to interop with [PostgREST](https://postgrest.org/en/stable/index.html), the tool that underpins the Supabase API, such that the `graphql.resolve` function can be called via RPC to safely and performantly expose the GraphQL API over HTTP/S.
+
+For more information about how the SQL schema is reflected into a GraphQL schema, see the [pg_graphql API docs](https://supabase.github.io/pg_graphql/api/).
+
+## Enable the extension
+
+
+
+
+1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard.
+2. Click on **Extensions** in the sidebar.
+3. Search for "pg_graphql" and enable the extension.
+
+
+
+
+{/* prettier-ignore */}
+```sql
+-- Enable the "pg_graphql" extension
+create extension pg_graphql;
+
+-- Disable the "pg_graphql" extension
+drop extension if exists pg_graphql;
+```
+
+Even though the SQL code is `create extension`, this is the equivalent of "enabling the extension".
+To disable an extension you can call `drop extension`.
+
+
+
+
+## Usage
+
+Given a table
+
+{/* prettier-ignore */}
+```sql
+create table "Blog"(
+ id serial primary key,
+ name varchar(255) not null,
+ description varchar(255),
+);
+
+insert into "Blog"(name)
+values ('My Blog');
+```
+
+The reflected GraphQL schema can be queries immediately as
+
+{/* prettier-ignore */}
+```sql
+select
+ graphql.resolve($$
+ {
+ blogCollection(first: 1) {
+ edges {
+ node {
+ id,
+ name
+ }
+ }
+ }
+ }
+ $$);
+```
+
+returning the JSON
+
+{/* prettier-ignore */}
+```json
+{
+ "data": {
+ "blogCollection": {
+ "edges": [
+ {
+ "node": {
+ "id": 1
+ "name": "My Blog"
+ }
+ }
+ ]
+ }
+ }
+}
+```
+
+Note that `pg_graphql` fully supports schema introspection so you can connect any GraphQL IDE or schema inspection tool to see the full set of fields and arguments available in the API.
+
+## API
+
+- [`graphql.resolve`](https://supabase.github.io/pg_graphql/sql_interface/): A SQL function for executing GraphQL queries.
+
+## Resources
+
+- Official [`pg_graphql` documentation](https://github.com/supabase/pg_graphql)
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/database/extensions/pg_hashids.mdx b/apps/docs/pages/guides/database/extensions/pg_hashids.mdx
new file mode 100644
index 00000000000..ad02c19b1ff
--- /dev/null
+++ b/apps/docs/pages/guides/database/extensions/pg_hashids.mdx
@@ -0,0 +1,81 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'pg_hashids',
+ title: 'pg_hashids: Short UIDs',
+ description: 'Generate Short UIDs from Numbers',
+}
+
+[pg_hashids](https://github.com/iCyberon/pg_hashids) provides a secure way to generate short, unique, non-sequential ids from numbers. The hashes are intended to be small, easy-to-remember identifiers that can be used to obfuscate data (optionally) with a password, alphabet, and salt. For example, you may wish to hide data like user IDs, order numbers, or tracking codes in favor of `pg_hashid`'s unique identifers.
+
+## Enable the Extension
+
+
+
+
+1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard.
+2. Click on **Extensions** in the sidebar.
+3. Search for "pg_hashids" and enable the extension.
+
+
+
+
+{/* prettier-ignore */}
+```sql
+-- Enable the "pg_hashids" extension
+create extension pg_hashids with schema extensions;
+
+-- Disable the "pg_hashids" extension
+drop extension if exists pg_hashids;
+```
+
+Even though the SQL code is `create extension`, this is the equivalent of "enabling the extension".
+To disable an extension you can call `drop extension`.
+
+It's good practice to create the extension within a separate schema (like `extensions`) to keep your `public` schema clean.
+
+
+
+
+## Usage
+
+Suppose we have a table that stores order information, and we want to give customers a unique identifer without exposing the sequential `id` column. To do this, we can use `pg_hashid`'s `id_encode` function.
+
+```sql
+create table orders (
+ id serial primary key,
+ description text,
+ price_cents bigint
+);
+
+insert into orders (description, price_cents)
+values ('a book', 9095);
+
+select
+ id,
+ id_encode(id) as short_id,
+ description,
+ price_cents
+from
+ orders;
+
+ id | short_id | description | price_cents
+----+----------+-------------+-------------
+ 1 | jR | a book | 9095
+(1 row)
+```
+
+To reverse the `short_id` back into an `id`, there is an equivalent function named `id_decode`.
+
+## Resources
+
+- Official [pg_hashids documention](https://github.com/iCyberon/pg_hashids)
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/database/extensions/pg_jsonschema.mdx b/apps/docs/pages/guides/database/extensions/pg_jsonschema.mdx
new file mode 100644
index 00000000000..4cf54f21a19
--- /dev/null
+++ b/apps/docs/pages/guides/database/extensions/pg_jsonschema.mdx
@@ -0,0 +1,111 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'pg_jsonschema',
+ title: 'pg_jsonschema: JSON Schema Validation',
+ description: 'Validate json/jsonb with JSON Schema in PostgreSQL.',
+}
+
+[JSON Schema](https://json-schema.org) is a language for annotating and validating JSON documents. [`pg_jsonschema`](https://github.com/supabase/pg_jsonschema) is a PostgreSQL extension that adds the ability to validate PostgreSQL's built-in `json` and `jsonb` data types against JSON Schema documents.
+
+## Enable the extension
+
+
+
+
+1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard.
+2. Click on **Extensions** in the sidebar.
+3. Search for "pg_jsonschema" and enable the extension.
+
+
+
+
+{/* prettier-ignore */}
+```sql
+-- Enable the "pg_jsonschema" extension
+create extension pg_jsonschema with schema extensions;
+
+-- Disable the "pg_jsonschema" extension
+drop extension if exists pg_jsonschema;
+```
+
+Even though the SQL code is `create extension`, this is the equivalent of "enabling the extension".
+To disable an extension you can call `drop extension`.
+
+It's good practice to create the extension within a separate schema (like `extensions`) to keep the `public` schema clean.
+
+
+
+
+## Functions
+
+- [`json_matches_schema(schema json, instance json)`](https://github.com/supabase/pg_jsonschema#api): Checks if a `json` _instance_ conforms to a JSON Schema _schema_.
+- [`jsonb_matches_schema(schema json, instance jsonb)`](https://github.com/supabase/pg_jsonschema#api): Checks if a `jsonb` _instance_ conforms to a JSON Schema _schema_.
+
+## Usage
+
+Since `pg_jsonschema` exposes its utilities as functions, we can execute them with a select statement:
+
+{/* prettier-ignore */}
+```sql
+select
+ extensions.json_matches_schema(
+ schema := '{"type": "object"}',
+ instance := '{}'
+ );
+```
+
+`pg_jsonschema` is generally used in tandem with a [check constraint](https://www.postgresql.org/docs/current/ddl-constraints.html) as a way to constrain the contents of a json/b column to match a JSON Schema.
+
+{/* prettier-ignore */}
+```sql
+create table customer(
+ id serial primary key,
+ ...
+ metadata json,
+
+ check (
+ json_matches_schema(
+ '{
+ "type": "object",
+ "properties": {
+ "tags": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "maxLength": 16
+ }
+ }
+ }
+ }',
+ metadata
+ )
+ )
+);
+
+-- Example: Valid Payload
+insert into customer(metadata)
+values ('{"tags": ["vip", "darkmode-ui"]}');
+-- Result:
+-- INSERT 0 1
+
+-- Example: Invalid Payload
+insert into customer(metadata)
+values ('{"tags": [1, 3]}');
+-- Result:
+-- ERROR: new row for relation "customer" violates check constraint "customer_metadata_check"
+-- DETAIL: Failing row contains (2, {"tags": [1, 3]}).
+```
+
+## Resources
+
+- Official [`pg_jsonschema` documentation](https://github.com/supabase/pg_jsonschema)
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/database/extensions/pg_plan_filter.mdx b/apps/docs/pages/guides/database/extensions/pg_plan_filter.mdx
new file mode 100644
index 00000000000..ab46d931ffe
--- /dev/null
+++ b/apps/docs/pages/guides/database/extensions/pg_plan_filter.mdx
@@ -0,0 +1,97 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'pg_plan_filter',
+ title: 'pg_plan_filter: Restrict Total Cost',
+ description: 'Block queries over a total cost limit',
+}
+
+[pg_plan_filter](https://github.com/pgexperts/pg_plan_filter) is PostgreSQL extension to block execution of statements where query planner's estimate of the total cost exceeds a threshold. This is intended to give database administrators a way to restrict the contribution an individual query has on database load.
+
+## Enable the extension
+
+`pg_plan_filter` can be enabled on a per connection basis:
+
+{/* prettier-ignore */}
+```sql
+load 'plan_filter';
+```
+
+or for all connections:
+
+{/* prettier-ignore */}
+```sql
+alter database some_db set session_preload_libraries = 'plan_filter';
+```
+
+## API
+
+`plan_filter.statement_cost_limit`: restricts the maximum total cost for executed statements
+`plan_filter.limit_select_only`: restricts to `select` statements
+
+Note that `limit_select_only = true` is not the same as read-only because `select` statements may modfiy data e.g. through a function call.
+
+## Example
+
+To demonstrate total cost filtering, we'll compare how `plan_filter.statement_cost_limit` treats queries that are under and over its cost limit. First, we set up a table with some data:
+
+{/* prettier-ignore */}
+```sql
+create table book(
+ id int primary key
+);
+-- CREATE TABLE
+
+insert into book(id) select * from generate_series(1, 10000);
+-- INSERT 0 10000
+```
+
+Next, we can review the explain plans for a single record select, and a whole table select.
+
+{/* prettier-ignore */}
+```sql
+explain select * from book where id =1;
+ QUERY PLAN
+---------------------------------------------------------------------------
+ Index Only Scan using book_pkey on book (cost=0.28..2.49 rows=1 width=4)
+ Index Cond: (id = 1)
+(2 rows)
+
+explain select * from book;
+ QUERY PLAN
+---------------------------------------------------------
+ Seq Scan on book (cost=0.00..135.00 rows=10000 width=4)
+(1 row)
+```
+
+Now we can choose a `statement_cost_filter` value between the total cost for the single select (2.49) and the whole table select (135.0) so one statement will succeed and one will fail.
+
+{/* prettier-ignore */}
+```sql
+load 'plan_filter';
+set plan_filter.statement_cost_limit = 50; -- between 2.49 and 135.0
+
+select * from book where id = 1;
+ id
+----
+ 1
+(1 row)
+-- SUCCESS
+```
+
+{/* prettier-ignore */}
+```sql
+select * from book;
+
+ERROR: plan cost limit exceeded
+HINT: The plan for your query shows that it would probably have an excessive run time. This may be due to a logic error in the SQL, or it maybe just a very costly query. Rewrite your query or increase the configuration parameter "plan_filter.statement_cost_limit".
+-- FAILURE
+```
+
+## Resources
+
+- Official [pg_plan_filter documentation](https://github.com/pgexperts/pg_plan_filter)
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/database/extensions/pg_stat_monitor.mdx b/apps/docs/pages/guides/database/extensions/pg_stat_monitor.mdx
new file mode 100644
index 00000000000..4c1363b83b9
--- /dev/null
+++ b/apps/docs/pages/guides/database/extensions/pg_stat_monitor.mdx
@@ -0,0 +1,88 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'pg_stat_monitor',
+ title: 'pg_stat_monitor: Query Performance Monitoring',
+ description: 'Query performance monitoring for PostgreSQL',
+}
+
+[pg_stat_monitor](https://github.com/percona/pg_stat_monitor) is query performance monitoring tool that provides query execution statistics in a SQL view named `pg_stat_monitor`. It tracks a superset of statistics available in [`pg_stat_statements`](https://www.postgresql.org/docs/current/pgstatstatements.html). Some of the most useful features are:
+
+- **Time Interval Grouping**: configurable time buckets to track query usage over time
+- **Capture Parameters**: optionally track parameters passed into queries instead of generic placeholders e.g. `$1`
+- **Query Plan**: store query plans used for execution
+
+For more information on query optimization, check out the [query performance guide](/docs/guides/platform/performance#examining-query-performance).
+
+## Enable the extension
+
+
+
+
+1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard.
+2. Click on **Extensions** in the sidebar.
+3. Search for "pg_stat_monitor" and enable the extension.
+
+
+
+
+{/* prettier-ignore */}
+```sql
+-- Enable the "pg_stat_monitor" extension
+create extension pg_stat_monitor with schema extensions;
+
+-- Disable the "pg_stat_monitor" extension
+drop extension if exists pg_stat_monitor;
+```
+
+Even though the SQL code is `create extension`, this is the equivalent of "enabling the extension".
+To disable an extension you can call `drop extension`.
+
+It's good practice to create the extension within a separate schema (like `extensions`) to keep the `public` schema clean.
+
+
+
+
+## Views
+
+{/* prettier-ignore */}
+```sql
+select * from extensions.pg_stat_monitor;
+```
+
+The following table shows a subset of available columns:
+
+| Column Type | Description |
+| ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
+| bucket bigint | Data collection unit. The number shows what bucket in a chain a record belongs to |
+| bucket_start_time timestampz | The start time of the bucket |
+| userid oid (references pg_authid.oid) | OID of user who executed the statement |
+| dbid oid (references pg_database.oid) | OID of database in which the statement was executed |
+| toplevel bool | True if the query was executed as a top-level statement (always true if pg_stat_statements.track is set to top) |
+| client_ip inet | The IP address of a client that ran the query |
+| queryid bigint | Hash code to identify identical normalized queries. |
+| planid text | An internally generated ID of a query plan |
+| query_plan text | The sequence of steps used to execute a query. This parameter is only available when pgsm_enable_query_plan is enabled |
+| query text | Text of a representative statement |
+| plans bigint | Number of times the statement was planned (if pg_stat_statements.track_planning is enabled, otherwise zero) |
+| total_plan_time double precision | Total time spent planning the statement, in milliseconds (if pg_stat_statements.track_planning is enabled, otherwise zero) |
+| min_plan_time double precision | Minimum time spent planning the statement, in milliseconds (if pg_stat_statements.track_planning is enabled, otherwise zero) |
+
+A full list of statistics is available in the [pg_stat_monitor docs](https://docs.percona.com/pg-stat-monitor/reference.html#postgresql-15).
+
+## Functions
+- [`pg_stat_monitor_reset()`](https://docs.percona.com/pg-stat-monitor/functions.html): Resets the statistics tracked by the `pg_stat_monitor` view and deletes all previous data.
+- [`pg_stat_monitor_version()`](https://docs.percona.com/pg-stat-monitor/functions.html): Displays the version of the `pg_stat_monitor` extension.
+
+## Resources
+
+- Official [`pg_stat_monitor` documentation](https://github.com/percona/pg_stat_monitor)
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/database/extensions/pg_stat_statements.mdx b/apps/docs/pages/guides/database/extensions/pg_stat_statements.mdx
new file mode 100644
index 00000000000..cb64a901b0c
--- /dev/null
+++ b/apps/docs/pages/guides/database/extensions/pg_stat_statements.mdx
@@ -0,0 +1,93 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'pg_stat_statements',
+ title: 'pg_stat_statements: Query Performance Monitoring',
+ description:
+ 'Track planning and execution statistics of all SQL statements executed on the database.',
+}
+
+`pg_stat_statements` is a database extension that exposes a view, of the same name, to track statistics about SQL statements executed on the database. The following table shows some of the available statistics and metadata:
+
+| Column Type | Description |
+| ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
+| userid oid (references pg_authid.oid) | OID of user who executed the statement |
+| dbid oid (references pg_database.oid) | OID of database in which the statement was executed |
+| toplevel bool | True if the query was executed as a top-level statement (always true if pg_stat_statements.track is set to top) |
+| queryid bigint | Hash code to identify identical normalized queries. |
+| query text | Text of a representative statement |
+| plans bigint | Number of times the statement was planned (if pg_stat_statements.track_planning is enabled, otherwise zero) |
+| total_plan_time double precision | Total time spent planning the statement, in milliseconds (if pg_stat_statements.track_planning is enabled, otherwise zero) |
+| min_plan_time double precision | Minimum time spent planning the statement, in milliseconds (if pg_stat_statements.track_planning is enabled, otherwise zero) |
+
+A full list of statistics is available in the [pg_stat_statements docs](https://www.postgresql.org/docs/current/pgstatstatements.html).
+
+For more information on query optimization, check out the [query performance guide](/docs/guides/platform/performance#examining-query-performance).
+
+## Enable the extension
+
+
+
+
+1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard.
+2. Click on **Extensions** in the sidebar.
+3. Search for "pg_stat_statements" and enable the extension.
+
+
+
+
+{/* prettier-ignore */}
+```sql
+-- Enable the "pg_stat_statements" extension
+create extension pg_stat_statements with schema extensions;
+
+-- Disable the "pg_stat_statements" extension
+drop extension if exists pg_stat_statements;
+```
+
+Even though the SQL code is `create extension`, this is the equivalent of "enabling the extension".
+To disable an extension you can call `drop extension`.
+
+It's good practice to create the extension within a separate schema (like `extensions`) to keep the `public` schema clean.
+
+
+
+
+## Inspecting activity
+
+A common use for `pg_stat_statements` is to track down expensive or slow queries. The `pg_stat_statements` view contains a row for each executed query with statistics inlined. For example, you can leverage the statistics to identify frequently executed and slow queries against a given table.
+
+{/* prettier-ignore */}
+```sql
+select
+ calls,
+ mean_exec_time,
+ max_exec_time,
+ total_exec_time,
+ stddev_exec_time,
+ query,
+from
+ pg_stat_statements
+where
+ calls > 50 -- at least 50 calls
+ and mean_exec_time > 2.0 -- averaging at least 2ms/call
+ and total_exec_time > 60000 -- at least one minute total server time spent
+ and query ilike '%user_in_organization%' -- filter to queries that touch the user_in_organization table
+order by
+ calls desc
+```
+
+From the results, we can make an informed decision about which queries to optimize or index.
+
+## Resources
+
+- Official [pg_stat_statements documentation](https://www.postgresql.org/docs/current/pgstatstatements.html)
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/database/extensions/pgaudit.mdx b/apps/docs/pages/guides/database/extensions/pgaudit.mdx
new file mode 100644
index 00000000000..0352fb6d131
--- /dev/null
+++ b/apps/docs/pages/guides/database/extensions/pgaudit.mdx
@@ -0,0 +1,105 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'pgaudit',
+ title: 'PGAudit: Postgres Auditing',
+ description: 'Session and object auditing via PostgreSQL standard logging',
+}
+
+[PGAudit](https://www.pgaudit.org) is a PostgreSQL extension for logging session and object auditing over the standard PostgreSQL logging utility.
+
+PGAudit grants fine grain control over which statements and objects are emitted to logs.
+
+## Enable the extension
+
+
+
+
+1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard.
+2. Click on **Extensions** in the sidebar.
+3. Search for "pgaudit" and enable the extension.
+
+
+
+
+{/* prettier-ignore */}
+```sql
+-- Enable the "pgaudit" extension
+create extension pgaudit;
+
+-- Disable the "pgaudit" extension
+drop extension if exists pgaudit;
+```
+
+Even though the SQL code is `create extension`, this is the equivalent of "enabling the extension".
+To disable an extension you can call `drop extension`.
+
+
+
+
+## Settings
+
+The `pgaudit.log` setting controls which statements to log. Available values include:
+
+- **read**: `SELECT` and `COPY` when the source is a relation or a query.
+- **write**: `INSERT`, `UPDATE`, `DELETE`, `TRUNCATE`, and `COPY` when the destination is a relation.
+- **function**: Function calls and `DO` blocks.
+- **role**: Statements related to roles and privileges: `GRANT`, `REVOKE`, `CREATE/ALTER/DROP ROLE`.
+- **ddl**: All `DDL` that is not included in the `ROLE` class.
+- **misc**: Miscellaneous commands, e.g. `DISCARD`, `FETCH`, `CHECKPOINT`, `VACUUM`, `SET`.
+- **misc_set**: Miscellaneous `SET` commands, e.g. `SET ROLE`.
+
+- **all**: Include all of the above.
+
+For a full list of available settings see [settings docs](https://github.com/pgaudit/pgaudit/blob/master/README.md#settings). Be aware that the `all` setting will generate a very large volume of logs.
+
+## Example
+
+Given a pgaudit setting
+
+{/* prettier-ignore */}
+```sql
+set pgaudit.log = 'read, ddl';
+```
+
+The following create table, insert and select statements
+
+{/* prettier-ignore */}
+```sql
+create table account (
+ id int primary key,
+ name text,
+ description text
+);
+
+insert into account (id, name, description)
+values (1, 'Foo Barsworth', 'Customer account');
+
+select * from account;
+```
+
+Results in the log output
+
+```text
+AUDIT: SESSION,1,1,DDL,CREATE TABLE,TABLE,public.account,create table account(
+ id int,
+ name text,
+ description text
+);,
+AUDIT: SESSION,2,1,READ,SELECT,,,select * from account,,
+```
+
+Note that the insert statement is not logged because we did not include the `write` option for `pgaudit.log`.
+
+## Resources
+
+- Official [`PGAudit` documentation](https://www.pgaudit.org)
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/database/extensions/pgcron.mdx b/apps/docs/pages/guides/database/extensions/pgcron.mdx
index 57a704e444a..1062db0fd32 100644
--- a/apps/docs/pages/guides/database/extensions/pgcron.mdx
+++ b/apps/docs/pages/guides/database/extensions/pgcron.mdx
@@ -63,6 +63,8 @@ The schedule uses the standard cron syntax, in which \* means "run every time pe
* * * * *
```
+You can use [crontab.guru](https://crontab.guru/) to help validate your cron schedules.
+
## Examples
### Delete data every week
@@ -71,8 +73,8 @@ Delete old data on Saturday at 3:30am (GMT):
```sql
select cron.schedule (
- 'webhook-every-minute', -- name of the cron job
- '* * * * *', -- every minute
+ 'saturday-cleanup', -- name of the cron job
+ '30 3 * * 6', -- Saturday at 3:30am (GMT)
$$ delete from events where event_time < now() - interval '1 week' $$
);
```
@@ -85,6 +87,26 @@ Vacuum every day at 3:00am (GMT)
SELECT cron.schedule('nightly-vacuum', '0 3 * * *', 'VACUUM');
```
+### Invoke Supabase Edge Function every minute
+
+Make a POST request to a Supabase Edge Function every minute. Note: this requires the [`pg_net` extension](/docs/guides/database/extensions/pgnet) to be enabled.
+
+```sql
+select
+ cron.schedule(
+ 'invoke-function-every-minute',
+ '* * * * *', -- every minute
+ $$
+ select
+ net.http_post(
+ url:='https://project-ref.functions.supabase.co/function-name',
+ headers:='{"Content-Type": "application/json", "Authorization": "Bearer YOUR_ANON_KEY"}'::jsonb,
+ body:=concat('{"time": "', now(), '"}')::jsonb
+ ) as request_id;
+ $$
+ );
+```
+
### Unschedule a job
Unschedules a job called `'nightly-vacuum'`
diff --git a/apps/docs/pages/guides/database/extensions/pgjwt.mdx b/apps/docs/pages/guides/database/extensions/pgjwt.mdx
new file mode 100644
index 00000000000..77c8bcc8dea
--- /dev/null
+++ b/apps/docs/pages/guides/database/extensions/pgjwt.mdx
@@ -0,0 +1,113 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'pgjwt',
+ title: 'pgjwt: JSON Web Tokens',
+ description: 'Encode and decode JWTs in PostgreSQL',
+}
+
+The [pgjwt](https://github.com/michelp/pgjwt) (PostgreSQL JSON Web Token) extension allows you to create and parse [JSON Web Tokens (JWTs)](https://en.wikipedia.org/wiki/JSON_Web_Token) within a PostgreSQL database. JWTs are commonly used for authentication and authorization in web applications and services.
+
+## Enable the extension
+
+
+
+
+1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard.
+2. Click on **Extensions** in the sidebar.
+3. Search for "pgjwt" and enable the extension.
+
+
+
+
+{/* prettier-ignore */}
+```sql
+-- Enable the "pgjwt" extension
+create extension pgjwt schema extensions;
+
+-- Disable the "pgjwt" extension
+drop extension if exists pgjwt;
+```
+
+Even though the SQL code is `create extension`, this is the equivalent of "enabling the extension".
+To disable an extension you can call `drop extension`.
+
+It's good practice to create the extension within a separate schema (like `extensions`) to keep the `public` schema clean.
+
+
+
+
+## API
+
+- [`sign(payload json, secret text, algorithm text default 'HSA256')`](https://github.com/michelp/pgjwt#usage): Signs a JWT containng _payload_ with _secret_ using _algorithm_.
+- [`verify(token text, secret text, algorithm text default 'HSA256')`](https://github.com/michelp/pgjwt#usage): Decodes a JWT _token_ that was signed with _secret_ using _algorithm_.
+
+Where:
+
+- `payload` is an encrypted JWT represented as a string.
+- `secret` is the private/secret passcode which is used to sign the JWT and verify its integrity.
+- `algorithm` is the method used to sign the JWT using the secret.
+- `token` is an encrypted JWT represented as a string.
+
+## Usage
+
+Once the extension is installed, you can use its functions to create and parse JWTs. Here's an example of how you can use the `sign` function to create a JWT:
+
+{/* prettier-ignore */}
+```sql
+select
+ extensions.sign(
+ payload := '{"sub":"1234567890","name":"John Doe","iat":1516239022}',
+ secret := 'secret',
+ algorithm := 'HS256'
+ );
+```
+
+The pgjwt_encode function returns a string that represents the JWT, which can then be safely transmitted between parties.
+
+{/* prettier-ignore */}
+```
+ sign
+---------------------------------
+ eyJhbGciOiJIUzI1NiIsInR5cCI6IkpX
+ VCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiw
+ ibmFtZSI6IkpvaG4gRG9lIiwiaWF0Ijo
+ xNTE2MjM5MDIyfQ.XbPfbIHMI6arZ3Y9
+ 22BhjWgQzWXcXNrz0ogtVhfEd2o
+(1 row)
+```
+
+To parse a JWT and extract its claims, you can use the `verify` function. Here's an example:
+
+{/* prettier-ignore */}
+```sql
+select
+ extensions.verify(
+ token := 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiRm9vIn0.Q8hKjuadCEhnCPuqIj9bfLhTh_9QSxshTRsA5Aq4IuM',
+ secret := 'secret',
+ algorithm := 'HS256'
+ );
+```
+
+Which returns the decoded contents and some associated metadata.
+
+{/* prettier-ignore */}
+```sql
+ header | payload | valid
+-----------------------------+----------------+-------
+ {"alg":"HS256","typ":"JWT"} | {"name":"Foo"} | t
+(1 row)
+```
+
+## Resources
+
+- Official [`pgjwt` documentation](https://github.com/michelp/pgjwt)
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/database/extensions/pgnet.mdx b/apps/docs/pages/guides/database/extensions/pgnet.mdx
index 29283ab4430..3246fb4f5df 100644
--- a/apps/docs/pages/guides/database/extensions/pgnet.mdx
+++ b/apps/docs/pages/guides/database/extensions/pgnet.mdx
@@ -16,9 +16,8 @@ The pg_net API is in alpha. Functions signatures may change.
It differs from the `http` extension in that it is asynchronous by default. This makes it useful in blocking functions (like triggers).
-## Usage
-### Enable the extension
+## Enable the extension
-## `http_get` [#http_get]
+## `http_get`
Creates an HTTP GET request returning the request's ID. HTTP requests are not started until the transaction is committed.
-### Signature
+### Signature [#get-signature]
@@ -86,7 +85,7 @@ net.http_get(
language plpgsql
```
-### Usage
+### Usage [#get-usage]
```sql
select net.http_get('https://news.ycombinator.com') as request_id;
@@ -98,13 +97,13 @@ request_id
After triggering `http_get`, use [`http_get_result`](#http_get_result) to get the result of the request.
-## `http_post` [#http_post]
+## `http_post`
Creates an HTTP POST request with a JSON body, returning the request's ID. HTTP requests are not started until the transaction is committed.
The body's character set encoding matches the database's `server_encoding` setting.
-### Signature
+### Signature [#post-signature]
@@ -134,7 +133,7 @@ net.http_post(
language plpgsql
```
-### Usage
+### Usage [#post-usage]
```sql
select
@@ -150,6 +149,21 @@ request_id
After triggering `http_post`, use [`http_get_result`](#http_get_result) to get the result of the request.
+## Examples
+
+### Invoke a Supabase Edge Function
+
+Make a POST request to a Supabase Edge Function with auth header and JSON body payload:
+
+```sql
+select
+ net.http_post(
+ url:='https://project-ref.functions.supabase.co/function-name',
+ headers:='{"Content-Type": "application/json", "Authorization": "Bearer YOUR_ANON_KEY"}'::jsonb,
+ body:='{"name": "pg_net"}'::jsonb
+ ) as request_id;
+```
+
## Resources
- Source code: [github.com/supabase/pg_net](https://github.com/supabase/pg_net/)
diff --git a/apps/docs/pages/guides/database/extensions/pgrepack.mdx b/apps/docs/pages/guides/database/extensions/pgrepack.mdx
new file mode 100644
index 00000000000..bea7f7474b2
--- /dev/null
+++ b/apps/docs/pages/guides/database/extensions/pgrepack.mdx
@@ -0,0 +1,110 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ title: 'pg_repack: Physical storage optimization and maintenance',
+ description:
+ 'A tool to remove bloat from tables and indexes and optimize physical data order and physical storage',
+}
+
+[pg_repack](https://github.com/reorg/pg_repack) is a PostgreSQL extension to remove bloat from tables and indexes, and optionally restore the physical order of clustered indexes. Unlike CLUSTER and VACUUM FULL it works online, without holding an exclusive lock on the processed tables during processing. pg_repack is efficient to boot, with performance comparable to using CLUSTER directly.
+
+pg_repack provides the following methods to optimize physical storage:
+
+- Online CLUSTER: ordering table data by cluster index in a non-blocking way
+- Ordering table data by specified columns
+- Online VACUUM FULL: packing rows only in a non-blocking way
+- Rebuild or relocate only the indexes of a table
+
+## Requirements
+
+- Only superusers can use the utility.
+- Target table must have a PRIMARY KEY, or a UNIQUE total index on a NOT NULL column.
+- Performing a full-table repack requires free disk space about twice as large as the target table and its indexes.
+
+## Usage
+
+### Enable the extension
+
+Get started with pg_repack by enabling the extension in the Supabase Dashboard.
+
+
+
+
+1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard.
+2. Click on **Extensions** in the sidebar.
+3. Search for "pg_repack" and enable the extension.
+
+
+
+
+```sql
+-- Example: enable the "pg_repack" extension
+create extension pg_repack with schema extensions;
+
+-- Example: disable the "pg_repack" extension
+drop extension if exists pg_repack;
+```
+
+
+
+
+### Syntax
+
+```sh
+pg_repack [OPTION]... [DBNAME]
+```
+
+## Examples
+
+It's useful for performance to support tables data ordered on disk and physically remove deleted data that remain
+otherwise.
+
+Perform an online CLUSTER of all the clustered tables in the database `db`, and perform an online `VACUUM FULL` of all the non-clustered tables:
+
+```sh
+pg_repack db
+```
+
+Perform an online `VACUUM FULL` on the tables `table1` and `table2` in the database `db` (an eventual cluster index is ignored):
+
+```sh
+pg_repack --no-order --table table1 --table table2 db
+```
+
+Moving indexes to a tablespace on a faster volume increases performance of `SELECT` queries using these indexes
+drastically. `INSERT`s and `UPDATE`s of a table with indexes on a fast volume are also faster. This is very useful
+when the fast volume is small and can not accommodate all tables, as indexes are much smaller than tables.
+
+Move all indexes of table `table1` to tablespace `tbs`:
+
+```sh
+pg_repack -d db --table table1 --only-indexes --tablespace tbs
+```
+
+Move the specified index `idx` to tablespace `tbs`:
+
+```sh
+pg_repack -d db --index idx --tablespace tbs
+```
+
+See the [official pg_repack documentation](https://reorg.github.io/pg_repack/) for the full list of options.
+
+## Restrictions
+
+- pg_repack cannot reorganize temp tables.
+- pg_repack cannot cluster tables by GiST indexes.
+- You cannot perform DDL commands of the target tables except VACUUM or ANALYZE while pg_repack is working.
+ pg_repack holds an ACCESS SHARE lock on the target table to enforce this restriction.
+
+## Resources
+
+- [Official pg_repack documentation](https://reorg.github.io/pg_repack/)
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/database/extensions/pgroonga.mdx b/apps/docs/pages/guides/database/extensions/pgroonga.mdx
new file mode 100644
index 00000000000..098107a580c
--- /dev/null
+++ b/apps/docs/pages/guides/database/extensions/pgroonga.mdx
@@ -0,0 +1,110 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'pgroonga',
+ title: 'PGroonga: Multilingual Full Text Search',
+ description: 'Full Text Search for multiple languages in PostgreSQL',
+}
+
+`PGroonga` is a PostgreSQL extension adding a full text search indexing method based on [Groonga](https://groonga.org). While native PostgreSQL supports full text indexing, it is limited to alphabet and digit based languages. `PGroonga` offers a wider range of character support making it viable for a superset of languages supported by PostgreSQL including Japanese, Chinese, etc.
+
+
+## Enable the extension
+
+
+
+
+1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard.
+2. Click on **Extensions** in the sidebar.
+3. Search for "pgroonga" and enable the extension.
+
+
+
+
+{/* prettier-ignore */}
+```sql
+-- Enable the "pgroonga" extension
+create extension pgroonga with schema extensions;
+
+-- Disable the "pgroonga" extension
+drop extension if exists pgroonga;
+```
+
+Even though the SQL code is `create extension`, this is the equivalent of "enabling the extension".
+To disable an extension you can call `drop extension`.
+
+
+
+
+## Creating a full text search index
+
+Given a table with a `text` column:
+
+{/* prettier-ignore */}
+```sql
+create table memos (
+ id serial primary key,
+ content text
+);
+```
+
+We can index the column for full text search with a `pgroonga` index:
+
+{/* prettier-ignore */}
+```sql
+create index ix_memos_content ON memos USING pgroonga(content);
+```
+
+To test the full text index, we'll add some data.
+
+{/* prettier-ignore */}
+```sql
+insert into memos(content)
+values
+ ('PostgreSQL is a relational database management system.'),
+ ('Groonga is a fast full text search engine that supports all languages.'),
+ ('PGroonga is a PostgreSQL extension that uses Groonga as index.'),
+ ('There is groonga command.');
+```
+
+The PostgreSQL query planner is smart enough to know that, for extremely small tables, it's faster to scan the whole table rather than loading an index. To force the index to be used, we can disable sequential scans:
+
+{/* prettier-ignore */}
+```sql
+-- For testing only. Don't do this in production
+set enable_seqscan = off;
+```
+
+Now if we run an explain plan on a query filtering on `memos.content`:
+
+{/* prettier-ignore */}
+```sql
+explain select * from memos where content like '%engine%';
+
+ QUERY PLAN
+-----------------------------------------------------------------------------
+Index Scan using ix_memos_content on memos (cost=0.00..1.11 rows=1 width=36)
+ Index Cond: (content ~~ '%engine%'::text)
+(2 rows)
+```
+
+The pgroonga index is used to retrive the result set:
+
+```markdown
+| id | content |
+| --- | ------------------------------------------------------------------------ |
+| 2 | 'Groonga is a fast full text search engine that supports all languages.' |
+```
+
+## Resources
+
+- Official [PGroonga documentation](https://pgroonga.github.io/tutorial/)
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/database/extensions/pgrouting.mdx b/apps/docs/pages/guides/database/extensions/pgrouting.mdx
new file mode 100644
index 00000000000..73a047fb362
--- /dev/null
+++ b/apps/docs/pages/guides/database/extensions/pgrouting.mdx
@@ -0,0 +1,159 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'pgrouting',
+ title: 'pgrouting: Geospatial Routing',
+ description: 'Extends PostGIS with Geospatial Routing',
+}
+
+[pgRouting](http://pgrouting.org) is PostgreSQL and [PostGIS](http://postgis.net) extension adding geospatial routing functionality.
+
+The core functionality of `pgRouting` is a set of path finding algorithms including:
+
+- All Pairs Shortest Path, Johnson’s Algorithm
+- All Pairs Shortest Path, Floyd-Warshall Algorithm
+- Shortest Path A\*
+- Bi-directional Dijkstra Shortest Path
+- Bi-directional A\* Shortest Path
+- Shortest Path Dijkstra
+- Driving Distance
+- K-Shortest Path, Multiple Alternative Paths
+- K-Dijkstra, One to Many Shortest Path
+- Traveling Sales Person
+- Turn Restriction Shortest Path (TRSP)
+
+## Enable the extension
+
+
+
+
+1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard.
+2. Click on **Extensions** in the sidebar.
+3. Search for "pgrouting" and enable the extension.
+
+
+
+
+{/* prettier-ignore */}
+```sql
+-- Enable the "pgRouting" extension
+create extension pgrouting cascade;
+
+-- Disable the "pgRouting" extension
+drop extension if exists pgRouting;
+```
+
+Even though the SQL code is `create extension`, this is the equivalent of "enabling the extension".
+To disable an extension you can call `drop extension`.
+
+
+
+
+## Example
+
+As an example, we'll solve the [traveling salesman probelem](https://en.wikipedia.org/wiki/Travelling_salesman_problem) using the pgRouting's `pgr_TSPeuclidean` function from some PostGIS coodinates.
+
+A summary of the traveling salesman problem is, given a set of city coordinates, solve for a path that goes through each city and minimizes the total distance traveled.
+
+First we populate a table with some X, Y coordinates
+
+{/* prettier-ignore */}
+```sql
+create table wi29 (
+ id bigint,
+ x float,
+ y float,
+ geom geometry
+);
+
+insert into wi29 (id, x, y)
+values
+ (1,20833.3333,17100.0000),
+ (2,20900.0000,17066.6667),
+ (3,21300.0000,13016.6667),
+ (4,21600.0000,14150.0000),
+ (5,21600.0000,14966.6667),
+ (6,21600.0000,16500.0000),
+ (7,22183.3333,13133.3333),
+ (8,22583.3333,14300.0000),
+ (9,22683.3333,12716.6667),
+ (10,23616.6667,15866.6667),
+ (11,23700.0000,15933.3333),
+ (12,23883.3333,14533.3333),
+ (13,24166.6667,13250.0000),
+ (14,25149.1667,12365.8333),
+ (15,26133.3333,14500.0000),
+ (16,26150.0000,10550.0000),
+ (17,26283.3333,12766.6667),
+ (18,26433.3333,13433.3333),
+ (19,26550.0000,13850.0000),
+ (20,26733.3333,11683.3333),
+ (21,27026.1111,13051.9444),
+ (22,27096.1111,13415.8333),
+ (23,27153.6111,13203.3333),
+ (24,27166.6667,9833.3333),
+ (25,27233.3333,10450.0000),
+ (26,27233.3333,11783.3333),
+ (27,27266.6667,10383.3333),
+ (28,27433.3333,12400.0000),
+ (29,27462.5000,12992.2222);
+```
+
+Next we use the `pgr_TSPeuclidean` function to find the best path.
+
+{/* prettier-ignore */}
+```sql
+select
+ *
+from
+ pgr_TSPeuclidean($$select * from wi29$$)
+```
+
+{/* prettier-ignore */}
+```sql
+ seq | node | cost | agg_cost
+-----+------+------------------+------------------
+ 1 | 1 | 0 | 0
+ 2 | 2 | 74.535614157127 | 74.535614157127
+ 3 | 6 | 900.617093380362 | 975.152707537489
+ 4 | 10 | 2113.77757765045 | 3088.93028518793
+ 5 | 11 | 106.718669615254 | 3195.64895480319
+ 6 | 12 | 1411.95293791574 | 4607.60189271893
+ 7 | 13 | 1314.23824873744 | 5921.84014145637
+ 8 | 14 | 1321.76283931305 | 7243.60298076942
+ 9 | 17 | 1202.91366735569 | 8446.5166481251
+ 10 | 18 | 683.333268292684 | 9129.84991641779
+ 11 | 15 | 1108.05137466134 | 10237.9012910791
+ 12 | 19 | 772.082339448903 | 11009.983630528
+ 13 | 22 | 697.666150054665 | 11707.6497805827
+ 14 | 23 | 220.141999627513 | 11927.7917802102
+ 15 | 21 | 197.926372783442 | 12125.7181529937
+ 16 | 29 | 440.456596290771 | 12566.1747492844
+ 17 | 28 | 592.939989005405 | 13159.1147382898
+ 18 | 26 | 648.288376333318 | 13807.4031146231
+ 19 | 20 | 509.901951359278 | 14317.3050659824
+ 20 | 25 | 1330.83095428717 | 15648.1360202696
+ 21 | 27 | 74.535658878487 | 15722.6716791481
+ 22 | 24 | 559.016994374947 | 16281.688673523
+ 23 | 16 | 1243.87392358622 | 17525.5625971092
+ 24 | 9 | 4088.0585364911 | 21613.6211336004
+ 25 | 7 | 650.85409697993 | 22264.4752305803
+ 26 | 3 | 891.004385199336 | 23155.4796157796
+ 27 | 4 | 1172.36699411442 | 24327.846609894
+ 28 | 8 | 994.708187806297 | 25322.5547977003
+ 29 | 5 | 1188.01888359478 | 26510.5736812951
+ 30 | 1 | 2266.91173136004 | 28777.4854126552
+```
+
+## Resources
+
+- Official [`pgRouting` documentation](https://docs.pgrouting.org/latest/en/index.html)
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/database/extensions/pgsodium.mdx b/apps/docs/pages/guides/database/extensions/pgsodium.mdx
new file mode 100644
index 00000000000..f11c5fc3ec8
--- /dev/null
+++ b/apps/docs/pages/guides/database/extensions/pgsodium.mdx
@@ -0,0 +1,49 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'pgsodium',
+ title: 'pgsodium: Encryption Features',
+ description: 'Encryption library for PostgreSQL',
+}
+
+[pgsodium](https://github.com/michelp/pgsodium) is a PostgreSQL extension which provides SQL access to libsodium's high-level cryptographic algorithms. It also enables some Postgres specific features including:
+
+- [Server Key Management](https://github.com/michelp/pgsodium#server-key-management)
+- [Transparent Column Encryption](https://github.com/michelp/pgsodium#transparent-column-encryption)
+
+## Enable the extension
+
+
+
+
+1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard.
+2. Click on **Extensions** in the sidebar.
+3. Search for "pgsodium" and enable the extension.
+
+
+
+
+{/* prettier-ignore */}
+```sql
+-- Enable the "pgsodium" extension
+create extension pgsodium;
+
+-- Disable the "pgsodium" extension
+drop extension if exists pgsodium;
+```
+
+
+
+
+## Resources
+
+- Official [`pgsodium` documentation](https://github.com/michelp/pgsodium)
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/database/extensions/pgtap.mdx b/apps/docs/pages/guides/database/extensions/pgtap.mdx
index fc642b522ab..219eedd11ee 100644
--- a/apps/docs/pages/guides/database/extensions/pgtap.mdx
+++ b/apps/docs/pages/guides/database/extensions/pgtap.mdx
@@ -15,9 +15,7 @@ Let's cover some basic concepts:
- Unit tests: allow you to test small parts of a system (like a database table!).
- TAP: stands for [Test Anything Protocol](http://testanything.org/). It is an framework which aims to simplify the error reporting during testing.
-## Usage
-
-### Enable the extension
+## Enable the extension
-### Testing tables
+## Testing tables
```sql
begin;
@@ -68,7 +66,7 @@ API:
- [`has_index()`](https://pgtap.org/documentation.html#has_index): Checks for the existence of a named index associated with the named table.
- [`has_relation()`](https://pgtap.org/documentation.html#has_relation): Tests whether or not a relation exists in the database.
-### Testing columns
+## Testing columns
```sql
begin;
@@ -86,7 +84,7 @@ API:
- [`has_column()`](https://pgtap.org/documentation.html#has_column): Tests whether or not a column exists in a given table, view, materialized view or composite type.
- [`col_is_pk()`](https://pgtap.org/documentation.html#col_is_pk): Tests whether the specified column or columns in a table is/are the primary key for that table.
-### Testing RLS Policies
+## Testing RLS Policies
```sql
begin;
@@ -133,7 +131,7 @@ API:
- [`results_eq()`](https://pgtap.org/documentation.html#results_eq)
- [`results_ne()`](https://pgtap.org/documentation.html#results_ne)
-### Testing Functions
+## Testing Functions
```sql
begin;
diff --git a/apps/docs/pages/guides/database/extensions/pgvector.mdx b/apps/docs/pages/guides/database/extensions/pgvector.mdx
new file mode 100644
index 00000000000..42118b9197f
--- /dev/null
+++ b/apps/docs/pages/guides/database/extensions/pgvector.mdx
@@ -0,0 +1,101 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'pgvector',
+ title: 'pgvector: Embeddings and vector similarity',
+ description:
+ 'pgvector: a PostgreSQL extension for storing embeddings and performing vector similarity search.',
+}
+
+[pgvector](https://github.com/pgvector/pgvector/) is a PostgreSQL extension for vector similarity search. It can also be used for storing [embeddings](https://en.wikipedia.org/wiki/Word_embedding).
+
+## Concepts
+
+### Vector similarity
+
+Vector similarity refers to a measure of the similarity between two related items. For example, if you have a list of products, you can use vector similarity to find similar products. To do this, you need to convert each product into a "vector" of numbers, using a mathematical model. You can use a similar model for text, images, and other types of data. Once all of these vectors are stored in the database, you can use vector similarity to find similar items.
+
+### Embeddings
+
+This is particularly useful if you're building on top of OpenAI's [GPT-3](https://openai.com/blog/gpt-3-apps/). You can create and store [embeddings](https://platform.openai.com/docs/guides/embeddings) which match the GPT model you're using.
+
+## Usage
+
+### Enable the extension
+
+
+
+
+1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard.
+2. Click on **Extensions** in the sidebar.
+3. Search for "vector" and enable the extension.
+
+
+
+
+```sql
+ -- Example: enable the "vector" extension.
+create extension vector
+with
+ schema extensions;
+
+-- Example: disable the "vector" extension
+drop
+ extension if exists vector;
+```
+
+Even though the SQL code is `create extension`, this is the equivalent of "enabling the extension".
+To disable an extension, call `drop extension`.
+
+
+
+
+## Usage
+
+### Create a table to store vectors
+
+```sql
+create table
+ posts (
+ id serial primary key,
+ title text not null,
+ body text not null,
+ embedding vector (1536)
+ );
+```
+
+### Storing a vector / embedding
+
+In this example we'll generate a vector using the OpenAI API client, then store it in the database using the Supabase client.
+
+```js
+const title = 'First post!'
+const body = 'Hello world!'
+
+// Generate a vector using OpenAI
+const embeddingResponse = await openai.createEmbedding({
+ model: 'text-embedding-ada-002',
+ input: body,
+})
+const [responseData] = embeddingResponse.data.data.
+
+// Store the vector in Postgres
+const { data, error } = await supabase.from('posts').insert({
+ title,
+ body,
+ embedding: responseData.embedding,
+})
+```
+
+## Resources
+
+- Source code: [github.com/pgvector/pgvector](https://github.com/pgvector/pgvector/)
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/database/extensions/plpgsql_check.mdx b/apps/docs/pages/guides/database/extensions/plpgsql_check.mdx
new file mode 100644
index 00000000000..a064f463df6
--- /dev/null
+++ b/apps/docs/pages/guides/database/extensions/plpgsql_check.mdx
@@ -0,0 +1,105 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'plpgsql_check',
+ title: 'plpgsql_check: PL/pgSQL Linter',
+ description: 'Lint PL/pgSQL code',
+}
+
+[plpgsql_check](https://github.com/okbob/plpgsql_check) is a PostgreSQL extension that lints plpgsql for syntax, semantic and other related issues. The tool helps developers to identify and correct errors before executing the code. plpgsql_check is most useful for developers who are working with large or complex SQL codebases, as it can help identify and resolve issues early in the development cycle.
+
+## Enable the extension
+
+
+
+
+1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard.
+2. Click on **Extensions** in the sidebar.
+3. Search for "plpgsql_check" and enable the extension.
+
+
+
+
+{/* prettier-ignore */}
+```sql
+-- Enable the "plpgsql_check" extension
+create extension plpgsql_check;
+
+-- Disable the "plpgsql_check" extension
+drop extension if exists plpgsql_check;
+```
+
+Even though the SQL code is `create extension`, this is the equivalent of "enabling the extension".
+To disable an extension you can call `drop extension`.
+
+
+
+
+## API
+
+- [`plpgsql_check_function( ... )`](https://github.com/okbob/plpgsql_check#active-mode): Scans a function for errors.
+
+`plpgsql_check_function` is highly customizable. For a complete list of available arguments see [the docs](https://github.com/okbob/plpgsql_check#arguments)
+
+## Usage
+
+To demonstrate `plpgsql_check` we can create a function with a known error. In this case we create a function `some_func`, that references a non-existent column `place.created_at`.
+
+{/* prettier-ignore */}
+```sql
+create table place(
+ x float,
+ y float
+);
+
+create or replace function public.some_func()
+ returns void
+ language plpgsql
+as $$
+declare
+ rec record;
+begin
+ for rec in select * from place
+ loop
+ -- Bug: There is no column `created_at` on table `place`
+ raise notice '%', rec.created_at;
+ end loop;
+end;
+$$;
+```
+
+Note that executing the function would not catch the invalid reference error because the `loop` does not execute if no rows are present in the table.
+
+{/* prettier-ignore */}
+```sql
+select public.some_func();
+ some_func
+ ───────────
+
+ (1 row)
+```
+
+Now we can use plpgsql_check's `plpgsql_check_function` function to identify the known error.
+
+{/* prettier-ignore */}
+```sql
+select plpgsql_check_function('public.some_func()');
+
+ plpgsql_check_function
+------------------------------------------------------------
+ error:42703:8:RAISE:record "rec" has no field "created_at"
+ Context: SQL expression "rec.created_at"
+```
+
+## Resources
+
+- Official [`plpgsql_check` documentation](https://github.com/okbob/plpgsql_check)
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/database/extensions/plv8.mdx b/apps/docs/pages/guides/database/extensions/plv8.mdx
index eb408b2c30f..fdb827f85da 100644
--- a/apps/docs/pages/guides/database/extensions/plv8.mdx
+++ b/apps/docs/pages/guides/database/extensions/plv8.mdx
@@ -15,9 +15,8 @@ While Postgres natively runs SQL, it can also run other "procedural languages".
It can be used for database functions, triggers, queries and more.
-## Usage
-### Enable the extension
+## Enable the extension
-### Create `plv8` functions
+## Create `plv8` functions
Functions written in `plv8` are written just like any other PostgreSQL functions, only
with the `language` identifier set to `plv8`.
@@ -135,6 +134,8 @@ returns setof messages as $$
return json_result;
$$ language plv8;
+
+select * from get_messages();
```
## Resources
diff --git a/apps/docs/pages/guides/database/extensions/postgis.mdx b/apps/docs/pages/guides/database/extensions/postgis.mdx
index 6f241b0774e..9e9a65098ad 100644
--- a/apps/docs/pages/guides/database/extensions/postgis.mdx
+++ b/apps/docs/pages/guides/database/extensions/postgis.mdx
@@ -14,9 +14,7 @@ While you may be able to store simple lat/long geographic coordinates as a set o
The additional data types that PostGIS provides include [Point](https://postgis.net/docs/using_postgis_dbmanagement.html#Point), [Polygon](https://postgis.net/docs/using_postgis_dbmanagement.html#Polygon), [Linestring](https://postgis.net/docs/using_postgis_dbmanagement.html#LineString), and many more to represent different types of geographical data. In this guide, we will mainly focus on how to interact with `Point` type, which represents a single set of latitude and longitude. If you are interested in digging deeper, you can learn more about different data types on the [data management section of PostGIS docs](https://postgis.net/docs/using_postgis_dbmanagement.html).
-## Usage
-
-### Enable the extension
+## Enable the extension
You can get started with PostGIS by enabling the PostGIS extension in your Supabase dashboard.
diff --git a/apps/docs/pages/guides/database/extensions/rum.mdx b/apps/docs/pages/guides/database/extensions/rum.mdx
new file mode 100644
index 00000000000..bfe5ede32ee
--- /dev/null
+++ b/apps/docs/pages/guides/database/extensions/rum.mdx
@@ -0,0 +1,163 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'rum',
+ title: 'RUM: improved inverted index for full-text search based on GIN index',
+ description: 'A GIN-like index with additional tree-organized data for each index entry',
+}
+
+[RUM](https://github.com/postgrespro/rum) is an extension which adds a RUM index to Postgresql.
+
+RUM index is based on GIN that stores additional per-entry information in a posting tree. For example, positional information of lexemes or timestamps. In comparison to GIN it can use this information to make faster index-only scans for:
+
+- Phrase search
+- Text search with ranking by text distance operator
+- Text `SELECT`s with ordering by some non-indexed additional column e.g. by timestamp.
+
+RUM works best in scenarios when the possible keys are highly repeatable. I.e. all texts are composed of a
+limited amount of words, so per-lexeme indexing gives significant speed-up in searching texts containing word
+combinations or phrases.
+
+Main operators for ordering are:
+
+tsvector `<=>` tsquery | float4 | Distance between tsvector and tsquery.
+value `<=>` value | float8 | Distance between two values.
+
+Where value is timestamp, timestamptz, int2, int4, int8, float4, float8, money and oid
+
+## Usage
+
+### Enable the extension
+
+You can get started with rum by enabling the extension in your Supabase dashboard.
+
+
+
+
+1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard.
+2. Click on **Extensions** in the sidebar.
+3. Search for "rum" and enable the extension.
+
+
+
+
+```sql
+-- Example: enable the "rum" extension
+create extension rum with schema extensions;
+
+-- Example: disable the "rum" extension
+drop extension if exists rum;
+```
+
+
+
+
+### Syntax
+
+#### For type: tsvector
+
+To understand the following you may need first to see [Official PostgreSQL documentation on text
+search](https://www.postgresql.org/docs/current/functions-textsearch.html)
+
+`rum_tsvector_ops`
+
+```sql
+CREATE TABLE test_rum(t text, a tsvector);
+
+CREATE TRIGGER tsvectorupdate
+BEFORE UPDATE OR INSERT ON test_rum
+FOR EACH ROW EXECUTE PROCEDURE tsvector_update_trigger('a', 'pg_catalog.english', 't');
+
+INSERT INTO test_rum(t) VALUES ('The situation is most beautiful');
+INSERT INTO test_rum(t) VALUES ('It is a beautiful');
+INSERT INTO test_rum(t) VALUES ('It looks like a beautiful place');
+
+CREATE INDEX rumidx ON test_rum USING rum (a rum_tsvector_ops);
+```
+
+And we can execute `tsvector` selects with ordering by text distance operator:
+
+```sql
+SELECT t, a `<=>` to_tsquery('english', 'beautiful | place') AS rank
+ FROM test_rum
+ WHERE a @@ to_tsquery('english', 'beautiful | place')
+ ORDER BY a `<=>` to_tsquery('english', 'beautiful | place');
+ t | rank
+---------------------------------+---------
+ It looks like a beautiful place | 8.22467
+ The situation is most beautiful | 16.4493
+ It is a beautiful | 16.4493
+(3 rows)
+```
+
+`rum_tsvector_addon_ops`
+
+```sql
+CREATE TABLE tsts (id int, t tsvector, d timestamp);
+CREATE INDEX tsts_idx ON tsts USING rum (t rum_tsvector_addon_ops, d)
+ WITH (attach = 'd', to = 't');
+```
+
+Now we can execute the selects with ordering distance operator on attached column:
+
+```sql
+SELECT id, d, d `<=>` '2016-05-16 14:21:25' FROM tsts WHERE t @@ 'wr&qh' ORDER BY d `<=>` '2016-05-16 14:21:25' LIMIT 5;
+ id | d | ?column?
+-----+---------------------------------+---------------
+ 355 | Mon May 16 14:21:22.326724 2016 | 2.673276
+ 354 | Mon May 16 13:21:22.326724 2016 | 3602.673276
+ 371 | Tue May 17 06:21:22.326724 2016 | 57597.326724
+ 406 | Wed May 18 17:21:22.326724 2016 | 183597.326724
+ 415 | Thu May 19 02:21:22.326724 2016 | 215997.326724
+(5 rows)
+```
+
+#### For type: anyarray
+
+`rum_anyarray_ops`
+
+This operator class stores anyarray elements with length of the array. It supports operators `&&`, `@>`, `<@`, `=`, `%` operators. It also supports ordering by `<=>` operator.
+
+```sql
+CREATE TABLE test_array (i int2[]);
+INSERT INTO test_array VALUES ('{}'), ('{0}'), ('{1,2,3,4}'), ('{1,2,3}'), ('{1,2}'), ('{1}');
+CREATE INDEX idx_array ON test_array USING rum (i rum_anyarray_ops);
+```
+
+Now we can execute the query using index scan:
+
+```sql
+SELECT * FROM test_array WHERE i && '{1}' ORDER BY i `<=>` '{1}' ASC;
+ i
+-----------
+ {1}
+ {1,2}
+ {1,2,3}
+ {1,2,3,4}
+(4 rows)
+```
+
+`rum_anyarray_addon_ops`
+
+The does the same with anyarray index as `rum_tsvector_addon_ops` i.e. allows to order select results using distance
+operator by attached column.
+
+## Limitations
+
+`RUM` has slower build and insert times than `GIN` due to:
+
+1. It is bigger due to the additional attributes stored in the index.
+2. It uses generic WAL records.
+
+## Resources
+
+- [Official RUM documentation](https://github.com/postgrespro/rum)
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/database/extensions/timescaledb.mdx b/apps/docs/pages/guides/database/extensions/timescaledb.mdx
new file mode 100644
index 00000000000..265e8662ad4
--- /dev/null
+++ b/apps/docs/pages/guides/database/extensions/timescaledb.mdx
@@ -0,0 +1,99 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'timescaledb',
+ title: 'timescaledb: Time-Series data',
+ description: 'Scalable time-series data storage and analysis',
+}
+
+[timescaledb](https://docs.timescale.com/timescaledb/latest/) is a PostgreSQL extension designed for improved handling of time-series data. It provides a scalable, high-performance solution for storing and querying time-series data on top of a standard PostgreSQL database.
+
+`timescaledb` uses a time-series-aware storage model and indexing techniques to improve performance of PostgreSQL in working with time-series data. The extension divides data into chunks based on time intervals, allowing it to scale efficiently, especially for large data sets. The data is then compressed, optimized for write-heavy workloads, and partitioned for parallel processing. `timescaledb` also includes a set of functions, operators, and indexes that work with time-series data to reduce query times, and make data easier to work with.
+
+## Enable the extension
+
+
+
+
+1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard.
+2. Click on **Extensions** in the sidebar.
+3. Search for "timescaledb" and enable the extension.
+
+
+
+
+{/* prettier-ignore */}
+```sql
+-- Enable the "timescaledb" extension
+create extension timescaledb;
+
+-- Disable the "timescaledb" extension
+drop extension if exists timescaledb;
+```
+
+
+
+
+## Usage
+
+To demonstrate how `timescaledb` works, let's consider a simple example where we have a table that stores temperature data from different sensors. We will create a table named "temperatures" and store data for two sensors.
+
+First we create a hypertable, which is a virtual table that is partitioned into chunks based on time intervals. The hypertable acts as a proxy for the actual table and makes it easy to query and manage time-series data.
+
+{/* prettier-ignore */}
+```sql
+create table temperatures (
+ time timestamp not null,
+ sensor_id int not null,
+ temperature double precision not null
+);
+
+select create_hypertable('temperatures', 'time');
+```
+
+Next, we can populate some values
+
+{/* prettier-ignore */}
+```sql
+insert into temperatures (time, sensor_id, temperature)
+values
+ ('2023-02-14 09:00:00', 1, 23.5),
+ ('2023-02-14 09:00:00', 2, 21.2),
+ ('2023-02-14 09:05:00', 1, 24.5),
+ ('2023-02-14 09:05:00', 2, 22.3),
+ ('2023-02-14 09:10:00', 1, 25.1),
+ ('2023-02-14 09:10:00', 2, 23.9),
+ ('2023-02-14 09:15:00', 1, 24.9),
+ ('2023-02-14 09:15:00', 2, 22.7),
+ ('2023-02-14 09:20:00', 1, 24.7),
+ ('2023-02-14 09:20:00', 2, 23.5);
+```
+
+And finally we can query the table using `timescaledb`'s `time_bucket` function to divide the time-series into intervals of the specified size (in this case, 1 hour) averaging the `temperature` reading within each group.
+
+{/* prettier-ignore */}
+```sql
+select
+ time_bucket('1 hour', time) AS hour,
+ avg(temperature) AS average_temperature
+from
+ temperatures
+where
+ sensor_id = 1
+ and time > NOW() - interval '1 hour'
+group by
+ hour;
+```
+
+## Resources
+
+- Official [`timescaledb` documentation](https://docs.timescale.com/timescaledb/latest/)
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/database/extensions/uuid-ossp.mdx b/apps/docs/pages/guides/database/extensions/uuid-ossp.mdx
index 167e9b92807..b270fdd581f 100644
--- a/apps/docs/pages/guides/database/extensions/uuid-ossp.mdx
+++ b/apps/docs/pages/guides/database/extensions/uuid-ossp.mdx
@@ -13,9 +13,7 @@ The `uuid-ossp` extension can be used to generate a `UUID`.
A `UUID` is a "Universally Unique Identifier" and it is, for practical purposes, unique.
This makes them particularly well suited as Primary Keys. It is occasionally referred to as a `GUID`, which stands for "Globally Unique Identifier".
-## Usage
-
-### Enable the extension
+## Enable the extension
-### The `uuid` type
+## The `uuid` type
Once the extension is enabled, you now have access to a `uuid` type.
-### `uuid_generate_v1()`
+## `uuid_generate_v1()`
Creates a UUID value based on the combination of computer’s MAC address, current timestamp, and a random value.
@@ -67,7 +65,7 @@ Creates a UUID value based on the combination of computer’s MAC address, curre
applications.
-### `uuid_generate_v4()`
+## `uuid_generate_v4()`
Creates UUID values based solely on random numbers. You can also use Postgres's built-in [`gen_random_uuid()`](https://www.postgresql.org/docs/current/functions-uuid.html) function to generate a UUIDv4.
diff --git a/apps/docs/pages/guides/database/extensions/wrappers.mdx b/apps/docs/pages/guides/database/extensions/wrappers.mdx
new file mode 100644
index 00000000000..943b1dd6391
--- /dev/null
+++ b/apps/docs/pages/guides/database/extensions/wrappers.mdx
@@ -0,0 +1,75 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'wrappers',
+ title: 'wrappers: 3rd Party Integrations',
+ description: '3rd party integrations for PostgreSQL ',
+}
+
+[supabase/wrappers](https://supabase.github.io/wrappers/) is a PostgreSQL extension that provides integrations with external sources so you can interact with third-party data using SQL.
+
+For example, the [Stripe wrapper](https://supabase.github.io/wrappers/stripe/) connects to [Stripe's API](https://stripe.com/docs/api) and exposes each endpoint as a SQL table.
+
+{/* prettier-ignore */}
+```sql
+select
+ customer_id
+ currency
+from
+ stripe.customers;
+
+ customer_id | currency
+--------------------+-----------
+ cus_MJiBtCqOF1Bb3F | usd
+(1 row)
+```
+
+## Usage
+
+### Enable the extension
+
+
+
+
+1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard.
+2. Click on **Extensions** in the sidebar.
+3. Search for "wrappers" and enable the extension.
+
+
+
+
+{/* prettier-ignore */}
+```sql
+-- Enable the "wrappers" extension
+create extension wrappers;
+
+-- Disable the "wrappers" extension
+drop extension if exists wrappers;
+```
+
+
+
+
+Instructions for setting up connections to each 3rd party service via SQL are available at the links in the integrations section below.
+A web interface for connecting to external data is coming to Supabase Studio in 2023.
+
+## Integrations
+
+- [Firebase](https://supabase.github.io/wrappers/firebase/)
+- [Stripe](https://supabase.github.io/wrappers/stripe/)
+- Airtable - Coming Soon
+- BigQuery - Coming Soon
+- Clickhouse - Coming Soon
+
+## Resources
+
+- Official [`supabase/wrappers` documentation](https://supabase.github.io/wrappers/)
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/database/full-text-search.mdx b/apps/docs/pages/guides/database/full-text-search.mdx
index c3fa7beaa92..6e2ec9ca087 100644
--- a/apps/docs/pages/guides/database/full-text-search.mdx
+++ b/apps/docs/pages/guides/database/full-text-search.mdx
@@ -4,10 +4,20 @@ export const meta = {
id: 'full-text-search',
title: 'Full Text Search',
description: 'How to use full text search in PostgreSQL.',
+ video: 'https://www.youtube.com/v/b-mgca_2Oe4',
}
Postgres has built-in functions to handle `Full Text Search` queries. This is like a "search engine" within Postgres.
+
+
+
+
## Preparation
For this guide we'll use the following example data:
diff --git a/apps/docs/pages/guides/database/functions.mdx b/apps/docs/pages/guides/database/functions.mdx
index 0204ef78eac..fda28116abe 100644
--- a/apps/docs/pages/guides/database/functions.mdx
+++ b/apps/docs/pages/guides/database/functions.mdx
@@ -4,6 +4,7 @@ export const meta = {
id: 'functions',
title: 'Database Functions',
description: 'Creating and using Postgres functions.',
+ video: 'https://www.youtube.com/v/MJZCCpCYEqk',
}
Postgres has built-in support for [SQL functions](https://www.postgresql.org/docs/current/sql-createfunction.html).
@@ -88,9 +89,8 @@ Reference: [rpc()](../../reference/javascript/rpc)
```dart
-final res = await supabase
- .rpc('hello_world')
- .execute();
+final data = await supabase
+ .rpc('hello_world');
```
Reference: [rpc()](../../reference/dart/rpc)
@@ -199,10 +199,9 @@ const { data, error } = supabase.rpc('get_planets').eq('id', 1)
```dart
-final res = await supabase
+final data = await supabase
.rpc('get_planets')
- .eq('id', 1)
- .execute();
+ .eq('id', 1);
```
@@ -254,9 +253,8 @@ const { data, error } = await supabase.rpc('add_planet', { name: 'Jakku' })
```dart
-final res = await supabase
- .rpc('add_planet', params: { 'name': 'Jakku' })
- .execute();
+final data = await supabase
+ .rpc('add_planet', params: { 'name': 'Jakku' });
```
diff --git a/apps/docs/pages/guides/database/overview.mdx b/apps/docs/pages/guides/database/overview.mdx
index 7efa2ad7513..d4667ed82a3 100644
--- a/apps/docs/pages/guides/database/overview.mdx
+++ b/apps/docs/pages/guides/database/overview.mdx
@@ -34,7 +34,10 @@ You don't have to be a database expert to start using Supabase. Our table view m
Dig into the relationships within your data.
### Clone tables
@@ -42,7 +45,12 @@ Dig into the relationships within your data.
You can duplicate your tables, just like you would inside a spreadsheet.
### The SQL Editor
@@ -50,7 +58,12 @@ You can duplicate your tables, just like you would inside a spreadsheet.
Supabase comes with a SQL Editor. You can also save your favorite queries to run later!
### Additional features
@@ -74,7 +87,12 @@ To expand the functionality of your Postgres database, you can use extensions.
You can enable Postgres extensions with the click of a button within the Supabase dashboard.
[Learn more](/docs/guides/database/extensions) about all the extensions provided on Supabase.
diff --git a/apps/docs/pages/guides/database/replication.mdx b/apps/docs/pages/guides/database/replication.mdx
index e7c349ca91f..15b20d3331a 100644
--- a/apps/docs/pages/guides/database/replication.mdx
+++ b/apps/docs/pages/guides/database/replication.mdx
@@ -21,7 +21,12 @@ Replication is done through _publications_, a method of choosing which changes t
4. Control which tables broadcast changes by selecting **Source** and toggling each table.
## Create a publication
diff --git a/apps/docs/pages/guides/database/tables.mdx b/apps/docs/pages/guides/database/tables.mdx
index 78d1b62115a..9ed2619b47d 100644
--- a/apps/docs/pages/guides/database/tables.mdx
+++ b/apps/docs/pages/guides/database/tables.mdx
@@ -4,6 +4,7 @@ export const meta = {
id: 'tables',
title: 'Tables and Data',
description: 'Creating and using Postgres tables.',
+ video: 'https://www.youtube.com/v/TKwF3IGij5c',
}
Tables are where you store your data.
@@ -41,7 +42,12 @@ and run the SQL queries yourself.
1. Go to the [Table Editor](https://app.supabase.com/project/_/editor) page in the Dashboard.
@@ -77,7 +83,7 @@ You must define the "data type" when you create a column.
### Data types
Every column is a predefined type. PostgreSQL provides many [default types](https://www.postgresql.org/docs/current/datatype.html), and you can even design your own (or use extensions)
-if the default types don't fit your needs.
+if the default types don't fit your needs. You can use any data type that Postgres supports via the SQL editor. We only support a subset of these in the Table Editor in an effort to keep the experience simple for people with less experience with databases.
Show/Hide default data types
@@ -166,7 +172,7 @@ create table movies (
## Loading data
-There are several ways to load data in Supabase. You can load data directly into the database or using the [APIs](../../guides/api).
+There are several ways to load data in Supabase. You can load data directly into the database or using the [APIs](../../guides/database/api).
Use the "Bulk Loading" instructions if you are loading large data sets.
### Basic data loading
@@ -206,7 +212,7 @@ const { data, error } = await supabase
```sql
-final res = await supabase
+await supabase
.from('movies')
.insert([{
name: 'The Empire Strikes Back',
@@ -214,7 +220,7 @@ final res = await supabase
}, {
name: 'Return of the Jedi',
description: 'After a daring mission to rescue Han Solo from Jabba the Hutt, the Rebels dispatch to Endor to destroy the second Death Star.'
- }]).execute();
+ }]);
```
@@ -236,9 +242,18 @@ You would [connect](../../guides/database/connecting-to-postgres#direct-connecti
```bash
psql -h DATABASE_URL -p 5432 -d postgres -U postgres \
- -c "COPY movies FROM './movies.csv';"
+ -c "\COPY movies FROM './movies.csv';"
```
+Additionally use the `DELIMITER`, `HEADER` and `FORMAT` options as defined in the PostgreSQL [COPY](https://www.postgresql.org/docs/current/sql-copy.html) docs.
+
+```bash
+psql -h DATABASE_URL -p 5432 -d postgres -U postgres \
+ -c "\COPY movies FROM './movies.csv' WITH DELIMITER ',' CSV HEADER"
+```
+
+If you receive an error `FATAL: password authentication failed for user "postgres"`, reset your database password in the Database Settings and try again.
+
## Joining tables with Foreign Keys
Tables can be "joined" together using Foreign Keys.
diff --git a/apps/docs/pages/guides/database/webhooks.mdx b/apps/docs/pages/guides/database/webhooks.mdx
index ebbe58ade3d..9cada9989cb 100644
--- a/apps/docs/pages/guides/database/webhooks.mdx
+++ b/apps/docs/pages/guides/database/webhooks.mdx
@@ -4,6 +4,7 @@ export const meta = {
id: 'webhooks',
title: 'Database Webhooks',
description: 'Trigger external payloads on database events.',
+ video: 'https://www.youtube.com/v/codAs9-NeHM',
}
Database Webhooks allow you to send real-time data from your database to another system whenever a table event occurs.
diff --git a/apps/docs/pages/guides/functions.mdx b/apps/docs/pages/guides/functions.mdx
index 9c970cb6bfe..a382f7ccde1 100644
--- a/apps/docs/pages/guides/functions.mdx
+++ b/apps/docs/pages/guides/functions.mdx
@@ -1,6 +1,4 @@
import Layout from '~/layouts/DefaultGuideLayout'
-import { Button, GlassPanel } from 'ui'
-import Link from 'next/link'
export const meta = {
id: 'functions',
diff --git a/apps/docs/pages/guides/functions/best-practices.mdx b/apps/docs/pages/guides/functions/best-practices.mdx
deleted file mode 100644
index b670cd73a05..00000000000
--- a/apps/docs/pages/guides/functions/best-practices.mdx
+++ /dev/null
@@ -1,64 +0,0 @@
-import Layout from '~/layouts/DefaultGuideLayout'
-
-export const meta = {
- id: 'functions-best-practices',
- title: 'Edge Functions Best Practices',
- description: 'Globally distributed TypeScript functions.',
-}
-
-## Database Functions vs Edge Functions
-
-For data-intensive operations we recommend using [Database Functions](/docs/guides/database/functions), which are executed within your database
-and can be called remotely using the [REST and GraphQL API](/docs/guides/api).
-
-For use-cases which require low-latency we recommend [Edge Functions](/docs/guides/functions), which are globally-distributed and can be written in TypeScript.
-
-## Organizing your Edge Functions
-
-We recommend developing “fat functions”. This means that you should develop few large functions, rather than many small functions. One common pattern when developing Functions is that you need to share code between two or more Functions. To do this, you can store any shared code in a folder prefixed with an underscore (`_`). We recommend this folder structure:
-
-```bash
-└── supabase
- ├── functions
- │ ├── _shared
- │ | ├── supabaseAdmin.ts # Supabase client with SERVICE_ROLE key
- │ │ └── supabaseClient.ts # Supabase client with ANON key
- │ ├── function-one # use hyphens to name functions
- │ │ └── index.ts
- │ └── function-two
- │ └── index.ts
- ├── migrations
- └── config.toml
-```
-
-## Naming Edge Functions
-
-We recommend using hyphens to name functions because hyphens are the most URL-friendly of all the naming conventions (snake_case, camelCase, PascalCase).
-
-## CORS (Cross-Origin Resource Sharing)
-
-We recommend adding a check to handle [CORS Preflight](https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request) requests in your edge function to be able to invoke the function from browsers.
-
-See the [example on GitHub](https://github.com/supabase/supabase/blob/master/examples/edge-functions/supabase/functions/browser-with-cors/index.ts).
-
-```ts
-export const corsHeaders = {
- 'Access-Control-Allow-Origin': '*',
- 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey',
-}
-
-serve(async (req) => {
- if (req.method === 'OPTIONS') {
- return new Response('ok', { headers: corsHeaders })
- }
- ...
-})
-```
-
-## Using HTTP Methods
-
-Edge Functions supports `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, and `OPTIONS`. A function can be designed to perform different actions based on a request's HTTP method. See the [example on building a RESTful service](https://github.com/supabase/supabase/tree/master/examples/edge-functions/supabase/functions/restful-tasks) to learn how to handle different HTTP methods in your function.
-
-export const Page = ({ children }) =>
-
-export default Page
diff --git a/apps/docs/pages/guides/functions/cicd-workflow.mdx b/apps/docs/pages/guides/functions/cicd-workflow.mdx
index e902f140370..e168c6c7642 100644
--- a/apps/docs/pages/guides/functions/cicd-workflow.mdx
+++ b/apps/docs/pages/guides/functions/cicd-workflow.mdx
@@ -4,6 +4,7 @@ export const meta = {
id: 'cicd-workflow',
title: 'CI / CD Workflow',
description: 'How to deploy Supabase Edge Functions with a CI / CD pipeline.',
+ video: 'https://www.youtube.com/v/6OMVWiiycLs',
}
As described in the Supabase CLI [Environments Guide](/docs/guides/cli/managing-environments), you can use the [`setup-cli` GitHub Action](https://github.com/marketplace/actions/supabase-cli-action) to run Supabase CLI commands in your GitHub Actions, for example to deploy a Supabase Edge Function:
@@ -35,6 +36,15 @@ jobs:
- run: supabase functions deploy your-function-name --project-ref $PROJECT_ID
```
+
+
+
+
See the [example on GitHub](https://github.com/supabase/supabase/blob/master/examples/edge-functions/.github/workflows/deploy.yaml).
export const Page = ({ children }) =>
diff --git a/apps/docs/pages/guides/functions/cors.mdx b/apps/docs/pages/guides/functions/cors.mdx
new file mode 100644
index 00000000000..766da74ae86
--- /dev/null
+++ b/apps/docs/pages/guides/functions/cors.mdx
@@ -0,0 +1,59 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'functions-cors',
+ title: 'CORS (Cross-Origin Resource Sharing)',
+ description: 'Add CORS headers to invoke Edge Functions from the browser.',
+}
+
+To invoke edge functions from the browser, you need to handle [CORS Preflight](https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request) requests.
+
+See the [example on GitHub](https://github.com/supabase/supabase/blob/master/examples/edge-functions/supabase/functions/browser-with-cors/index.ts).
+
+### Recommended setup
+
+We recommend adding a `cors.ts` file within a [`_shared` folder](/docs/guides/functions/quickstart#organizing-your-edge-functions) which makes it easy to reuse the CORS headers across functions:
+
+```ts cors.ts
+export const corsHeaders = {
+ 'Access-Control-Allow-Origin': '*',
+ 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
+}
+```
+
+You can then import and use the CORS headers within your functions:
+
+```ts index.ts
+import { serve } from 'https://deno.land/std@0.131.0/http/server.ts'
+import { corsHeaders } from '../_shared/cors.ts'
+
+console.log(`Function "browser-with-cors" up and running!`)
+
+serve(async (req) => {
+ // This is needed if you're planning to invoke your function from a browser.
+ if (req.method === 'OPTIONS') {
+ return new Response('ok', { headers: corsHeaders })
+ }
+
+ try {
+ const { name } = await req.json()
+ const data = {
+ message: `Hello ${name}!`,
+ }
+
+ return new Response(JSON.stringify(data), {
+ headers: { ...corsHeaders, 'Content-Type': 'application/json' },
+ status: 200,
+ })
+ } catch (error) {
+ return new Response(JSON.stringify({ error: error.message }), {
+ headers: { ...corsHeaders, 'Content-Type': 'application/json' },
+ status: 400,
+ })
+ }
+})
+```
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/functions/debugging.mdx b/apps/docs/pages/guides/functions/debugging.mdx
new file mode 100644
index 00000000000..9b18d06b1fd
--- /dev/null
+++ b/apps/docs/pages/guides/functions/debugging.mdx
@@ -0,0 +1,20 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'functions-debugging',
+ title: 'Debugging Edge Functions',
+ description: 'Debug Edge Functions in production.',
+}
+
+You can debug your deployed Edge Functions using the ["Functions" section](https://app.supabase.com/project/_/functions) of the Dashboard. There are two debugging tools available:
+
+- Invocations: shows the Request and Response for each execution.
+- Logs: shows any platform events, including deployments and errors.
+
+
+
+When [developing locally](/docs/guides/functions/local-development) you will see error messages and console log statements printed to your local terminal window.
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/functions/examples/cloudflare-turnstile.mdx b/apps/docs/pages/guides/functions/examples/cloudflare-turnstile.mdx
new file mode 100644
index 00000000000..ef38552855f
--- /dev/null
+++ b/apps/docs/pages/guides/functions/examples/cloudflare-turnstile.mdx
@@ -0,0 +1,97 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'examples-cloudflare-turnstile',
+ title: 'Cloudflare Turnstile',
+ description: 'Protecting Forms with Cloudflare Turnstile.',
+ video: 'https://www.youtube.com/v/OwW0znboh60',
+}
+
+
+
+
+
+[Clouflare Turnstile](https://www.cloudflare.com/products/turnstile/) is a friendly, free CAPTCHA replacement, and it works seamlessly with Supabase Edge Functions to protect your forms. [View on GitHub](https://github.com/supabase/supabase/tree/master/examples/edge-functions/supabase/functions/cloudflare-turnstile).
+
+## Setup
+
+- Follow these steps to set up a new site: https://developers.cloudflare.com/turnstile/get-started/
+- Add the Cloudflare Turnstile widget to your site: https://developers.cloudflare.com/turnstile/get-started/client-side-rendering/
+
+## Code
+
+Create a new function in your project:
+
+```bash
+supabase functions new cloudflare-turnstile
+```
+
+And add the code to the `index.ts` file:
+
+```ts index.ts
+import { serve } from 'https://deno.land/std@0.131.0/http/server.ts'
+import { corsHeaders } from '../_shared/cors.ts'
+
+console.log('Hello from Cloudflare Trunstile!')
+
+function ips(req: Request) {
+ return req.headers.get('x-forwarded-for')?.split(/\s*,\s*/)
+}
+
+serve(async (req) => {
+ // This is needed if you're planning to invoke your function from a browser.
+ if (req.method === 'OPTIONS') {
+ return new Response('ok', { headers: corsHeaders })
+ }
+
+ const { token } = await req.json()
+ const clientIps = ips(req) || ['']
+ const ip = clientIps[0]
+
+ // Validate the token by calling the
+ // "/siteverify" API endpoint.
+ let formData = new FormData()
+ formData.append('secret', Deno.env.get('CLOUDFLARE_SECRET_KEY') ?? '')
+ formData.append('response', token)
+ formData.append('remoteip', ip)
+
+ const url = 'https://challenges.cloudflare.com/turnstile/v0/siteverify'
+ const result = await fetch(url, {
+ body: formData,
+ method: 'POST',
+ })
+
+ const outcome = await result.json()
+ console.log(outcome)
+ if (outcome.success) {
+ return new Response('success', { headers: corsHeaders })
+ }
+ return new Response('failure', { headers: corsHeaders })
+})
+```
+
+## Deploy the server-side validation Edge Functions
+
+- https://developers.cloudflare.com/turnstile/get-started/server-side-validation/
+
+```bash
+supabase functions deploy cloudflare-turnstile
+supabase secrets set CLOUDFLARE_TURNSTILE_SECRET_KEY=your_secret_key
+```
+
+## Invoke the function from your site
+
+```js
+const { data, error } = await supabase.functions.invoke('cloudflare-turnstile', {
+ body: { token },
+})
+```
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/functions/examples/connect-to-postgres.mdx b/apps/docs/pages/guides/functions/examples/connect-to-postgres.mdx
new file mode 100644
index 00000000000..3b66a4affd9
--- /dev/null
+++ b/apps/docs/pages/guides/functions/examples/connect-to-postgres.mdx
@@ -0,0 +1,68 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'examples-postgres-on-the-edge',
+ title: 'Connect to Postgres',
+ description: 'Connecting to Postgres from Edge Functions.',
+}
+
+
+
+
+
+Supabase Edge Functions allow you to go beyond HTTP and can connect to your Postgres Database directly!
+
+```ts index.ts
+import * as postgres from 'https://deno.land/x/postgres@v0.14.2/mod.ts'
+import { serve } from 'https://deno.land/std@0.168.0/http/server.ts'
+
+// Get the connection string from the environment variable "DATABASE_URL"
+const databaseUrl = Deno.env.get('DATABASE_URL')!
+
+// Create a database pool with three connections that are lazily established
+const pool = new postgres.Pool(databaseUrl, 3, true)
+
+serve(async (_req) => {
+ try {
+ // Grab a connection from the pool
+ const connection = await pool.connect()
+
+ try {
+ // Run a query
+ const result = await connection.queryObject`SELECT * FROM animals`
+ const animals = result.rows // [{ id: 1, name: "Lion" }, ...]
+ console.log(animals)
+
+ // Encode the result as pretty printed JSON
+ const body = JSON.stringify(
+ animals,
+ (key, value) => (typeof value === 'bigint' ? value.toString() : value),
+ 2
+ )
+
+ // Return the response with the correct content type header
+ return new Response(body, {
+ status: 200,
+ headers: {
+ 'Content-Type': 'application/json; charset=utf-8',
+ },
+ })
+ } finally {
+ // Release the connection back into the pool
+ connection.release()
+ }
+ } catch (err) {
+ console.error(err)
+ return new Response(String(err?.message ?? err), { status: 500 })
+ }
+})
+```
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/functions/examples/github-actions.mdx b/apps/docs/pages/guides/functions/examples/github-actions.mdx
new file mode 100644
index 00000000000..94777f4366d
--- /dev/null
+++ b/apps/docs/pages/guides/functions/examples/github-actions.mdx
@@ -0,0 +1,50 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'examples-github-actions',
+ title: 'GitHub Actions',
+ description: 'Deploying Edge Functions with GitHub Actions.',
+ video: 'https://www.youtube.com/v/l2KlzGrhB6w',
+}
+
+
+
+
+
+Use the Supabase CLI together with GitHub Actions to automatically deploy our Supabase Edge Functions. [View on GitHub](https://github.com/supabase/supabase/tree/master/examples/edge-functions/supabase/functions/github-action-deploy).
+
+```yaml deploy.yaml
+name: Deploy Function
+
+on:
+ push:
+ branches:
+ - main
+ workflow_dispatch:
+
+jobs:
+ deploy:
+ runs-on: ubuntu-latest
+
+ env:
+ SUPABASE_ACCESS_TOKEN: YOUR_SUPABASE_ACCESS_TOKEN
+ PROJECT_ID: YOUR_SUPABASE_PROJECT_ID
+
+ steps:
+ - uses: actions/checkout@v3
+
+ - uses: supabase/setup-cli@v1
+ with:
+ version: 1.0.0
+
+ - run: supabase functions deploy github-action-deploy --project-ref $PROJECT_ID
+```
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/functions/examples/og-image.mdx b/apps/docs/pages/guides/functions/examples/og-image.mdx
index 2974d5ef853..efa8eb99e59 100644
--- a/apps/docs/pages/guides/functions/examples/og-image.mdx
+++ b/apps/docs/pages/guides/functions/examples/og-image.mdx
@@ -4,6 +4,7 @@ export const meta = {
id: 'examples-og-image',
title: 'Generating OG Images',
description: 'Generate Open Graph images with Deno and Supabase Edge Functions.',
+ video: 'https://www.youtube.com/v/jZgyOJGWayQ',
}
@@ -17,6 +18,10 @@ export const meta = {
Generate Open Graph images with Deno and Supabase Edge Functions. [View on GitHub](https://github.com/supabase/supabase/tree/master/examples/edge-functions/supabase/functions/opengraph).
+## Code
+
+Create a `handler.tsx` file to construct the OG image in React:
+
```tsx handler.tsx
import React from 'https://esm.sh/react@18.2.0'
import { ImageResponse } from 'https://deno.land/x/og_edge@0.0.4/mod.ts'
@@ -42,6 +47,8 @@ export default function handler(req: Request) {
}
```
+Create an `index.ts` file to execute the handler on incoming requests:
+
```ts index.ts
import { serve } from 'https://deno.land/std@0.131.0/http/server.ts'
import handler from './handler.tsx'
diff --git a/apps/docs/pages/guides/functions/examples/storage-caching.mdx b/apps/docs/pages/guides/functions/examples/storage-caching.mdx
new file mode 100644
index 00000000000..c9cf4de7be9
--- /dev/null
+++ b/apps/docs/pages/guides/functions/examples/storage-caching.mdx
@@ -0,0 +1,23 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'examples-storage-caching',
+ title: 'Caching Images with Supabase Storage CDN',
+ description: 'Integrate Edge Functions with Supabase Storage to cache images on the Edge (CDN).',
+ video: 'https://www.youtube.com/v/wW6L52v9Ldo',
+}
+
+
+
+
+
+Integrate Edge Functions with Supabase Storage to cache images on the Edge (CDN). [View on GitHub](https://github.com/supabase/supabase/tree/master/examples/edge-functions/supabase/functions/og-image-with-storage-cdn).
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/functions/examples/stripe-webhooks.mdx b/apps/docs/pages/guides/functions/examples/stripe-webhooks.mdx
new file mode 100644
index 00000000000..b71a4ca2b36
--- /dev/null
+++ b/apps/docs/pages/guides/functions/examples/stripe-webhooks.mdx
@@ -0,0 +1,23 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'examples-stripe-webhooks',
+ title: 'Handling Stripe Webhooks',
+ description: 'Handling signed Stripe Webhooks with Edge Functions.',
+ video: 'https://www.youtube.com/v/6OMVWiiycLs',
+}
+
+
+
+
+
+Handling signed Stripe Webhooks with Edge Functions. [View on GitHub](https://github.com/supabase/supabase/blob/master/examples/edge-functions/supabase/functions/stripe-webhooks/index.ts).
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/functions/examples/telegram-bot.mdx b/apps/docs/pages/guides/functions/examples/telegram-bot.mdx
new file mode 100644
index 00000000000..ad87c1a7103
--- /dev/null
+++ b/apps/docs/pages/guides/functions/examples/telegram-bot.mdx
@@ -0,0 +1,23 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'examples-telegram-bot',
+ title: 'Telegram Bot',
+ description: 'Building a Telegram Bot with Edge Functions.',
+ video: 'https://www.youtube.com/v/AWfE3a9J_uo',
+}
+
+
+
+
+
+Handle Telegram Bot Webhooks with the [grammY framework](https://grammy.dev/). grammY is an open source Telegram Bot Framework which makes it easy to handle and respond to incoming messages. [View on GitHub](https://github.com/supabase/supabase/tree/master/examples/edge-functions/supabase/functions/telegram-bot).
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/functions/examples/upstash-redis.mdx b/apps/docs/pages/guides/functions/examples/upstash-redis.mdx
new file mode 100644
index 00000000000..bbe58dd4dd1
--- /dev/null
+++ b/apps/docs/pages/guides/functions/examples/upstash-redis.mdx
@@ -0,0 +1,90 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ title: 'Upstash Redis',
+ description: 'Build an Edge Functions Counter with Upstash Redis.',
+}
+
+A Redis counter example that stores a [hash](https://redis.io/commands/hincrby/) of function invocation count per region.
+
+## Redis database setup
+
+Create a Redis database using the [Upstash Console](https://console.upstash.com/) or [Upstash CLI](https://github.com/upstash/cli).
+
+Select the `Global` type to minimize the latency from all edge locations. Copy the `UPSTASH_REDIS_REST_URL` and `UPSTASH_REDIS_REST_TOKEN` to your .env file.
+
+You'll find them under **Details > REST API > .env**.
+
+```bash
+cp supabase/functions/upstash-redis-counter/.env.example supabase/functions/upstash-redis-counter/.env
+```
+
+## Code
+
+Make sure you have the latest version of the [Supabase CLI installed](/docs/guides/cli#installation).
+
+Create a new function in your project:
+
+```bash
+supabase functions new upstash-redis-counter
+```
+
+And add the code to the `index.ts` file:
+
+```ts index.ts
+import { serve } from 'https://deno.land/std@0.177.0/http/server.ts'
+import { Redis } from 'https://deno.land/x/upstash_redis@v1.19.3/mod.ts'
+
+console.log(`Function "upstash-redis-counter" up and running!`)
+
+serve(async (_req) => {
+ try {
+ const redis = new Redis({
+ url: Deno.env.get('UPSTASH_REDIS_REST_URL')!,
+ token: Deno.env.get('UPSTASH_REDIS_REST_TOKEN')!,
+ })
+
+ const deno_region = Deno.env.get('DENO_REGION')
+ if (deno_region) {
+ // Increment region counter
+ await redis.hincrby('supa-edge-counter', deno_region, 1)
+ } else {
+ // Increment localhost counter
+ await redis.hincrby('supa-edge-counter', 'localhost', 1)
+ }
+
+ // Get all values
+ const counterHash: Record | null = await redis.hgetall('supa-edge-counter')
+ const counters = Object.entries(counterHash!)
+ .sort(([, a], [, b]) => b - a) // sort desc
+ .reduce((r, [k, v]) => ({ total: r.total + v, regions: { ...r.regions, [k]: v } }), {
+ total: 0,
+ regions: {},
+ })
+
+ return new Response(JSON.stringify({ counters }), { status: 200 })
+ } catch (error) {
+ return new Response(JSON.stringify({ error: error.message }), { status: 200 })
+ }
+})
+```
+
+## Run locally
+
+```bash
+supabase start
+supabase functions serve upstash-redis-counter --no-verify-jwt --env-file supabase/functions/upstash-redis-counter/.env
+```
+
+Navigate to http://localhost:54321/functions/v1/upstash-redis-counter.
+
+## Deploy
+
+```bash
+supabase functions deploy upstash-redis-counter --no-verify-jwt
+supabase secrets set --env-file supabase/functions/upstash-redis-counter/.env
+```
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/functions/import-maps.mdx b/apps/docs/pages/guides/functions/import-maps.mdx
new file mode 100644
index 00000000000..6d61e774b55
--- /dev/null
+++ b/apps/docs/pages/guides/functions/import-maps.mdx
@@ -0,0 +1,63 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'functions-import-maps',
+ title: 'Import Maps',
+ description: 'Using Import Maps.',
+ video: 'https://www.youtube.com/v/ILr3cneZuFk',
+}
+
+
+
+
+
+Since [Supabase CLI](https://supabase.com/docs/guides/resources/supabase-cli) version `1.33.0` [import maps](https://deno.land/manual@v1.29.2/node/import_maps) can be used with Supabase Edge Functions.
+
+[Import maps](https://github.com/WICG/import-maps#the-import-map) is a web-platform standard that allows you to use bare specifiers with Deno without having to install the Node.js package locally.
+
+So if we want to do the following in our code:
+
+```ts, ignore
+import lodash from "lodash";
+```
+
+We can accomplish this using an import map, and we don't even have to install the `lodash` package locally. We would want to create a JSON file (for example **import_map.json**) with the following:
+
+```json
+{
+ "imports": {
+ "lodash": "https://cdn.skypack.dev/lodash"
+ }
+}
+```
+
+## Import Map Placement
+
+We recommend creating one `import_map.json` within the `/supabase/functions` folder (see [Organizing your Edge Functions](/docs/guides/functions/quickstart#organizing-your-edge-functions)), similar to a `package.json` file, to define imports that can be used across all of your project's functions.
+
+Alternatively, you can create one `import_map.json` file in each function folder, which will take priority over a top-level file.
+
+Lastly, you can override this default behaviour by providing the `--import-map ` flag to the `serve` and `deploy` commands.
+
+## Visual Studio Code Configuration
+
+In order for vscode to understand the imports correctly, you need to specify the `deno.importMap` flag in your `.vscode/settings.json` file:
+
+```json settings.json
+{
+ "deno.enable": true,
+ "deno.unstable": true,
+ "deno.importMap": "./supabase/functions/import_map.json"
+}
+```
+
+For a full guide on developing with Deno in Visual Studio Code, see [this guide](https://deno.land/manual@v1.25.4/vscode_deno).
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/functions/local-development.mdx b/apps/docs/pages/guides/functions/local-development.mdx
new file mode 100644
index 00000000000..e342abe9b14
--- /dev/null
+++ b/apps/docs/pages/guides/functions/local-development.mdx
@@ -0,0 +1,60 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'functions-local-development',
+ title: 'Local Development',
+ description: 'Run your Edge Functions locally.',
+}
+
+You can run your Edge Function locally using [`supabase functions serve`](/docs/reference/cli/usage#supabase-functions-serve):
+
+```bash
+supabase start # start the supabase stack
+supabase functions serve hello-world # start the Function watcher
+```
+
+The `functions serve` command has hot-reloading capabilities. It will watch for any changes to your files and restart the Deno server.
+
+### Invoking Edge Functions locally
+
+While serving your local Edge Function, you can invoke it using curl:
+
+```bash
+curl --request POST 'http://localhost:54321/functions/v1/hello-world' \
+ --header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24ifQ.625_WdcF3KHqz5amU0x2X5WWHP-OEs_4qj0ssLNHzTs' \
+ --header 'Content-Type: application/json' \
+ --data '{ "name":"Functions" }'
+```
+
+or using one of the [client libraries](/docs#reference-documentation), e.g. using [supabase-js](/docs/reference/javascript/functions-invoke):
+
+```js
+import { createClient } from '@supabase/supabase-js'
+
+// Use the credentials outputted in your terminal when running `supabase start`
+const supabase = createClient(
+ 'http://localhost:54321',
+ 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0'
+)
+
+const { data, error } = await supabase.functions.invoke('hello-world', {
+ body: { name: 'Functions' },
+})
+```
+
+You should see the response `{ "message":"Hello Functions!" }`.
+
+If you execute the function with a different payload the response will change.
+Modify the `--data '{"name":"Functions"}'` line to `--data '{"name":"World"}'` and try invoking the command again!
+
+
+
+- Edge Functions don't serve HTML content (`GET` requests that return `text/html` are rewritten to `text/plain`).
+- The `Authorization` header is required. You can use either the `ANON` key, the `SERVICE_ROLE` key, or a logged-in user's JWT.
+- The Function is proxied through the local API (`http://localhost:54321`)
+
+
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/functions/quickstart.mdx b/apps/docs/pages/guides/functions/quickstart.mdx
index 55b9ed617c6..f878b495c65 100644
--- a/apps/docs/pages/guides/functions/quickstart.mdx
+++ b/apps/docs/pages/guides/functions/quickstart.mdx
@@ -3,8 +3,9 @@ import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'functions-quickstart',
title: 'Edge Functions Quickstart',
- description: 'Globally distributed TypeScript functions.',
+ description: 'Globally distributed TypeScript Functions.',
sidebar_label: 'Quickstart',
+ video: 'https://www.youtube.com/v/rzglqRdZUQE',
}
Learn how to build an Edge Function locally and deploy it to the Supabase Platform in less than 7 minutes.
@@ -28,7 +29,7 @@ Follow the steps to prepare your Supabase project on your local machine.
- Link to your Remote Project using the command `supabase link --project-ref your-project-ref`. [Docs](/docs/reference/cli/usage#supabase-link).
- Optional: Setup your environment: Follow [this setup guide](https://deno.land/manual/getting_started/setup_your_environment) to integrate the Deno language server with your editor.
-## Create a function
+## Create an Edge Function
Let's create a new Edge Function called `hello-world` inside your project:
@@ -55,9 +56,9 @@ If you want to use Edge Functions to handle webhooks (e.g. [Stripe payment webho
-## Execute remote functions
+## Invoking remote functions
-You can execute Edge Functions using curl. Copy the curl command from the Dashboard. It should look like this:
+You can invoke Edge Functions using curl:
```bash
curl --request POST 'https://.functions.supabase.co/hello-world' \
@@ -66,141 +67,67 @@ curl --request POST 'https://.functions.supabase.co/hello-world' \
--data '{ "name":"Functions" }'
```
+
+
If you receive an error `Invalid JWT`, find the `ANON_KEY` of your project in the Dashboard under `Settings > API`.
+
+
+or using one of the [client libraries](/docs#reference-documentation), e.g. using [supabase-js](/docs/reference/javascript/functions-invoke):
+
+```js
+import { createClient } from '@supabase/supabase-js'
+
+// Create a single supabase client for interacting with your database
+const supabase = createClient('https://xyzcompany.supabase.co', 'public-anon-key')
+
+const { data, error } = await supabase.functions.invoke('hello-world', {
+ body: { name: 'Functions' },
+})
+```
+
After invoking your Edge Function you should see the response `{ "message":"Hello Functions!" }`.
-## Debug functions
+## Database Functions vs Edge Functions
-You can debug your deployed Edge Functions using the "Functions" section of the Dashboard. There are two types debugging tools available:
+For data-intensive operations we recommend using [Database Functions](/docs/guides/database/functions), which are executed within your database
+and can be called remotely using the [REST and GraphQL API](/docs/guides/api).
-- Invocations: shows the Request and Response for each execution.
-- Logs: shows any platform events, including deployments and errors.
+For use-cases which require low-latency we recommend [Edge Functions](/docs/guides/functions), which are globally-distributed and can be written in TypeScript.
-
+## Organizing your Edge Functions
-## Develop locally
-
-You can run your Edge Function locally using [`supabase functions serve`](/docs/reference/cli/usage#supabase-functions-serve):
+We recommend developing “fat functions”. This means that you should develop few large functions, rather than many small functions. One common pattern when developing Functions is that you need to share code between two or more Functions. To do this, you can store any shared code in a folder prefixed with an underscore (`_`). We recommend this folder structure:
```bash
-supabase start # start the supabase stack
-supabase functions serve hello-world # start the Function watcher
+└── supabase
+ ├── functions
+ │ ├── import_map.json # A top-level import map to use across functions.
+ │ ├── _shared
+ │ │ ├── supabaseAdmin.ts # Supabase client with SERVICE_ROLE key.
+ │ │ └── supabaseClient.ts # Supabase client with ANON key.
+ │ │ └── cors.ts # Reusable CORS headers.
+ │ ├── function-one # Use hyphens to name functions.
+ │ │ └── index.ts
+ │ └── function-two
+ │ └── index.ts
+ ├── migrations
+ └── config.toml
```
-The `functions serve` command has hot-reloading capabilities. It will watch for any changes to your files and restart the Deno server.
+## Naming Edge Functions
-### Invoke functions locally
+We recommend using hyphens to name functions because hyphens are the most URL-friendly of all the naming conventions (snake_case, camelCase, PascalCase).
-While serving your local Function, you can execute it using curl:
+## Using HTTP Methods
-```bash
-curl --request POST 'http://localhost:54321/functions/v1/hello-world' \
- --header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24ifQ.625_WdcF3KHqz5amU0x2X5WWHP-OEs_4qj0ssLNHzTs' \
- --header 'Content-Type: application/json' \
- --data '{ "name":"Functions" }'
-```
-
-You should see the response `{ "message":"Hello Functions!" }`.
-
-
-Implementation details
-
-- Edge Functions don't serve HTML content (`GET` requests that return `text/html` are rewritten to `text/plain`).
-- The `Authorization` header is required. You can use either the `ANON` key, the `SERVICE_ROLE` key, or a logged-in user's JWT.
-- The Function is proxied through the local API (`http://localhost:54321`)
-
-
-
-
-If you execute Function with a different payload the response will change.
-Modify the `--data '{"name":"Functions"}'` line to `--data '{"name":"World"}'` and try invoking the command again!
-
-## Secrets and Environment Variables
-
-It's common that you will need to use sensitive information or environment-specific variables inside your Edge Functions. You can access these using Deno's built-in handler
-
-```js
-Deno.env.get(MY_SECRET_NAME)
-```
-
-### Default secrets
-
-By default, Edge Functions have access to these secrets:
-
-- `SUPABASE_URL`: The API gateway for your Supabase project.
-- `SUPABASE_ANON_KEY`: The `anon` key for your Supabase API. This is safe to use in a browser when you have [Row Level Security](/docs/guides/auth/row-level-security) enabled.
-- `SUPABASE_SERVICE_ROLE_KEY`: The `service_role` key for your Supabase API. This is safe to use in Edge Functions, but it should NEVER be used in a browser. This key will bypass [Row Level Security](/docs/guides/auth/row-level-security).
-- `SUPABASE_DB_URL`: The URL for your [PostgreSQL database](/docs/guides/database). You can use this to connect directly to your database.
-
-### Local secrets
-
-Let's create a local file for storing our secrets, and inside it we can store a secret `MY_NAME`:
-
-```jsx
-echo "MY_NAME=Yoda" >> ./supabase/.env.local
-```
-
-This creates a new file `./supabase/.env.local` for storing your local development secrets.
-
-
-
-Never check your .env files into Git!
-
-
-
-Now let's access this environment variable `MY_NAME` inside our Function. Anywhere in your function, add this line:
-
-```jsx
-console.log(Deno.env.get('MY_NAME'))
-```
-
-Now we can invoke our function locally, by serving it with our new `.env.local` file:
-
-```bash
-supabase functions serve hello-world --env-file ./supabase/.env.local
-```
-
-When the function starts you should see the name “Yoda” output to the terminal.
-
-### Production secrets
-
-Let's create a `.env` for production. In this case we'll just use the same as our local secrets:
-
-```bash
-cp ./supabase/.env.local ./supabase/.env
-```
-
-This creates a new file `./supabase/.env` for storing your production secrets.
-
-
-
-Never check your `.env` files into Git!
-
-
-
-Let's push all the secrets from the `.env` file to our remote project using [`supabase secrets set`](/docs/reference/cli/usage#supabase-secrets-set):
-
-```bash
-supabase secrets set --env-file ./supabase/.env
-
-# You can also set secrets individually using:
-supabase secrets set MY_NAME=Chewbacca
-```
-
-You don't need to re-deploy after setting your secrets.
-
-To see all the secrets which you have set remotely, use [`supabase secrets list`](/docs/reference/cli/usage#supabase-secrets-list):
-
-```bash
-supabase secrets list
-```
+Edge Functions supports `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, and `OPTIONS`. A function can be designed to perform different actions based on a request's HTTP method. See the [example on building a RESTful service](https://github.com/supabase/supabase/tree/master/examples/edge-functions/supabase/functions/restful-tasks) to learn how to handle different HTTP methods in your function.
## Limitations
- Deno Deploy limitations
- Deno does not support outgoing connections to ports `25`, `465`, and `587`.
- - Cannot write to File System
+ - Cannot read or write to File System
- Edge Functions
- Local development - only one function at a time
- Serving of HTML content is not supported (`GET` requests that return `text/html` will be rewritten to `text/plain`).
diff --git a/apps/docs/pages/guides/functions/schedule-functions.mdx b/apps/docs/pages/guides/functions/schedule-functions.mdx
new file mode 100644
index 00000000000..3339fcab8fa
--- /dev/null
+++ b/apps/docs/pages/guides/functions/schedule-functions.mdx
@@ -0,0 +1,51 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'schedule-functions',
+ title: 'Schedule Edge Functions',
+ description: 'Schedule Edge Functions with pg_cron.',
+}
+
+
+
+
+
+The hosted Supabase Platform supports the [`pg_cron` extension](/docs/guides/database/extensions/pgcron), a simple cron-based job scheduler for PostgreSQL that runs inside the database.
+
+In combination with the [`pg_net` extension](/docs/guides/database/extensions/pgnet), this allows us to invoke Edge Functions periodically on a set schedule.
+
+## Examples
+
+### Invoke an Edge Function every minute
+
+Make a POST request to a Supabase Edge Function every minute:
+
+```sql
+select
+ cron.schedule(
+ 'invoke-function-every-minute',
+ '* * * * *', -- every minute
+ $$
+ select
+ net.http_post(
+ url:='https://project-ref.functions.supabase.co/function-name',
+ headers:='{"Content-Type": "application/json", "Authorization": "Bearer YOUR_ANON_KEY"}'::jsonb,
+ body:=concat('{"time": "', now(), '"}')::jsonb
+ ) as request_id;
+ $$
+ );
+```
+
+## Resources
+
+- [`pg_net` extension](/docs/guides/database/extensions/pgnet)
+- [`pg_cron` extension](/docs/guides/database/extensions/pgcron)
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/functions/secrets.mdx b/apps/docs/pages/guides/functions/secrets.mdx
new file mode 100644
index 00000000000..3d60a239a8b
--- /dev/null
+++ b/apps/docs/pages/guides/functions/secrets.mdx
@@ -0,0 +1,89 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'functions-secrets',
+ title: 'Secrets and Environment Variables',
+ description: 'Managing secrets and environment variables.',
+}
+
+It's common that you will need to use sensitive information or environment-specific variables inside your Edge Functions. You can access these using Deno's built-in handler
+
+```js
+Deno.env.get(MY_SECRET_NAME)
+```
+
+### Default secrets
+
+By default, Edge Functions have access to these secrets:
+
+- `SUPABASE_URL`: The API gateway for your Supabase project.
+- `SUPABASE_ANON_KEY`: The `anon` key for your Supabase API. This is safe to use in a browser when you have [Row Level Security](/docs/guides/auth/row-level-security) enabled.
+- `SUPABASE_SERVICE_ROLE_KEY`: The `service_role` key for your Supabase API. This is safe to use in Edge Functions, but it should NEVER be used in a browser. This key will bypass [Row Level Security](/docs/guides/auth/row-level-security).
+- `SUPABASE_DB_URL`: The URL for your [PostgreSQL database](/docs/guides/database). You can use this to connect directly to your database.
+
+### Local secrets
+
+Let's create a local file for storing our secrets, and inside it we can store a secret `MY_NAME`:
+
+```bash
+echo "MY_NAME=Yoda" >> ./supabase/.env.local
+```
+
+This creates a new file `./supabase/.env.local` for storing your local development secrets.
+
+
+
+Never check your .env files into Git!
+
+
+
+Now let's access this environment variable `MY_NAME` inside our Function. Anywhere in your function, add this line:
+
+```jsx
+console.log(Deno.env.get('MY_NAME'))
+```
+
+Now we can invoke our function locally, by serving it with our new `.env.local` file:
+
+```bash
+supabase functions serve hello-world --env-file ./supabase/.env.local
+```
+
+When the function starts you should see the name “Yoda” output to the terminal.
+
+### Production secrets
+
+Let's create a `.env` for production. In this case we'll just use the same as our local secrets:
+
+```bash
+cp ./supabase/.env.local ./supabase/.env
+```
+
+This creates a new file `./supabase/.env` for storing your production secrets.
+
+
+
+Never check your `.env` files into Git!
+
+
+
+Let's push all the secrets from the `.env` file to our remote project using [`supabase secrets set`](/docs/reference/cli/usage#supabase-secrets-set):
+
+```bash
+supabase secrets set --env-file ./supabase/.env
+
+# You can also set secrets individually using:
+supabase secrets set MY_NAME=Chewbacca
+```
+
+You don't need to re-deploy after setting your secrets.
+
+To see all the secrets which you have set remotely, use [`supabase secrets list`](/docs/reference/cli/usage#supabase-secrets-list):
+
+```bash
+supabase secrets list
+```
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/getting-started.mdx b/apps/docs/pages/guides/getting-started.mdx
index 5cb4efb70e2..820eae29e0c 100644
--- a/apps/docs/pages/guides/getting-started.mdx
+++ b/apps/docs/pages/guides/getting-started.mdx
@@ -36,7 +36,23 @@ export const meta = {
+
+export const quickstarts = [
+ {
+ title: 'React',
+ href: '/guides/getting-started/quickstarts/reactjs',
+ description:
+ 'Learn how to create a Supabase project, add some sample data to your database, and query the data from a React app.',
+ icon: '/docs/img/icons/react-icon',
+ },
+ {
+ title: 'NextJS',
+ href: '/guides/getting-started/quickstarts/nextjs',
+ description:
+ 'Learn how to create a Supabase project, add some sample data to your database, and query the data from a NextJS app.',
+ icon: '/docs/img/icons/nextjs-icon',
+ },
+ {
+ title: 'Flutter',
+ href: '/guides/getting-started/quickstarts/flutter',
+ description:
+ 'Learn how to create a Supabase project, add some sample data to your database, and query the data from a Flutter app.',
+ icon: '/docs/img/icons/flutter-icon',
+ },
+]
+
export const webapps = [
{
title: 'NextJS',
@@ -85,30 +141,33 @@ export const webapps = [
title: 'Angular',
href: '/guides/getting-started/tutorials/with-angular',
description:
- 'Learn how to build a user management app with Nuxt 3 and Supabase Database, Auth, and Storage functionality.',
+ 'Learn how to build a user management app with Angular and Supabase Database, Auth, and Storage functionality.',
icon: '/docs/img/icons/angular-icon',
},
{
title: 'RedwoodJS',
href: '/guides/getting-started/tutorials/with-redwoodjs',
description:
- 'Learn how to build a user management app with Nuxt 3 and Supabase Database, Auth, and Storage functionality.',
+ 'Learn how to build a user management app with RedwoodJS and Supabase Database, Auth, and Storage functionality.',
icon: '/docs/img/icons/redwood-icon',
},
{
title: 'Svelte',
href: '/guides/getting-started/tutorials/with-svelte',
description:
- 'Learn how to build a user management app with Nuxt 3 and Supabase Database, Auth, and Storage functionality.',
+ 'Learn how to build a user management app with Svelte and Supabase Database, Auth, and Storage functionality.',
icon: '/docs/img/icons/svelte-icon',
},
{
title: 'SvelteKit',
href: '/guides/getting-started/tutorials/with-sveltekit',
description:
- 'Learn how to build a user management app with Nuxt 3 and Supabase Database, Auth, and Storage functionality.',
+ 'Learn how to build a user management app with SvelteKit and Supabase Database, Auth, and Storage functionality.',
icon: '/docs/img/icons/svelte-icon',
},
+]
+
+export const mobile = [
{
title: 'Flutter',
href: '/guides/getting-started/tutorials/with-flutter',
@@ -120,28 +179,28 @@ export const webapps = [
title: 'Expo',
href: '/guides/getting-started/tutorials/with-expo',
description:
- 'Learn how to build a user management app with React and Supabase Database, Auth, and Storage functionality.',
+ 'Learn how to build a user management app with Expo and Supabase Database, Auth, and Storage functionality.',
icon: '/docs/img/icons/expo-icon',
},
{
title: 'Ionic React',
href: '/guides/getting-started/tutorials/with-ionic-react',
description:
- 'Learn how to build a user management app with React and Supabase Database, Auth, and Storage functionality.',
+ 'Learn how to build a user management app with Ionic React and Supabase Database, Auth, and Storage functionality.',
icon: '/docs/img/icons/ionic-icon',
},
{
title: 'Ionic Vue',
href: '/guides/getting-started/tutorials/with-ionic-vue',
description:
- 'Learn how to build a user management app with Flutter and Supabase Database, Auth, and Storage functionality.',
+ 'Learn how to build a user management app with Ionic Vue and Supabase Database, Auth, and Storage functionality.',
icon: '/docs/img/icons/ionic-icon',
},
{
title: 'Ionic Angular',
href: '/guides/getting-started/tutorials/with-ionic-angular',
description:
- 'Learn how to build a user management app with NextJS and Supabase Database, Auth, and Storage functionality.',
+ 'Learn how to build a user management app with Ionic Angular and Supabase Database, Auth, and Storage functionality.',
icon: '/docs/img/icons/ionic-icon',
},
]
diff --git a/apps/docs/pages/guides/getting-started/features.mdx b/apps/docs/pages/guides/getting-started/features.mdx
index c1e1a0dcfc9..4cf63f342c0 100755
--- a/apps/docs/pages/guides/getting-started/features.mdx
+++ b/apps/docs/pages/guides/getting-started/features.mdx
@@ -84,11 +84,11 @@ Build login and registration pages with custom themes. [Docs](/docs/guides/auth/
### Auto-generated REST API
-RESTful APIs are autogenerated from your database, without a single line of code. [Docs](/docs/guides/api#rest-api-overview).
+RESTful APIs are autogenerated from your database, without a single line of code. [Docs](/docs/guides/database/api#rest-api-overview).
### Auto-generated GraphQL API
-Fast GraphQL APIs using our custom Postgres GraphQL extension. [Docs](/docs/guides/api#graphql-api-overview).
+Fast GraphQL APIs using our custom Postgres GraphQL extension. [Docs](/docs/guides/database/api#graphql-api-overview).
### Realtime Database changes
@@ -96,11 +96,11 @@ Receive your database changes through websockets. [Docs](/docs/guides/realtime/p
### User Broadcasting
-Send messages between connected users through websockets. [Docs](/docs/guides/realtime#broadcast).
+Send messages between connected users through websockets. [Docs](/docs/guides/realtime/broadcast).
### User Presence
-Synchronize shared state across your users, including online status and typing indicators. [Docs](/docs/guides/realtime#presence).
+Synchronize shared state across your users, including online status and typing indicators. [Docs](/docs/guides/realtime/presence).
### Client libraries
diff --git a/apps/docs/pages/guides/getting-started/quickstarts/solidjs.mdx b/apps/docs/pages/guides/getting-started/quickstarts/solidjs.mdx
new file mode 100644
index 00000000000..d72aa4da959
--- /dev/null
+++ b/apps/docs/pages/guides/getting-started/quickstarts/solidjs.mdx
@@ -0,0 +1,140 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+import StepHikeCompact from '~/components/StepHikeCompact'
+
+export const meta = {
+ title: 'Use Supabase with SolidJS',
+ subtitle:
+ 'Learn how to create a Supabase project, add some sample data to your database, and query the data from a SolidJS app.',
+ breadcrumb: 'Framework Quickstarts',
+}
+
+
+
+
+
+
+ [Create a new project](https://app.supabase.com) in the Supabase Dashboard.
+
+ After your project is ready, create a table in your Supabase database using the [SQL Editor](https://app.supabase.com/project/_/sql) in the Dashboard. Use the following SQL statement to create a `countries` table with some sample data.
+
+
+
+
+
+ ```sql SQL_EDITOR
+ -- Create the table
+ CREATE TABLE countries (
+ id SERIAL PRIMARY KEY,
+ name VARCHAR(255) NOT NULL
+ );
+ -- Insert some sample data into the table
+ INSERT INTO countries (name) VALUES ('United States');
+ INSERT INTO countries (name) VALUES ('Canada');
+ INSERT INTO countries (name) VALUES ('Mexico');
+ ````
+
+
+
+
+
+
+
+
+
+ Create a SolidJS app using the `degit` command.
+
+
+
+
+
+ ```bash Terminal
+ npx degit solidjs/templates/js my-app
+ ```
+
+
+
+
+
+
+
+
+ The fastest way to get started is to use the `supabase-js` client library which provides a convenient interface for working with Supabase from a SolidJS app.
+
+ Navigate to the SolidJS app and install `supabase-js`.
+
+
+
+
+
+ ```bash Terminal
+ cd my-app && npm install @supabase/supabase-js
+ ```
+
+
+
+
+
+
+
+
+ In `App.jsx`, create a Supabase client using your [Project URL and public API (anon) key](https://app.supabase.com/project/_/settings/api).
+
+ Add a `getCountries` function to fetch the data and display the query result to the page.
+
+
+
+
+
+ ```jsx src/App.jsx
+ import { createClient } from "@supabase/supabase-js";
+ import { createEffect, createSignal, For } from "solid-js";
+
+ const supabase = createClient('https://.supabase.co', '');
+
+ function App() {
+ const [countries, setCountries] = createSignal();
+
+ createEffect(() => {
+ getCountries();
+ });
+
+ async function getCountries() {
+ const { data } = await supabase.from("countries").select();
+ setCountries(data);
+ }
+
+ return (
+
+ {(country) =>
{country.name}
}
+
+ );
+ }
+
+ export default App;
+ ```
+
+
+
+
+
+
+
+
+ Start the app and go to http://localhost:3000 in a browser and you should see the list of countries.
+
+
+
+
+
+ ```bash Terminal
+ npm run dev
+ ```
+
+
+
+
+
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/getting-started/quickstarts/sveltekit.mdx b/apps/docs/pages/guides/getting-started/quickstarts/sveltekit.mdx
new file mode 100644
index 00000000000..f4989365455
--- /dev/null
+++ b/apps/docs/pages/guides/getting-started/quickstarts/sveltekit.mdx
@@ -0,0 +1,166 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+import StepHikeCompact from '~/components/StepHikeCompact'
+
+export const meta = {
+ title: 'Use Supabase with SvelteKit',
+ subtitle:
+ 'Learn how to create a Supabase project, add some sample data to your database, and query the data from a SvelteKit app.',
+ breadcrumb: 'Framework Quickstarts',
+}
+
+
+
+
+
+
+ [Create a new project](https://app.supabase.com) in the Supabase Dashboard.
+
+ After your project is ready, create a table in your Supabase database using the [SQL Editor](https://app.supabase.com/project/_/sql) in the Dashboard. Use the following SQL statement to create a `countries` table with some sample data.
+
+
+
+
+
+ ```sql SQL_EDITOR
+ -- Create the table
+ CREATE TABLE countries (
+ id SERIAL PRIMARY KEY,
+ name VARCHAR(255) NOT NULL
+ );
+ -- Insert some sample data into the table
+ INSERT INTO countries (name) VALUES ('United States');
+ INSERT INTO countries (name) VALUES ('Canada');
+ INSERT INTO countries (name) VALUES ('Mexico');
+ ````
+
+
+
+
+
+
+
+
+
+ Create a SvelteKit app using the `npm create` command.
+
+
+
+
+
+ ```bash Terminal
+ npm create svelte@latest myapp
+ ```
+
+
+
+
+
+
+
+
+ The fastest way to get started is to use the `supabase-js` client library which provides a convenient interface for working with Supabase from a SvelteKit app.
+
+ Navigate to the SvelteKit app and install `supabase-js`.
+
+
+
+
+
+ ```bash Terminal
+ cd myapp && npm install @supabase/supabase-js
+ ```
+
+
+
+
+
+
+
+
+ Create a `/src/lib` directory in your SvelteKit app, create a file called `supabaseClient.js` and add the following code to initialize the Supabase client with your [project URL and public API (anon) key](https://app.supabase.com/project/_/settings/api).
+
+
+
+
+
+ ```js src/lib/supabaseClient.js
+ import { createClient } from '@supabase/supabase-js'
+
+ export const supabase = createClient('https://.supabase.co', '')
+ ```
+
+
+
+
+
+
+
+
+ Use `load` method to fetch the data server-side and display the query results as a simple list.
+
+ Create `+page.server.js` file in the `routes` directory with the following code.
+
+
+
+
+
+ ```js src/routes/+page.server.js
+ import { supabase } from "$lib/supabaseClient";
+
+ export async function load() {
+ const { data } = await supabase.from("countries").select();
+ return {
+ countries: data ?? [],
+ };
+ }
+ ```
+
+
+
+
+
+ Replace the existing content in your `+page.svelte` file in the `routes` directory with the following code.
+
+
+
+
+
+ ```svelte src/routes/+page.svelte
+
+
+
+ {#each countries as country}
+
{country.name}
+ {/each}
+
+ ```
+
+
+
+
+
+
+
+
+ Start the app and go to http://localhost:5173 in a browser and you should see the list of countries.
+
+
+
+
+
+ ```bash Terminal
+ npm run dev
+ ```
+
+
+
+
+
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/getting-started/quickstarts/vue.mdx b/apps/docs/pages/guides/getting-started/quickstarts/vue.mdx
new file mode 100644
index 00000000000..b353d6ef35d
--- /dev/null
+++ b/apps/docs/pages/guides/getting-started/quickstarts/vue.mdx
@@ -0,0 +1,153 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+import StepHikeCompact from '~/components/StepHikeCompact'
+
+export const meta = {
+ title: 'Use Supabase with Vue',
+ subtitle:
+ 'Learn how to create a Supabase project, add some sample data to your database, and query the data from a Vue app.',
+ breadcrumb: 'Framework Quickstarts',
+}
+
+
+
+
+
+
+ [Create a new project](https://app.supabase.com) in the Supabase Dashboard.
+
+ After your project is ready, create a table in your Supabase database using the [SQL Editor](https://app.supabase.com/project/_/sql) in the Dashboard. Use the following SQL statement to create a `countries` table with some sample data.
+
+
+
+
+
+ ```sql SQL_EDITOR
+ -- Create the table
+ CREATE TABLE countries (
+ id SERIAL PRIMARY KEY,
+ name VARCHAR(255) NOT NULL
+ );
+ -- Insert some sample data into the table
+ INSERT INTO countries (name) VALUES ('United States');
+ INSERT INTO countries (name) VALUES ('Canada');
+ INSERT INTO countries (name) VALUES ('Mexico');
+ ````
+
+
+
+
+
+
+
+
+
+ Create a Vue app using the `npm init` command.
+
+
+
+
+
+ ```sh Terminal
+ npm init vue@latest my-app
+ ```
+
+
+
+
+
+
+
+
+ The fastest way to get started is to use the `supabase-js` client library which provides a convenient interface for working with Supabase from a Vue app.
+
+ Navigate to the Vue app and install `supabase-js`.
+
+
+
+
+
+ ```bash Terminal
+ cd my-app && npm install @supabase/supabase-js
+ ```
+
+
+
+
+
+
+
+
+ Create a `/src/lib` directory in your Vue app, create a file called `supabaseClient.js` and add the following code to initialize the Supabase client with your [project URL and public API (anon) key](https://app.supabase.com/project/_/settings/api).
+
+
+
+
+
+ ```js src/lib/supabaseClient.js
+ import { createClient } from '@supabase/supabase-js'
+
+ export const supabase = createClient('https://.supabase.co', '')
+ ```
+
+
+
+
+
+
+
+
+ Replace the existing content in your `App.vue` file with the following code.
+
+
+
+
+
+ ```vue src/App.vue
+
+
+
+
+
{{ country.name }}
+
+
+ ```
+
+
+
+
+
+
+
+
+ Start the app and go to http://localhost:5173 in a browser and you should see the list of countries.
+
+
+
+
+
+ ```bash Terminal
+ npm run dev
+ ```
+
+
+
+
+
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/getting-started/tutorials/with-angular.mdx b/apps/docs/pages/guides/getting-started/tutorials/with-angular.mdx
index 8f2754453f9..819cf492c20 100644
--- a/apps/docs/pages/guides/getting-started/tutorials/with-angular.mdx
+++ b/apps/docs/pages/guides/getting-started/tutorials/with-angular.mdx
@@ -1,19 +1,18 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
- id: 'with-angular',
- title: 'Quickstart: Angular',
+ title: 'Build a User Management App with Angular',
description: 'Learn how to use Supabase in your Angular App.',
- sidebar_label: 'Angular',
}

-### GitHub
-
-Should you get stuck while working through the guide, refer to [this repo](https://github.com/supabase/supabase/tree/master/examples/user-management/angular-user-management).
+
+ If you get stuck while working through this guide, refer to the [full example on
+ GitHub](https://github.com/supabase/supabase/tree/master/examples/user-management/angular-user-management).
+
@@ -62,7 +61,6 @@ import {
User,
} from '@supabase/supabase-js'
import { environment } from 'src/environments/environment'
-import { Database } from 'src/schema'
export interface Profile {
id?: string
@@ -136,7 +134,7 @@ Let's set up an Angular component to manage logins and sign ups. We'll use Magic
Create an **AuthComponent** with `ng g c auth` Angular CLI command.
```ts title=src/app/auth/auth.component.ts
-import { Component, OnInit } from '@angular/core'
+import { Component } from '@angular/core'
import { FormBuilder } from '@angular/forms'
import { SupabaseService } from '../supabase.service'
@@ -145,7 +143,7 @@ import { SupabaseService } from '../supabase.service'
templateUrl: './auth.component.html',
styleUrls: ['./auth.component.css'],
})
-export class AuthComponent implements OnInit {
+export class AuthComponent {
loading = false
signInForm = this.formBuilder.group({
@@ -157,8 +155,6 @@ export class AuthComponent implements OnInit {
private readonly formBuilder: FormBuilder
) {}
- ngOnInit(): void {}
-
async onSubmit(): Promise {
try {
this.loading = true
@@ -195,11 +191,7 @@ export class AuthComponent implements OnInit {
/>
-
+
{{ loading ? 'Loading' : 'Send magic link' }}
@@ -406,7 +398,7 @@ Let's create an avatar for the user so that they can upload a profile photo.
Create an **AvatarComponent** with `ng g c avatar` Angular CLI command.
```ts title=src/app/avatar/avatar.component.ts
-import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'
+import { Component, EventEmitter, Input, Output } from '@angular/core'
import { SafeResourceUrl, DomSanitizer } from '@angular/platform-browser'
import { SupabaseService } from '../supabase.service'
@@ -415,7 +407,7 @@ import { SupabaseService } from '../supabase.service'
templateUrl: './avatar.component.html',
styleUrls: ['./avatar.component.css'],
})
-export class AvatarComponent implements OnInit {
+export class AvatarComponent {
_avatarUrl: SafeResourceUrl | undefined
uploading = false
@@ -430,8 +422,6 @@ export class AvatarComponent implements OnInit {
constructor(private readonly supabase: SupabaseService, private readonly dom: DomSanitizer) {}
- ngOnInit(): void {}
-
async downloadImage(path: string) {
try {
const { data } = await this.supabase.downLoadImage(path)
@@ -530,13 +520,12 @@ export class AccountComponent implements OnInit {
}
```
-## Next steps
+### Storage management
+
+
At this stage you have a fully functional application!
-- Got a question? [Ask here](https://github.com/supabase/supabase/discussions).
-- Sign in: [app.supabase.com](https://app.supabase.com)
-
export const Page = ({ children }) =>
export default Page
diff --git a/apps/docs/pages/guides/getting-started/tutorials/with-expo.mdx b/apps/docs/pages/guides/getting-started/tutorials/with-expo.mdx
index b60db313a51..5c47ae577b3 100644
--- a/apps/docs/pages/guides/getting-started/tutorials/with-expo.mdx
+++ b/apps/docs/pages/guides/getting-started/tutorials/with-expo.mdx
@@ -1,20 +1,18 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
- id: 'with-expo',
- title: 'Quickstart: Expo',
+ title: 'Build a User Management App with Expo',
description: 'Learn how to use Supabase in your React Native App.',
- sidebar_label: 'Expo',
- hide_table_of_contents: false,
}

-### GitHub
-
-Should you get stuck while working through the guide, refer to [this repo](https://github.com/supabase/supabase/tree/master/examples/user-management/expo-user-management).
+
+ If you get stuck while working through this guide, refer to the [full example on
+ GitHub](https://github.com/supabase/supabase/tree/master/examples/user-management/expo-user-management).
+
@@ -500,13 +498,12 @@ Now you will need to run the prebuild command to get the application working on
expo prebuild
```
-## Next steps
+### Storage management
+
+
At this stage you have a fully functional application!
-- Got a question? [Ask here](https://github.com/supabase/supabase/discussions).
-- Sign in: [app.supabase.com](https://app.supabase.com)
-
export const Page = ({ children }) =>
export default Page
diff --git a/apps/docs/pages/guides/getting-started/tutorials/with-flutter.mdx b/apps/docs/pages/guides/getting-started/tutorials/with-flutter.mdx
index 6a73c7aa8ab..af7ff9fd3e6 100644
--- a/apps/docs/pages/guides/getting-started/tutorials/with-flutter.mdx
+++ b/apps/docs/pages/guides/getting-started/tutorials/with-flutter.mdx
@@ -1,19 +1,18 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
- id: 'with-flutter',
- title: 'Quickstart: Flutter',
+ title: 'Build a User Management App with Flutter',
description: 'Learn how to use Supabase in your Flutter App.',
- sidebar_label: 'Flutter',
}

-### GitHub
-
-Should you get stuck while working through the guide, refer to [this repo](https://github.com/supabase/supabase/tree/master/examples/user-management/flutter-user-management).
+
+ If you get stuck while working through this guide, refer to the [full example on
+ GitHub](https://github.com/supabase/supabase/tree/master/examples/user-management/flutter-user-management).
+
@@ -77,7 +76,7 @@ Add an intent-filter to enable deep linking:
```
-For iOS, edit the ios/Runner/Info.plist file.
+For iOS, edit the ios/Runner/Info.plist file.
Add CFBundleURLTypes to enable deep linking:
@@ -793,14 +792,17 @@ class _AccountPageState extends State {
}
```
-Congratulations, that is it! You have now built a fully functional user management app using Flutter and Supabase!
+### Storage management
+
+
+
+Congratulations, you've built a fully functional user management app using Flutter and Supabase!
## See also
- [Flutter Tutorial: building a Flutter chat app](https://supabase.com/blog/flutter-tutorial-building-a-chat-app)
- [Flutter Tutorial - Part 2: Authentication and Authorization with RLS](https://supabase.com/blog/flutter-authentication-and-authorization-with-rls)
-
export const Page = ({ children }) =>
export default Page
diff --git a/apps/docs/pages/guides/getting-started/tutorials/with-ionic-angular.mdx b/apps/docs/pages/guides/getting-started/tutorials/with-ionic-angular.mdx
index 88c1d9e6433..43516ef65f3 100644
--- a/apps/docs/pages/guides/getting-started/tutorials/with-ionic-angular.mdx
+++ b/apps/docs/pages/guides/getting-started/tutorials/with-ionic-angular.mdx
@@ -1,19 +1,18 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
- id: 'with-ionic-angular',
- title: 'Quickstart: Ionic Angular',
+ title: 'Build a User Management App with Ionic Angular',
description: 'Learn how to use Supabase in your Ionic Angular App.',
- sidebar_label: 'Ionic Angular',
}

-### GitHub
-
-Should you get stuck while working through the guide, refer to [this repo](https://github.com/mhartington/supabase-ionic-angular).
+
+ If you get stuck while working through this guide, refer to the [full example on
+ GitHub](https://github.com/mhartington/supabase-ionic-angular).
+
@@ -522,14 +521,13 @@ template: `
`
```
-## Next steps
+### Storage management
+
+
At this stage you have a fully functional application!
-- Got a question? [Ask here](https://github.com/supabase/supabase/discussions).
-- Sign in: [app.supabase.com](https://app.supabase.com)
-
-## Resources
+## See also
- [Authentication in Ionic Angular with Supabase](https://supabase.com/blog/authentication-in-ionic-angular)
diff --git a/apps/docs/pages/guides/getting-started/tutorials/with-ionic-react.mdx b/apps/docs/pages/guides/getting-started/tutorials/with-ionic-react.mdx
index c14bb343ed3..3e3d261a5e4 100644
--- a/apps/docs/pages/guides/getting-started/tutorials/with-ionic-react.mdx
+++ b/apps/docs/pages/guides/getting-started/tutorials/with-ionic-react.mdx
@@ -1,19 +1,18 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
- id: 'with-ionic-react',
- title: 'Quickstart: Ionic React',
+ title: 'Build a User Management App with Ionic React',
description: 'Learn how to use Supabase in your Ionic React App.',
- sidebar_label: 'Ionic React',
}

-### GitHub
-
-Should you get stuck while working through the guide, refer to [this repo](https://github.com/mhartington/supabase-ionic-react).
+
+ If you get stuck while working through this guide, refer to the [full example on
+ GitHub](https://github.com/mhartington/supabase-ionic-react).
+
@@ -495,13 +494,12 @@ return (
```
-## Next steps
+### Storage management
+
+
At this stage you have a fully functional application!
-- Got a question? [Ask here](https://github.com/supabase/supabase/discussions).
-- Sign in: [app.supabase.com](https://app.supabase.com)
-
export const Page = ({ children }) =>
export default Page
diff --git a/apps/docs/pages/guides/getting-started/tutorials/with-ionic-vue.mdx b/apps/docs/pages/guides/getting-started/tutorials/with-ionic-vue.mdx
index 2f368771b78..dc884f9d8d5 100644
--- a/apps/docs/pages/guides/getting-started/tutorials/with-ionic-vue.mdx
+++ b/apps/docs/pages/guides/getting-started/tutorials/with-ionic-vue.mdx
@@ -1,19 +1,18 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
- id: 'with-ionic-vue',
- title: 'Quickstart: Ionic Vue',
+ title: 'Build a User Management App with Ionic Vue',
description: 'Learn how to use Supabase in your Ionic Vue App.',
- sidebar_label: 'Ionic Vue',
}

-### GitHub
-
-Should you get stuck while working through the guide, refer to [this repo](https://github.com/mhartington/supabase-ionic-vue).
+
+ If you get stuck while working through this guide, refer to the [full example on
+ GitHub](https://github.com/mhartington/supabase-ionic-vue).
+
@@ -555,13 +554,12 @@ export default defineComponent({
```
-## Next steps
+### Storage management
+
+
At this stage you have a fully functional application!
-- Got a question? [Ask here](https://github.com/supabase/supabase/discussions).
-- Sign in: [app.supabase.com](https://app.supabase.com)
-
export const Page = ({ children }) =>
export default Page
diff --git a/apps/docs/pages/guides/getting-started/tutorials/with-nextjs.mdx b/apps/docs/pages/guides/getting-started/tutorials/with-nextjs.mdx
index 81b86e96b2f..bbee6933442 100644
--- a/apps/docs/pages/guides/getting-started/tutorials/with-nextjs.mdx
+++ b/apps/docs/pages/guides/getting-started/tutorials/with-nextjs.mdx
@@ -1,17 +1,16 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
- id: 'with-nextjs',
- title: 'Quickstart: Next.js',
+ title: 'Build a User Management App with NextJS',
description: 'Learn how to use Supabase in your Next App.',
- sidebar_label: 'Next.js',
+ video: 'https://www.youtube.com/v/0Fs96oZ4se0',
}

-### Video Guide
+## Video Guide
-### GitHub
-
-Should you get stuck while working through the guide, refer to [this repo](https://github.com/supabase/supabase/tree/master/examples/user-management/nextjs-ts-user-management).
+
+ If you get stuck while working through this guide, refer to the [full example on
+ GitHub](https://github.com/supabase/supabase/tree/master/examples/user-management/nextjs-ts-user-management).
+
@@ -718,17 +718,19 @@ return (
)
```
-## Next steps
+### Storage management
+
+
At this stage you have a fully functional application!
-- See the complete [example on GitHub](https://github.com/supabase/supabase/tree/master/examples/user-management/nextjs-ts-user-management) and deploy it to Vercel.
-- Explore the [pre-built Auth UI for React](/docs/guides/auth/auth-helpers/auth-ui).
-- Explore the [Auth Helpers for Next.js](/docs/guides/auth/auth-helpers/nextjs).
-- Explore the [Supabase Cache Helpers](https://github.com/psteinroe/supabase-cache-helpers).
-- See the [Next.js Subscription Payments Starter](https://github.com/vercel/nextjs-subscription-payments) template on GitHub.
-- Got a question? [Ask here](https://github.com/supabase/supabase/discussions).
-- Sign in: [app.supabase.com](https://app.supabase.com)
+## See also
+
+- See the complete [example on GitHub](https://github.com/supabase/supabase/tree/master/examples/user-management/nextjs-ts-user-management) and deploy it to Vercel
+- Explore the [pre-built Auth UI for React](/docs/guides/auth/auth-helpers/auth-ui)
+- Explore the [Auth Helpers for Next.js](/docs/guides/auth/auth-helpers/nextjs)
+- Explore the [Supabase Cache Helpers](https://github.com/psteinroe/supabase-cache-helpers)
+- See the [Next.js Subscription Payments Starter](https://github.com/vercel/nextjs-subscription-payments) template on GitHub
export const Page = ({ children }) =>
diff --git a/apps/docs/pages/guides/getting-started/tutorials/with-nuxt-3.mdx b/apps/docs/pages/guides/getting-started/tutorials/with-nuxt-3.mdx
index b4f5af0a1a3..be8fcf69c34 100644
--- a/apps/docs/pages/guides/getting-started/tutorials/with-nuxt-3.mdx
+++ b/apps/docs/pages/guides/getting-started/tutorials/with-nuxt-3.mdx
@@ -1,19 +1,18 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
- id: 'with-nuxt-3',
- title: 'Quickstart: Nuxt 3',
+ title: 'Build a User Management App with Nuxt 3',
description: 'Learn how to use Supabase in your Nuxt 3 App.',
- sidebar_label: 'Nuxt 3',
}

-### Github
-
-Should you get stuck while working through the guide, refer to [this repo](https://github.com/supabase/supabase/tree/master/examples/user-management/nuxt3-user-management).
+
+ If you get stuck while working through this guide, refer to the [full example on
+ GitHub](https://github.com/supabase/supabase/tree/master/examples/user-management/nuxt3-user-management).
+
@@ -66,7 +65,27 @@ export default defineNuxtConfig({
Let's set up a Vue component to manage logins and sign ups. We'll use Magic Links, so users can sign in with their email without using passwords.
-```html title=/components/Auth.vue
+```vue title=/components/Auth.vue
+
+
-
-
```
### User state
@@ -116,7 +116,70 @@ To access the user information, use the composable [useSupabaseUser](https://sup
After a user is signed in we can allow them to edit their profile details and manage their account.
Let's create a new component for that called `Account.vue`.
-```html title=components/Account.vue
+```vue title=components/Account.vue
+
+
-
-
```
### Launch!
Now that we have all the components in place, let's update `app.vue`:
-```html title=app.vue
+```vue title=app.vue
+
+
-
-
```
Once that's done, run this in a terminal window:
@@ -242,7 +246,65 @@ Every Supabase project is configured with [Storage](/docs/guides/storage) for ma
Let's create an avatar for the user so that they can upload a profile photo. We can start by creating a new component:
-```html title=components/Avatar.vue
+```vue title=components/Avatar.vue
+
+
-
-
```
### Add the new widget
And then we can add the widget to the Account page:
-```html title=components/Account.vue
+```vue title=components/Account.vue
+
+
-
-
```
-That is it! You should now be able to upload a profile photo to Supabase Storage.
+### Storage management
-## Next steps
+
-At this stage you have a fully functional application!
-
-- Got a question? [Ask here](https://github.com/supabase/supabase/discussions).
-- Sign in: [app.supabase.com](https://app.supabase.com)
+That is it! You should now be able to upload a profile photo to Supabase Storage and you have a fully functional application.
export const Page = ({ children }) =>
diff --git a/apps/docs/pages/guides/getting-started/tutorials/with-react.mdx b/apps/docs/pages/guides/getting-started/tutorials/with-react.mdx
index 7d1418b7146..73c30cd56cb 100644
--- a/apps/docs/pages/guides/getting-started/tutorials/with-react.mdx
+++ b/apps/docs/pages/guides/getting-started/tutorials/with-react.mdx
@@ -1,19 +1,18 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
- id: 'with-react',
- title: 'Quickstart: React',
+ title: 'Build a User Management App with React',
description: 'Learn how to use Supabase in your React App.',
- sidebar_label: 'React',
}

-### GitHub
-
-Should you get stuck while working through the guide, refer to [this repo](https://github.com/supabase/supabase/tree/master/examples/user-management/react-user-management).
+
+ If you get stuck while working through this guide, refer to the [full example on
+ GitHub](https://github.com/supabase/supabase/tree/master/examples/user-management/react-user-management).
+
@@ -397,13 +396,12 @@ return (
)
```
-## Next steps
+### Storage management
+
+
At this stage you have a fully functional application!
-- Got a question? [Ask here](https://github.com/supabase/supabase/discussions).
-- Sign in: [app.supabase.com](https://app.supabase.com)
-
export const Page = ({ children }) =>
export default Page
diff --git a/apps/docs/pages/guides/getting-started/tutorials/with-redwoodjs.mdx b/apps/docs/pages/guides/getting-started/tutorials/with-redwoodjs.mdx
index cae8504a010..cbc647692aa 100644
--- a/apps/docs/pages/guides/getting-started/tutorials/with-redwoodjs.mdx
+++ b/apps/docs/pages/guides/getting-started/tutorials/with-redwoodjs.mdx
@@ -1,49 +1,19 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
- id: 'with-redwoodjs',
- title: 'Quickstart: RedwoodJS',
+ title: 'Build a User Management App with RedwoodJS',
description: 'Learn how to use Supabase in your RedwoodJS App.',
- sidebar_label: 'RedwoodJS',
}
-## Intro
-
-For the sake of consistency with the other framework Quickstart examples, we'll build a RedwoodJS a little differently than normal.
-
-We **_won't use_** Prisma to connect to the Supabase Postgres database or [Prisma migrations](https://redwoodjs.com/docs/cli-commands#prisma-migrate) as one typically might in a Redwood app.
-Instead, we'll rely on the Supabase client to do some of the work on the **web** side and use the client again on the **API** side to do data fetching as well.
-
-That means you will want to refrain from running any `yarn rw prisma migrate` commands and also double check your build commands on deployment to ensure Prisma won't reset your database.
-
-
-
-TLDR; Prisma currently doesn't support cross-schema foreign keys, so introspecting the schema fails due to how your Supabase `public` schema references the `auth.users`.
-
-
-
-This example provides the steps to build a simple user management app (from scratch!) using Supabase and [RedwoodJS](https://redwoodjs.com/docs/introduction). It includes:
-
-- Supabase [Database](/docs/guides/database): a Postgres database for storing your user data.
-- Supabase [Auth](/docs/guides/auth): users can sign in with magic links (no passwords, only email).
-- Supabase [Storage](/docs/guides/storage): users can upload a photo.
-- Instant [APIs](/docs/guides/api): APIs will be automatically generated when you create your database tables.
-- [Row Level Security](/docs/guides/auth#row-level-security): data is protected so that individuals can only access their own data.
-
-By the end of this guide you'll have an app which allows users to login and update some basic profile details:
+

-
-Note: For RedwoodJS apps, port will be 8910
-
+ If you get stuck while working through this guide, refer to the [full example on
+ GitHub](https://github.com/redwoodjs/redwoodjs-supabase-quickstart).
-### GitHub
-
-If you get stuck at any point, take a look at [this repo](https://github.com/redwoodjs/redwoodjs-supabase-quickstart).
-
{/* ## Video demo */}
{/* Coming soon. */}
@@ -84,128 +54,14 @@ The **`web side`** is built with React. Redwood's router makes it simple to map
Pages may contain a "Layout" component to wrap content. They also contain "Cells" and regular React components.
Cells allow you to declaratively manage the lifecycle of a component that fetches and displays data.
-
-
-For the sake of consistency with the other framework Quickstart examples, we'll build a RedwoodJS a little differently than normal.
+For the sake of consistency with the other framework tutorials, we'll build this app a little differently than normal.
We **_won't use_** Prisma to connect to the Supabase Postgres database or [Prisma migrations](https://redwoodjs.com/docs/cli-commands#prisma-migrate) as one typically might in a Redwood app.
Instead, we'll rely on the Supabase client to do some of the work on the **`web`** side and use the client again on the **`api`** side to do data fetching as well.
-
+That means you will want to refrain from running any `yarn rw prisma migrate` commands and also double check your build commands on deployment to ensure Prisma won't reset your database. Prisma currently doesn't support cross-schema foreign keys, so introspecting the schema fails due
+to how your Supabase `public` schema references the `auth.users`.
-## Project set up
-
-Before we start building we're going to set up our Database and API. This is as simple as starting a new Project in Supabase
-and then creating a "schema" inside the database.
-
-
-
-When setting up Supabase, you'll interact with your project dashboard. However, most of the RedwoodJS setup steps will interact with the [Redwood CLI](https://redwoodjs.com/docs/cli-commands) to generate routes, pages, components, and more.
-So be sure to have a terminal at the ready and in your project directory.
-
-
-
-### Create a project
-
-1. Go to [app.supabase.com](https://app.supabase.com).
-1. Click on "New Project".
-1. Enter your project details.
-1. Wait for the new database to launch.
-
-### Set up the database schema
-
-Now we are going to set up the database schema. We can use the "User Management Starter" quickstart in the SQL Editor,
-or you can just copy/paste the SQL from below and run it yourself.
-
-
-
-
-1. Go to the [SQL Editor](https://app.supabase.com/project/_/sql) page in the Dashboard.
-2. Click **User Management Starter**.
-3. Click **Run**.
-
-
-
-
-
-
-```sql
--- Create a table for public "profiles"
-create table profiles (
- id uuid references auth.users not null,
- updated_at timestamp with time zone,
- username text unique,
- avatar_url text,
- website text,
-
- primary key (id),
- unique(username),
- constraint username_length check (char_length(username) >= 3)
-);
-
-alter table profiles enable row level security;
-
-create policy "Public profiles are viewable by everyone."
- on profiles for select
- using ( true );
-
-create policy "Users can insert their own profile."
- on profiles for insert
- with check ( auth.uid() = id );
-
-create policy "Users can update own profile."
- on profiles for update
- using ( auth.uid() = id );
-
--- Set up Realtime!
-begin;
- drop publication if exists supabase_realtime;
- create publication supabase_realtime;
-commit;
-alter publication supabase_realtime add table profiles;
-
--- Set up Storage!
-insert into storage.buckets (id, name)
-values ('avatars', 'avatars');
-
-create policy "Avatar images are publicly accessible."
- on storage.objects for select
- using ( bucket_id = 'avatars' );
-
-create policy "Anyone can upload an avatar."
- on storage.objects for insert
- with check ( bucket_id = 'avatars' );
-
-```
-
-
-
-
-#### Important Note about Prisma and Migrations
-
-
-
-Because this Quick Start does not use Prisma to manage the database schema, seed it, or run migrations, you **need** to take extra care when running some of the typical Redwood CLI commands.
-
-
-
-### Get the API Keys
-
-Now that you've created some database tables, you are ready to insert data using the auto-generated API.
-We just need to get the URL as well as the `anon`, `service_role` and `JWT_SECRET` keys from the API settings.
-
-1. Go to the [API Settings](https://app.supabase.com/project/_/settings/api) page in the Dashboard.
-2. Find your API `URL`, `anon`, and `service_role` keys on this page.
-
-
+
## Building the App
@@ -281,7 +137,7 @@ And finally, you will also need to save **just** the `web side` environment vari
```bash title=redwood.toml
[web]
- title = "Supabase Redwood Quickstart"
+ title = "Supabase Redwood Tutorial"
port = 8910
apiProxyPath = "/.redwood/functions"
includeEnvironmentVariables = ["SUPABASE_URL", "SUPABASE_KEY"]
@@ -333,9 +189,7 @@ yarn rw dev
```
-
-Note: You can also use the alias `rw` for `redwood`, as in `yarn rw` to run Redwood CLI commands
-
+ `rw` is an alias for `redwood`, as in `yarn rw` to run Redwood CLI commands.
You should see a "Welcome to RedwoodJS" page and a message about not having any pages yet.
@@ -355,7 +209,7 @@ yarn rw generate page home /
-Note: the slash `/` is important here as it creates a root level route.
+The `/` is important here as it creates a root level route.
@@ -591,14 +445,10 @@ const Account = () => {
export default Account
```
-
-
-Note: You'll see the use of `useAuth()` several times in the quickstart. Redwood's `useAuth` hook provides convenient ways to access
+You'll see the use of `useAuth()` several times. Redwood's `useAuth` hook provides convenient ways to access
logIn, logOut, currentUser, and access the `supabase` authenticate client. We'll use it to get an instance
of the supabase client to interact with your API.
-
-
### Update Home Page
Now that we have all the components in place, let's update your `HomePage` page to use them:
@@ -771,12 +621,14 @@ return (
)
```
-## Next steps
+### Storage management
+
+
At this stage you have a fully functional application!
-- Got a question? [Ask here](https://github.com/supabase/supabase/discussions).
-- Sign in: [app.supabase.com](https://app.supabase.com)
+## See also
+
- Learn more about [RedwoodJS](https://redwoodjs.com)
- Visit the [RedwoodJS Discourse Community](https://community.redwoodjs.com)
diff --git a/apps/docs/pages/guides/getting-started/tutorials/with-solidjs.mdx b/apps/docs/pages/guides/getting-started/tutorials/with-solidjs.mdx
index 7de71d63910..c56047307f9 100644
--- a/apps/docs/pages/guides/getting-started/tutorials/with-solidjs.mdx
+++ b/apps/docs/pages/guides/getting-started/tutorials/with-solidjs.mdx
@@ -1,19 +1,18 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
- id: 'with-solidjs',
- title: 'Quickstart: SolidJS',
+ title: 'Build a User Management App with SolidJS',
description: 'Learn how to use Supabase in your SolidJS App.',
- sidebar_label: 'SolidJS',
}

-### GitHub
-
-Should you get stuck while working through the guide, refer to [this repo](https://github.com/supabase/supabase/tree/master/examples/user-management/solid-user-management).
+
+ If you get stuck while working through this guide, refer to the [full example on
+ GitHub](https://github.com/supabase/supabase/tree/master/examples/user-management/solid-user-management).
+
@@ -415,13 +414,12 @@ return (
)
```
-## Next steps
+### Storage management
+
+
At this stage you have a fully functional application!
-- Got a question? [Ask here](https://github.com/supabase/supabase/discussions).
-- Sign in: [app.supabase.com](https://app.supabase.com)
-
export const Page = ({ children }) =>
export default Page
diff --git a/apps/docs/pages/guides/getting-started/tutorials/with-svelte.mdx b/apps/docs/pages/guides/getting-started/tutorials/with-svelte.mdx
index 69cdea17b87..9e362231fa3 100644
--- a/apps/docs/pages/guides/getting-started/tutorials/with-svelte.mdx
+++ b/apps/docs/pages/guides/getting-started/tutorials/with-svelte.mdx
@@ -1,19 +1,18 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
- id: 'with-svelte',
- title: 'Quickstart: Svelte',
+ title: 'Build a User Management App with Svelte',
description: 'Learn how to use Supabase in your Svelte App.',
- sidebar_label: 'Svelte',
}

-### GitHub
-
-Should you get stuck while working through the guide, refer to [this repo](https://github.com/supabase/supabase/tree/master/examples/user-management/svelte-user-management).
+
+ If you get stuck while working through this guide, refer to the [full example on
+ GitHub](https://github.com/supabase/supabase/tree/master/examples/user-management/svelte-user-management).
+
@@ -368,13 +367,12 @@ And then we can add the widget to the Account page:
```
-## Next steps
+### Storage management
+
+
At this stage you have a fully functional application!
-- Got a question? [Ask here](https://github.com/supabase/supabase/discussions).
-- Sign in: [app.supabase.com](https://app.supabase.com)
-
export const Page = ({ children }) =>
export default Page
diff --git a/apps/docs/pages/guides/getting-started/tutorials/with-sveltekit.mdx b/apps/docs/pages/guides/getting-started/tutorials/with-sveltekit.mdx
index fd1cced34fb..3be4542731a 100644
--- a/apps/docs/pages/guides/getting-started/tutorials/with-sveltekit.mdx
+++ b/apps/docs/pages/guides/getting-started/tutorials/with-sveltekit.mdx
@@ -1,19 +1,18 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
- id: 'with-sveltekit',
- title: 'Quickstart: SvelteKit',
+ title: 'Build a User Management App with SvelteKit',
description: 'Learn how to use Supabase in your SvelteKit App.',
- sidebar_label: 'SvelteKit',
}

-### GitHub
-
-Should you get stuck while working through the guide, refer to [this repo](https://github.com/supabase/supabase/tree/master/examples/user-management/sveltekit-user-management).
+
+ If you get stuck while working through this guide, refer to the [full example on
+ GitHub](https://github.com/supabase/supabase/tree/master/examples/user-management/sveltekit-user-management).
+
@@ -24,7 +23,7 @@ Let's start building the Svelte app from scratch.
### Initialize a Svelte app
We can use the [SvelteKit Skeleton Project](https://kit.svelte.dev/docs) to initialize
-an app called `supabase-sveltekit` (for this tutorial you do not need TypeScript, ESLint, Prettier, or Playwright):
+an app called `supabase-sveltekit` (for this tutorial we will be using TypeScript):
```bash
npm init svelte@next supabase-sveltekit
@@ -434,13 +433,12 @@ And then we can add the widget to the Account page:
```
-## Next steps
+### Storage management
+
+
At this stage you have a fully functional application!
-- Got a question? [Ask here](https://github.com/supabase/supabase/discussions).
-- Sign in: [app.supabase.com](https://app.supabase.com)
-
export const Page = ({ children }) =>
export default Page
diff --git a/apps/docs/pages/guides/getting-started/tutorials/with-vue-3.mdx b/apps/docs/pages/guides/getting-started/tutorials/with-vue-3.mdx
index a1ec84a8c92..5480df2d4aa 100644
--- a/apps/docs/pages/guides/getting-started/tutorials/with-vue-3.mdx
+++ b/apps/docs/pages/guides/getting-started/tutorials/with-vue-3.mdx
@@ -1,19 +1,18 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
- id: 'with-vue-3',
- title: 'Quickstart: Vue 3',
+ title: 'Build a User Management App with Vue 3',
description: 'Learn how to use Supabase in your Vue 3 App.',
- sidebar_label: 'Vue 3',
}

-### GitHub
-
-Should you get stuck while working through the guide, refer to [this repo](https://github.com/supabase/supabase/tree/master/examples/user-management/vue3-user-management).
+
+ If you get stuck while working through this guide, refer to the [full example on
+ GitHub](https://github.com/supabase/supabase/tree/master/examples/user-management/vue3-user-management).
+
@@ -50,7 +49,7 @@ VITE_SUPABASE_URL=YOUR_SUPABASE_URL
VITE_SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY
```
-Now that we have the API credentials in place, let's create a helper file to initialize the Supabase client. These variables will be exposed
+With the API credentials in place, create an `src/supabase.js` helper file to initialize the Supabase client. These variables are exposed
on the browser, and that's completely fine since we have [Row Level Security](/docs/guides/auth#row-level-security) enabled on our Database.
```js title=src/supabase.js
@@ -66,32 +65,32 @@ Optionally, update [src/style.css](https://raw.githubusercontent.com/supabase/su
### Set up a Login component
-Let's set up a Vue component to manage logins and sign ups. We'll use Magic Links, so users can sign in with their email without using passwords.
+Set up an `src/components/Auth.vue` component to manage logins and sign ups. We'll use Magic Links, so users can sign in with their email without using passwords.
-```html title=/src/components/Auth.vue
+```vue title=/src/components/Auth.vue
@@ -118,84 +117,84 @@ Let's set up a Vue component to manage logins and sign ups. We'll use Magic Link
### Account page
After a user is signed in we can allow them to edit their profile details and manage their account.
-Let's create a new component for that called `Account.vue`.
+Create a new `src/components/Account.vue` component to handle this.
-```html title=src/components/Account.vue
+```vue title=src/components/Account.vue
@@ -233,24 +232,24 @@ Let's create a new component for that called `Account.vue`.
Now that we have all the components in place, let's update `App.vue`:
-```html title=src/App.vue
+```vue title=src/App.vue
@@ -277,58 +276,58 @@ Every Supabase project is configured with [Storage](/docs/guides/storage) for ma
### Create an upload widget
-Let's create an avatar for the user so that they can upload a profile photo. We can start by creating a new component:
+Create a new `src/components/Avatar.vue` component that allows users to upload profile photos:
-```html title=src/components/Avatar.vue
+```vue title=src/components/Avatar.vue
@@ -344,7 +343,7 @@ Let's create an avatar for the user so that they can upload a profile photo. We
- // Import the new component
- import Avatar from './Avatar.vue'
+// Import the new component
+import Avatar from './Avatar.vue'
@@ -379,13 +378,12 @@ And then we can add the widget to the Account page:
```
-## Next steps
+### Storage management
+
+
At this stage you have a fully functional application!
-- Got a question? [Ask here](https://github.com/supabase/supabase/discussions).
-- Sign in: [app.supabase.com](https://app.supabase.com)
-
export const Page = ({ children }) =>
export default Page
diff --git a/apps/docs/pages/guides/integrations/estuary.mdx b/apps/docs/pages/guides/integrations/estuary.mdx
index e8287eaff62..77e52581a5a 100644
--- a/apps/docs/pages/guides/integrations/estuary.mdx
+++ b/apps/docs/pages/guides/integrations/estuary.mdx
@@ -18,7 +18,7 @@ Once created, the pipeline backfills all your historical data from Firestore and
Before you begin, you'll need:
-- An [Estuary account](https://go.estuary.dev/sign-up).
+- An Estuary account. [Head to the web app to start for free](https://dashboard.estuary.dev).
- For your Firestore database:
@@ -31,36 +31,35 @@ Before you begin, you'll need:
You'll start by creating a **capture**, a task in Flow that connects to your data source system: in this case, Firestore. This process will create one or more data **collections**, backed by a real-time data lake.
-1. Go to the Flow web application at [dashboard.estuary.dev](http://dashboard.estuary.dev) and sign in using the credentials provided by your Estuary account manager.
+1. Go to the [**Captures** tab](https://dashboard.estuary.dev/captures) of the Flow web app and choose **New Capture**.
-2. Click the **Captures** tab and choose **New Capture**.
-
-3. Locate and select the **Google Firestore** card.
+2. Locate and select the **Google Firestore** card.
A form appears with the properties required for a Firestore capture.
-4. Set a name for your capture.
+3. Set a name for your capture.
Click inside the **Name** field to generate a drop-down menu of available **prefixes** and select one (likely, this will be the name of your organization). Append a unique capture name after the `/` to create the full name, for example, `acmeCo/myFirestoreCapture`.
-5. Fill out the required properties for Firestore.
+4. Fill out the required properties for Firestore.
- - **Database**: Flow can autodetect the database name, but you may optionally specify it here. This is helpful if the service account used has access to multiple Firebase projects. Your database name usually follows the format `projects/$PROJECTID/databases/(default)`.
- - **Credentials**: The JSON service account key created per the prerequisites.
+ **Database**: Flow can autodetect the database name, but you may optionally specify it here. This is helpful if the service account used has access to multiple Firebase projects. Your database name usually follows the format `projects/$PROJECTID/databases/(default)`.
+
+ **Credentials**: The JSON service account key created per the prerequisites.
-6. Click **Discover Endpoint**.
+5. Click **Next**.
-Flow uses the provided configuration to initiate a connection with Firestore. It generates a capture specification and details of the collections that it will create, once published.
+ Flow uses the provided configuration to initiate a connection with Firestore. It maps each collection in the Firestore database to a Flow collection.
-7. Use the **Specification Editor** to view the [JSON schemas](https://docs.estuary.dev/concepts/schemas/) for each collection and make sure they are formatted correctly for your needs. If they're not, you can edit them.
+6. Optionally, use the **Collection Selector** to remove any collections you don't need to migrate to Supabase.
-8. Click **Save and publish**.
+7. Click **Save and Publish**.
-You'll see a notification when the capture publishes successfully.
+ You'll see a notification when the capture publishes successfully.
-The data currently in your Firestore database has been captured, and future updates to it will be captured continuously.
+ The data currently in your Firestore database has been captured to Flow, and future updates to it will be captured continuously.
-Click **Materialize Collections** to continue.
+ Click **Materialize Collections** to continue.
## Step 2: Materialize your collections to Postgres
@@ -72,23 +71,31 @@ A form appears with the properties required for a Postgres materialization.
2. Choose a unique name for your materialization like you did when naming your capture; for example, `acmeCo/mySupabaseMaterialization`.
-3. Fill out the required properties for PostgreSQL. You can find most of these in Supabase by going to the **Settings** section and clicking **Database**.
+3. Fill out the required properties for Postgres. You can find most of these in Supabase by going to the **Settings** section and clicking **Database**.
- - **Address**: Format at `:`.
- - **User**: Usually, this is `postgres`.
- - **Password**: The password you set when you created your Supabase project.
+ **Address**: Format at `:`.
+
+ **User**: Usually, this is `postgres`.
+
+ **Password**: The password you set when you created your Supabase project.
-4. Scroll down to view the **Collection Selector** and fill in the **Table** field for each collection.
+4. Click **Next**.
-The collections you just created have already been selected, but you must provide names for the tables to which they'll be materialized.
+ Flow initiates a connection with the database and the **Collection Selector** expands.
+ It's populated with your collections from Firestore, each mapped to a Postgres table.
-5. Click **Discover Endpoint**.
+5. For each collection, apply a stricter JSON schema.
+ This ensure that the less-structured Firestore data will be written to a Postgres table in the correct shape.
-Flow uses the provided configuration to initiate a connection to your Supabase Postgres database and generate a specification.
+ In the Collection Selector, choose a collection and click its **Specification** tab.
+
+ Click **Schema Inference**. Flow scans the data in your collection and infers a new schema to use for materialization.
+
+ Review the new schema and click **Apply Inferred Schema**.
-6. Click **Save and Publish**. You'll see a notification when the full materialization publishes successfully.
+6. Click **Save and Publish**. You'll see a notification when the materialization publishes successfully.
-Your Firestore collections are copied to tables in Supabase. As long as you leave the capture and materialation running, any changes to the Firestore data will be reflected in Supabase in milliseconds.
+ Your Firestore collections are copied to tables in Supabase. As long as you leave the capture and materialation running, any changes to the Firestore data will be reflected in Supabase in milliseconds.
## Resources
diff --git a/apps/docs/pages/guides/integrations/fezto.mdx b/apps/docs/pages/guides/integrations/fezto.mdx
index 17d48db27f4..60007bc3ed3 100644
--- a/apps/docs/pages/guides/integrations/fezto.mdx
+++ b/apps/docs/pages/guides/integrations/fezto.mdx
@@ -4,6 +4,7 @@ export const meta = {
id: 'fezto',
title: 'Frontend Zero to One',
description: 'Create an app automatically from your Supabase Postgres using OpenAPI',
+ video: 'https://www.youtube.com/v/GOC6a0_AlgI',
}
[Frontend Zero to One is](https://www.fezto.xyz) is a service which creates an app for your Supabase Postgres database on-the-fly without any drag and drop, using the OpenAPI spec provided by PostgREST.
diff --git a/apps/docs/pages/guides/integrations/flutterflow.mdx b/apps/docs/pages/guides/integrations/flutterflow.mdx
new file mode 100644
index 00000000000..ed6ddf1f046
--- /dev/null
+++ b/apps/docs/pages/guides/integrations/flutterflow.mdx
@@ -0,0 +1,152 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'flutterflow',
+ title: 'FlutterFlow',
+ description:
+ 'FlutterFlow is a low-code tool that allows you to build Flutter apps incredibly fast.',
+ canonical: 'https://docs.flutterflow.io/actions/actions/backend-database/supabase',
+}
+
+
+ FlutterFlow and Supabase integration is currently in alpha, and supported features may be limited.
+
+
+[FlutterFlow](https://flutterflow.io/) is a low-code builder for developing native mobile applications using Flutter. You can use the simple drag-and-drop interface to build your app faster than traditional development.
+
+This guide gives you a quick overview of implementing basic CRUD operations using FlutterFlow and Supabase. You can find the full docs on FlutterFlow and Supabase [here](https://docs.flutterflow.io/actions/actions/backend-database/supabase).
+
+
+
+
+
+
+## Step 1: Connect FlutterFlow to Supabase
+
+Before we dive into the code, this guide assumes that you have the following ready:
+
+- [Supabase](https://database.new/) project created
+- Have setup tables in your Supabase project
+- [FlutterFlow](https://app.flutterflow.io/) project created
+
+You can then connect your Supabase project to your FlutterFlow project with the following steps:
+
+1. In your Supabase project, navigate to Project Settings > API. Copy the Project URL.
+2. Return to FlutterFlow, navigate to Settings and Integrations > Integrations > Supabase. Turn on the toggle (i.e., enable Supabase) and paste the API URL.
+3. Similarly, from the Supabase API section, copy the anon key (under Project API keys) and paste it inside the FlutterFlow > Settings and Integrations > Integrations > Supabase > Anon Key.
+4. Click on the Get Schema button. This will show the list of all tables with their schema (structure) created in Supabase.
+5. (Optional) If you have defined an Array for any Column Data Type in Supabase, you must set its type here. To do so, tap the "Click to set Array type" and choose the right one.
+
+
+
+## Step 2: Inserting rows
+
+Go to your project page on FlutterFlow and follow the steps below to define the Action to any widget.
+
+1. Select the Widget (e.g., Button) on which you want to define the action.
+2. Select Actions from the Properties panel (the right menu), and click Open. This will open an Action flow Editor in a new popup window.
+ 1. Click on + Add Action.
+ 2. On the right side, search and select the Supabase > Insert Row action.
+ 3. Set the Table to your table name (e.g., assignments).
+ 4. Under the Set Fields section, click on the + Add Field button.
+ 5. Click on the Field name and scroll down to find the Value Source dropdown and change it to From Variable.
+ 6. Click on UNSET and select Widget State > Name of the TextField.
+ 7. Similarly, add the field for the other UI elements.
+
+
+
+## Step 3: Selecting and displaying rows
+
+To query a Supabase table on a ListView:
+
+1. Select the ListView widget. Make sure you choose the ListView widget, not the ListTile.
+2. Select Backend Query from the properties panel (the right menu), and click Add Backend Query.
+3. Set the Query Type to Supabase Query.
+4. Select your Table from the dropdown list
+5. Set the Query Type to List of Rows.
+6. Optional: If you want to display the limited result, say, for example, you have thousands of entries, but you want to display only 100, you can specify the limit.
+7. Click Confirm.
+
+
+
+## Step 4: Updating rows
+
+Go to your project page on FlutterFlow and follow the steps below to define the Action to any widget.
+
+1. Select the Widget (e.g., Button) on which you want to define the action.
+2. Select Actions from the Properties panel (the right menu), and click Open. This will open an Action flow Editor in a new popup window.
+ 1. Click on + Add Action.
+ 2. On the right side, search and select the Supabase > Update Row action.
+ 3. Set the Table to your table name (e.g., assignments).
+ 4. Optional: If you want to get the rows after the update is finished, enable the Return Matching Rows option.
+ 5. Now, you must set the row you want to update. Usually, this is done by finding a row in a table that matches the current row ID. To do so, click + Add Filter button inside the Matching Rows section.
+ 1. Set the Field Name to the field that contains the IDs. Typically, this is the id column.
+ 2. Set the Relation to Equal To because you want to find a row with the exact id.
+ 3. Into the Value Source, you can select the From Variable and provide the id of the row for which you just updated values in the UI.
+ 6. Under the Set Fields section, click on the + Add Field button.
+ 7. Click on the field name.
+ 8. Scroll down to find the Value Source dropdown and change it to From Variable.
+ 9. Click on UNSET and select Widget State > Name of the TextField.
+ 10. Similarly, add the field for the other UI elements.
+
+## Step 5: Deleting rows
+
+Go to your project page on FlutterFlow and follow the steps below to define the Action to any widget.
+
+1. Select the Widget (e.g., Button) on which you want to define the action.
+2. Select Actions from the Properties panel (the right menu), and click Open. This will open an Action flow Editor in a new popup window.
+ 1. Click on + Add Action.
+ 2. On the right side, search and select the Supabase -> Delete Row action.
+ 3. Set the Table to your table name (e.g., assignments).
+ 4. Optional: Later, if you want to know which rows were deleted from a table, enable the Return Matching Rows option.
+ 5. Now, you must set the row you want to delete. Usually, this is done by finding a row in a table that matches the current row ID. To do so, click + Add Filter button inside the Matching Rows section.
+ 1. Set the Field Name to the field that contains the IDs. Typically, this is the id column.
+ 2. Set the Relation to Equal To because you want to find a row with the exact id.
+ 3. Into the Value Source, you can select the From Variable and provide the id of the row you want to delete.
+
+
+
+## Resources
+
+You can find more detailed guides on FlutterFlow’s docs.
+
+- [FlutterFlow Supabase available actions](https://docs.flutterflow.io/actions/actions/backend-database/supabase)
+- [Retrieving Data from Supabase on FlutterFlow](https://docs.flutterflow.io/data-and-backend/supabase/supabase-database/retrieving-data)
+- [Adding data to Supabase DB from FlutterFlow](https://docs.flutterflow.io/data-and-backend/supabase/supabase-database/adding-data)
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/integrations/illa.mdx b/apps/docs/pages/guides/integrations/illa.mdx
new file mode 100644
index 00000000000..e30c07e88f3
--- /dev/null
+++ b/apps/docs/pages/guides/integrations/illa.mdx
@@ -0,0 +1,107 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'illa',
+ title: 'ILLA',
+ description:
+ 'Get started with Supabase and ILLA, a low-code platform for developers that enables the rapid development and deployment of internal tools.',
+}
+
+This tutorial outlines the process of creating an Admin Panel using ILLA Builder and Supabase in a few simple steps. ILLA is a low-code platform for developers that enables the rapid development and deployment of internal tools. It allows for creating pages by dragging and dropping UI components, connecting to any database or API, and writing JavaScript. To learn more about ILLA and give it a try, visit their website at [https://www.illacloud.com/](https://www.illacloud.com/). Let's begin!
+
+### Step 1: Set up your Back end on Supabase
+
+On the [Supabase dashboard](https://app.supabase.com/projects), click `New project` and set the name to adminPanel.
+
+
+
+Create a new table by clicking on the `Create a new table` .
+
+Supabase offers a variety of options for populating tables with data, including writing queries, creating schemas through a user interface, and uploading CSV files.
+
+
+
+
+
+Fill out the info in the table. The database is now set up.
+
+### Step 2: Build UI on ILLA Builder
+
+On [ILLA Builder](https://fast-try.illacloud.com/), click Create New to create a new application.
+
+
+
+Drag components from the `Insert` panel to the canvas.
+
+Select the components on the canvas and configure the property on the `Inspect` panel.
+
+As seen in the below screenshot, we have built a simple admin panel.
+
+
+
+### Step 3: Connect to Supabase and config CRUD
+
+Note down the database connection information under [Project Settings](https://app.supabase.com/project/hdcfnsxpwwgboqomdrhp/settings/database) in Supabase.
+
+
+
+In the Action List, click `+ New` and select Supabase DB.
+
+
+
+Fill out the form to connect to your Supabase instance. Test connection and save resource.
+
+
+
+Click `Create Action` to create an action with the Supabase resource and config your CRUD.
+
+
+
+Use `{{` to get the front-end input data. The following is an example of the User Management page in the Admin Panel.
+
+Search for a user by the name inputted in input1
+
+```
+SELECT *
+FROM user
+WHERE name = "{{input1.value}}"
+;
+```
+
+Update user data. Update user information when id matches
+
+```
+UPDATE user
+SET name = "{{input3.value}}"
+, email = "{{input4.value}}"
+WHERE id="{{input2.value}}"
+;
+```
+
+Insert user data
+
+```
+INSERT INTO user VALUES("{{input5.value}}","{{input6.value}}","{{input7.value}}");
+```
+
+Delete a user by id
+
+```
+DELETE FROM user WHERE id = "{{input2.value}}";
+```
+
+### Step 4: Show data on components
+
+Configure the properties of components with `{{` . For example:
+
+
+
+## Resources
+
+- [ILLA Cloud official website](https://www.illacloud.com/)
+- [ILLA Cloud GitHub](https://github.com/illacloud/illa-builder)
+- [ILLA Cloud documentation](https://www.illacloud.com/docs/about-illa)
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/integrations/integrations.mdx b/apps/docs/pages/guides/integrations/integrations.mdx
index de7a444ec58..a42b311571d 100644
--- a/apps/docs/pages/guides/integrations/integrations.mdx
+++ b/apps/docs/pages/guides/integrations/integrations.mdx
@@ -16,8 +16,8 @@ The Supabase Marketplace brings together all the tools you need to extend your S
Supabase provides several integration points:
- The [Postgres connection](/docs/guides/database/connecting-to-postgres). Anything that works with Postgres also works with Supabase projects.
-- The [Project REST API](/docs/guides/api#rest-api) & client libraries.
-- The [Project GraphQL API](/docs/guides/api#graphql-api).
+- The [Project REST API](/docs/guides/database/api#rest-api) & client libraries.
+- The [Project GraphQL API](/docs/guides/database/api#graphql-api).
- The [Platform API](/docs/reference/api).
## List your integration
diff --git a/apps/docs/pages/guides/integrations/keyri.mdx b/apps/docs/pages/guides/integrations/keyri.mdx
index 3956655167b..6e607205669 100644
--- a/apps/docs/pages/guides/integrations/keyri.mdx
+++ b/apps/docs/pages/guides/integrations/keyri.mdx
@@ -5,6 +5,7 @@ export const meta = {
title: 'Keyri',
description:
'QR authentication for an easy and flexible biometric solution across all platforms.',
+ video: 'https://www.youtube.com/v/jrjrcpc2PFQ',
}
Keyri can be used to incorporate sign-in-with-QR functionality into your Supabase app, allowing users to scan a QR code on your web app with your mobile app and be instantly logged into the web app without having to input any credentials.
@@ -23,7 +24,14 @@ Note your application key from the Keys and Credentials section - this will be u
# Web
-[](http://www.youtube.com/watch?v=jrjrcpc2PFQ 'Supabase & Keyri | Web Integration Overview')
+
+
+
For your web app, first download KeyriQR.html (available [here](https://raw.githubusercontent.com/Keyri-Co/library-keyri-connect/main/KeyriQR.html)) and save it to a public directory.
@@ -64,7 +72,15 @@ That's it!
# Mobile
-[](http://www.youtube.com/watch?v=oGMsSKyh6tc 'Supabase & Keyri | Mobile Integration Overview')
+
+
+
+
### Install Flutter
diff --git a/apps/docs/pages/guides/integrations/onesignal.mdx b/apps/docs/pages/guides/integrations/onesignal.mdx
index cd6b7021dd8..3f270c6f60e 100644
--- a/apps/docs/pages/guides/integrations/onesignal.mdx
+++ b/apps/docs/pages/guides/integrations/onesignal.mdx
@@ -5,6 +5,7 @@ export const meta = {
title: 'OneSignal',
description:
'OneSignal allows you to send cloud messages to your users. Combine OneSignal with your Supabase apps and you can reach out to your users whenever there is a change in your database.',
+ video: 'https://www.youtube.com/v/mw0DLwItue4',
}
[OneSignal](https://onesignal.com/) is a tool that allows you to send messages across different channels such as the following to keep your users engaged.
@@ -14,7 +15,18 @@ export const meta = {
- Emails
- In-app notifications
-This guide steps you through how you can integrate OneSignal with Supabase to create a seamless cloud messaging experience for your users using Database webhooks and edge functions through a simple Next.js application.
+Here is William giving us the overview of how OneSignal can work with Supabase to send notifications to your users.
+
+
+
+
+
+In this guide, we will build a similar app and steps you through how you can integrate OneSignal with Supabase to create a seamless cloud messaging experience for your users using Database webhooks and edge functions through a simple Next.js application.

diff --git a/apps/docs/pages/guides/integrations/picket.mdx b/apps/docs/pages/guides/integrations/picket.mdx
new file mode 100644
index 00000000000..d33fa8f7988
--- /dev/null
+++ b/apps/docs/pages/guides/integrations/picket.mdx
@@ -0,0 +1,572 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'picket',
+ title: 'Picket',
+ description:
+ "Get the best of web2 and web3. Picket allows your users to log in with their wallet without sacrificing Supabase's awesome data management and security features.",
+}
+
+[Picket](https://picketapi.com) is a developer-first, multi-chain web3 auth platform. With Picket, you can easily authenticate users via their wallets and token gate anything.
+
+This guide steps through building a simple todo list Next.js application with Picket and Supabase. We use Picket to allow users to login into our app with their wallets and leverage Supabase's Row Level Security (RLS) to securely store user information off-chain.
+
+> Checkout a [live demo](https://picket-supabase-auth-example-v36z.vercel.app/) of a Picket + Supabase integration
+
+The code for this guide is based of [this example repo](https://github.com/picketapi/picket-supabase-auth-example).
+
+## Requirements
+
+- You have [Supabase](https://supabase.com) account. If you don't, sign up at https://supabase.com/
+- You have a [Picket](https://picketapi.com) account. If you don't, sign up at https://picketapi.com/
+- You've read the [Picket Setup Guide](https://docs.picketapi.com/picket-docs/quick-start-guides/quick-start-guides/start-here-setup)
+- Familiarity with [React](https://reactjs.org/) and [Next.js](https://nextjs.org/)
+
+## Step 1: Create a Picket Project
+
+First, we'll create a new project in our [Picket dashboard](https://picketapi.com/dashboard).
+
+Click the `Create New Project` button at the top of the Projects section on your [Picket dashboard](https://picketapi.com/dashboard). Edit the project to give it a memorable name.
+
+
+
+We're done for now! We'll revisit this project when we are setting up environment variables in our app.
+
+## Step 2: Create a Supabase Project
+
+From your [Supabase dashboard](https://app.supabase.com/), click `New project`.
+
+Enter a `Name` for your Supabase project.
+
+Enter a secure `Database Password`.
+
+Select the any `Region`.
+
+Click `Create new project`.
+
+
+
+## Step 3: Create new New Table with RLS in Supabase
+
+### Create a `todos` Table
+
+From the sidebar menu in the [Supabase dashboard](https://app.supabase.com/), click `Table editor`, then `New table`.
+
+Enter `todos` as the `Name` field.
+
+Select `Enable Row Level Security (RLS)`.
+
+Create four columns:
+
+- `name` as `text`
+- `wallet_address` as `text`
+- `completed` as `bool` with the default value `false`
+- `created_at` as timestamptz with a default value of `now()`
+
+Click `Save` to create the new table.
+
+
+
+### Setup Row Level Security (RLS)
+
+Now we want to make sure that only the `todos` owner, the user's `wallet_address`, can access their todos. The key component of the this RLS policy is the expression
+
+```sql
+((jwt() ->> 'walletAddress'::text) = wallet_address)
+```
+
+This expression checks that the wallet address in the requesting JWT access token is the same as the `wallet_address` in the `todos` table.
+
+
+
+## Step 4: Create a Next.js app
+
+Now, let's start building!
+
+Create a [new Typescript Next.js app](https://nextjs.org/docs/getting-started)
+
+```bash
+npx create-next-app@latest --typescript
+```
+
+Create a `.env.local` file and enter the following values
+
+- `NEXT_PUBLIC_PICKET_PUBLISHABLE_KEY` => Copy the publishable key from the Picket project you created in step 1
+- `PICKET_PROJECT_SECRET_KEY` => Copy the secret key from the Picket project you created in the step 1
+- `NEXT_PUBLIC_SUPABASE_URL` => You can find this URL under "Settings > API" in your Supabase project
+- `NEXT_PUBLIC_SUPABASE_ANON_KEY` => You can find this project API key under "Settings > API" in your Supabase project
+- `SUAPBASE_JWT_SECRET`=> You can find this secret under "Settings > API" in your Supabase project
+
+```txt
+NEXT_PUBLIC_PICKET_PUBLISHABLE_KEY="YOUR_PICKET_PUBLISHABLE_KEY"
+PICKET_PROJECT_SECRET_KEY="YOUR_PICKET_PROJECT_SECRET_KEY"
+NEXT_PUBLIC_SUPABASE_URL="YOUR_SUPABASE_URL"
+NEXT_PUBLIC_SUPABASE_ANON_KEY="YOUR_SUPABASE_ANON_KEY"
+SUPABASE_JWT_SECRET="YOUR_SUPABASE_JWT_SECRET"
+```
+
+## Step 5: Setup Picket for Wallet Login
+
+> For more information on how to setup [Picket](https://picketapi.com) in your Next.js app, checkout the [Picket getting started guide](https://docs.picketapi.com/picket-docs/quick-start-guides/quick-start-guides/wallet-login)
+> After initializing our app, we can setup Picket.
+
+Install the Picket [React](/picket-docs/reference/libraries-and-sdks/react-sdk-picket-react) and [Node](/picket-docs/reference/libraries-and-sdks/node.js-library-picket-node) libraries
+
+```bash
+npm i @picketapi/picket-react @picketapi/picket-node
+```
+
+Update `pages/_app.tsx` to setup the `PicketProvider`
+
+```tsx
+import '../styles/globals.css'
+import type { AppProps } from 'next/app'
+
+import { PicketProvider } from '@picketapi/picket-react'
+
+export default function App({ Component, pageProps }: AppProps) {
+ return (
+
+
+
+ )
+}
+```
+
+Update `pages/index.tsx` to let users log in and out with their wallet
+
+```tsx
+import { GetServerSideProps } from 'next'
+import { useRouter } from 'next/router'
+import { useCallback } from 'react'
+
+import styles from '../styles/Home.module.css'
+
+import { usePicket } from '@picketapi/picket-react'
+import { cookieName } from '../utils/supabase'
+
+type Props = {
+ loggedIn: boolean
+}
+
+export default function Home(props: Props) {
+ const { loggedIn } = props
+ const { login, logout, authState } = usePicket()
+ const router = useRouter()
+
+ const handleLogin = useCallback(async () => {
+ let auth = authState
+ // no need to re-login if they've already connected with Picket
+ if (!auth) {
+ // login with Picket
+ auth = await login()
+ }
+
+ // login failed
+ if (!auth) return
+
+ // create a corresponding supabase access token
+ await fetch('/api/login', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ accessToken: auth.accessToken,
+ }),
+ })
+ // redirect to their todos page
+ router.push('/todos')
+ }, [authState, login, router])
+
+ const handleLogout = useCallback(async () => {
+ // clear both picket and supabase session
+ await logout()
+ await fetch('/api/logout', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ })
+ // refresh the page
+ router.push('/')
+ }, [logout, router])
+
+ return (
+
+
+ {loggedIn ? (
+ Log Out to Switch Wallets
+ ) : (
+ Log In with Your Wallet
+ )}
+
+
+ )
+}
+
+export const getServerSideProps: GetServerSideProps = async ({ req }) => {
+ // get supabase token server-side
+ const accessToken = req.cookies[cookieName]
+
+ if (!accessToken) {
+ return {
+ props: {
+ loggedIn: false,
+ },
+ }
+ }
+
+ return {
+ props: {
+ loggedIn: true,
+ },
+ }
+}
+```
+
+## Step 6: Issue a Supabase JWT on Wallet Login
+
+Great, now we have setup a typical Picket Next.js app. Next, we need to implement the log in/out API routes to allow users to securely query our Supabase project.
+
+First, install dependencies
+
+```bash
+npm install @supabase/supabase-js jsonwebtoken cookie js-cookie
+```
+
+Create a utility function to create a Supabase client with a custom access token in `utils/supabase.ts`
+
+```ts
+import { createClient, SupabaseClientOptions } from '@supabase/supabase-js'
+
+export const cookieName = 'sb-access-token'
+
+const getSupabase = (accessToken: string) => {
+ const options: SupabaseClientOptions<'public'> = {}
+
+ if (accessToken) {
+ options.global = {
+ headers: {
+ // This gives Supabase information about the user (wallet) making the request
+ Authorization: `Bearer ${accessToken}`,
+ },
+ }
+ }
+
+ const supabase = createClient(
+ process.env.NEXT_PUBLIC_SUPABASE_URL!,
+ process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
+ options
+ )
+
+ return supabase
+}
+
+export { getSupabase }
+```
+
+Create new api route `pages/api/login.ts`. This route validates the Picket access token then issues another equivalent Supabase access token for us to use with the Supabase client.
+
+```ts
+import type { NextApiRequest, NextApiResponse } from 'next'
+import jwt from 'jsonwebtoken'
+import cookie from 'cookie'
+import Picket from '@picketapi/picket-node'
+
+import { cookieName } from '../../utils/supabase'
+
+// create picket node client with your picket secret api key
+const picket = new Picket(process.env.PICKET_PROJECT_SECRET_KEY!)
+
+const expToExpiresIn = (exp: number) => exp - Math.floor(Date.now() / 1000)
+
+export default async function handler(req: NextApiRequest, res: NextApiResponse) {
+ const { accessToken } = req.body
+ // omit expiration time,.it will conflict with jwt.sign
+ const { exp, ...payload } = await picket.validate(accessToken)
+ const expiresIn = expToExpiresIn(exp)
+
+ const supabaseJWT = jwt.sign(
+ {
+ ...payload,
+ },
+ process.env.SUPABASE_JWT_SECRET!,
+ {
+ expiresIn,
+ }
+ )
+
+ // Set a new cookie with the name
+ res.setHeader(
+ 'Set-Cookie',
+ cookie.serialize(cookieName, supabaseJWT, {
+ path: '/',
+ secure: process.env.NODE_ENV !== 'development',
+ // allow the cookie to be accessed client-side
+ httpOnly: false,
+ sameSite: 'strict',
+ maxAge: expiresIn,
+ })
+ )
+ res.status(200).json({})
+}
+```
+
+And now create an equivalent logout api route `/pages/api/logout.ts` to delete the Supabase access token cookie.
+
+```ts
+import type { NextApiRequest, NextApiResponse } from 'next'
+import cookie from 'cookie'
+
+import { cookieName } from '../../utils/supabase'
+
+export default async function handler(_req: NextApiRequest, res: NextApiResponse) {
+ // Clear the supabase cookie
+ res.setHeader(
+ 'Set-Cookie',
+ cookie.serialize(cookieName, '', {
+ path: '/',
+ maxAge: -1,
+ })
+ )
+
+ res.status(200).json({})
+}
+```
+
+We can now login and logout to the app with our wallet!
+
+## Step 7: Interacting with Data in Supabase
+
+Now that we can login to the app, it's time to start interacting with Supabase. Let's make a todo list page for authenticated users.
+
+Create a new file `pages/todos.tsx`
+
+```tsx
+import { GetServerSideProps } from 'next'
+import Head from 'next/head'
+import Link from 'next/link'
+import { useState, useMemo } from 'react'
+import jwt from 'jsonwebtoken'
+import Cookies from 'js-cookie'
+
+import styles from '../styles/Home.module.css'
+
+import { getSupabase, cookieName } from '../utils/supabase'
+
+type Todo = {
+ name: string
+ completed: boolean
+}
+
+type Props = {
+ walletAddress: string
+ todos: Todo[]
+}
+
+const displayWalletAddress = (walletAddress: string) =>
+ `${walletAddress.slice(0, 6)}...${walletAddress.slice(-4)}`
+
+export default function Todos(props: Props) {
+ const { walletAddress } = props
+ const [todos, setTodos] = useState(props.todos)
+
+ // avoid re-creating supabase client every render
+ const supabase = useMemo(() => {
+ const accessToken = Cookies.get(cookieName)
+ return getSupabase(accessToken || '')
+ }, [])
+
+ return (
+
+
+ Picket 💜 Supabase
+
+
+
+
Your Personal Todo List
+
+
Welcome {displayWalletAddress(walletAddress)},
+
+ Your todo list is stored in Supabase and are only accessible to you and your wallet
+ address. Picket + Supabase makes it easy to build scalable, hybrid web2 and web3 apps.
+ Use Supabase to store non-critical or private data off-chain like user app preferences
+ or todo lists.
+
+ )
+}
+
+export const getServerSideProps: GetServerSideProps = async ({ req }) => {
+ // example of fetching data server-side
+ const accessToken = req.cookies[cookieName]
+
+ // require authentication
+ if (!accessToken) {
+ return {
+ redirect: {
+ destination: '/',
+ },
+ props: {
+ walletAddress: '',
+ todos: [],
+ },
+ }
+ }
+
+ // check if logged in user has completed the tutorial
+ const supabase = getSupabase(accessToken)
+ const { walletAddress } = jwt.decode(accessToken) as {
+ walletAddress: string
+ }
+
+ // get todos for the users
+ // if none exist, create the default todos
+ let { data } = await supabase.from('todos').select('*')
+
+ if (!data || data.length === 0) {
+ let error = null
+ ;({ data, error } = await supabase
+ .from('todos')
+ .insert([
+ {
+ wallet_address: walletAddress,
+ name: 'Complete the Picket + Supabase Tutorial',
+ completed: true,
+ },
+ {
+ wallet_address: walletAddress,
+ name: 'Create a Picket Account (https://picketapi.com/)',
+ completed: false,
+ },
+ {
+ wallet_address: walletAddress,
+ name: 'Read the Picket Docs (https://docs.picketapi.com/)',
+ completed: false,
+ },
+ {
+ wallet_address: walletAddress,
+ name: 'Build an Awesome Web3 Experience',
+ completed: false,
+ },
+ ])
+ .select('*'))
+
+ if (error) {
+ // log error and redirect home
+ console.error(error)
+ return {
+ redirect: {
+ destination: '/',
+ },
+ props: {
+ walletAddress: '',
+ todos: [],
+ },
+ }
+ }
+ }
+
+ return {
+ props: {
+ walletAddress,
+ todos: data as Todo[],
+ },
+ }
+}
+```
+
+This is a long file, but don't be intimidated. The page is actually straightforward. It
+
+1. Verifies server-side that the user is authenticated and if they are not redirects them to the homepage
+2. Checks to see if they already have `todos` . If so, it returns them. If not, it initializes them for the users
+3. We render the `todos` and when the user selects or deselects a todo, we update the data in the database
+
+## Step 8: Try it Out!
+
+And that's it. If you haven't already, run your app to test it out yourself
+
+```bash
+# start the app
+npm run dev
+# open http://localhost:3000
+```
+
+### What's Next?
+
+- Explore the [Picket documentation](https://docs.picketapi.com/picket-docs/)
+- Play with the live [Picket + Supabase demo](https://picket-supabase-auth-example-v36z.vercel.app/)
+- Checkout Picket's [example Github repositories](https://github.com/picketapi)
+
+### Common Use-Case for Picket + Supabase
+
+- Account linking. Allow users to associate their wallet address(es) with their existing web2 account in your app
+- Leverage Supabase's awesome libraries and ecosystem while still enabling wallet login
+- Store app-specific data, like user preferences, about your user's wallet adress off-chain
+- Cache on-chain data to improve your DApp's performance
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/integrations/plasmic.mdx b/apps/docs/pages/guides/integrations/plasmic.mdx
index 2ca62ccabcb..f90eb871bcc 100644
--- a/apps/docs/pages/guides/integrations/plasmic.mdx
+++ b/apps/docs/pages/guides/integrations/plasmic.mdx
@@ -106,7 +106,7 @@ To tell Plasmic to look for your code components on your dev server, you’ll ne
diff --git a/apps/docs/pages/learn/auth-deep-dive/auth-deep-dive-jwts.mdx b/apps/docs/pages/learn/auth-deep-dive/auth-deep-dive-jwts.mdx
index 766c8a04806..d33020220ed 100644
--- a/apps/docs/pages/learn/auth-deep-dive/auth-deep-dive-jwts.mdx
+++ b/apps/docs/pages/learn/auth-deep-dive/auth-deep-dive-jwts.mdx
@@ -4,6 +4,7 @@ export const meta = {
id: 'auth-deep-dive-jwts',
title: 'Part One: JWTs',
description: 'Supabase Auth Deep Dive Part 1 - JWTs',
+ video: 'https://www.youtube.com/v/v3Exg5YpJvE',
}
### About
diff --git a/apps/docs/pages/learn/auth-deep-dive/auth-google-oauth.mdx b/apps/docs/pages/learn/auth-deep-dive/auth-google-oauth.mdx
index 96a643aa696..c2b91673f74 100644
--- a/apps/docs/pages/learn/auth-deep-dive/auth-google-oauth.mdx
+++ b/apps/docs/pages/learn/auth-deep-dive/auth-google-oauth.mdx
@@ -4,6 +4,7 @@ export const meta = {
id: 'auth-google-oauth',
title: 'Part Five: Google Oauth',
description: 'Supabase Deep Dive Part 5: Google OAuth Provider',
+ video: 'https://www.youtube.com/v/_XM9ziOzWk4',
}
### About
diff --git a/apps/docs/pages/learn/auth-deep-dive/auth-policies.mdx b/apps/docs/pages/learn/auth-deep-dive/auth-policies.mdx
index 902fbe6f8be..00caf731fbc 100644
--- a/apps/docs/pages/learn/auth-deep-dive/auth-policies.mdx
+++ b/apps/docs/pages/learn/auth-deep-dive/auth-policies.mdx
@@ -4,6 +4,7 @@ export const meta = {
id: 'auth-policies',
title: 'Part Three: Policies',
description: 'Supabase Auth Deep Dive Part 3: User Based Access Policies',
+ video: 'https://www.youtube.com/v/0LvCOlELs5U',
}
### About
@@ -154,6 +155,17 @@ create policy "Only Blizzard staff can update leaderboard"
Supabase comes with two built-in helper functions: `auth.uid()` and `auth.jwt()`.
+To create your own functions, navigate to the SQL editor and create a a new query.
+
+```sql
+create or replace function user_agent()
+returns text
+language sql
+as $$
+ select nullif(current_setting('request.headers', true)::json->>'user-agent', '')::text;
+$$;
+```
+
See the full PostgreSQL policy docs here: [https://www.postgresql.org/docs/12/sql-createpolicy.html](https://www.postgresql.org/docs/12/sql-createpolicy.html)
You can get as creative as you like with these policies.
diff --git a/apps/docs/pages/learn/auth-deep-dive/auth-row-level-security.mdx b/apps/docs/pages/learn/auth-deep-dive/auth-row-level-security.mdx
index 66687b36a7f..95a03060bc9 100644
--- a/apps/docs/pages/learn/auth-deep-dive/auth-row-level-security.mdx
+++ b/apps/docs/pages/learn/auth-deep-dive/auth-row-level-security.mdx
@@ -4,6 +4,7 @@ export const meta = {
id: 'auth-row-level-security',
title: 'Part Two: Row Level Security',
description: 'Supabase Auth Deep Dive Part Two - Row Level Security',
+ video: 'https://www.youtube.com/v/qY_iQ10IUhs',
}
### About
diff --git a/apps/docs/pages/new/auth.mdx b/apps/docs/pages/new/auth.mdx
deleted file mode 100644
index 5228f5129da..00000000000
--- a/apps/docs/pages/new/auth.mdx
+++ /dev/null
@@ -1,77 +0,0 @@
-import Layout from '~/layouts/DefaultLayout'
-import Link from 'next/link'
-// import { useTheme } from 'common/Providers'
-import { GlassPanel } from 'ui'
-
-{/* // export const { isDarkMode } = useTheme() */}
-
-export const meta = {
- title: 'Auth',
-}
-
-## Add and manage email and password, passwordless, OAuth, and mobile logins to your project through a suite of identity providers and APIs.
-
-### Popular Guides
-
-
- )
-}
-```
-
-
-
-
-### Add the new widget
-
-And then we can add the widget to the Account page:
-
-```jsx title=components/Account.js
-// Import the new component
-import Avatar from './Avatar'
-
-// ...
-
-return (
-
- {/* Add to the body */}
- {
- setAvatarUrl(url)
- updateProfile({ username, website, avatar_url: url })
- }}
- />
- {/* ... */}
-
-)
-```
-
-## Next steps
-
-At this stage you have a fully functional application!
-
-- See the complete [example on GitHub](https://github.com/supabase/supabase/tree/master/examples/user-management/nextjs-ts-user-management) and deploy it to Vercel.
-- Explore the [pre-built Auth UI for React](https://supabase.com/docs/guides/auth/auth-helpers/auth-ui).
-- Explore the [Auth Helpers for Next.js](https://supabase.com/docs/guides/auth/auth-helpers/nextjs).
-- Explore the [Supabase Cache Helpers](https://github.com/psteinroe/supabase-cache-helpers).
-- See the [Next.js Subscription Payments Starter](https://github.com/vercel/nextjs-subscription-payments) template on GitHub.
-- Got a question? [Ask here](https://github.com/supabase/supabase/discussions).
-- Sign in: [app.supabase.com](https://app.supabase.com)
-
-export default ({ children }) =>
diff --git a/apps/docs/public/humans.txt b/apps/docs/public/humans.txt
index 03b3076672c..2daf85cdf42 100644
--- a/apps/docs/public/humans.txt
+++ b/apps/docs/public/humans.txt
@@ -17,6 +17,7 @@ Dave Wilson
Div Arora
Egor Romanov
Fabrizio Fenoglio
+Haydn Maley
Hieu Pham
Inian P
Isaiah Hamilton
@@ -27,6 +28,7 @@ Jonny Summers-Muir
Joshen Lim
Kang Ming Tay
Karlo Ison
+Kevin Brolly
Kevin Grüneberg
Lakshmipathi G
Laura C
@@ -44,7 +46,7 @@ Paul Copplestone
Pavel Borisov
Qiao Han
Ramiro Nuñez Dosio
-Rodrigo Mansueli Nunes
+Rodrigo Martins Mansueli
Rory Wilding
Stanislav M
Steve Chavez
diff --git a/apps/docs/public/img/clippy-dark.png b/apps/docs/public/img/clippy-dark.png
new file mode 100644
index 00000000000..be142ffbe5e
Binary files /dev/null and b/apps/docs/public/img/clippy-dark.png differ
diff --git a/apps/docs/public/img/clippy.png b/apps/docs/public/img/clippy.png
new file mode 100644
index 00000000000..5acb1b6e1d3
Binary files /dev/null and b/apps/docs/public/img/clippy.png differ
diff --git a/apps/docs/public/img/guides/integrations/illa/supabase-illa-UI.png b/apps/docs/public/img/guides/integrations/illa/supabase-illa-UI.png
new file mode 100644
index 00000000000..e90b8ae8ee3
Binary files /dev/null and b/apps/docs/public/img/guides/integrations/illa/supabase-illa-UI.png differ
diff --git a/apps/docs/public/img/guides/integrations/illa/supabase-illa-connect-2.png b/apps/docs/public/img/guides/integrations/illa/supabase-illa-connect-2.png
new file mode 100644
index 00000000000..61c3219b625
Binary files /dev/null and b/apps/docs/public/img/guides/integrations/illa/supabase-illa-connect-2.png differ
diff --git a/apps/docs/public/img/guides/integrations/illa/supabase-illa-connect.png b/apps/docs/public/img/guides/integrations/illa/supabase-illa-connect.png
new file mode 100644
index 00000000000..b1787ee8473
Binary files /dev/null and b/apps/docs/public/img/guides/integrations/illa/supabase-illa-connect.png differ
diff --git a/apps/docs/public/img/guides/integrations/illa/supabase-illa-create-project.png b/apps/docs/public/img/guides/integrations/illa/supabase-illa-create-project.png
new file mode 100644
index 00000000000..d04b4c5f5a7
Binary files /dev/null and b/apps/docs/public/img/guides/integrations/illa/supabase-illa-create-project.png differ
diff --git a/apps/docs/public/img/guides/integrations/illa/supabase-illa-project.png b/apps/docs/public/img/guides/integrations/illa/supabase-illa-project.png
new file mode 100644
index 00000000000..ffbd1876873
Binary files /dev/null and b/apps/docs/public/img/guides/integrations/illa/supabase-illa-project.png differ
diff --git a/apps/docs/public/img/guides/integrations/illa/supabase-illa-select.png b/apps/docs/public/img/guides/integrations/illa/supabase-illa-select.png
new file mode 100644
index 00000000000..f6160065d70
Binary files /dev/null and b/apps/docs/public/img/guides/integrations/illa/supabase-illa-select.png differ
diff --git a/apps/docs/public/img/guides/integrations/illa/supabase-illa-show-data.png b/apps/docs/public/img/guides/integrations/illa/supabase-illa-show-data.png
new file mode 100644
index 00000000000..87db66800a9
Binary files /dev/null and b/apps/docs/public/img/guides/integrations/illa/supabase-illa-show-data.png differ
diff --git a/apps/docs/public/img/guides/integrations/illa/supabase-information.png b/apps/docs/public/img/guides/integrations/illa/supabase-information.png
new file mode 100644
index 00000000000..8becd5e3ab9
Binary files /dev/null and b/apps/docs/public/img/guides/integrations/illa/supabase-information.png differ
diff --git a/apps/docs/public/img/guides/integrations/illa/supabase-table-1.png b/apps/docs/public/img/guides/integrations/illa/supabase-table-1.png
new file mode 100644
index 00000000000..1452c0a3289
Binary files /dev/null and b/apps/docs/public/img/guides/integrations/illa/supabase-table-1.png differ
diff --git a/apps/docs/public/img/guides/integrations/illa/supabase-table-2.png b/apps/docs/public/img/guides/integrations/illa/supabase-table-2.png
new file mode 100644
index 00000000000..94d8711c6d5
Binary files /dev/null and b/apps/docs/public/img/guides/integrations/illa/supabase-table-2.png differ
diff --git a/apps/docs/public/img/guides/integrations/picket/new_table.png b/apps/docs/public/img/guides/integrations/picket/new_table.png
new file mode 100644
index 00000000000..72aa9b21ea2
Binary files /dev/null and b/apps/docs/public/img/guides/integrations/picket/new_table.png differ
diff --git a/apps/docs/public/img/guides/integrations/picket/picket_project.png b/apps/docs/public/img/guides/integrations/picket/picket_project.png
new file mode 100644
index 00000000000..6d359e9af83
Binary files /dev/null and b/apps/docs/public/img/guides/integrations/picket/picket_project.png differ
diff --git a/apps/docs/public/img/guides/integrations/picket/rls_policy.png b/apps/docs/public/img/guides/integrations/picket/rls_policy.png
new file mode 100644
index 00000000000..347038ebe90
Binary files /dev/null and b/apps/docs/public/img/guides/integrations/picket/rls_policy.png differ
diff --git a/apps/docs/public/img/guides/integrations/picket/supabase_project.png b/apps/docs/public/img/guides/integrations/picket/supabase_project.png
new file mode 100644
index 00000000000..97c441b6ef8
Binary files /dev/null and b/apps/docs/public/img/guides/integrations/picket/supabase_project.png differ
diff --git a/apps/docs/public/img/guides/resources/migrating-to-supabase/render/render_dashboard.png b/apps/docs/public/img/guides/resources/migrating-to-supabase/render/render_dashboard.png
new file mode 100644
index 00000000000..1645901dc23
Binary files /dev/null and b/apps/docs/public/img/guides/resources/migrating-to-supabase/render/render_dashboard.png differ
diff --git a/apps/docs/public/img/guides/resources/migrating-to-supabase/render/supabase_dashboard.png b/apps/docs/public/img/guides/resources/migrating-to-supabase/render/supabase_dashboard.png
new file mode 100644
index 00000000000..3a1ff5b9daf
Binary files /dev/null and b/apps/docs/public/img/guides/resources/migrating-to-supabase/render/supabase_dashboard.png differ
diff --git a/apps/docs/public/img/icons/csharp.png b/apps/docs/public/img/icons/csharp.png
new file mode 100644
index 00000000000..37f9fd725fa
Binary files /dev/null and b/apps/docs/public/img/icons/csharp.png differ
diff --git a/apps/docs/public/img/icons/menu/c-sharp-icon.svg b/apps/docs/public/img/icons/menu/c-sharp-icon.svg
new file mode 100644
index 00000000000..40d0375079d
--- /dev/null
+++ b/apps/docs/public/img/icons/menu/c-sharp-icon.svg
@@ -0,0 +1,9 @@
+
+
+
diff --git a/apps/docs/public/img/icons/menu/reference-csharp-light.svg b/apps/docs/public/img/icons/menu/reference-csharp-light.svg
new file mode 100644
index 00000000000..782f87bcc40
--- /dev/null
+++ b/apps/docs/public/img/icons/menu/reference-csharp-light.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/apps/docs/public/img/icons/menu/reference-csharp.svg b/apps/docs/public/img/icons/menu/reference-csharp.svg
new file mode 100644
index 00000000000..40d0375079d
--- /dev/null
+++ b/apps/docs/public/img/icons/menu/reference-csharp.svg
@@ -0,0 +1,9 @@
+
+
+
diff --git a/apps/docs/public/img/icons/menu/reference-dart-light.svg b/apps/docs/public/img/icons/menu/reference-dart-light.svg
index 245516d78f7..178f51a5b88 100644
--- a/apps/docs/public/img/icons/menu/reference-dart-light.svg
+++ b/apps/docs/public/img/icons/menu/reference-dart-light.svg
@@ -1,9 +1,9 @@
-
+ Every project on the Supabase Platform comes with its own dedicated Postgres
+ instance running inside a virtual machine (VM). The following table describes the
+ base instance with additional compute add-ons available if you need extra
+ performance when scaling up Supabase.
+