diff --git a/.github/workflows/ai-tests.yml b/.github/workflows/ai-tests.yml index 90f94476155..64314d44fe7 100644 --- a/.github/workflows/ai-tests.yml +++ b/.github/workflows/ai-tests.yml @@ -36,7 +36,6 @@ jobs: - uses: pnpm/action-setup@v4 name: Install pnpm with: - version: 9.10.0 run_install: false - name: Use Node.js uses: actions/setup-node@v4 diff --git a/.github/workflows/autofix_linters.yml b/.github/workflows/autofix_linters.yml index 650d5776a82..548b97912d5 100644 --- a/.github/workflows/autofix_linters.yml +++ b/.github/workflows/autofix_linters.yml @@ -26,7 +26,6 @@ jobs: - uses: pnpm/action-setup@v4 name: Install pnpm with: - version: 9.10.0 run_install: false - uses: actions/setup-node@v4 diff --git a/.github/workflows/docs-last-changed.yml b/.github/workflows/docs-last-changed.yml index d8d88c53af5..94967a403f2 100644 --- a/.github/workflows/docs-last-changed.yml +++ b/.github/workflows/docs-last-changed.yml @@ -29,7 +29,6 @@ jobs: - uses: pnpm/action-setup@v4 name: Install pnpm with: - version: 9.10.0 run_install: false - name: Setup node diff --git a/.github/workflows/docs-lint-v2.yml b/.github/workflows/docs-lint-v2.yml index 5ec6e0d3127..fc1112313fc 100644 --- a/.github/workflows/docs-lint-v2.yml +++ b/.github/workflows/docs-lint-v2.yml @@ -38,10 +38,10 @@ jobs: ~/.cargo/registry/index/ ~/.cargo/registry/cache/ ~/.cargo/git/db/ - key: 6435a4cd1eeea7c2bbd343731de7e8a5127cb2d1 + key: 0be447eba738c3fd56698eff8ad01fa56c2391d7 - name: install linter if: steps.cache-cargo.outputs.cache-hit != 'true' - run: cargo install --locked --git https://github.com/supabase-community/supa-mdx-lint --rev 6435a4cd1eeea7c2bbd343731de7e8a5127cb2d1 + run: cargo install --locked --git https://github.com/supabase-community/supa-mdx-lint --rev 0be447eba738c3fd56698eff8ad01fa56c2391d7 - name: install reviewdog uses: reviewdog/action-setup@3f401fe1d58fe77e10d665ab713057375e39b887 # v1.3.0 with: @@ -54,7 +54,7 @@ jobs: run: | set -o pipefail git diff --name-only origin/$BASE_REF HEAD \ - | { grep -E "^apps/docs/content/guides/" || test $? = 1; } \ + | { grep -E "^apps/docs/content/" || test $? = 1; } \ | xargs -r supa-mdx-lint --format rdf \ | reviewdog -f=rdjsonl -reporter=github-pr-review - name: run linter on push or workflow dispatch diff --git a/.github/workflows/docs-mgmt-api-update.yml b/.github/workflows/docs-mgmt-api-update.yml index 47508dc49c2..f395be7acad 100644 --- a/.github/workflows/docs-mgmt-api-update.yml +++ b/.github/workflows/docs-mgmt-api-update.yml @@ -20,7 +20,6 @@ jobs: - uses: pnpm/action-setup@v4 name: Install pnpm with: - version: 9.10.0 run_install: false - name: Use Node.js diff --git a/.github/workflows/docs-tests-smoke.yml b/.github/workflows/docs-tests-smoke.yml index 930406127c1..c5a38916a46 100644 --- a/.github/workflows/docs-tests-smoke.yml +++ b/.github/workflows/docs-tests-smoke.yml @@ -25,7 +25,6 @@ jobs: - uses: pnpm/action-setup@v4 name: Install pnpm with: - version: 9.10.0 run_install: false - name: Use Node.js diff --git a/.github/workflows/docs-tests.yml b/.github/workflows/docs-tests.yml index e1de7081e50..f9a36f3cf20 100644 --- a/.github/workflows/docs-tests.yml +++ b/.github/workflows/docs-tests.yml @@ -26,7 +26,6 @@ jobs: - uses: pnpm/action-setup@v4 name: Install pnpm with: - version: 9.10.0 run_install: false - name: Use Node.js diff --git a/.github/workflows/local-studio-tests.yml b/.github/workflows/local-studio-tests.yml index 9c64715b1e0..24ccc70cce5 100644 --- a/.github/workflows/local-studio-tests.yml +++ b/.github/workflows/local-studio-tests.yml @@ -28,7 +28,6 @@ jobs: - uses: pnpm/action-setup@v4 name: Install pnpm with: - version: 9.10.0 run_install: false - name: Use Node.js uses: actions/setup-node@v4 diff --git a/.github/workflows/prettier.yml b/.github/workflows/prettier.yml index 0886ee2337a..9381cd0e98e 100644 --- a/.github/workflows/prettier.yml +++ b/.github/workflows/prettier.yml @@ -21,7 +21,6 @@ jobs: - uses: pnpm/action-setup@v4 name: Install pnpm with: - version: 9.10.0 run_install: false - name: Setup node uses: actions/setup-node@v4 @@ -47,7 +46,6 @@ jobs: - uses: pnpm/action-setup@v4 name: Install pnpm with: - version: 9.10.0 run_install: false - name: Setup node uses: actions/setup-node@v4 @@ -73,7 +71,6 @@ jobs: - uses: pnpm/action-setup@v4 name: Install pnpm with: - version: 9.10.0 run_install: false - name: Setup node uses: actions/setup-node@v4 diff --git a/.github/workflows/search.yml b/.github/workflows/search.yml index 70148e80480..60385047ea4 100644 --- a/.github/workflows/search.yml +++ b/.github/workflows/search.yml @@ -41,7 +41,6 @@ jobs: - uses: pnpm/action-setup@v4 name: Install pnpm with: - version: 9.10.0 run_install: false - name: Setup node diff --git a/.github/workflows/studio-tests.yml b/.github/workflows/studio-tests.yml index 7390691e03d..0f41b9e49cf 100644 --- a/.github/workflows/studio-tests.yml +++ b/.github/workflows/studio-tests.yml @@ -34,7 +34,6 @@ jobs: - uses: pnpm/action-setup@v4 name: Install pnpm with: - version: 9.10.0 run_install: false - name: Use Node.js uses: actions/setup-node@v4 diff --git a/.github/workflows/typecheck.yml b/.github/workflows/typecheck.yml index 42e5e2c0cb5..76e474371a1 100644 --- a/.github/workflows/typecheck.yml +++ b/.github/workflows/typecheck.yml @@ -22,7 +22,6 @@ jobs: - uses: pnpm/action-setup@v4 name: Install pnpm with: - version: 9.10.0 run_install: false - name: Use Node.js diff --git a/.github/workflows/ui-patterns-tests.yml b/.github/workflows/ui-patterns-tests.yml index 2209729286c..1f5b93696d8 100644 --- a/.github/workflows/ui-patterns-tests.yml +++ b/.github/workflows/ui-patterns-tests.yml @@ -24,7 +24,6 @@ jobs: - uses: pnpm/action-setup@v4 name: Install pnpm with: - version: 9.10.0 run_install: false - name: Use Node.js diff --git a/.github/workflows/ui-tests.yml b/.github/workflows/ui-tests.yml index ba4861cfb9b..80b75b974cd 100644 --- a/.github/workflows/ui-tests.yml +++ b/.github/workflows/ui-tests.yml @@ -24,7 +24,6 @@ jobs: - uses: pnpm/action-setup@v4 name: Install pnpm with: - version: 9.10.0 run_install: false - name: Use Node.js diff --git a/.npmrc b/.npmrc index 4fd021952d5..a4047535344 100644 --- a/.npmrc +++ b/.npmrc @@ -1 +1,2 @@ -engine-strict=true \ No newline at end of file +engine-strict=true +update-notifier=false diff --git a/apps/design-system/__registry__/index.tsx b/apps/design-system/__registry__/index.tsx index 8d55cc7b07a..9ecdd2dd9cc 100644 --- a/apps/design-system/__registry__/index.tsx +++ b/apps/design-system/__registry__/index.tsx @@ -1919,6 +1919,17 @@ export const Index: Record = { subcategory: "undefined", chunks: [] }, + "multi-select-combobox-creatable": { + name: "multi-select-combobox-creatable", + type: "components:example", + registryDependencies: undefined, + component: React.lazy(() => import("@/registry/default/example/multi-select-combobox-creatable")), + source: "", + files: ["registry/default/example/multi-select-combobox-creatable.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, "multi-select-deletable-badge": { name: "multi-select-deletable-badge", type: "components:example", diff --git a/apps/design-system/config/docs.ts b/apps/design-system/config/docs.ts index 471a9c89158..e96ec0b2e5c 100644 --- a/apps/design-system/config/docs.ts +++ b/apps/design-system/config/docs.ts @@ -349,6 +349,11 @@ export const docsConfig: DocsConfig = { href: '/docs/components/slider', items: [], }, + { + title: 'Sidebar', + href: '/docs/components/sidebar', + items: [], + }, { title: 'Sonner', href: '/docs/components/sonner', diff --git a/apps/design-system/content/docs/components/sidebar.mdx b/apps/design-system/content/docs/components/sidebar.mdx new file mode 100644 index 00000000000..f8706d0f4ae --- /dev/null +++ b/apps/design-system/content/docs/components/sidebar.mdx @@ -0,0 +1,1372 @@ +--- +title: Sidebar +description: A composable, themeable and customizable sidebar component. +component: true +--- + +
+ +
+ A sidebar that collapses to icons. +
+
+ +Sidebars are one of the most complex components to build. They are central +to any application and often contain a lot of moving parts. + +I don't like building sidebars. So I built 30+ of them. All kinds of +configurations. Then I extracted the core components into `sidebar.tsx`. + +We now have a solid foundation to build on top of. Composable. Themeable. +Customizable. + +[Browse the Blocks Library](/blocks). + +## Installation + + + + + CLI + Manual + + + + + +Run the following command to install `sidebar.tsx` + +```bash +npx shadcn@latest add sidebar +``` + +Add the following colors to your CSS file + +The command above should install the colors for you. If not, copy and paste the following in your CSS file. + +We'll go over the colors later in the [theming section](/docs/components/sidebar#theming). + +```css title="app/globals.css" +@layer base { + :root { + --sidebar-background: 0 0% 98%; + --sidebar-foreground: 240 5.3% 26.1%; + --sidebar-primary: 240 5.9% 10%; + --sidebar-primary-foreground: 0 0% 98%; + --sidebar-accent: 240 4.8% 95.9%; + --sidebar-accent-foreground: 240 5.9% 10%; + --sidebar-border: 220 13% 91%; + --sidebar-ring: 217.2 91.2% 59.8%; + } + + .dark { + --sidebar-background: 240 5.9% 10%; + --sidebar-foreground: 240 4.8% 95.9%; + --sidebar-primary: 224.3 76.3% 48%; + --sidebar-primary-foreground: 0 0% 100%; + --sidebar-accent: 240 3.7% 15.9%; + --sidebar-accent-foreground: 240 4.8% 95.9%; + --sidebar-border: 240 3.7% 15.9%; + --sidebar-ring: 217.2 91.2% 59.8%; + } +} +``` + + + + + + + + + +Copy and paste the following code into your project. + + + +Update the import paths to match your project setup. + +Add the following colors to your CSS file + +We'll go over the colors later in the [theming section](/docs/components/sidebar#theming). + +```css title="app/globals.css" +@layer base { + :root { + --sidebar-background: 0 0% 98%; + --sidebar-foreground: 240 5.3% 26.1%; + --sidebar-primary: 240 5.9% 10%; + --sidebar-primary-foreground: 0 0% 98%; + --sidebar-accent: 240 4.8% 95.9%; + --sidebar-accent-foreground: 240 5.9% 10%; + --sidebar-border: 220 13% 91%; + --sidebar-ring: 217.2 91.2% 59.8%; + } + + .dark { + --sidebar-background: 240 5.9% 10%; + --sidebar-foreground: 240 4.8% 95.9%; + --sidebar-primary: 224.3 76.3% 48%; + --sidebar-primary-foreground: 0 0% 100%; + --sidebar-accent: 240 3.7% 15.9%; + --sidebar-accent-foreground: 240 4.8% 95.9%; + --sidebar-border: 240 3.7% 15.9%; + --sidebar-ring: 217.2 91.2% 59.8%; + } +} +``` + +Add the Sidebar tailwind config into `tailwind.config.js` + +Add the following object in the `theme.extend.colors` section of your `tailwind.config.js` file +this config enable sidebar related style utilities like bg-sidebar + +```javascript title="tailwind.config.js" +// ... +sidebar: { + DEFAULT: 'hsl(var(--sidebar-background))', + foreground: 'hsl(var(--sidebar-foreground))', + primary: 'hsl(var(--sidebar-primary))', + 'primary-foreground': 'hsl(var(--sidebar-primary-foreground))', + accent: 'hsl(var(--sidebar-accent))', + 'accent-foreground': 'hsl(var(--sidebar-accent-foreground))', + border: 'hsl(var(--sidebar-border))', + ring: 'hsl(var(--sidebar-ring))', +}, +// ... +``` + + + + + + + +## Structure + +A `Sidebar` component is composed of the following parts: + +- `SidebarProvider` - Handles collapsible state. +- `Sidebar` - The sidebar container. +- `SidebarHeader` and `SidebarFooter` - Sticky at the top and bottom of the sidebar. +- `SidebarContent` - Scrollable content. +- `SidebarGroup` - Section within the `SidebarContent`. +- `SidebarTrigger` - Trigger for the `Sidebar`. + +Sidebar Structure +Sidebar Structure + +## Usage + +```tsx showLineNumbers title="app/layout.tsx" +import { SidebarProvider, SidebarTrigger } from '@/components/ui/sidebar' +import { AppSidebar } from '@/components/app-sidebar' + +export default function Layout({ children }: { children: React.ReactNode }) { + return ( + + +
+ + {children} +
+
+ ) +} +``` + +```tsx showLineNumbers title="components/app-sidebar.tsx" +import { + Sidebar, + SidebarContent, + SidebarFooter, + SidebarGroup, + SidebarHeader, +} from '@/components/ui/sidebar' + +export function AppSidebar() { + return ( + + + + + + + + + ) +} +``` + +## Your First Sidebar + +Let's start with the most basic sidebar. A collapsible sidebar with a menu. + + + +Add a `SidebarProvider` and `SidebarTrigger` at the root of your application. + +```tsx showLineNumbers title="app/layout.tsx" +import { SidebarProvider, SidebarTrigger } from '@/components/ui/sidebar' +import { AppSidebar } from '@/components/app-sidebar' + +export default function Layout({ children }: { children: React.ReactNode }) { + return ( + + +
+ + {children} +
+
+ ) +} +``` + +Create a new sidebar component at `components/app-sidebar.tsx`. + +```tsx showLineNumbers title="components/app-sidebar.tsx" +import { Sidebar, SidebarContent } from '@/components/ui/sidebar' + +export function AppSidebar() { + return ( + + + + ) +} +``` + +Now, let's add a `SidebarMenu` to the sidebar. + +We'll use the `SidebarMenu` component in a `SidebarGroup`. + +```tsx showLineNumbers title="components/app-sidebar.tsx" +import { Calendar, Home, Inbox, Search, Settings } from 'lucide-react' + +import { + Sidebar, + SidebarContent, + SidebarGroup, + SidebarGroupContent, + SidebarGroupLabel, + SidebarMenu, + SidebarMenuButton, + SidebarMenuItem, +} from '@/components/ui/sidebar' + +// Menu items. +const items = [ + { + title: 'Home', + url: '#', + icon: Home, + }, + { + title: 'Inbox', + url: '#', + icon: Inbox, + }, + { + title: 'Calendar', + url: '#', + icon: Calendar, + }, + { + title: 'Search', + url: '#', + icon: Search, + }, + { + title: 'Settings', + url: '#', + icon: Settings, + }, +] + +export function AppSidebar() { + return ( + + + + Application + + + {items.map((item) => ( + + + + + {item.title} + + + + ))} + + + + + + ) +} +``` + +You've created your first sidebar. + +
+ +
Your first sidebar.
+
+ +
+ +## Components + +The components in `sidebar.tsx` are built to be composable i.e you build your sidebar by putting the provided components together. They also compose well with other shadcn/ui components such as `DropdownMenu`, `Collapsible` or `Dialog` etc. + +**If you need to change the code in `sidebar.tsx`, you are encouraged to do so. The code is yours. Use `sidebar.tsx` as a starting point and build your own.** + +In the next sections, we'll go over each component and how to use them. + +## SidebarProvider + +The `SidebarProvider` component is used to provide the sidebar context to the `Sidebar` component. You should always wrap your application in a `SidebarProvider` component. + +### Props + +| Name | Type | Description | +| -------------- | ------------------------- | -------------------------------------------- | +| `defaultOpen` | `boolean` | Default open state of the sidebar. | +| `open` | `boolean` | Open state of the sidebar (controlled). | +| `onOpenChange` | `(open: boolean) => void` | Sets open state of the sidebar (controlled). | + +### Width + +If you have a single sidebar in your application, you can use the `SIDEBAR_WIDTH` and `SIDEBAR_WIDTH_MOBILE` variables in `sidebar.tsx` to set the width of the sidebar. + +```tsx showLineNumbers title="components/ui/sidebar.tsx" +const SIDEBAR_WIDTH = '16rem' +const SIDEBAR_WIDTH_MOBILE = '18rem' +``` + +For multiple sidebars in your application, you can use the `style` prop to set the width of the sidebar. + +To set the width of the sidebar, you can use the `--sidebar-width` and `--sidebar-width-mobile` CSS variables in the `style` prop. + +```tsx showLineNumbers title="components/ui/sidebar.tsx" + + + +``` + +This will handle the width of the sidebar but also the layout spacing. + +### Keyboard Shortcut + +The `SIDEBAR_KEYBOARD_SHORTCUT` variable is used to set the keyboard shortcut used to open and close the sidebar. + +To trigger the sidebar, you use the `cmd+b` keyboard shortcut on Mac and `ctrl+b` on Windows. + +You can change the keyboard shortcut by updating the `SIDEBAR_KEYBOARD_SHORTCUT` variable. + +```tsx showLineNumbers title="components/ui/sidebar.tsx" +const SIDEBAR_KEYBOARD_SHORTCUT = 'b' +``` + +### Persisted State + +The `SidebarProvider` supports persisting the sidebar state across page reloads and server-side rendering. It uses cookies to store the current state of the sidebar. When the sidebar state changes, a default cookie named `sidebar_state` is set with the current open/closed state. This cookie is then read on subsequent page loads to restore the sidebar state. + +To persist sidebar state in Next.js, set up your `SidebarProvider` in `app/layout.tsx` like this: + +```tsx showLineNumbers title="app/layout.tsx" +import { cookies } from 'next/headers' + +import { SidebarProvider, SidebarTrigger } from '@/components/ui/sidebar' +import { AppSidebar } from '@/components/app-sidebar' + +export async function Layout({ children }: { children: React.ReactNode }) { + const cookieStore = await cookies() + const defaultOpen = cookieStore.get('sidebar_state')?.value === 'true' + + return ( + + +
+ + {children} +
+
+ ) +} +``` + +You can change the name of the cookie by updating the `SIDEBAR_COOKIE_NAME` variable in `sidebar.tsx`. + +```tsx showLineNumbers title="components/ui/sidebar.tsx" +const SIDEBAR_COOKIE_NAME = 'sidebar_state' +``` + +## Sidebar + +The main `Sidebar` component used to render a collapsible sidebar. + +```tsx showLineNumbers +import { Sidebar } from '@/components/ui/sidebar' + +export function AppSidebar() { + return +} +``` + +### Props + +| Property | Type | Description | +| ------------- | --------------------------------- | --------------------------------- | +| `side` | `left` or `right` | The side of the sidebar. | +| `variant` | `sidebar`, `floating`, or `inset` | The variant of the sidebar. | +| `collapsible` | `offcanvas`, `icon`, or `none` | Collapsible state of the sidebar. | + +### side + +Use the `side` prop to change the side of the sidebar. + +Available options are `left` and `right`. + +```tsx showLineNumbers +import { Sidebar } from '@/components/ui/sidebar' + +export function AppSidebar() { + return +} +``` + +### variant + +Use the `variant` prop to change the variant of the sidebar. + +Available options are `sidebar`, `floating` and `inset`. + +```tsx showLineNumbers +import { Sidebar } from '@/components/ui/sidebar' + +export function AppSidebar() { + return +} +``` + + + **Note:** If you use the `inset` variant, remember to wrap your main content in a `SidebarInset` + component. + + +```tsx showLineNumbers + + + +
{children}
+
+
+``` + +### collapsible + +Use the `collapsible` prop to make the sidebar collapsible. + +Available options are `offcanvas`, `icon` and `none`. + +```tsx showLineNumbers +import { Sidebar } from '@/components/ui/sidebar' + +export function AppSidebar() { + return +} +``` + +| Prop | Description | +| ----------- | ------------------------------------------------------------ | +| `offcanvas` | A collapsible sidebar that slides in from the left or right. | +| `icon` | A sidebar that collapses to icons. | +| `none` | A non-collapsible sidebar. | + +## useSidebar + +The `useSidebar` hook is used to control the sidebar. + +```tsx showLineNumbers +import { useSidebar } from '@/components/ui/sidebar' + +export function AppSidebar() { + const { state, open, setOpen, openMobile, setOpenMobile, isMobile, toggleSidebar } = useSidebar() +} +``` + +| Property | Type | Description | +| --------------- | ------------------------- | --------------------------------------------- | +| `state` | `expanded` or `collapsed` | The current state of the sidebar. | +| `open` | `boolean` | Whether the sidebar is open. | +| `setOpen` | `(open: boolean) => void` | Sets the open state of the sidebar. | +| `openMobile` | `boolean` | Whether the sidebar is open on mobile. | +| `setOpenMobile` | `(open: boolean) => void` | Sets the open state of the sidebar on mobile. | +| `isMobile` | `boolean` | Whether the sidebar is on mobile. | +| `toggleSidebar` | `() => void` | Toggles the sidebar. Desktop and mobile. | + +## SidebarHeader + +Use the `SidebarHeader` component to add a sticky header to the sidebar. + +The following example adds a `` to the `SidebarHeader`. + +
+ +
+ A sidebar header with a dropdown menu. +
+
+ +```tsx showLineNumbers title="components/app-sidebar.tsx" + + + + + + + + Select Workspace + + + + + + Acme Inc + + + Acme Corp. + + + + + + + +``` + +## SidebarFooter + +Use the `SidebarFooter` component to add a sticky footer to the sidebar. + +The following example adds a `` to the `SidebarFooter`. + +
+ +
+ A sidebar footer with a dropdown menu. +
+
+ +```tsx showLineNumbers title="components/app-sidebar.tsx" +export function AppSidebar() { + return ( + + + + + + + + + + + Username + + + + + + Account + + + Billing + + + Sign out + + + + + + + + + ) +} +``` + +## SidebarContent + +The `SidebarContent` component is used to wrap the content of the sidebar. This is where you add your `SidebarGroup` components. It is scrollable. + +```tsx showLineNumbers +import { Sidebar, SidebarContent } from '@/components/ui/sidebar' + +export function AppSidebar() { + return ( + + + + + + + ) +} +``` + +## SidebarGroup + +Use the `SidebarGroup` component to create a section within the sidebar. + +A `SidebarGroup` has a `SidebarGroupLabel`, a `SidebarGroupContent` and an optional `SidebarGroupAction`. + +
+ +
A sidebar group.
+
+ +```tsx showLineNumbers +import { Sidebar, SidebarContent, SidebarGroup } from '@/components/ui/sidebar' + +export function AppSidebar() { + return ( + + + + Application + + Add Project + + + + + + ) +} +``` + +## Collapsible SidebarGroup + +To make a `SidebarGroup` collapsible, wrap it in a `Collapsible`. + +
+ +
+ A collapsible sidebar group. +
+
+ +```tsx showLineNumbers +export function AppSidebar() { + return ( + + + + + Help + + + + + + + + + ) +} +``` + + + **Note:** We wrap the `CollapsibleTrigger` in a `SidebarGroupLabel` to render a button. + + +## SidebarGroupAction + +Use the `SidebarGroupAction` component to add an action button to the `SidebarGroup`. + +```tsx showLineNumbers {5-7} +export function AppSidebar() { + return ( + + Projects + + Add Project + + + + ) +} +``` + +
+ +
+ A sidebar group with an action button. +
+
+ +## SidebarMenu + +The `SidebarMenu` component is used for building a menu within a `SidebarGroup`. + +A `SidebarMenu` component is composed of `SidebarMenuItem`, `SidebarMenuButton`, `` and `` components. + +Sidebar Menu +Sidebar Menu + +Here's an example of a `SidebarMenu` component rendering a list of projects. + +
+ +
+ A sidebar menu with a list of projects. +
+
+ +```tsx showLineNumbers + + + + Projects + + + {projects.map((project) => ( + + + + + {project.name} + + + + ))} + + + + + +``` + +## SidebarMenuButton + +The `SidebarMenuButton` component is used to render a menu button within a `SidebarMenuItem`. + +### Link or Anchor + +By default, the `SidebarMenuButton` renders a button but you can use the `asChild` prop to render a different component such as a `Link` or an `a` tag. + +```tsx showLineNumbers + + Home + +``` + +### Icon and Label + +You can render an icon and a truncated label inside the button. Remember to wrap the label in a ``. + +```tsx showLineNumbers + + + + Home + + +``` + +### isActive + +Use the `isActive` prop to mark a menu item as active. + +```tsx showLineNumbers + + Home + +``` + +## SidebarMenuAction + +The `SidebarMenuAction` component is used to render a menu action within a `SidebarMenuItem`. + +This button works independently of the `SidebarMenuButton` i.e you can have the `` as a clickable link and the `` as a button. + +```tsx showLineNumbers + + + + + Home + + + + Add Project + + +``` + +### DropdownMenu + +Here's an example of a `SidebarMenuAction` component rendering a `DropdownMenu`. + +
+ +
+ A sidebar menu action with a dropdown menu. +
+
+ +```tsx showLineNumbers + + + + + Home + + + + + + + + + + + Edit Project + + + Delete Project + + + + +``` + +## SidebarMenuSub + +The `SidebarMenuSub` component is used to render a submenu within a `SidebarMenu`. + +Use `` and `` to render a submenu item. + +
+ +
+ A sidebar menu with a submenu. +
+
+ +```tsx showLineNumbers + + + + + + + + + + + +``` + +## Collapsible SidebarMenu + +To make a `SidebarMenu` component collapsible, wrap it and the `SidebarMenuSub` components in a `Collapsible`. + +
+ +
A collapsible sidebar menu.
+
+ +```tsx showLineNumbers + + + + + + + + + + + + + + +``` + +## SidebarMenuBadge + +The `SidebarMenuBadge` component is used to render a badge within a `SidebarMenuItem`. + +
+ +
+ A sidebar menu with a badge. +
+
+ +```tsx showLineNumbers + + + 24 + +``` + +## SidebarMenuSkeleton + +The `SidebarMenuSkeleton` component is used to render a skeleton for a `SidebarMenu`. You can use this to show a loading state when using React Server Components, SWR or react-query. + +```tsx showLineNumbers +function NavProjectsSkeleton() { + return ( + + {Array.from({ length: 5 }).map((_, index) => ( + + + + ))} + + ) +} +``` + +## SidebarSeparator + +The `SidebarSeparator` component is used to render a separator within a `Sidebar`. + +```tsx showLineNumbers + + + + + + + + + +``` + +## SidebarTrigger + +Use the `SidebarTrigger` component to render a button that toggles the sidebar. + +The `SidebarTrigger` component must be used within a `SidebarProvider`. + +```tsx showLineNumbers + + +
+ +
+
+``` + +### Custom Trigger + +To create a custom trigger, you can use the `useSidebar` hook. + +```tsx showLineNumbers +import { useSidebar } from '@/components/ui/sidebar' + +export function CustomTrigger() { + const { toggleSidebar } = useSidebar() + + return +} +``` + +## SidebarRail + +The `SidebarRail` component is used to render a rail within a `Sidebar`. This rail can be used to toggle the sidebar. + +```tsx showLineNumbers + + + + + + + + +``` + +## Data Fetching + +### React Server Components + +Here's an example of a `SidebarMenu` component rendering a list of projects using React Server Components. + +
+ +
+ A sidebar menu using React Server Components. +
+
+ +```tsx showLineNumbers {6} title="Skeleton to show loading state." +function NavProjectsSkeleton() { + return ( + + {Array.from({ length: 5 }).map((_, index) => ( + + + + ))} + + ) +} +``` + +```tsx showLineNumbers {2} title="Server component fetching data." +async function NavProjects() { + const projects = await fetchProjects() + + return ( + + {projects.map((project) => ( + + + + + {project.name} + + + + ))} + + ) +} +``` + +```tsx showLineNumbers {8-10} title="Usage with React Suspense." +function AppSidebar() { + return ( + + + + Projects + + }> + + + + + + + ) +} +``` + +### SWR and React Query + +You can use the same approach with [SWR](https://swr.vercel.app/) or [react-query](https://tanstack.com/query/latest/docs/framework/react/overview). + +```tsx showLineNumbers title="SWR" +function NavProjects() { + const { data, isLoading } = useSWR("/api/projects", fetcher) + + if (isLoading) { + return ( + + {Array.from({ length: 5 }).map((_, index) => ( + + + + ))} + + ) + } + + if (!data) { + return ... + } + + return ( + + {data.map((project) => ( + + + + + {project.name} + + + + ))} + + ) +} +``` + +```tsx showLineNumbers title="React Query" +function NavProjects() { + const { data, isLoading } = useQuery() + + if (isLoading) { + return ( + + {Array.from({ length: 5 }).map((_, index) => ( + + + + ))} + + ) + } + + if (!data) { + return ... + } + + return ( + + {data.map((project) => ( + + + + + {project.name} + + + + ))} + + ) +} +``` + +## Controlled Sidebar + +Use the `open` and `onOpenChange` props to control the sidebar. + +
+ +
A controlled sidebar.
+
+ +```tsx showLineNumbers +export function AppSidebar() { + const [open, setOpen] = React.useState(false) + + return ( + + + + ) +} +``` + +## Theming + +We use the following CSS variables to theme the sidebar. + +```css +@layer base { + :root { + --sidebar-background: 0 0% 98%; + --sidebar-foreground: 240 5.3% 26.1%; + --sidebar-primary: 240 5.9% 10%; + --sidebar-primary-foreground: 0 0% 98%; + --sidebar-accent: 240 4.8% 95.9%; + --sidebar-accent-foreground: 240 5.9% 10%; + --sidebar-border: 220 13% 91%; + --sidebar-ring: 217.2 91.2% 59.8%; + } + + .dark { + --sidebar-background: 240 5.9% 10%; + --sidebar-foreground: 240 4.8% 95.9%; + --sidebar-primary: 0 0% 98%; + --sidebar-primary-foreground: 240 5.9% 10%; + --sidebar-accent: 240 3.7% 15.9%; + --sidebar-accent-foreground: 240 4.8% 95.9%; + --sidebar-border: 240 3.7% 15.9%; + --sidebar-ring: 217.2 91.2% 59.8%; + } +} +``` + +**We intentionally use different variables for the sidebar and the rest of the application** to make it easy to have a sidebar that is styled differently from the rest of the application. Think a sidebar with a darker shade from the main application. + +## Styling + +Here are some tips for styling the sidebar based on different states. + +- **Styling an element based on the sidebar collapsible state.** The following will hide the `SidebarGroup` when the sidebar is in `icon` mode. + +```tsx + + + + + +``` + +- **Styling a menu action based on the menu button active state.** The following will force the menu action to be visible when the menu button is active. + +```tsx + + + + +``` + +You can find more tips on using states for styling in this [Twitter thread](https://x.com/shadcn/status/1842329158879420864). + +## Changelog + +### 2024-10-30 Cookie handling in setOpen + +- [#5593](https://github.com/shadcn-ui/ui/pull/5593) - Improved setOpen callback logic in ``. + +Update the `setOpen` callback in `` as follows: + +```tsx showLineNumbers +const setOpen = React.useCallback( + (value: boolean | ((value: boolean) => boolean)) => { + const openState = typeof value === 'function' ? value(open) : value + if (setOpenProp) { + setOpenProp(openState) + } else { + _setOpen(openState) + } + + // This sets the cookie to keep the sidebar state. + document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}` + }, + [setOpenProp, open] +) +``` + +### 2024-10-21 Fixed `text-sidebar-foreground` + +- [#5491](https://github.com/shadcn-ui/ui/pull/5491) - Moved `text-sidebar-foreground` from `` to `` component. + +### 2024-10-20 Typo in `useSidebar` hook. + +Fixed typo in `useSidebar` hook. + +```diff showLineNumbers title="sidebar.tsx" +- throw new Error("useSidebar must be used within a Sidebar.") ++ throw new Error("useSidebar must be used within a SidebarProvider.") +``` diff --git a/apps/design-system/content/docs/fragments/multi-select.mdx b/apps/design-system/content/docs/fragments/multi-select.mdx index 8425bb4a988..1c3d40faf46 100644 --- a/apps/design-system/content/docs/fragments/multi-select.mdx +++ b/apps/design-system/content/docs/fragments/multi-select.mdx @@ -47,6 +47,13 @@ Use `MultiSelectorInput`to add a search input. +### Badge Combo Box with creatable selector + +creatable: `boolean` +`creatable` prop on the `MultiSelectorList` component can be used to add a new option if no items match + + + ### Badge Limit badgeLimit: `number` | `"wrap"`. diff --git a/apps/design-system/registry/default/example/combobox-dropdown-menu.tsx b/apps/design-system/registry/default/example/combobox-dropdown-menu.tsx index f2e06033516..b1c29126099 100644 --- a/apps/design-system/registry/default/example/combobox-dropdown-menu.tsx +++ b/apps/design-system/registry/default/example/combobox-dropdown-menu.tsx @@ -3,7 +3,7 @@ import * as React from 'react' import { Calendar, MoreHorizontal, Tags, Trash, User } from 'lucide-react' -import { Button, CommandList_Shadcn_ } from 'ui' +import { Button, CommandList_Shadcn_, ScrollArea } from 'ui' import { Command_Shadcn_, CommandEmpty_Shadcn_, @@ -75,23 +75,25 @@ export default function ComboboxDropdownMenu() { - - No label found. - - {labels.map((label) => ( - { - setLabel(value) - setOpen(false) - }} - > - {label} - - ))} - - + + + No label found. + + {labels.map((label) => ( + { + setLabel(value) + setOpen(false) + }} + > + {label} + + ))} + + + diff --git a/apps/design-system/registry/default/example/multi-select-combobox-creatable.tsx b/apps/design-system/registry/default/example/multi-select-combobox-creatable.tsx new file mode 100644 index 00000000000..5b8ddf96c71 --- /dev/null +++ b/apps/design-system/registry/default/example/multi-select-combobox-creatable.tsx @@ -0,0 +1,34 @@ +import { useState } from 'react' +import { + MultiSelector, + MultiSelectorContent, + MultiSelectorInput, + MultiSelectorItem, + MultiSelectorList, + MultiSelectorTrigger, +} from 'ui-patterns/multi-select' + +export default function MultiSelectDemo() { + const [selectedValues, setSelectedValues] = useState([]) + + return ( + + + + + + Mango + Date + Apple + Elderberrie + Fig + Grape + Banana + Kiwi + Strawberry + Cherry + + + + ) +} diff --git a/apps/design-system/registry/examples.ts b/apps/design-system/registry/examples.ts index 58d7c1dd508..a9f4de3e7c8 100644 --- a/apps/design-system/registry/examples.ts +++ b/apps/design-system/registry/examples.ts @@ -1091,6 +1091,11 @@ export const examples: Registry = [ type: 'components:example', files: ['example/multi-select-combobox.tsx'], }, + { + name: 'multi-select-combobox-creatable', + type: 'components:example', + files: ['example/multi-select-combobox-creatable.tsx'], + }, { name: 'multi-select-deletable-badge', type: 'components:example', diff --git a/apps/design-system/tailwind.config.js b/apps/design-system/tailwind.config.js index 5a512287d82..5be9a0d5a1d 100644 --- a/apps/design-system/tailwind.config.js +++ b/apps/design-system/tailwind.config.js @@ -8,7 +8,8 @@ module.exports = config({ // purge styles from grid library // './../../packages/ui/src/**/*.{tsx,ts,js}', - './../../packages/ui-patterns/!(node_modules)/**/*.{tsx,ts,js}', + './../../packages/ui-patterns/**/*.{tsx,ts,js}', + '!./../../packages/ui-patterns/node_modules/**/*', // ignore node_modules rule ], theme: { extend: { diff --git a/apps/docs/app/guides/getting-started/ai-prompts/[slug]/AiPrompts.utils.ts b/apps/docs/app/guides/getting-started/ai-prompts/[slug]/AiPrompts.utils.ts index 935bd6d388e..0c8f83d7f0c 100644 --- a/apps/docs/app/guides/getting-started/ai-prompts/[slug]/AiPrompts.utils.ts +++ b/apps/docs/app/guides/getting-started/ai-prompts/[slug]/AiPrompts.utils.ts @@ -1,3 +1,4 @@ +import matter from 'gray-matter' import type { Code, Heading, Root } from 'mdast' import { fromMarkdown } from 'mdast-util-from-markdown' import { toMarkdown } from 'mdast-util-to-markdown' @@ -11,7 +12,8 @@ import { EXAMPLES_DIRECTORY } from '~/lib/docs' const PROMPTS_DIRECTORY = join(EXAMPLES_DIRECTORY, 'prompts') function parseMarkdown(markdown: string) { - const mdast = fromMarkdown(markdown) + const { content: withoutFrontmatter } = matter(markdown) + const mdast = fromMarkdown(withoutFrontmatter) let heading = '' visit(mdast, 'heading', (node: Heading) => { diff --git a/apps/docs/app/reference/[...slug]/page.tsx b/apps/docs/app/reference/[...slug]/page.tsx index f1e0798591f..c15880c4e06 100644 --- a/apps/docs/app/reference/[...slug]/page.tsx +++ b/apps/docs/app/reference/[...slug]/page.tsx @@ -12,6 +12,8 @@ import { redirectNonexistentReferenceSection, } from '~/features/docs/Reference.utils' +export const dynamicParams = false + export default async function ReferencePage({ params: { slug }, }: { @@ -39,7 +41,6 @@ export default async function ReferencePage({ return } else if (isCliReference) { - console.log('Returning CLI reference page: %o', parsedPath) return } else if (isApiReference) { return diff --git a/apps/docs/components/Breadcrumbs.tsx b/apps/docs/components/Breadcrumbs.tsx index d8d2e4856fb..d41b2b0e941 100644 --- a/apps/docs/components/Breadcrumbs.tsx +++ b/apps/docs/components/Breadcrumbs.tsx @@ -109,15 +109,15 @@ const BreadcrumbsInternal = ({
- {breadcrumbs - .slice(1, -2) - .map((crumb) => - crumb.url ? ( - {crumb.title || crumb.name} - ) : ( - crumb.title || crumb.name - ) - )} + {breadcrumbs.slice(1, -2).map((crumb, index) => + crumb.url ? ( + + {crumb.title || crumb.name} + + ) : ( + crumb.title || crumb.name + ) + )}
@@ -131,12 +131,12 @@ const BreadcrumbsInternal = ({ )} - {appendedBreadcrumbs?.map((crumb, i) => ( - + {appendedBreadcrumbs?.map((crumb, index) => ( + {crumb.url ? ( @@ -148,7 +148,7 @@ const BreadcrumbsInternal = ({ )} ))} @@ -170,7 +170,8 @@ function useBreadcrumbs() { if (isAiPromptsPage) { const breadcrumbs = [ { name: 'Getting started', url: '/guides/getting-started' }, - { name: 'AI Prompts', url: '/guides/getting-started/ai-prompts' }, + { name: 'AI Tools' }, + { name: 'Prompts', url: '/guides/getting-started/ai-prompts' }, ] return breadcrumbs } diff --git a/apps/docs/components/HomePageCover.tsx b/apps/docs/components/HomePageCover.tsx index dd67ee4e641..25bd3b32a48 100644 --- a/apps/docs/components/HomePageCover.tsx +++ b/apps/docs/components/HomePageCover.tsx @@ -74,7 +74,7 @@ const HomePageCover = (props) => { href: '/guides/getting-started/quickstarts/vue', }, { - tooltip: 'NuxtJS', + tooltip: 'Nuxt', icon: '/docs/img/icons/nuxt-icon', href: '/guides/getting-started/quickstarts/nuxtjs', }, diff --git a/apps/docs/components/MDX/billing/pricing/pricing_branching.mdx b/apps/docs/components/MDX/billing/pricing/pricing_branching.mdx new file mode 100644 index 00000000000..6aa5ca8ed90 --- /dev/null +++ b/apps/docs/components/MDX/billing/pricing/pricing_branching.mdx @@ -0,0 +1 @@ +There is no fixed fee for a Preview branch. You only pay for the usage it incurs. With Compute costs of $0.01344 per hour, a branch running on Micro Compute size starts at $0.32 per day. diff --git a/apps/docs/components/MDX/billing/pricing/pricing_edge_functions.mdx b/apps/docs/components/MDX/billing/pricing/pricing_edge_functions.mdx new file mode 100644 index 00000000000..4a68dc3316a --- /dev/null +++ b/apps/docs/components/MDX/billing/pricing/pricing_edge_functions.mdx @@ -0,0 +1,8 @@ +$2 per 1 million invocations. You are only charged for usage exceeding your subscription plan's quota. + +| Plan | Quota | Over-Usage | +| ---------- | --------- | ---------------------------- | +| Free | 500,000 | - | +| Pro | 2 million | $2 per 1 million invocations | +| Team | 2 million | $2 per 1 million invocations | +| Enterprise | Custom | Custom | diff --git a/apps/docs/components/MDX/billing/pricing/pricing_mau.mdx b/apps/docs/components/MDX/billing/pricing/pricing_mau.mdx new file mode 100644 index 00000000000..c3cf0efbc1d --- /dev/null +++ b/apps/docs/components/MDX/billing/pricing/pricing_mau.mdx @@ -0,0 +1 @@ +$0.00325 per MAU. You are only charged for usage exceeding your subscription plan's quota. diff --git a/apps/docs/components/MDX/billing/pricing/pricing_mau_sso.mdx b/apps/docs/components/MDX/billing/pricing/pricing_mau_sso.mdx new file mode 100644 index 00000000000..a2a65032667 --- /dev/null +++ b/apps/docs/components/MDX/billing/pricing/pricing_mau_sso.mdx @@ -0,0 +1 @@ +$0.015 per SSO MAU. You are only charged for usage exceeding your subscription plan's quota. diff --git a/apps/docs/components/MDX/billing/pricing/pricing_mau_third_party.mdx b/apps/docs/components/MDX/billing/pricing/pricing_mau_third_party.mdx new file mode 100644 index 00000000000..fe567ae3e55 --- /dev/null +++ b/apps/docs/components/MDX/billing/pricing/pricing_mau_third_party.mdx @@ -0,0 +1 @@ +$0.00325 per Third-Party MAU. You are only charged for usage exceeding your subscription plan's quota. diff --git a/apps/docs/components/MDX/billing/pricing/pricing_mfa_phone.mdx b/apps/docs/components/MDX/billing/pricing/pricing_mfa_phone.mdx new file mode 100644 index 00000000000..ea50511e1ca --- /dev/null +++ b/apps/docs/components/MDX/billing/pricing/pricing_mfa_phone.mdx @@ -0,0 +1,7 @@ +$0.1027 per hour ($75 per month) for the first project. $0.0137 per hour ($10 per month) for every additional project. + +| Plan | Project 1 per month | Project 2 per month | Project 3 per month | +| ---------- | ------------------- | ------------------- | ------------------- | +| Pro | $75 | $10 | $10 | +| Team | $75 | $10 | $10 | +| Enterprise | Custom | Custom | Custom | diff --git a/apps/docs/components/MDX/billing/pricing/pricing_pitr.mdx b/apps/docs/components/MDX/billing/pricing/pricing_pitr.mdx new file mode 100644 index 00000000000..435f156a2c0 --- /dev/null +++ b/apps/docs/components/MDX/billing/pricing/pricing_pitr.mdx @@ -0,0 +1,7 @@ +Pricing depends on the recovery retention period, which determines how many days back you can restore data to any chosen point of up to seconds in granularity. + +| Recovery Retention Period in Days | Hourly Price USD | Monthly Price USD | +| --------------------------------- | ---------------- | ----------------- | +| 7 | $0.137 | $100 | +| 14 | $0.274 | $200 | +| 28 | $0.55 | $400 | diff --git a/apps/docs/components/MDX/billing/pricing/pricing_realtime_connections.mdx b/apps/docs/components/MDX/billing/pricing/pricing_realtime_connections.mdx new file mode 100644 index 00000000000..22a03499d3f --- /dev/null +++ b/apps/docs/components/MDX/billing/pricing/pricing_realtime_connections.mdx @@ -0,0 +1,8 @@ +$10 per 1,000 peak connections. You are only charged for usage exceeding your subscription plan's quota. + +| Plan | Quota | Over-Usage | +| ---------- | ------ | ------------------------------ | +| Free | 200 | - | +| Pro | 500 | $10 per 1,000 peak connections | +| Team | 500 | $10 per 1,000 peak connections | +| Enterprise | Custom | Custom | diff --git a/apps/docs/components/MDX/billing/pricing/pricing_realtime_messages.mdx b/apps/docs/components/MDX/billing/pricing/pricing_realtime_messages.mdx new file mode 100644 index 00000000000..2e64090fd92 --- /dev/null +++ b/apps/docs/components/MDX/billing/pricing/pricing_realtime_messages.mdx @@ -0,0 +1,8 @@ +$2.50 per 1 million messages. You are only charged for usage exceeding your subscription plan's quota. + +| Plan | Quota | Over-Usage | +| ---------- | --------- | ---------------------------- | +| Free | 2 million | - | +| Pro | 5 million | $2.50 per 1 million messages | +| Team | 5 million | $2.50 per 1 million messages | +| Enterprise | Custom | Custom | diff --git a/apps/docs/components/MDX/billing/pricing/pricing_storage_image_transformations.mdx b/apps/docs/components/MDX/billing/pricing/pricing_storage_image_transformations.mdx new file mode 100644 index 00000000000..0525d0e76a2 --- /dev/null +++ b/apps/docs/components/MDX/billing/pricing/pricing_storage_image_transformations.mdx @@ -0,0 +1,13 @@ +$5 per 1,000 origin images. You are only charged for usage exceeding your subscription plan's quota. + + + +The count resets at the start of each billing cycle. + + + +| Plan | Quota | Over-Usage | +| ---------- | ------ | -------------------------- | +| Pro | 100 | $5 per 1,000 origin images | +| Team | 100 | $5 per 1,000 origin images | +| Enterprise | Custom | Custom | diff --git a/apps/docs/components/MDX/billing/pricing/pricing_storage_size.mdx b/apps/docs/components/MDX/billing/pricing/pricing_storage_size.mdx new file mode 100644 index 00000000000..beb464319a3 --- /dev/null +++ b/apps/docs/components/MDX/billing/pricing/pricing_storage_size.mdx @@ -0,0 +1,8 @@ +$0.00002919 per GB-Hr ($0.021 per GB per month). You are only charged for usage exceeding your subscription plan's quota. + +| Plan | Quota in GB | Over-Usage per GB | Quota in GB-Hrs | Over-Usage per GB-Hrs | +| ---------- | ----------- | ----------------- | --------------- | --------------------- | +| Free | 1 | - | 744 | - | +| Pro | 100 | $0.021 | 74,400 | $0.00002919 | +| Team | 100 | $0.021 | 74,400 | $0.00002919 | +| Enterprise | Custom | Custom | Custom | Custom | diff --git a/apps/docs/components/MDX/partials.tsx b/apps/docs/components/MDX/partials.tsx index a931fcf5877..2ade384d579 100644 --- a/apps/docs/components/MDX/partials.tsx +++ b/apps/docs/components/MDX/partials.tsx @@ -15,6 +15,17 @@ import QuickstartIntro from './quickstart_intro.mdx' import SocialProviderSettingsSupabase from './social_provider_settings_supabase.mdx' import SocialProviderSetup from './social_provider_setup.mdx' import PostgresInstallation from './postgres_installation.mdx' +import PricingMfaPhone from './billing/pricing/pricing_mfa_phone.mdx' +import PricingBranching from './billing/pricing/pricing_branching.mdx' +import PricingEdgeFunctions from './billing/pricing/pricing_edge_functions.mdx' +import PricingMau from './billing/pricing/pricing_mau.mdx' +import PricingMauSso from './billing/pricing/pricing_mau_sso.mdx' +import PricingMauThirdParty from './billing/pricing/pricing_mau_third_party.mdx' +import PricingPitr from './billing/pricing/pricing_pitr.mdx' +import PricingRealtimeMessages from './billing/pricing/pricing_realtime_messages.mdx' +import PricingRealtimeConnections from './billing/pricing/pricing_realtime_connections.mdx' +import PricingStorageImageTransformations from './billing/pricing/pricing_storage_image_transformations.mdx' +import PricingStorageSize from './billing/pricing/pricing_storage_size.mdx' export { AuthErrorCodesTable, @@ -32,4 +43,15 @@ export { SocialProviderSettingsSupabase, SocialProviderSetup, PostgresInstallation, + PricingMfaPhone, + PricingBranching, + PricingEdgeFunctions, + PricingMau, + PricingMauSso, + PricingMauThirdParty, + PricingPitr, + PricingRealtimeMessages, + PricingRealtimeConnections, + PricingStorageImageTransformations, + PricingStorageSize, } diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts index aed0de1cdac..bd5f0b3c2a3 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts @@ -258,7 +258,7 @@ export const gettingstarted: NavMenuConstant = { items: [ { name: 'Next.js', url: '/guides/getting-started/quickstarts/nextjs' }, { name: 'React', url: '/guides/getting-started/quickstarts/reactjs' }, - { name: 'NuxtJS', url: '/guides/getting-started/quickstarts/nuxtjs' }, + { name: 'Nuxt', url: '/guides/getting-started/quickstarts/nuxtjs' }, { name: 'Vue', url: '/guides/getting-started/quickstarts/vue' }, { name: 'Hono', url: '/guides/getting-started/quickstarts/hono' }, { name: 'Flutter', url: '/guides/getting-started/quickstarts/flutter' }, @@ -366,13 +366,17 @@ export const gettingstarted: NavMenuConstant = { ], }, { - name: 'AI Prompts', + name: 'AI Tools', url: undefined, items: [ { - name: 'Overview', + name: 'Prompts', url: '/guides/getting-started/ai-prompts', }, + { + name: 'Model context protocol (MCP)', + url: '/guides/getting-started/mcp', + }, ], }, ], @@ -1009,10 +1013,6 @@ export const database: NavMenuConstant = { name: 'pg_plan_filter: Restrict Total Cost', url: '/guides/database/extensions/pg_plan_filter', }, - { - name: 'pg_stat_monitor: Extended Query Performance Monitoring', - url: '/guides/database/extensions/pg_stat_monitor', - }, { name: 'postgres_fdw: query data from an external Postgres server', url: '/guides/database/extensions/postgres_fdw', @@ -1415,6 +1415,10 @@ export const functions: NavMenuConstant = { name: 'Limits', url: '/guides/functions/limits', }, + { + name: 'Pricing', + url: '/guides/functions/pricing', + }, ], }, { @@ -1583,6 +1587,7 @@ export const realtime: NavMenuConstant = { url: undefined, items: [ { name: 'Quotas', url: '/guides/realtime/quotas' }, + { name: 'Pricing', url: '/guides/realtime/pricing' }, { name: 'Architecture', url: '/guides/realtime/architecture' }, { name: 'Message Protocol', url: '/guides/realtime/protocol', items: [] }, { name: 'Benchmarks', url: '/guides/realtime/benchmarks' }, @@ -1668,6 +1673,7 @@ export const storage: NavMenuConstant = { items: [ { name: 'Copy / Move Objects', url: '/guides/storage/management/copy-move-objects' }, { name: 'Delete Objects', url: '/guides/storage/management/delete-objects' }, + { name: 'Pricing', url: '/guides/storage/management/pricing' }, ], }, { @@ -1747,6 +1753,10 @@ export const ai = { url: '/guides/ai/vector-indexes', items: vectorIndexItems, }, + { + name: 'Automatic embeddings', + url: '/guides/ai/automatic-embeddings', + }, { name: 'Engineering for scale', url: '/guides/ai/engineering-for-scale', @@ -1920,6 +1930,22 @@ export const local_development: NavMenuConstant = { }, ], }, + { + name: 'Testing', + url: undefined, + items: [ + { name: 'Getting started', url: '/guides/local-development/testing/overview' }, + { + name: 'pgTAP advanced guide', + url: '/guides/local-development/testing/pgtap-extended', + }, + { name: 'Database testing', url: '/guides/database/testing' }, + { + name: 'RLS policies testing', + url: '/guides/database/extensions/pgtap#testing-rls-policies', + }, + ], + }, ], } @@ -2083,12 +2109,114 @@ export const platform: NavMenuConstant = { url: undefined, items: [ { - name: 'How billing works', - url: '/guides/platform/org-based-billing', + name: 'About billing on Supabase', + url: '/guides/platform/billing-on-supabase', }, { - name: 'Spend caps and cost-controls', - url: '/guides/platform/spend-cap', + name: 'Get set up for billing', + url: '/guides/platform/get-set-up-for-billing', + }, + { + name: 'Manage your subscription', + url: '/guides/platform/manage-your-subscription', + }, + { + name: 'Manage your usage', + url: '/guides/platform/manage-your-usage', + items: [ + { + name: 'Compute', + url: '/guides/platform/manage-your-usage/compute', + }, + { + name: 'Egress', + url: '/guides/platform/manage-your-usage/egress', + }, + { + name: 'Disk Size', + url: '/guides/platform/manage-your-usage/disk-size', + }, + { + name: 'Disk Throughput', + url: '/guides/platform/manage-your-usage/disk-throughput', + }, + { + name: 'Disk IOPS', + url: '/guides/platform/manage-your-usage/disk-iops', + }, + { + name: 'Monthly Active Users', + url: '/guides/platform/manage-your-usage/monthly-active-users', + }, + { + name: 'Monthly Active Third-Party Users', + url: '/guides/platform/manage-your-usage/monthly-active-users-third-party', + }, + { + name: 'Monthly Active SSO Users', + url: '/guides/platform/manage-your-usage/monthly-active-users-sso', + }, + { + name: 'Storage Size', + url: '/guides/platform/manage-your-usage/storage-size', + }, + { + name: 'Storage Image Transformations', + url: '/guides/platform/manage-your-usage/storage-image-transformations', + }, + { + name: 'Edge Function Invocations', + url: '/guides/platform/manage-your-usage/edge-function-invocations', + }, + { + name: 'Realtime Messages', + url: '/guides/platform/manage-your-usage/realtime-messages', + }, + { + name: 'Realtime Peak Connections', + url: '/guides/platform/manage-your-usage/realtime-peak-connections', + }, + { + name: 'Custom Domains', + url: '/guides/platform/manage-your-usage/custom-domains', + }, + { + name: 'Point-in-Time Recovery', + url: '/guides/platform/manage-your-usage/point-in-time-recovery', + }, + { + name: 'IPv4', + url: '/guides/platform/manage-your-usage/ipv4', + }, + { + name: 'MFA Phone', + url: '/guides/platform/manage-your-usage/advanced-mfa-phone', + }, + { + name: 'Read Replicas', + url: '/guides/platform/manage-your-usage/read-replicas', + }, + { + name: 'Branching', + url: '/guides/platform/manage-your-usage/branching', + }, + { + name: 'Log Drains', + url: '/guides/platform/manage-your-usage/log-drains', + }, + ], + }, + { + name: 'Your monthly invoice', + url: '/guides/platform/your-monthly-invoice', + }, + { + name: 'Control your costs', + url: '/guides/platform/cost-control', + }, + { + name: 'Credits', + url: '/guides/platform/credits', }, { name: 'Billing FAQ', diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenuGuideList.tsx b/apps/docs/components/Navigation/NavigationMenu/NavigationMenuGuideList.tsx index e9e211a8265..5ce453b67de 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenuGuideList.tsx +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenuGuideList.tsx @@ -39,19 +39,36 @@ const NavigationMenuGuideList = ({ } } + // Inject federated prompts into the 'AI Tools > Prompts' section if (id === MenuId.GettingStarted && additionalNavItems?.prompts) { - const promptsSectionIndex = menu.items.findIndex((item) => item.name === 'AI Prompts') - if (promptsSectionIndex !== -1) { - menu = { - ...menu, - items: [ - ...menu.items.slice(0, promptsSectionIndex), - { - ...menu.items[promptsSectionIndex], - items: [...menu.items[promptsSectionIndex].items, ...additionalNavItems.prompts], - }, - ...menu.items.slice(promptsSectionIndex + 1), - ], + const aiToolsSectionIndex = menu.items.findIndex((item) => item.name === 'AI Tools') + if (aiToolsSectionIndex !== -1) { + const beforeAITools = menu.items.slice(0, aiToolsSectionIndex) + const afterAITools = menu.items.slice(aiToolsSectionIndex + 1) + + const aiToolsSection = menu.items[aiToolsSectionIndex] + const promptsSectionIndex = aiToolsSection.items.findIndex((item) => item.name === 'Prompts') + + if (promptsSectionIndex !== -1) { + const beforePrompts = aiToolsSection.items.slice(0, promptsSectionIndex) + const afterPrompts = aiToolsSection.items.slice(promptsSectionIndex + 1) + + const promptsSection = aiToolsSection.items[promptsSectionIndex] + + const modifiedPromptsSection = { + ...promptsSection, + items: additionalNavItems.prompts, + } + + const modifiedAIToolsSection = { + ...aiToolsSection, + items: [...beforePrompts, modifiedPromptsSection, ...afterPrompts], + } + + menu = { + ...menu, + items: [...beforeAITools, modifiedAIToolsSection, ...afterAITools], + } } } } diff --git a/apps/docs/content/guides/ai/automatic-embeddings.mdx b/apps/docs/content/guides/ai/automatic-embeddings.mdx new file mode 100644 index 00000000000..48f1f7389c6 --- /dev/null +++ b/apps/docs/content/guides/ai/automatic-embeddings.mdx @@ -0,0 +1,817 @@ +--- +id: 'ai-automatic-embeddings' +title: 'Automatic embeddings' +description: 'Automate embedding generation and updates in Postgres' +sidebar_label: 'Automatic embeddings' +--- + +Vector embeddings enable powerful [semantic search](/docs/guides/ai/semantic-search) capabilities in Postgres, but managing them alongside your content has traditionally been complex. This guide demonstrates how to automate embedding generation and updates using Supabase [Edge Functions](/docs/guides/functions), [pgmq](/docs/guides/database/extensions/pgmq), [pg_net](/docs/guides/database/extensions/pg_net), and [pg_cron](/docs/guides/cron). + +## Understanding the challenge + +When implementing semantic search with pgvector, developers typically need to: + +1. Generate embeddings via an external API (like OpenAI) +2. Store these embeddings alongside the content +3. Keep embeddings in sync when content changes +4. Handle failures and retries in the embedding generation process + +While Postgres [full-text search](/docs/guides/database/full-text-search) can handle this internally through synchronous calls to `to_tsvector` and [triggers](https://www.postgresql.org/docs/current/textsearch-features.html#TEXTSEARCH-UPDATE-TRIGGERS), semantic search requires asynchronous API calls to a provider like OpenAI to generate vector embeddings. This guide demonstrates how to use triggers, queues, and Supabase Edge Functions to bridge this gap. + +## Understanding the architecture + +We'll leverage the following Postgres and Supabase features to create the automated embedding system: + +1. [pgvector](/docs/guides/database/extensions/pgvector): Stores and queries vector embeddings +1. [pgmq](/docs/guides/queues): Queues embedding generation requests for processing and retries +1. [pg_net](/docs/guides/database/extensions/pg_net): Handles asynchronous HTTP requests to Edge Functions directly from Postgres +1. [pg_cron](/docs/guides/cron): Automatically processes and retries embedding generations +1. [Triggers](/docs/guides/database/postgres/triggers): Detects content changes and enqueues embedding generation requests +1. [Edge Functions](/docs/guides/functions): Generates embeddings via an API like OpenAI (customizable) + +We'll design the system to: + +1. Be generic, so that it can be used with any table and content. This allows you to configure embeddings in multiple places, each with the ability to customize the input used for embedding generation. These will all use the same queue infrastructure and Edge Function to generate the embeddings. + +1. Handle failures gracefully, by retrying failed jobs and providing detailed information about the status of each job. + +## Implementation + +We'll start by setting up the infrastructure needed to queue and process embedding generation requests. Then we'll create an example table with triggers to enqueue these embedding requests whenever content is inserted or updated. + +### Step 1: Enable extensions + +First, let's enable the required extensions: + + + + +```sql +-- For vector operations +create extension if not exists vector +with + schema extensions; + +-- For queueing and processing jobs +-- (pgmq will create its own schema) +create extension if not exists pgmq; + +-- For async HTTP requests +create extension if not exists pg_net +with + schema extensions; + +-- For scheduled processing and retries +-- (pg_cron will create its own schema) +create extension if not exists pg_cron; + +-- For clearing embeddings during updates +create extension if not exists hstore +with + schema extensions; +``` + +Even though the SQL code is `create extension`, this is the equivalent of "enabling the extension". +To disable an extension, call `drop extension`. + + + + +1. Go to the [Extensions](/dashboard/project/_/database/extensions) page in the Dashboard. +2. Search for and enable the following extensions: + - `vector` + - `pgmq` + - `pg_net` + - `pg_cron` + - `hstore` + + + + +### Step 2: Create utility functions + +Before we set up our embedding logic, we need to create some utility functions: + +```sql +-- Schema for utility functions +create schema util; + +-- Utility function to get the Supabase project URL (required for Edge Functions) +create function util.project_url() +returns text +language plpgsql +security definer +as $$ +declare + secret_value text; +begin + -- Retrieve the project URL from Vault + select decrypted_secret into secret_value from vault.decrypted_secrets where name = 'project_url'; + return secret_value; +end; +$$; + +-- Generic function to invoke any Edge Function +create or replace function util.invoke_edge_function( + name text, + body jsonb, + timeout_milliseconds int = 5 * 60 * 1000 -- default 5 minute timeout +) +returns void +language plpgsql +as $$ +declare + headers_raw text; + auth_header text; +begin + -- If we're in a PostgREST session, reuse the request headers for authorization + headers_raw := current_setting('request.headers', true); + + -- Only try to parse if headers are present + auth_header := case + when headers_raw is not null then + (headers_raw::json->>'authorization') + else + null + end; + + -- Perform async HTTP request to the edge function + perform net.http_post( + url => util.project_url() || '/functions/v1/' || name, + headers => jsonb_build_object( + 'Content-Type', 'application/json', + 'Authorization', auth_header + ), + body => body, + timeout_milliseconds => timeout_milliseconds + ); +end; +$$; + +-- Generic trigger function to clear a column on update +create or replace function util.clear_column() +returns trigger +language plpgsql as $$ +declare + clear_column text := TG_ARGV[0]; +begin + NEW := NEW #= hstore(clear_column, NULL); + return NEW; +end; +$$; +``` + +Here we create: + +- A schema `util` to store utility functions. +- A function to retrieve the Supabase project URL from [Vault](/docs/guides/database/vault). We'll add this secret next. +- A generic function to invoke any Edge Function with a given name and request body. +- A generic trigger function to clear a column on update. This function accepts the column name as an argument and sets it to `NULL` in the `NEW` record. We'll explain how to use this function later. + +Every project has a unique API URL that is required to invoke Edge Functions. Let's go ahead and add the project URL secret to Vault depending on your environment. + +When working with a local Supabase stack, add the following to your `supabase/seed.sql` file: + +```sql +select + vault.create_secret('http://api.supabase.internal:8000', 'project_url'); +``` + +When deploying to the cloud platform, open the [SQL editor](/dashboard/project/_/sql/new) and run the following, replacing `` with your [project's API URL](/dashboard/project/_/settings/api): + +```sql +select + vault.create_secret('', 'project_url'); +``` + +### Step 3: Create queue and triggers + +Our goal is to automatically generate embeddings whenever content is inserted or updated within a table. We can use triggers and queues to achieve this. Our approach is to automatically queue embedding jobs whenever records are inserted or updated in a table, then process them asynchronously using a cron job. If a job fails, it will remain in the queue and be retried in the next scheduled task. + +First we create a `pgmq` queue for processing embedding requests: + +```sql +-- Queue for processing embedding jobs +select pgmq.create('embedding_jobs'); +``` + +Next we create a trigger function to queue embedding jobs. We'll use this function to handle both insert and update events: + +```sql +-- Generic trigger function to queue embedding jobs +create or replace function util.queue_embeddings() +returns trigger +language plpgsql +as $$ +declare + content_function text = TG_ARGV[0]; + embedding_column text = TG_ARGV[1]; +begin + perform pgmq.send( + queue_name => 'embedding_jobs', + msg => jsonb_build_object( + 'id', NEW.id, + 'schema', TG_TABLE_SCHEMA, + 'table', TG_TABLE_NAME, + 'contentFunction', content_function, + 'embeddingColumn', embedding_column + ) + ); + return NEW; +end; +$$; +``` + +Our `util.queue_embeddings` trigger function is generic and can be used with any table and content function. It accepts two arguments: + +1. `content_function`: The name of a function that returns the text content to be embedded. The function should accept a single row as input and return text (see the `embedding_input` example). + + This allows you to customize the text input passed to the embedding model - for example, you could concatenate multiple columns together like `title` and `content` and use the result as input. + +2. `embedding_column`: The name of the destination column where the embedding will be stored. + +Note that the `util.queue_embeddings` trigger function requires a `for each row` clause to work correctly. See [Usage](#usage) for an example of how to use this trigger function with your table. + +Next we'll create a function to process the embedding jobs. This function will read jobs from the queue, group them into batches, and invoke the Edge Function to generate embeddings. We'll use `pg_cron` to schedule this function to run every 10 seconds. + +```sql +-- Function to process embedding jobs from the queue +create or replace function util.process_embeddings( + batch_size int = 10, + max_requests int = 10, + timeout_milliseconds int = 5 * 60 * 1000 -- default 5 minute timeout +) +returns void +language plpgsql +as $$ +declare + job_batches jsonb[]; + batch jsonb; +begin + with + -- First get jobs and assign batch numbers + numbered_jobs as ( + select + message || jsonb_build_object('jobId', msg_id) as job_info, + (row_number() over (order by 1) - 1) / batch_size as batch_num + from pgmq.read( + queue_name => 'embedding_jobs', + vt => timeout_milliseconds / 1000, + qty => max_requests * batch_size + ) + ), + -- Then group jobs into batches + batched_jobs as ( + select + jsonb_agg(job_info) as batch_array, + batch_num + from numbered_jobs + group by batch_num + ) + -- Finally aggregate all batches into array + select array_agg(batch_array) + from batched_jobs + into job_batches; + + -- Invoke the embed edge function for each batch + foreach batch in array job_batches loop + perform util.invoke_edge_function( + name => 'embed', + body => batch, + timeout_milliseconds => timeout_milliseconds + ); + end loop; +end; +$$; + +-- Schedule the embedding processing +select + cron.schedule( + 'process-embeddings', + '10 seconds', + $$ + select util.process_embeddings(); + $$ + ); +``` + +Let's discuss some common questions about this approach: + +#### Why not generate all embeddings in a single Edge Function request? + +While this is possible, it can lead to long processing times and potential timeouts. Batching allows us to process multiple embeddings concurrently and handle failures more effectively. + +#### Why not one request per row? + +This approach can lead to API rate limiting and performance issues. Batching provides a balance between efficiency and reliability. + +#### Why queue requests instead of processing them immediately? + +Queuing allows us to handle failures gracefully, retry requests, and manage concurrency more effectively. Specifically we are using `pgmq`'s visibility timeouts to ensure that failed requests are retried. + +#### How do visibility timeouts work? + +Every time we read a message from the queue, we set a visibility timeout which tells `pgmq` to hide the message from other readers for a certain period. If the Edge Function fails to process the message within this period, the message becomes visible again and will be retried by the next scheduled task. + +#### How do we handle retries? + +We use `pg_cron` to schedule a task that reads messages from the queue and processes them. If the Edge Function fails to process a message, it becomes visible again after a timeout and can be retried by the next scheduled task. + +#### Is 10 seconds a good interval for processing? + +This interval is a good starting point, but you may need to adjust it based on your workload and the time it takes to generate embeddings. You can adjust the `batch_size`, `max_requests`, and `timeout_milliseconds` parameters to optimize performance. + +### Step 4: Create the Edge Function + +Finally we'll create the Edge Function to generate embeddings. We'll use OpenAI's API in this example, but you can replace it with any other embedding generation service. + +Use the Supabase CLI to create a new Edge Function: + +```bash +supabase functions new embed +``` + +This will create a new directory `supabase/functions/embed` with an `index.ts` file. Replace the contents of this file with the following: + +_supabase/functions/embed/index.ts_: + +```typescript +// Setup type definitions for built-in Supabase Runtime APIs +import 'jsr:@supabase/functions-js/edge-runtime.d.ts' + +// We'll use the OpenAI API to generate embeddings +import OpenAI from 'jsr:@openai/openai' + +import { z } from 'npm:zod' + +// We'll make a direct Postgres connection to update the document +import postgres from 'https://deno.land/x/postgresjs@v3.4.5/mod.js' + +// Initialize OpenAI client +const openai = new OpenAI({ + // We'll need to manually set the `OPENAI_API_KEY` environment variable + apiKey: Deno.env.get('OPENAI_API_KEY'), +}) + +// Initialize Postgres client +const sql = postgres( + // `SUPABASE_DB_URL` is a built-in environment variable + Deno.env.get('SUPABASE_DB_URL')! +) + +const jobSchema = z.object({ + jobId: z.number(), + id: z.number(), + schema: z.string(), + table: z.string(), + contentFunction: z.string(), + embeddingColumn: z.string(), +}) + +const failedJobSchema = jobSchema.extend({ + error: z.string(), +}) + +type Job = z.infer +type FailedJob = z.infer + +type Row = { + id: string + content: unknown +} + +const QUEUE_NAME = 'embedding_jobs' + +// Listen for HTTP requests +Deno.serve(async (req) => { + if (req.method !== 'POST') { + return new Response('expected POST request', { status: 405 }) + } + + if (req.headers.get('content-type') !== 'application/json') { + return new Response('expected json body', { status: 400 }) + } + + // Use Zod to parse and validate the request body + const parseResult = z.array(jobSchema).safeParse(await req.json()) + + if (parseResult.error) { + return new Response(`invalid request body: ${parseResult.error.message}`, { + status: 400, + }) + } + + const pendingJobs = parseResult.data + + // Track jobs that completed successfully + const completedJobs: Job[] = [] + + // Track jobs that failed due to an error + const failedJobs: FailedJob[] = [] + + async function processJobs() { + let currentJob: Job | undefined + + while ((currentJob = pendingJobs.shift()) !== undefined) { + try { + await processJob(currentJob) + completedJobs.push(currentJob) + } catch (error) { + failedJobs.push({ + ...currentJob, + error: error instanceof Error ? error.message : JSON.stringify(error), + }) + } + } + } + + try { + // Process jobs while listening for worker termination + await Promise.race([processJobs(), catchUnload()]) + } catch (error) { + // If the worker is terminating (e.g. wall clock limit reached), + // add pending jobs to fail list with termination reason + failedJobs.push( + ...pendingJobs.map((job) => ({ + ...job, + error: error instanceof Error ? error.message : JSON.stringify(error), + })) + ) + } + + // Log completed and failed jobs for traceability + console.log('finished processing jobs:', { + completedJobs: completedJobs.length, + failedJobs: failedJobs.length, + }) + + return new Response( + JSON.stringify({ + completedJobs, + failedJobs, + }), + { + // 200 OK response + status: 200, + + // Custom headers to report job status + headers: { + 'Content-Type': 'application/json', + 'X-Completed-Jobs': completedJobs.length.toString(), + 'X-Failed-Jobs': failedJobs.length.toString(), + }, + } + ) +}) + +/** + * Generates an embedding for the given text. + */ +async function generateEmbedding(text: string) { + const response = await openai.embeddings.create({ + model: 'text-embedding-3-small', + input: text, + }) + const [data] = response.data + + if (!data) { + throw new Error('failed to generate embedding') + } + + return data.embedding +} + +/** + * Processes an embedding job. + */ +async function processJob(job: Job) { + const { jobId, id, schema, table, contentFunction, embeddingColumn } = job + + // Fetch content for the schema/table/row combination + const [row]: [Row] = await sql` + select + id, + ${sql(contentFunction)}(t) as content + from + ${sql(schema)}.${sql(table)} t + where + id = ${id} + ` + + if (!row) { + throw new Error(`row not found: ${schema}.${table}/${id}`) + } + + if (typeof row.content !== 'string') { + throw new Error(`invalid content - expected string: ${schema}.${table}/${id}`) + } + + const embedding = await generateEmbedding(row.content) + + await sql` + update + ${sql(schema)}.${sql(table)} + set + ${sql(embeddingColumn)} = ${JSON.stringify(embedding)} + where + id = ${id} + ` + + await sql` + select pgmq.delete(${QUEUE_NAME}, ${jobId}::bigint) + ` +} + +/** + * Returns a promise that rejects if the worker is terminating. + */ +function catchUnload() { + return new Promise((reject) => { + addEventListener('beforeunload', (ev: any) => { + reject(new Error(ev.detail?.reason)) + }) + }) +} +``` + +The Edge Function listens for incoming HTTP requests from `pg_net` and processes each embedding job. It is a generic worker that can handle embedding jobs for any table and column. It uses OpenAI's API to generate embeddings and updates the corresponding row in the database. It also deletes the job from the queue once it has been processed. + +The function is designed to process multiple jobs independently. If one job fails, it will not affect the processing of other jobs. The function returns a `200 OK` response with a list of completed and failed jobs. We can use this information to diagnose failed jobs. See [Troubleshooting](#troubleshooting) for more details. + +You will need to set the `OPENAI_API_KEY` environment variable to authenticate with OpenAI. When running the Edge Function locally, you can add it to a `.env` file: + +_.env_: + +``` +OPENAI_API_KEY=your-api-key +``` + +When you're ready to deploy the Edge Function, set can set the environment variable using the Supabase CLI: + +```shell +supabase secrets set --env-file .env +``` + +or + +```shell +supabase secrets set OPENAI_API_KEY= +``` + +Alternatively, you can replace the `generateEmbedding` function with your own embedding generation logic. + +See [Deploy to Production](/docs/guides/functions/deploy) for more information on how to deploy the Edge Function. + +## Usage + +Now that the infrastructure is in place, let's go through an example of how to use this system to automatically generate embeddings for a table of documents. You can use this approach with multiple tables and customize the input for each embedding generation as needed. + +### 1. Create table to store documents with embeddings + +We'll set up a new `documents` table that will store our content and embeddings: + +```sql +-- Table to store documents with embeddings +create table documents ( + id integer primary key generated always as identity, + title text not null, + content text not null, + embedding halfvec, + created_at timestamp with time zone default now() +); + +-- Index for vector search over document embeddings +create index on documents using hnsw (embedding halfvec_cosine_ops); +``` + +Our `documents` table stores the title and content of each document along with its vector embedding. We use a `halfvec` column to store the embeddings, which is a `pgvector` data type that stores float values in half precision (16 bits) to save space. + +We use an [HNSW index](/docs/guides/ai/vector-indexes/hnsw-indexes) on the vector column. Note that we are choosing `halfvec_cosine_ops` as the index method, which means our future queries will need to use cosine distance (`<=>`) to find similar embeddings. Also note that HNSW indexes support a maximum of 4000 dimensions for `halfvec` vectors, so keep this in mind when choosing an embedding model. If your model generates embeddings with more than 4000 dimensions, you will need to reduce the dimensionality before indexing them. See [Matryoshka embeddings](/blog/matryoshka-embeddings) for a potential solution to shortening dimensions. + +Also note that the table must have a primary key column named `id` for our triggers to work correctly with the `util.queue_embeddings` function and for our Edge Function to update the correct row. + +### 2. Create triggers to enqueue embedding jobs + +Now we'll set up the triggers to enqueue embedding jobs whenever content is inserted or updated: + +```sql +-- Customize the input for embedding generation +-- e.g. Concatenate title and content with a markdown header +create or replace function embedding_input(doc documents) +returns text +language plpgsql +immutable +as $$ +begin + return '# ' || doc.title || E'\n\n' || doc.content; +end; +$$; + +-- Trigger for insert events +create trigger embed_documents_on_insert + after insert + on documents + for each row + execute function util.queue_embeddings('embedding_input', 'embedding'); + +-- Trigger for update events +create trigger embed_documents_on_update + after update of title, content -- must match the columns in embedding_input() + on documents + for each row + execute function util.queue_embeddings('embedding_input', 'embedding'); +``` + +We create 2 triggers: + +1. `embed_documents_on_insert`: Enqueues embedding jobs whenever new rows are inserted into the `documents` table. + +2. `embed_documents_on_update`: Enqueues embedding jobs whenever the `title` or `content` columns are updated in the `documents` table. + +Both of these triggers use the same `util.queue_embeddings` function that will queue the embedding jobs for processing. They accept 2 arguments: + +1. `embedding_input`: The name of the function that generates the input for embedding generation. This function allows you to customize the text input passed to the embedding model (e.g. concatenating the title and content). The function should accept a single row as input and return text. + +2. `embedding`: The name of the destination column where the embedding will be stored. + +Note that the update trigger only fires when the `title` or `content` columns are updated. This is to avoid unnecessary updates to the embedding column when other columns are updated. Make sure that these columns match the columns used in the `embedding_input` function. + +{/* supa-mdx-lint-disable-next-line Rule001HeadingCase */} + +#### (Optional) Clearing embeddings on update + +Note that our trigger will enqueue new embedding jobs when content is updated, but it will not clear any existing embeddings. This means that an embedding can be temporarily out of sync with the content until the new embedding is generated and updated. + +If it is more important to have _accurate_ embeddings than _any_ embedding, you can add another trigger to clear the existing embedding until the new one is generated: + +```sql +-- Trigger to clear the embedding column on update +create trigger clear_document_embedding_on_update + before update of title, content -- must match the columns in embedding_input() + on documents + for each row + execute function util.clear_column('embedding'); +``` + +`util.clear_column` is a generic trigger function we created earlier that can be used to clear any column in a table. + +- It accepts the column name as an argument. This column must be nullable. +- It requires a `before` trigger with a `for each row` clause. +- It requires the `hstore` extension we created earlier. + +This example will clear the `embedding` column whenever the `title` or `content` columns are updated (note the `of title, content` clause). This ensures that the embedding is always in sync with the title and content, but it will result in temporary gaps in search results until the new embedding is generated. + +We intentionally use a `before` trigger because it allows us to modify the record before it's written to disk, avoiding an extra `update` statement that would be needed with an `after` trigger. + +### 3. Insert and update documents + +Let's insert a new document and update its content to see the embedding generation in action: + +```sql +-- Insert a new document +insert into documents (title, content) +values + ('Understanding Vector Databases', 'Vector databases are specialized...'); + +-- Immediately check the embedding column +select id, embedding +from documents +where title = 'Understanding Vector Databases'; +``` + +You should observe that the `embedding` column is initially `null` after inserting the document. This is because the embedding generation is asynchronous and will be processed by the Edge Function in the next scheduled task. + +Wait up to 10 seconds for the next task to run, then check the `embedding` column again: + +```sql +select id, embedding +from documents +where title = 'Understanding Vector Databases'; +``` + +You should see the generated embedding for the document. + +Next let's update the content of the document: + +```sql +-- Update the content of the document +update documents +set content = 'Vector databases allow you to query...' +where title = 'Understanding Vector Databases'; + +-- Immediately check the embedding column +select id, embedding +from documents +where title = 'Understanding Vector Databases'; +``` + +You should observe that the `embedding` column is reset to `null` after updating the content. This is because of the trigger we added to clear existing embeddings whenever the content is updated. The embedding will be regenerated by the Edge Function in the next scheduled task. + +Wait up to 10 seconds for the next task to run, then check the `embedding` column again: + +```sql +select id, embedding +from documents +where title = 'Understanding Vector Databases'; +``` + +You should see the updated embedding for the document. + +Finally we'll update the title of the document: + +```sql +-- Update the title of the document +update documents +set title = 'Understanding Vector Databases with Supabase' +where title = 'Understanding Vector Databases'; +``` + +You should observe that the `embedding` column is once again reset to `null` after updating the title. This is because the trigger we added to clear existing embeddings fires when either the `content` or `title` columns are updated. The embedding will be regenerated by the Edge Function in the next scheduled task. + +Wait up to 10 seconds for the next task to run, then check the `embedding` column again: + +```sql +select id, embedding +from documents +where title = 'Understanding Vector Databases with Supabase'; +``` + +You should see the updated embedding for the document. + +## Troubleshooting + +The `embed` Edge Function processes a batch of embedding jobs and returns a `200 OK` response with a list of completed and failed jobs in the body. For example: + +```json +{ + "completedJobs": [ + { + "jobId": "1", + "id": "1", + "schema": "public", + "table": "documents", + "contentFunction": "embedding_input", + "embeddingColumn": "embedding" + } + ], + "failedJobs": [ + { + "jobId": "2", + "id": "2", + "schema": "public", + "table": "documents", + "contentFunction": "embedding_input", + "embeddingColumn": "embedding", + "error": "error connecting to openai api" + } + ] +} +``` + +It also returns the number of completed and failed jobs in the response headers. For example: + +``` +X-Completed-Jobs: 1 +X-Failed-Jobs: 1 +``` + +You can also use the `X-Deno-Execution-Id` header to trace the execution of the Edge Function within the [dashboard](/dashboard/project/_/functions) logs. + +Each failed job includes an `error` field with a description of the failure. Reasons for a job failing could include: + +- An error generating the embedding via external API +- An error connecting to the database +- The edge function being terminated (e.g. due to a wall clock limit) +- Any other error thrown during processing + +`pg_net` stores HTTP responses in the `net._http_response` table, which can be queried to diagnose issues with the embedding generation process. + +```sql +select + * +from + net._http_response +where + (headers->>'X-Failed-Jobs')::int > 0; +``` + +## Conclusion + +Automating embedding generation and updates in Postgres allow you to build powerful semantic search capabilities without the complexity of managing embeddings manually. + +By combining Postgres features like triggers, queues, and other extensions with Supabase Edge Functions, we can create a robust system that handles embedding generation asynchronously and retries failed jobs automatically. + +This system can be customized to work with any content and embedding generation service, providing a flexible and scalable solution for semantic search in Postgres. + +## See also + +- [What are embeddings?](/docs/guides/ai/concepts) +- [Semantic search](/docs/guides/ai/semantic-search) +- [Vector indexes](/docs/guides/ai/vector-indexes) +- [Supabase Edge Functions](/docs/guides/functions) diff --git a/apps/docs/content/guides/ai/choosing-compute-addon.mdx b/apps/docs/content/guides/ai/choosing-compute-addon.mdx index fe8c562b023..7c36ccfdd72 100644 --- a/apps/docs/content/guides/ai/choosing-compute-addon.mdx +++ b/apps/docs/content/guides/ai/choosing-compute-addon.mdx @@ -277,7 +277,7 @@ There are various ways to improve your pgvector performance. Here are some tips: It's useful to execute a few thousand “warm-up” queries before going into production. This helps help with RAM utilization. This can also help to determine that you've selected the right compute size for your workload. -### Finetune index parameters +### Fine-tune index parameters You can increase the Requests per Second by increasing `m` and `ef_construction` or `lists`. This also has an important caveat: building the index takes longer with higher values for these parameters. @@ -301,7 +301,7 @@ You can increase the Requests per Second by increasing `m` and `ef_construction` - + multi database - Include the script in your `package.json` script commands to enable Vercel to automaticall run it at build time. + Include the script in your `package.json` script commands to enable Vercel to automatically run it at build time. diff --git a/apps/docs/content/guides/ai/examples/openai.mdx b/apps/docs/content/guides/ai/examples/openai.mdx index b437530179c..eeda26ee421 100644 --- a/apps/docs/content/guides/ai/examples/openai.mdx +++ b/apps/docs/content/guides/ai/examples/openai.mdx @@ -91,7 +91,7 @@ You should see a GPT response come back from OpenAI! ## Deploy -Deploy your function to the cloud by runnning: +Deploy your function to the cloud by running: ```bash supabase functions deploy --no-verify-jwt openai diff --git a/apps/docs/content/guides/ai/examples/semantic-image-search-amazon-titan.mdx b/apps/docs/content/guides/ai/examples/semantic-image-search-amazon-titan.mdx index 373720b7797..4f831e609da 100644 --- a/apps/docs/content/guides/ai/examples/semantic-image-search-amazon-titan.mdx +++ b/apps/docs/content/guides/ai/examples/semantic-image-search-amazon-titan.mdx @@ -84,7 +84,7 @@ bedrock_client = boto3.client( ## Create embeddings for your images -In the root of your project, create a new folder called `images` and add some images. You can use the images from the example project on [GitHub](https://github.com/supabase/supabase/tree/master/examples/ai/aws_bedrock_image_search/images) or you can find license free images on [unsplash](https://unsplash.com). +In the root of your project, create a new folder called `images` and add some images. You can use the images from the example project on [GitHub](https://github.com/supabase/supabase/tree/master/examples/ai/aws_bedrock_image_search/images) or you can find license free images on [Unsplash](https://unsplash.com). To send images to the Amazon Bedrock API we need to need to encode them as `base64` strings. Create the following helper methods: @@ -188,7 +188,7 @@ seed = "image_search.main:seed" search = "image_search.main:search" ``` -After activating the virtual environtment with `poetry shell` you can now run your seed script via `poetry run seed`. You can inspect the generated embeddings in your Supabase Dashboard by visiting the [Table Editor](https://supabase.com/dashboard/project/_/editor), selecting the `vecs` schema, and the `image_vectors` table. +After activating the virtual environment with `poetry shell` you can now run your seed script via `poetry run seed`. You can inspect the generated embeddings in your Supabase Dashboard by visiting the [Table Editor](https://supabase.com/dashboard/project/_/editor), selecting the `vecs` schema, and the `image_vectors` table. ## Perform an image search from a text query diff --git a/apps/docs/content/guides/ai/going-to-prod.mdx b/apps/docs/content/guides/ai/going-to-prod.mdx index a82522b043b..5d43a161a6f 100644 --- a/apps/docs/content/guides/ai/going-to-prod.mdx +++ b/apps/docs/content/guides/ai/going-to-prod.mdx @@ -29,7 +29,7 @@ On the other hand, if you need to scale your application, you will need to [crea `pgvector` supports two types of indexes: HNSW and IVFFlat. We recommend using [HNSW](/docs/guides/ai/vector-indexes/hnsw-indexes) because of its [performance](https://supabase.com/blog/increase-performance-pgvector-hnsw#hnsw-performance-1536-dimensions) and [robustness against changing data](/docs/guides/ai/vector-indexes/hnsw-indexes#when-should-you-create-hnsw-indexes). dbpedia embeddings comparing ivfflat and hnsw queries-per-second using the 4XL compute addon @@ -66,5 +66,5 @@ You can view the inserted items in the [Table Editor](https://supabase.com/dashb ## Resources -- Visit the LlamaIndex + SupabaseVectorStore [docs](https://gpt-index.readthedocs.io/en/latest/examples/vector_stores/SupabaseVectorIndexDemo.html) +- Visit the LlamaIndex + `SupabaseVectorStore` [docs](https://gpt-index.readthedocs.io/en/latest/examples/vector_stores/SupabaseVectorIndexDemo.html) - Visit the official LlamaIndex [repo](https://github.com/jerryjliu/llama_index/) diff --git a/apps/docs/content/guides/ai/langchain.mdx b/apps/docs/content/guides/ai/langchain.mdx index df6a2b80dde..f0874ca37a7 100644 --- a/apps/docs/content/guides/ai/langchain.mdx +++ b/apps/docs/content/guides/ai/langchain.mdx @@ -147,7 +147,7 @@ export const run = async () => { ### Advanced metadata filtering -You can also use query builder-style filtering ([similar to how the Supabase JavaScript library works](https://supabase.com/docs/reference/javascript/using-filters)) instead of passing an object. Note that since the filter properties will be in the metadata column, you need to use arrow operators (`->` for integer or `->>` for text) as defined in [Postgrest API documentation](https://postgrest.org/en/stable/references/api/tables_views.html?highlight=operators#json-columns) and specify the data type of the property (e.g. the column should look something like `metadata->some_int_value::int`). +You can also use query builder-style filtering ([similar to how the Supabase JavaScript library works](https://supabase.com/docs/reference/javascript/using-filters)) instead of passing an object. Note that since the filter properties will be in the metadata column, you need to use arrow operators (`->` for integer or `->>` for text) as defined in [PostgREST API documentation](https://postgrest.org/en/stable/references/api/tables_views.html?highlight=operators#json-columns) and specify the data type of the property (e.g. the column should look something like `metadata->some_int_value::int`). ```js import { SupabaseFilterRPCCall, SupabaseVectorStore } from 'langchain/vectorstores/supabase' diff --git a/apps/docs/content/guides/ai/quickstarts/generate-text-embeddings.mdx b/apps/docs/content/guides/ai/quickstarts/generate-text-embeddings.mdx index 816b5ccaf2e..197d928c2e2 100644 --- a/apps/docs/content/guides/ai/quickstarts/generate-text-embeddings.mdx +++ b/apps/docs/content/guides/ai/quickstarts/generate-text-embeddings.mdx @@ -109,7 +109,7 @@ Let's build an Edge Function that will accept an input string and generate an em Note the two options we pass to `session.run()`: - - `mean_pool`: The first option sets `pooling` to `mean`. Pooling referes to how token-level embedding representations are compressed into a single sentence embedding that reflects the meaning of the entire sentence. Average pooling is the most common type of pooling for sentence embeddings. + - `mean_pool`: The first option sets `pooling` to `mean`. Pooling refers to how token-level embedding representations are compressed into a single sentence embedding that reflects the meaning of the entire sentence. Average pooling is the most common type of pooling for sentence embeddings. - `normalize`: The second option tells to normalize the embedding vector so that it can be used with distance measures like dot product. A normalized vector means its length (magnitude) is 1 - also referred to as a unit vector. A vector is normalized by dividing each element by the vector's length (magnitude), which maintains its direction but changes its length to 1. diff --git a/apps/docs/content/guides/ai/rag-with-permissions.mdx b/apps/docs/content/guides/ai/rag-with-permissions.mdx index 3a3256527e0..996659da5fd 100644 --- a/apps/docs/content/guides/ai/rag-with-permissions.mdx +++ b/apps/docs/content/guides/ai/rag-with-permissions.mdx @@ -50,7 +50,7 @@ on document_sections for select to authenticated using ( ); ``` - + In this example, the current user is determined using the built-in `auth.uid()` function when the query is executed through your project's auto-generated [REST API](/docs/guides/api). If you are connecting to your Supabase database through a direct Postgres connection, see [Direct Postgres Connection](#direct-postgres-connection) below for directions on how to achieve the same access control. @@ -117,7 +117,7 @@ RLS is latency-sensitive, so extra caution should be taken before implementing t - + For data sources other than Postgres, see [Foreign Data Wrappers](/docs/guides/database/extensions/wrappers/overview) for a list of external sources supported today. If your data lives in a source not provided in the list, please contact [support](https://supabase.com/dashboard/support/new) and we'll be happy to discuss your use case. @@ -161,7 +161,7 @@ import foreign schema public limit to (users, documents) from server foreign_server into external; ``` - + This example maps the `authenticated` role in Supabase to the `postgres` user in the external DB. In production, it's best to create a custom user on the external DB that has the minimum permissions necessary to access the information you need. @@ -224,7 +224,7 @@ where document_sections.embedding <#> embedding < -match_threshold order by document_sections.embedding <#> embedding; ``` - + You might be tempted to discard RLS completely and simply filter by user within the `where` clause. Though this will work, we recommend RLS as a general best practice since RLS is always applied even as new queries and application logic is introduced in the future. diff --git a/apps/docs/content/guides/ai/vector-columns.mdx b/apps/docs/content/guides/ai/vector-columns.mdx index 9c8c2c811b9..eee63e05822 100644 --- a/apps/docs/content/guides/ai/vector-columns.mdx +++ b/apps/docs/content/guides/ai/vector-columns.mdx @@ -61,7 +61,7 @@ create table documents ( In the above SQL snippet, we create a `documents` table with a column called `embedding` (note this is just a regular Postgres column - you can name it whatever you like). We give the `embedding` column a `vector` data type with 384 dimensions. Change this to the number of dimensions produced by your embedding model. For example, if you are [generating embeddings](/docs/guides/ai/quickstarts/generate-text-embeddings) using the open source [`gte-small`](https://huggingface.co/Supabase/gte-small) model, you would set this number to 384 since that model produces 384 dimensions. - + In general, embeddings with fewer dimensions perform best. See our [analysis on fewer dimensions in pgvector](https://supabase.com/blog/fewer-dimensions-are-better-pgvector). @@ -155,7 +155,7 @@ const { data: documents } = await supabaseClient.rpc('match_documents', { In this example `embedding` would be another embedding you wish to compare against your table of pre-generated embedding documents. For example if you were building a search engine, every time the user submits their query you would first generate an embedding on the search query itself, then pass it into the above `rpc()` function to match. - + Be sure to use embeddings produced from the same embedding model when calculating distance. Comparing embeddings from two different models will produce no meaningful result. diff --git a/apps/docs/content/guides/api/automatic-retries-in-supabase-js.mdx b/apps/docs/content/guides/api/automatic-retries-in-supabase-js.mdx new file mode 100644 index 00000000000..e9cfbe80eaf --- /dev/null +++ b/apps/docs/content/guides/api/automatic-retries-in-supabase-js.mdx @@ -0,0 +1,118 @@ +--- +id: automatic-retries-in-supabase-js +title: 'How to do automatic retries with `supabase-js`' +subtitle: 'Learn how to add automatic retries to your Supabase API requests using `fetch-retry`.' +--- + + + +You should only enable retries if your requests fail with network errors (e.g. 520 status from Cloudflare). A high number of retries have the potential to exhaust the Data API connection pool, which could result in lower throughput and failed requests. + + + +The `fetch-retry` package allows you to add retry logic to `fetch` requests, making it a useful tool for enhancing the resilience of API calls in your Supabase applications. Here's a step-by-step guide on how to integrate `fetch-retry` with the `supabase-js` library. + +## 1. Install dependencies + +To get started, ensure you have both `supabase-js` and `fetch-retry` installed in your project: + +```bash +npm install @supabase/supabase-js fetch-retry +``` + +## 2. Wrap the fetch function + +The `fetch-retry` package works by wrapping the native `fetch` function. You can create a custom fetch instance with retry logic and pass it to the `supabase-js` client. + +```javascript +import { createClient } from '@supabase/supabase-js' +import fetchRetry from 'fetch-retry' + +// Wrap the global fetch with fetch-retry +const fetchWithRetry = fetchRetry(fetch) + +// Create a Supabase client instance with the custom fetch +const supabase = createClient('https://your-supabase-url.supabase.co', 'your-anon-key', { + global: { + fetch: fetchWithRetry, + }, +}) +``` + +## 3. Configure retry options + +You can configure `fetch-retry` options to control retry behavior, such as the number of retries, retry delay, and which errors should trigger a retry. + +Here is an example with custom retry options: + +```javascript +const fetchWithRetry = fetchRetry(fetch, { + retries: 3, // Number of retry attempts + retryDelay: (attempt) => Math.min(1000 * 2 ** attempt, 30000), // Exponential backoff + retryOn: [520], // Retry only on Cloudflare errors +}) +``` + +In this example, the `retryDelay` function implements an exponential backoff strategy, and retries are triggered only for specific HTTP status codes. + +## 4. Using the Supabase client + +With `fetch-retry` integrated, you can use the Supabase client as usual. The retry logic will automatically apply to all network requests made by `supabase-js`. + +```javascript +async function fetchData() { + const { data, error } = await supabase.from('your_table').select('*') + + if (error) { + console.error('Error fetching data:', error) + } else { + console.log('Fetched data:', data) + } +} + +fetchData() +``` + +## 5. Fine-Tuning retries for specific requests + +If you need different retry logic for certain requests, you can use the `retryOn` with a custom function to inspect the URL or response and decide whether to retry the request. + +```javascript +const fetchWithRetry = fetchRetry(fetch, { + retryDelay: (attempt) => Math.min(1000 * 2 ** attempt, 30000), + retryOn: (attempt, error, response) => { + const shouldRetry + = (attempt: number, error: Error | null, response: Response | null) => + attempt < 3 + && response + && response.status == 520 // Cloudflare errors + && response.url.includes('rpc/your_stored_procedure') + + if (shouldRetry(attempt, error, response)) { + console.log(`Retrying request... Attempt #${attempt}`, response) + return true + } + + return false + } +}) + +async function yourStoredProcedure() { + const { data, error } = await supabase + .rpc('your_stored_procedure', { param1: 'value1' }); + + if (error) { + console.log('Error executing RPC:', error); + } else { + console.log('Response:', data); + } +} + +yourStoredProcedure(); +``` + +By using `retryOn` with a custom function, you can define specific conditions for retrying requests. In this example, the retry logic is applied only to requests targeting a specific stored procedure. + +## Conclusion + +Integrating `fetch-retry` with `supabase-js` is a straightforward way to add robustness to your Supabase API requests. By handling transient errors and implementing retry strategies, you can improve the reliability of your application while maintaining a seamless user experience. diff --git a/apps/docs/content/guides/api/creating-routes.mdx b/apps/docs/content/guides/api/creating-routes.mdx index 43e78a99f84..64c44884024 100644 --- a/apps/docs/content/guides/api/creating-routes.mdx +++ b/apps/docs/content/guides/api/creating-routes.mdx @@ -105,9 +105,9 @@ curl '/rest/v1/todos' \ -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). +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). diff --git a/apps/docs/content/guides/api/rest/generating-types.mdx b/apps/docs/content/guides/api/rest/generating-types.mdx index c5cf62d86e8..ad6036620e7 100644 --- a/apps/docs/content/guides/api/rest/generating-types.mdx +++ b/apps/docs/content/guides/api/rest/generating-types.mdx @@ -258,7 +258,7 @@ jobs: branch: ${{ github.ref }} ``` -Alternatively, you can use a community-supported GitHub action: [generate-supabase-db-types-github-action](https://github.com/lyqht/generate-supabase-db-types-github-action). +Alternatively, you can use a community-supported GitHub action: [`generate-supabase-db-types-github-action`](https://github.com/lyqht/generate-supabase-db-types-github-action). ## Resources diff --git a/apps/docs/content/guides/api/sql-to-api.mdx b/apps/docs/content/guides/api/sql-to-api.mdx index 2e2183d0766..e3456d988b4 100644 --- a/apps/docs/content/guides/api/sql-to-api.mdx +++ b/apps/docs/content/guides/api/sql-to-api.mdx @@ -66,7 +66,7 @@ const { data, error } = await supabase ## Resources - [Supabase - Get started for free](https://supabase.com) -- [Postgrest Operators](https://postgrest.org/en/stable/api.html#operators) +- [PostgREST Operators](https://postgrest.org/en/stable/api.html#operators) - [Supabase API: JavaScript select](/docs/reference/javascript/select) - [Supabase API: JavaScript modifiers](/docs/reference/javascript/using-modifiers) - [Supabase API: JavaScript filters](/docs/reference/javascript/using-filters) diff --git a/apps/docs/content/guides/api/sql-to-rest.mdx b/apps/docs/content/guides/api/sql-to-rest.mdx index e96e844932b..13d3ad031ce 100644 --- a/apps/docs/content/guides/api/sql-to-rest.mdx +++ b/apps/docs/content/guides/api/sql-to-rest.mdx @@ -7,7 +7,7 @@ description: 'Translate SQL queries to HTTP requests and Supabase client code' Sometimes it's challenging to translate SQL queries to the equivalent [PostgREST](https://postgrest.org/) request or Supabase client code. Use this tool to help with this translation. - + PostgREST supports a subset of SQL, so not all SQL queries will translate. diff --git a/apps/docs/content/guides/auth.mdx b/apps/docs/content/guides/auth.mdx index 87f827ff5c3..8fefd30e7a3 100644 --- a/apps/docs/content/guides/auth.mdx +++ b/apps/docs/content/guides/auth.mdx @@ -38,3 +38,12 @@ Supabase Auth works with many popular Auth methods, including Social and Phone A ### Phone Auth + +## Pricing + +Charges apply to Monthly Active Users (MAU), Monthly Active Third-Party Users (Third-Party MAU), and Monthly Active SSO Users (SSO MAU) and Advanced MFA Add-ons. For a detailed breakdown of how these charges are calculated, refer to the following pages: + +- [Pricing MAU](/docs/guides/platform/manage-your-usage/monthly-active-users) +- [Pricing Third-Party MAU](/docs/guides/platform/manage-your-usage/monthly-active-users-third-party) +- [Pricing SSO MAU](/docs/guides/platform/manage-your-usage/monthly-active-users-sso) +- [Advanced MFA - Phone](/docs/guides/platform/manage-your-usage/advanced-mfa-phone) diff --git a/apps/docs/content/guides/auth/auth-anonymous.mdx b/apps/docs/content/guides/auth/auth-anonymous.mdx index 1e4a506e1b6..04433660137 100644 --- a/apps/docs/content/guides/auth/auth-anonymous.mdx +++ b/apps/docs/content/guides/auth/auth-anonymous.mdx @@ -189,7 +189,7 @@ response = supabase.auth.update_user({ > -You can use the [`linkIdentity()`](/docs/reference/javascript/auth-linkidentity) method to link an oauth identity to the anonymous user. +You can use the [`linkIdentity()`](/docs/reference/javascript/auth-linkidentity) method to link an OAuth identity to the anonymous user. ```js const { data, error } = await supabase.auth.linkIdentity({ provider: 'google' }) @@ -216,7 +216,7 @@ try await supabase.auth.linkIdentity(provider: .google) -You can use the [`linkIdentity()`](/docs/reference/kotlin/auth-linkidentity) method to link an oauth identity to the anonymous user. +You can use the [`linkIdentity()`](/docs/reference/kotlin/auth-linkidentity) method to link an OAuth identity to the anonymous user. ```kotlin supabase.auth.linkIdentity(Google) @@ -225,7 +225,7 @@ supabase.auth.linkIdentity(Google) -You can use the [`link_identity()`](/docs/reference/python/auth-linkidentity) method to link an oauth identity to the anonymous user. +You can use the [`link_identity()`](/docs/reference/python/auth-linkidentity) method to link an OAuth identity to the anonymous user. ```python response = supabase.auth.link_identity({'provider': 'google'}) @@ -250,7 +250,7 @@ to authenticated using ( true ); ``` - + RLS policies are permissive by default, which means that they are combined using an "OR" operator when multiple policies are applied. It is important to construct restrictive policies to ensure that the checks for an anonymous user are always enforced when combined with other policies. @@ -313,7 +313,7 @@ async function resolveDataConflicts(anonymousUserId, existingUserId) { ## Abuse prevention and rate limits -Since anonymous users are stored in your database, bad actors can abuse the endpoint to increase your database size drastically. It is strongly recommended to [enable invisible Captcha or Cloudflare Turnstile](/docs/guides/auth/auth-captcha) to prevent abuse for anonymous sign-ins. An IP-based rate limit is enforced at 30 requests per hour which can be modified in your [dashboard](/dashboard/project/_/auth/rate-limits). You can refer to the full list of rate limits [here](/docs/guides/platform/going-into-prod#rate-limiting-resource-allocation--abuse-prevention). +Since anonymous users are stored in your database, bad actors can abuse the endpoint to increase your database size drastically. It is strongly recommended to [enable invisible CAPTCHA or Cloudflare Turnstile](/docs/guides/auth/auth-captcha) to prevent abuse for anonymous sign-ins. An IP-based rate limit is enforced at 30 requests per hour which can be modified in your [dashboard](/dashboard/project/_/auth/rate-limits). You can refer to the full list of rate limits [here](/docs/guides/platform/going-into-prod#rate-limiting-resource-allocation--abuse-prevention). ## Automatic cleanup diff --git a/apps/docs/content/guides/auth/auth-captcha.mdx b/apps/docs/content/guides/auth/auth-captcha.mdx index a55b4e46ab2..9d5e8c99dab 100644 --- a/apps/docs/content/guides/auth/auth-captcha.mdx +++ b/apps/docs/content/guides/auth/auth-captcha.mdx @@ -41,13 +41,13 @@ In the Settings page, look for the **Sitekey** section and copy the key. ## Enable CAPTCHA protection for your Supabase project -Navigate to the **[Auth](https://supabase.com/dashboard/project/_/settings/auth)** section of your Project Settings in the Supabase Dashboard and find the **Enable Captcha protection** toggle under Settings > Authentication > Bot and Abuse Protection > Enable Captcha protection. +Navigate to the **[Auth](https://supabase.com/dashboard/project/_/settings/auth)** section of your Project Settings in the Supabase Dashboard and find the **Enable CAPTCHA protection** toggle under Settings > Authentication > Bot and Abuse Protection > Enable CAPTCHA protection. -Select your CAPTCHA provider from the dropdown, enter your Captcha **Secret key**, and click **Save**. +Select your CAPTCHA provider from the dropdown, enter your CAPTCHA **Secret key**, and click **Save**. ## Add the CAPTCHA frontend component -The frontend requires some changes to provide the captcha on-screen for the user. This example uses React and the corresponding Captcha React component, but both Captcha providers can be used with any JavaScript framework. +The frontend requires some changes to provide the CAPTCHA on-screen for the user. This example uses React and the corresponding CAPTCHA React component, but both CAPTCHA providers can be used with any JavaScript framework. ``` -We will pass it the sitekey we copied from the hCaptcha website as a property along with a onVerify property which takes a callback function. This callback function will have a token as one of its properties. Let's set the token in the state using `setCaptchaToken` +We will pass it the sitekey we copied from the hCaptcha website as a property along with a `onVerify` property which takes a callback function. This callback function will have a token as one of its properties. Let's set the token in the state using `setCaptchaToken` ```jsx ``` -Now lets use the captcha token we receive in our Supabase signUp function. +Now lets use the CAPTCHA token we receive in our Supabase signUp function. ```jsx await supabase.auth.signUp({ @@ -104,9 +104,9 @@ await supabase.auth.signUp({ }) ``` -We will also need to reset the captcha challenge after we have made a call to the function above. +We will also need to reset the CAPTCHA challenge after we have made a call to the function above. -Create a ref to use on our HCaptcha component. +Create a ref to use on our `HCaptcha` component. ```jsx const captcha = useRef() @@ -136,7 +136,7 @@ In order to test that this works locally we will need to use something like [ngr -The frontend requires some changes to provide the captcha on-screen for the user. Turnstile can be used with any JavaScript framework but we'll use React and the Turnstile React component for this example. +The frontend requires some changes to provide the CAPTCHA on-screen for the user. Turnstile can be used with any JavaScript framework but we'll use React and the Turnstile React component for this example. Install @marsidev/react-turnstile in your project as a dependency. @@ -183,9 +183,9 @@ await supabase.auth.signUp({ }) ``` -To test locally, you will need to add localhost to the domain whitelist as per the [Cloudflare docs](https://developers.cloudflare.com/turnstile/reference/testing/) +To test locally, you will need to add localhost to the domain allowlist as per the [Cloudflare docs](https://developers.cloudflare.com/turnstile/reference/testing/) -Run the application and you should now be provided with a captcha challenge. +Run the application and you should now be provided with a CAPTCHA challenge. diff --git a/apps/docs/content/guides/auth/auth-helpers/auth-ui.mdx b/apps/docs/content/guides/auth/auth-helpers/auth-ui.mdx index 55d96374d17..7b239511062 100644 --- a/apps/docs/content/guides/auth/auth-helpers/auth-ui.mdx +++ b/apps/docs/content/guides/auth/auth-helpers/auth-ui.mdx @@ -5,7 +5,7 @@ description: 'A prebuilt, customizable React component for authenticating users. sitemapPriority: 0.5 --- - + As of 7th Feb 2024, [this repository](https://github.com/supabase-community/auth-ui) is no longer maintained by the Supabase Team. At the moment, the team does not have capacity to give the expected level of care to this repository. We may revisit Auth UI in the future but regrettably have to leave it on hold for now as we focus on other priorities such as improving the Server-Side Rendering (SSR) package and advanced Auth primitives. @@ -444,7 +444,7 @@ A full list of the available variables is below: | `phone_input_label` | Phone number | | `phone_input_placeholder` | Your phone number | | `token_input_label` | Token | -| `token_input_placeholder` | Your Otp token | +| `token_input_placeholder` | Your OTP token | | `button_label` | Verify token | | `loading_button_label` | Signing in ... | diff --git a/apps/docs/content/guides/auth/auth-helpers/flutter-auth-ui.mdx b/apps/docs/content/guides/auth/auth-helpers/flutter-auth-ui.mdx index c088488c816..73507acfc71 100644 --- a/apps/docs/content/guides/auth/auth-helpers/flutter-auth-ui.mdx +++ b/apps/docs/content/guides/auth/auth-helpers/flutter-auth-ui.mdx @@ -36,9 +36,9 @@ void main() async { ### Email Auth -Use a SupaEmailAuth widget to create an email and password signin and signup form. It also contains a button to toggle to display a forgot password form. +Use a `SupaEmailAuth` widget to create an email and password signin and signup form. It also contains a button to toggle to display a forgot password form. -You can pass metadataFields to add additional fields to the form to pass as metadata to Supabase. +You can pass `metadataFields` to add additional fields to the form to pass as metadata to Supabase. ```dart SupaEmailAuth( @@ -63,7 +63,7 @@ SupaEmailAuth( ### Magic link Auth -Use SupaMagicAuth widget to create a magic link signIn form. +Use `SupaMagicAuth` widget to create a magic link signIn form. ```dart SupaMagicAuth( @@ -75,7 +75,7 @@ SupaMagicAuth( ### Reset password -Use SupaResetPassword to create a password reset form. +Use `SupaResetPassword` to create a password reset form. ```dart SupaResetPassword( @@ -87,7 +87,7 @@ SupaResetPassword( ### Phone Auth -Use SupaPhoneAuth to create a phone authentication form. +Use `SupaPhoneAuth` to create a phone authentication form. ```dart SupaPhoneAuth( @@ -100,7 +100,7 @@ SupaPhoneAuth( The package supports login with [official social providers](../../auth#providers). -Use SupaSocialsAuth to create list of social login buttons. +Use `SupaSocialsAuth` to create list of social login buttons. ```dart SupaSocialsAuth( diff --git a/apps/docs/content/guides/auth/auth-helpers/nextjs-pages.mdx b/apps/docs/content/guides/auth/auth-helpers/nextjs-pages.mdx index 79747f20853..abc435c7719 100644 --- a/apps/docs/content/guides/auth/auth-helpers/nextjs-pages.mdx +++ b/apps/docs/content/guides/auth/auth-helpers/nextjs-pages.mdx @@ -20,7 +20,7 @@ The `auth-helpers` package has been replaced with the `@supabase/ssr` package. W size="medium" className="text-foreground-light border-b mt-8 pb-2" > - + () ``` -Retrieving a supabase client object from the SessionContext: +Retrieving a `supabase` client object from the `SessionContext`: ```tsx import { useSupabaseClient } from '@supabase/auth-helpers-react' @@ -286,7 +286,7 @@ export default LoginPage ## Server-side rendering (SSR) -Create a server supabase client to retrieve the logged in user's session: +Create a server Supabase client to retrieve the logged in user's session: ```jsx pages/profile.js import { createPagesServerClient } from '@supabase/auth-helpers-nextjs' @@ -321,7 +321,7 @@ export const getServerSideProps = async (ctx) => { ## Server-side data fetching with RLS -You can use the server supabase client to run [row level security](/docs/learn/auth-deep-dive/auth-row-level-security) authenticated queries server-side: +You can use the server Supabase client to run [row level security](/docs/learn/auth-deep-dive/auth-row-level-security) authenticated queries server-side: { ## Protecting API routes -Create a server supabase client to retrieve the logged in user's session: +Create a server Supabase client to retrieve the logged in user's session: { - The `useUser` hook now returns the `user` object or `null`. - Usage with TypeScript: You can pass types that were [generated with the Supabase CLI](/docs/reference/javascript/typescript-support#generating-types) to the Supabase Client to get enhanced type safety and auto completion: -Creating a new supabase client object: +Creating a new `supabase` client object: ```tsx import { Database } from '../database.types' @@ -925,7 +925,7 @@ import { Database } from '../database.types' const [supabaseClient] = useState(() => createPagesBrowserClient()) ``` -Retrieving a supabase client object from the SessionContext: +Retrieving a `supabase` client object from the `SessionContext`: ```tsx import { useSupabaseClient } from '@supabase/auth-helpers-react' diff --git a/apps/docs/content/guides/auth/auth-helpers/nextjs.mdx b/apps/docs/content/guides/auth/auth-helpers/nextjs.mdx index 3b715239ec7..d69f84558e8 100644 --- a/apps/docs/content/guides/auth/auth-helpers/nextjs.mdx +++ b/apps/docs/content/guides/auth/auth-helpers/nextjs.mdx @@ -1304,7 +1304,7 @@ supabase.auth.signUp({ #### Deprecated functions -With v0.7.x of the Next.js Auth Helpers a new naming convention has been implemented for createClient functions. The `createMiddlewareSupabaseClient`, `createBrowserSupabaseClient`, `createServerComponentSupabaseClient` and `createRouteHandlerSupabaseClient` functions have been marked as deprecated, and will be removed in a future version of the Auth Helpers. +With v0.7.x of the Next.js Auth Helpers a new naming convention has been implemented for `createClient` functions. The `createMiddlewareSupabaseClient`, `createBrowserSupabaseClient`, `createServerComponentSupabaseClient` and `createRouteHandlerSupabaseClient` functions have been marked as deprecated, and will be removed in a future version of the Auth Helpers. - `createMiddlewareSupabaseClient` has been replaced with `createMiddlewareClient` - `createBrowserSupabaseClient` has been replaced with `createClientComponentClient` diff --git a/apps/docs/content/guides/auth/auth-helpers/sveltekit.mdx b/apps/docs/content/guides/auth/auth-helpers/sveltekit.mdx index b94647a0660..ddb5107adb1 100644 --- a/apps/docs/content/guides/auth/auth-helpers/sveltekit.mdx +++ b/apps/docs/content/guides/auth/auth-helpers/sveltekit.mdx @@ -20,7 +20,7 @@ We generally recommend using the new `@supabase/ssr` package instead of `auth-he size="medium" className="text-foreground-light border-b mt-8 pb-2" > - + { -Note that we are specifying filterSerializedResponseHeaders here. We need to tell SvelteKit that supabase needs the `content-range` and `x-supabase-api-version` headers. +Note that we are specifying `filterSerializedResponseHeaders` here. We need to tell SvelteKit that Supabase needs the `content-range` and `x-supabase-api-version` headers. @@ -213,7 +213,7 @@ export const GET = async ({ url, locals: { supabase } }) => { ### Generate types from your database -In order to get the most out of TypeScript and it's intellisense, you should import the generated Database types into the `app.d.ts` type definition file that comes with your SvelteKit project, where `import('./DatabaseDefinitions')` points to the generated types file outlined in [v2 docs here](https://supabase.com/docs/reference/javascript/release-notes#typescript-support) after you have logged in, linked, and generated types through the Supabase CLI. +In order to get the most out of TypeScript and its IntelliSense, you should import the generated Database types into the `app.d.ts` type definition file that comes with your SvelteKit project, where `import('./DatabaseDefinitions')` points to the generated types file outlined in [v2 docs here](https://supabase.com/docs/reference/javascript/release-notes#typescript-support) after you have logged in, linked, and generated types through the Supabase CLI. ```ts src/app.d.ts // src/app.d.ts @@ -377,13 +377,13 @@ TypeScript types can be [generated with the Supabase CLI](https://supabase.com/d Access the client inside pages by `$page.data.supabase` or `data.supabase` when using `export let data`. -The usage of `depends` tells sveltekit that this load function should be executed whenever `invalidate` is called to keep the page store in sync. +The usage of `depends` tells SvelteKit that this load function should be executed whenever `invalidate` is called to keep the page store in sync. `createSupabaseLoadClient` caches the client when running in a browser environment and therefore does not create a new client for every time the load function runs. #### Setting up the event listener on the client side -We need to create an event listener in the root `+layout.svelte` file in order to catch supabase events being triggered. +We need to create an event listener in the root `+layout.svelte` file in order to catch Supabase events being triggered. ```svelte src/routes/+layout.svelte @@ -416,7 +416,7 @@ The usage of `invalidate` tells SvelteKit that the root `+layout.ts` load functi #### Sign in / sign up / sign out -We can access the supabase instance in our `+page.svelte` file through the data object. +We can access the Supabase instance in our `+page.svelte` file through the data object. ```svelte src/routes/auth/+page.svelte @@ -634,7 +634,7 @@ If you try to submit a form with the action `?/createPost` without a valid sessi To avoid writing the same auth logic in every single route you can also use the handle hook to protect multiple routes at once. For this to work with your Supabase session, you need to use -Sveltekit's [sequence helper](https://kit.svelte.dev/docs/modules#sveltejs-kit-hooks) function. +SvelteKit's [sequence helper](https://kit.svelte.dev/docs/modules#sveltejs-kit-hooks) function. Edit your `/src/hooks.server.js` with the below: { Since version 0.9 relies on `hooks.server.ts` to setup our client, we no longer need the `hooks.client.ts` in our project for Supabase related code. -#### Typings [#migration-typings] +#### Types [#migration-typings] -#### Typings [#migration-typings-0-8] +#### Types [#migration-typings-0-8] -#### Typings [#migration-typings-0-7] +#### Types [#migration-typings-0-7] -A [postgres function](/docs/guides/database/functions) can be configured as a hook. The function should take in a single argument -- the event of type JSONB -- and return a JSONB object. Since the postgres function runs on your database, the request does not leave your project's instance. +A [Postgres function](/docs/guides/database/functions) can be configured as a hook. The function should take in a single argument -- the event of type JSONB -- and return a JSONB object. Since the Postgres function runs on your database, the request does not leave your project's instance. @@ -59,9 +59,9 @@ Sign the payload and grant permissions selectively in order to guard the integri > -When you configure a postgres function as a hook, Supabase will automatically apply the following grants to the function for these reasons: +When you configure a Postgres function as a hook, Supabase will automatically apply the following grants to the function for these reasons: -- Allow the `supabase_auth_admin` role to execute the function. The `supabase_auth_admin` role is the postgres role that is used by Supabase Auth to make requests to your database. +- Allow the `supabase_auth_admin` role to execute the function. The `supabase_auth_admin` role is the Postgres role that is used by Supabase Auth to make requests to your database. - Revoke permissions from other roles (e.g. `anon`, `authenticated`, `public`) to ensure the function is not accessible by Supabase Data APIs. ```sql @@ -81,7 +81,7 @@ revoke execute You will need to alter your row-level security (RLS) policies to allow the `supabase_auth_admin` role to access tables that you have RLS policies on. You can read more about RLS policies [here](/docs/guides/database/postgres/row-level-security). -Alternatively, you can create your postgres function via the dashboard with the `security definer` tag. The `security definer` tag specifies that the function is to be executed with the privileges of the user that owns it. +Alternatively, you can create your Postgres function via the dashboard with the `security definer` tag. The `security definer` tag specifies that the function is to be executed with the privileges of the user that owns it. Currently, functions created via the dashboard take on the `postgres` role. Read more about the `security definer` tag [in our database guide](/docs/guides/database/functions#security-definer-vs-invoker) @@ -96,16 +96,16 @@ HTTP Hooks in Supabase follow the [Standard Webhooks Specification](https://www. When the request is made to the HTTP hook, you should use the [Standard Webhooks libraries](https://github.com/standard-webhooks/standard-webhooks/tree/main/libraries) to verify these headers. -When a HTTP hook is created, the secret generated should be of the following format: `v1,whsec_`: +When a HTTP hook is created, the secret generated should be of the `v1,whsec_` format: - `v1` denotes the version of the hook - `whsec_` signifies that the secret is symmetric -- `` implies a Standard Base64 encoded secret which can contain the characters `+`, `/` and `=` +- `` implies a Standard Base64 encoded secret which can contain the characters `+`, `/` and `=` -The secret is used to verify the payload received in your hook. Create an entry in your `.env.local` file to store the secret for each hook that you have. For example: +The secret is used to verify the payload received in your hook. Create an entry in your `.env.local` file to store the `` portion of the secret for each hook that you have. For example: -```bash -HOOK_SECRETS= +```ini +SEND_SMS_HOOK_SECRETS=v1,whsec_ ``` There field is expressed in plural rather than singular as there are plans to allow for asymmetric signing and multiple hook secrets for ease of secret rotation. For instance: `|`. @@ -117,7 +117,7 @@ import { Webhook } from 'https://esm.sh/standardwebhooks@1.0.0' Deno.serve(async (req) => { const payload = await req.text() - const hookSecret = Deno.env.get('HOOK_SECRETS') + const hookSecret = Deno.env.get('SEND_SMS_HOOK_SECRETS').replace('v1,whsec_', '') // Extract headers and security specific fields const headers = Object.fromEntries(req.headers) const wh = new Webhook(hookSecret) @@ -229,19 +229,25 @@ You can visit `SQL Editor > Templates` for hook templates. Modify the `auth.hook.` field and set `uri` to a valid HTTP URI. For example, the `send_sms` hook would take the following fields: -``` + +```toml [auth.hook.send_sms] enabled = true uri = "http://host.docker.internal:54321/functions/v1/send_sms" -secrets = "env(SEND_SMS_HOOK_SECRET)" +# Comma separated list of secrets +secrets = "env(SEND_SMS_HOOK_SECRETS)" ``` -`host.docker.internal` is a special DNS name used in Docker to allow a container to access the host machine's network. This allows the Auth container to reach the Functions container + + +`host.docker.internal` is a special DNS name used in Docker to allow a container to access the host machine's network. This allows the Auth container to reach your HTTP function, no matter if it's a Supabase Edge Function or a custom endpoint. + + Fill in the Hook Secret in `supabase/functions/.env` -```bash -SEND_SMS_HOOK_SECRET='v1,whsec_' +```ini +SEND_SMS_HOOK_SECRETS='v1,whsec_' ``` Start the function locally: @@ -344,7 +350,7 @@ Outside of runtime errors, both HTTP Hooks and Postgres Hooks return timeout err ## Available Hooks -Each Hook description contains an example JSONSchema which you can use in conjunction with [JSONSchema Faker](https://json-schema-faker.js.org/) in order to generate a mock payload. For HTTP Hooks, you can also use [the Standard Webhooks Testing Tool](https://www.standardwebhooks.com/simulate) to simulate a request. +Each Hook description contains an example JSON Schema which you can use in conjunction with [JSON Schema Faker](https://json-schema-faker.js.org/) in order to generate a mock payload. For HTTP Hooks, you can also use [the Standard Webhooks Testing Tool](https://www.standardwebhooks.com/simulate) to simulate a request.
{[ diff --git a/apps/docs/content/guides/auth/auth-hooks/custom-access-token-hook.mdx b/apps/docs/content/guides/auth/auth-hooks/custom-access-token-hook.mdx index a689d23dd79..aff8766f938 100644 --- a/apps/docs/content/guides/auth/auth-hooks/custom-access-token-hook.mdx +++ b/apps/docs/content/guides/auth/auth-hooks/custom-access-token-hook.mdx @@ -431,7 +431,7 @@ import * as base64 from 'https://denopkg.com/chiefbiiko/base64/mod.ts' Deno.serve(async (req) => { const payload = await req.text() - const base64_secret = Deno.env.get('AUTH_CUSTOM_ACCESS_TOKEN_SECRET') + const base64_secret = Deno.env.get('CUSTOM_ACCESS_TOKEN_SECRET').replace('v1,whsec_', '') const headers = Object.fromEntries(req.headers) const wh = new Webhook(base64_secret) try { @@ -477,7 +477,7 @@ import * as base64 from 'https://denopkg.com/chiefbiiko/base64/mod.ts' Deno.serve(async (req) => { const payload = await req.text() - const base64_secret = Deno.env.get('AUTH_CUSTOM_ACCESS_TOKEN_SECRET') + const base64_secret = Deno.env.get('CUSTOM_ACCESS_TOKEN_SECRET').replace('v1,whsec_', '') const headers = Object.fromEntries(req.headers) const wh = new Webhook(base64_secret) try { diff --git a/apps/docs/content/guides/auth/auth-hooks/send-email-hook.mdx b/apps/docs/content/guides/auth/auth-hooks/send-email-hook.mdx index 8f68c3b9dd4..f7a08c4934d 100644 --- a/apps/docs/content/guides/auth/auth-hooks/send-email-hook.mdx +++ b/apps/docs/content/guides/auth/auth-hooks/send-email-hook.mdx @@ -282,7 +282,7 @@ Email sending depends on two settings: Email Provider and Auth Hook status. }, "email_action_type": { "type": "string", - "enum": ["signup", "login", "reset_password"] + "enum": ["signup", "invite", "magiclink", "recovery", "email_change", "email"] }, "site_url": { "type": "string", @@ -472,14 +472,14 @@ If you want to send emails through the Supabase Resend integration, which uses R Create a `.env` file with the following environment variables: -```bash -RESEND_API_KEY=your_resend_api_key -SEND_EMAIL_HOOK_SECRET= +```ini +RESEND_API_KEY="your_resend_api_key" +SEND_EMAIL_HOOK_SECRET="v1,whsec_" ``` - + -You can generate the secret in the [Auth Hooks](/dashboard/project/_/auth/hooks) section of the Supabase dashboard. Make sure to remove the `v1,whsec_` prefix! +You can generate the secret in the [Auth Hooks](/dashboard/project/_/auth/hooks) section of the Supabase dashboard. @@ -502,7 +502,7 @@ import { Webhook } from "https://esm.sh/standardwebhooks@1.0.0"; import { Resend } from "npm:resend"; const resend = new Resend(Deno.env.get("RESEND_API_KEY") as string); -const hookSecret = Deno.env.get("SEND_EMAIL_HOOK_SECRET") as string; +const hookSecret = (Deno.env.get("SEND_EMAIL_HOOK_SECRET") as string).replace("v1,whsec_", ""); Deno.serve(async (req) => { if (req.method !== "POST") { @@ -569,7 +569,7 @@ supabase functions deploy send-email --no-verify-jwt -Your company is expanding to France and Spain. As part of expansion efforts, the company would like to deliver internationalized email templates to best support local users in their native language. Ensure that you have configured `POSTMARK_SERVER_TOKEN` and `AUTH_SEND_EMAIL_HOOK_SECRET` in your `.env` file. +Your company is expanding to France and Spain. As part of expansion efforts, the company would like to deliver internationalized email templates to best support local users in their native language. Ensure that you have configured `POSTMARK_SERVER_TOKEN` and `SEND_EMAIL_HOOK_SECRET` in your `.env` file. ```javascript import { Webhook } from 'https://esm.sh/standardwebhooks@1.0.0' @@ -650,7 +650,7 @@ Deno.serve(async (req) => { const payload = await req.text() const serverToken = Deno.env.get('POSTMARK_SERVER_TOKEN') const headers = Object.fromEntries(req.headers) - const base64_secret = Deno.env.get('AUTH_SEND_EMAIL_HOOK_SECRET') + const base64_secret = Deno.env.get('SEND_EMAIL_HOOK_SECRET').replace('v1,whsec_', '') const wh = new Webhook(base64_secret) const { user, email_data } = wh.verify(payload, headers) @@ -717,7 +717,7 @@ Deno.serve(async (req) => { {/* Your company is rapidly growing and depends heavily on email signups. You'd like to configure a backup email provider in case the email provider runs out of credits during your new product launch. Postmark and Sengrid are used as examples but in practice you can use any email provider. -Ensure that you have configured `POSTMARK_SERVER_TOKEN`, `SENDGRID_API_KEY` and `AUTH_SEND_EMAIL_HOOK_SECRET` in your `.env` file. +Ensure that you have configured `POSTMARK_SERVER_TOKEN`, `SENDGRID_API_KEY` and `SEND_EMAIL_HOOK_SECRET` in your `.env` file. ```javascript import { @@ -829,7 +829,7 @@ Deno.serve(async (req) => { const postmarkServerToken = Deno.env.get("POSTMARK_SERVER_TOKEN"); const sendGridApiKey = Deno.env.get("SENDGRID_API_KEY"); const headers = Object.fromEntries(req.headers); - const base64_secret = Deno.env.get('AUTH_SEND_EMAIL_HOOK_SECRET'); + const base64_secret = Deno.env.get('SEND_EMAIL_HOOK_SECRET').replace('v1,whsec_', ''); const wh = new Webhook(base64_secret); const { user, diff --git a/apps/docs/content/guides/auth/auth-hooks/send-sms-hook.mdx b/apps/docs/content/guides/auth/auth-hooks/send-sms-hook.mdx index 3a9a27cc63f..6a0a06a76d8 100644 --- a/apps/docs/content/guides/auth/auth-hooks/send-sms-hook.mdx +++ b/apps/docs/content/guides/auth/auth-hooks/send-sms-hook.mdx @@ -8,7 +8,7 @@ Runs before a message is sent. Use the hook to: - Use a regional SMS Provider - Use alternate messaging channels such as WhatsApp -- Adjust the message body to include platform specific fields such as the [AppHash](https://developers.google.com/identity/sms-retriever/overview) +- Adjust the message body to include platform specific fields such as the [`AppHash`](https://developers.google.com/identity/sms-retriever/overview) **Inputs** @@ -464,7 +464,7 @@ const sendTextMessage = async ( Deno.serve(async (req) => { const payload = await req.text() - const base64_secret = Deno.env.get('SEND_SMS_HOOK_SECRET') + const base64_secret = Deno.env.get('SEND_SMS_HOOK_SECRET').replace('v1,whsec_', '') const headers = Object.fromEntries(req.headers) const wh = new Webhook(base64_secret) try { @@ -527,12 +527,8 @@ Deno.serve(async (req) => { Your company is expanding into Latin America and would like to use WhatsApp for higher deliverability. Write a hook to send WhatsApp messages to requests from the continent and SMS messages to all other numbers. ```javascript -import { - Webhook -} from "https://esm.sh/standardwebhooks@1.0.0"; -import { - readAll -} from "https://deno.land/std/io/read_all.ts"; +import { Webhook } from "https://esm.sh/standardwebhooks@1.0.0"; +import { readAll } from "https://deno.land/std/io/read_all.ts"; import * as base64 from "https://denopkg.com/chiefbiiko/base64/mod.ts"; const accountSid: string | undefined = Deno.env.get("TWILIO_ACCOUNT_SID"); @@ -580,7 +576,7 @@ const sendMessage = async ( Deno.serve(async (req) => { const payload = await req.text(); - const base64_secret = Deno.env.get("SEND_SMS_HOOK_SECRET"); + const base64_secret = Deno.env.get("SEND_SMS_HOOK_SECRET").replace('v1,whsec_', ''); const headers = Object.fromEntries(req.headers); const wh = new Webhook(base64_secret); try { diff --git a/apps/docs/content/guides/auth/auth-mfa.mdx b/apps/docs/content/guides/auth/auth-mfa.mdx index e2146375370..71dd4398b78 100644 --- a/apps/docs/content/guides/auth/auth-mfa.mdx +++ b/apps/docs/content/guides/auth/auth-mfa.mdx @@ -111,7 +111,7 @@ Below is an example that creates a new `UnenrollMFA` component that illustrates - When the component appears on screen, the `supabase.auth.mfa.listFactors()` endpoint fetches all existing factors together with their details. - The existing factors for a user are displayed in a table. -- Once the user has selected a factor to unenroll, they can type in the factorId and click **Unenroll** +- Once the user has selected a factor to unenroll, they can type in the `factorId` and click **Unenroll** which creates a confirmation modal. diff --git a/apps/docs/content/guides/auth/auth-mfa/phone.mdx b/apps/docs/content/guides/auth/auth-mfa/phone.mdx index e06b7e458cc..0c5916a7faa 100644 --- a/apps/docs/content/guides/auth/auth-mfa/phone.mdx +++ b/apps/docs/content/guides/auth/auth-mfa/phone.mdx @@ -21,8 +21,6 @@ Below is a flow chart illustrating how the Enrollment and Verify APIs work in th containerClassName="max-w-[700px]" /> -Phone MFA is part of the Auth Advanced MFA Add-on and costs an additional $75 per month for the first project in the organization and an additional $10 per month for additional projects. - ### Add enrollment flow An enrollment flow provides a UI for users to set up additional authentication factors. Most applications add the enrollment flow in two places within their app: @@ -311,3 +309,9 @@ function AuthMFA() { Each code is valid for up to 5 minutes, after which a new one can be sent. Successive codes remain valid until expiry. When possible choose the longest code length acceptable to your use case, at a minimum of 6. This can be configured in the [Authentication Settings](/dashboard/project/_/settings/auth). Please be aware that Phone MFA is vulnerable to SIM swap attacks where an attacker will call a mobile provider and ask to port the target's phone number to a new SIM card and then use the said SIM card to intercept an MFA code. Please evaluate the your application's tolerance for such an attack. You can read more about SIM swapping attacks [here](https://en.wikipedia.org/wiki/SIM_swap_scam) + +## Pricing + + + +For a detailed breakdown of how charges are calculated, refer to [Manage Advanced MFA Phone usage](/docs/guides/platform/manage-your-usage/advanced-mfa-phone). diff --git a/apps/docs/content/guides/auth/auth-smtp.mdx b/apps/docs/content/guides/auth/auth-smtp.mdx index cac7981f737..1d1558d3c18 100644 --- a/apps/docs/content/guides/auth/auth-smtp.mdx +++ b/apps/docs/content/guides/auth/auth-smtp.mdx @@ -63,7 +63,7 @@ They use lists of known email addresses to sign up users to your project with pr Usually the goal for this behavior is: - To negatively affect your email sending reputation, after which they might ask for a ransom promising to stop the behavior. -- To cause a short-term or even long-term Denial of Service attack on your service, by preventing new account creation, sign ins with magic links or one-time passwords, or to severely impact important security flows in your application (such as reset password or forgot password). +- To cause a short-term or even long-term Denial of Service attack on your service, by preventing new account creation, signins with magic links or one-time passwords, or to severely impact important security flows in your application (such as reset password or forgot password). - To force you to reduce the security posture of your project, such as by disabling email confirmations. At that point, they may target specific or a broad number of users by creating an account in their name. Then they can use social engineering techniques to trick them to use your application in such a way that both attacker and victim have access to the same account. Mitigation strategies: diff --git a/apps/docs/content/guides/auth/enterprise-sso/auth-sso-saml.mdx b/apps/docs/content/guides/auth/enterprise-sso/auth-sso-saml.mdx index ef9b8c8d573..9aa1f5a4e84 100644 --- a/apps/docs/content/guides/auth/enterprise-sso/auth-sso-saml.mdx +++ b/apps/docs/content/guides/auth/enterprise-sso/auth-sso-saml.mdx @@ -5,7 +5,7 @@ description: 'Use Single Sign-On (SSO) authentication on your project with SAML video: 'https://www.youtube.com/v/em1cpOAXknM' --- - + Looking for guides on how to use Single Sign-On with the Supabase dashboard? Head on over to [Enable SSO for Your Organization](/docs/guides/platform/sso). @@ -13,7 +13,7 @@ Looking for guides on how to use Single Sign-On with the Supabase dashboard? Hea Supabase Auth supports enterprise-level Single Sign-On (SSO) for any identity providers compatible with the SAML 2.0 protocol. This is a non-exclusive list of supported identity providers: -- Google Workspaces (formerly known as GSuite) +- Google Workspaces (formerly known as G Suite) - Okta, Auth0 - Microsoft Active Directory, Azure Active Directory, Microsoft Entra - PingIdentity @@ -36,14 +36,14 @@ Please note that SAML 2.0 support is offered on plans Pro and above. Check the [ The number of SAML and SSO acronyms can often be overwhelming. Here's a glossary which you can refer back to at any time: - **Identity Provider**, **IdP**, or **IDP** - An identity provider is a service that manages user accounts at a company or organization. It can verify the identity of a user and exchange that information with your Supabase project and other applications. It acts as a single source of truth for user identities and access rights. Commonly used identity providers are: Microsoft Active Directory (Azure AD, Microsoft Entra), Okta, Google Workspaces (GSuite), PingIdentity, OneLogin, and many others. There are also self-hosted and on-prem versions of identity providers, and sometimes they are accessible only by having access to a company VPN or being in a specific building. + An identity provider is a service that manages user accounts at a company or organization. It can verify the identity of a user and exchange that information with your Supabase project and other applications. It acts as a single source of truth for user identities and access rights. Commonly used identity providers are: Microsoft Active Directory (Azure AD, Microsoft Entra), Okta, Google Workspaces (G Suite), PingIdentity, OneLogin, and many others. There are also self-hosted and on-prem versions of identity providers, and sometimes they are accessible only by having access to a company VPN or being in a specific building. - **Service Provider**, **SP** This is the software that is asking for user information from an identity provider. In Supabase, this is your project's Auth server. - **Assertion** An assertion is a statement issued by an identity provider that contains information about a user. -- **EntityID** +- **`EntityID`** A globally unique ID (usually a URL) that identifies an Identity Provider or Service Provider across the world. -- **NameID** +- **`NameID`** A unique ID (usually an email address) that identifies a user at an Identity Provider. - **Metadata** An XML document that describes the features and configuration of an Identity Provider or Service Provider. It can be as a standalone document or as a URL. Usually (but not always) the `EntityID` is the URL at which you can access the Metadata. @@ -53,7 +53,7 @@ The number of SAML and SSO acronyms can often be overwhelming. Here's a glossary This is one of the most important SAML URLs. It is the URL where Supabase Auth will accept assertions from an identity provider. Basically, once the identity provider verifies the user's identity it will redirect to this URL and the redirect request will contain the assertion. - **Binding (Redirect, POST, or Artifact)** This is a description of the way an identity provider communicates with Supabase Auth. When using the Redirect binding, the communication occurs using HTTP 301 redirects. When it's `POST`, it's using `POST` requests sent with `
` elements on a page. When using Artifact, it's using a more secure exchange over a Redirect or `POST`. -- **RelayState** +- **`RelayState`** State used by Supabase Auth to hold information about a request to verify the identity of a user. ## Important SAML 2.0 information @@ -62,12 +62,12 @@ Below is information about your project's SAML 2.0 configuration which you can s | Name | Value | | --------------------------- | ----------------------------------------------------------------------- | -| EntityID | `https://.supabase.co/auth/v1/sso/saml/metadata` | +| `EntityID` | `https://.supabase.co/auth/v1/sso/saml/metadata` | | Metadata URL | `https://.supabase.co/auth/v1/sso/saml/metadata` | | Metadata URL
(download) | `https://.supabase.co/auth/v1/sso/saml/metadata?download=true` | | ACS URL | `https://.supabase.co/auth/v1/sso/saml/acs` | | SLO URL | `https://.supabase.co/auth/v1/sso/slo` | -| NameID | Required `emailAddress` or `persistent` | +| `NameID` | Required `emailAddress` or `persistent` | Note that SLO (Single Logout) is not supported at this time with Supabase Auth as it is a rarely supported feature by identity providers. However, the URL is registered and advertised for when this does become available. @@ -155,7 +155,7 @@ Commonly used SAML 2.0 Identity Providers that support Metadata URLs: Commonly used SAML 2.0 Identity Providers that only support Metadata XML files: -- Google Workspaces (GSuite) +- Google Workspaces (G Suite) - Any self-hosted or on-prem identity provider behind a VPN Once you've obtained the SAML 2.0 Metadata XML file or URL you can [establish a connection](/docs/reference/cli/supabase-sso-add) with your project's Supabase Auth server by running: @@ -323,7 +323,7 @@ Most SAML 2.0 identity providers use Lightweight Directory Access Protocol (LDAP **Accessing the stored attributes** -The stored attributes, once mapped, show up in the access token (a JWT) of the user. If you need to look these values up in the database, you can find them in the `auth.identities` table under the `identity_data` JSON column. Identities created for SSO providers have `sso:` in the `provider` column, while `id` contains the unique NameID of the user account. +The stored attributes, once mapped, show up in the access token (a JWT) of the user. If you need to look these values up in the database, you can find them in the `auth.identities` table under the `identity_data` JSON column. Identities created for SSO providers have `sso:` in the `provider` column, while `id` contains the unique `NameID` of the user account. Furthermore, you can find the same identity data under `raw_app_meta_data` inside `auth.users`. @@ -382,6 +382,12 @@ supabase sso show --project-ref You can use the `-o json` flag to output the information as JSON, should you need to. Other formats may be supported, please use `--help` to see all available options. +## Pricing + + + +For a detailed breakdown of how charges are calculated, refer to [Manage Monthly Active SSO Users usage](/docs/guides/platform/manage-your-usage/monthly-active-users-sso). + ## Frequently asked questions ### How do I publish my application to an identity provider's marketplace? diff --git a/apps/docs/content/guides/auth/jwts.mdx b/apps/docs/content/guides/auth/jwts.mdx index c75509646d2..bbd6857349b 100644 --- a/apps/docs/content/guides/auth/jwts.mdx +++ b/apps/docs/content/guides/auth/jwts.mdx @@ -41,7 +41,7 @@ The JSON object starts out looking something like this: Just note that the more data you store in your token, the longer the encoded string will be. -When we want to send the JWT to the user, we first encode the data using an algorithm such as `HS256`. There are many libraries (and several different algorithms) that can be used to do this encoding/decoding, such as [jsonwebtoken](https://www.npmjs.com/package/jsonwebtoken). The signing is as simple as: +When we want to send the JWT to the user, we first encode the data using an algorithm such as `HS256`. There are many libraries (and several different algorithms) that can be used to do this encoding/decoding, such as [`jsonwebtoken`](https://www.npmjs.com/package/jsonwebtoken). The signing is as simple as: ```js // from https://replit.com/@awalias/jsonwebtokens#index.js diff --git a/apps/docs/content/guides/auth/native-mobile-deep-linking.mdx b/apps/docs/content/guides/auth/native-mobile-deep-linking.mdx index ba96b451495..18d8d01a937 100644 --- a/apps/docs/content/guides/auth/native-mobile-deep-linking.mdx +++ b/apps/docs/content/guides/auth/native-mobile-deep-linking.mdx @@ -113,14 +113,14 @@ With Deep Linking, you can configure this redirect to open a specific page. This - // Currently supabase_flutter supports deep links on Android, iOS, Web, MacOS and Windows. + // Currently supabase_flutter supports deep links on Android, iOS, Web, macOS and Windows. ### Deep link config - Go to your [auth settings](https://supabase.com/dashboard/project/_/auth/url-configuration) page. - You need to enter your app redirect callback on `Additional Redirect URLs` field. - The redirect callback url should have this format `[YOUR_SCHEME]://[YOUR_HOSTNAME]`. Here, `io.supabase.flutterquickstart://login-callback` is just an example, you can choose whatever you would like for `YOUR_SCHEME` and `YOUR_HOSTNAME` as long as the scheme is unique across the user's device. For this reason, typically a reverse domain of your website is used. + The redirect callback URL should have this format `[YOUR_SCHEME]://[YOUR_HOSTNAME]`. Here, `io.supabase.flutterquickstart://login-callback` is just an example, you can choose whatever you would like for `YOUR_SCHEME` and `YOUR_HOSTNAME` as long as the scheme is unique across the user's device. For this reason, typically a reverse domain of your website is used. ![Supabase console deep link setting](/docs/img/deeplink-setting.png) @@ -269,11 +269,10 @@ With Deep Linking, you can configure this redirect to open a specific page. This You can achieve it with [url_protocol](https://pub.dev/packages/url_protocol) inside you app. - The most relevant solution is to include those registry modifications into your installer to allow - for deregistration. + The most relevant solution is to include those registry modifications into your installer to allow for deregistration. - + Add this XML chapter in your `macos/Runner/Info.plist` inside `` chapter: @@ -311,7 +310,7 @@ With Deep Linking, you can configure this redirect to open a specific page. This 1. Go to your [auth settings](https://supabase.com/dashboard/project/_/auth/url-configuration) page. 2. Enter your app redirect URL in the `Additional Redirect URLs` field. This is the URL that the user gets redirected to after clicking a magic link. - The redirect callback url should have the format `[YOUR_SCHEME]://[YOUR_HOSTNAME]`. Here, `io.supabase.user-management://login-callback` is just an example. You can choose whatever you would like for `YOUR_SCHEME` and `YOUR_HOSTNAME` as long as the scheme is unique across the user's device. For this reason, typically a reverse domain of your website is used. + The redirect callback URL should have the format `[YOUR_SCHEME]://[YOUR_HOSTNAME]`. Here, `io.supabase.user-management://login-callback` is just an example. You can choose whatever you would like for `YOUR_SCHEME` and `YOUR_HOSTNAME` as long as the scheme is unique across the user's device. For this reason, typically a reverse domain of your website is used. ![Supabase console deep link setting](/docs/img/deeplink-setting.png) @@ -349,7 +348,7 @@ With Deep Linking, you can configure this redirect to open a specific page. This 1. Go to your [auth settings](https://supabase.com/dashboard/project/_/auth/url-configuration) page. 2. Enter your app redirect URL in the `Additional Redirect URLs` field. This is the URL that the user gets redirected to after clicking a magic link. - The redirect callback url should have the format `[YOUR_SCHEME]://[YOUR_HOSTNAME]`. Here, `io.supabase.user-management://login-callback` is just an example. You can choose whatever you would like for `YOUR_SCHEME` and `YOUR_HOSTNAME` as long as the scheme is unique across the user's device. For this reason, typically a reverse domain of your website is used. + The redirect callback URL should have the format `[YOUR_SCHEME]://[YOUR_HOSTNAME]`. Here, `io.supabase.user-management://login-callback` is just an example. You can choose whatever you would like for `YOUR_SCHEME` and `YOUR_HOSTNAME` as long as the scheme is unique across the user's device. For this reason, typically a reverse domain of your website is used. Now, edit the Android manifest to make sure the app opens when the user clicks on the magic link. @@ -394,7 +393,7 @@ With Deep Linking, you can configure this redirect to open a specific page. This } ``` - The user will now be authenticated when your app receives a valid deeplink! + The user will now be authenticated when your app receives a valid deep link! diff --git a/apps/docs/content/guides/auth/passwords.mdx b/apps/docs/content/guides/auth/passwords.mdx index c7115044276..9606e651da7 100644 --- a/apps/docs/content/guides/auth/passwords.mdx +++ b/apps/docs/content/guides/auth/passwords.mdx @@ -465,7 +465,7 @@ data = supabase.auth.sign_up({ > -When your user signs in, call [signInWithPassword()](/docs/reference/javascript/auth-signinwithpassword) with their email address and password: +When your user signs in, call [`signInWithPassword()`](/docs/reference/javascript/auth-signinwithpassword) with their email address and password: ```js async function signInWithEmail() { @@ -479,7 +479,7 @@ async function signInWithEmail() { -When your user signs in, call [signInWithPassword()](/docs/reference/dart/auth-signinwithpassword) with their email address and password: +When your user signs in, call [`signInWithPassword()`](/docs/reference/dart/auth-signinwithpassword) with their email address and password: ```dart Future signInWithEmail() async { @@ -576,7 +576,7 @@ try await supabase.auth.resetPasswordForEmail( ```kotlin -supabase.gotrue.sendRecoveryEmail( +supabase.auth.resetPasswordForEmail( email = "valid.email@supabase.io", redirectUrl = "http://example.com/account/update-password" ) diff --git a/apps/docs/content/guides/auth/phone-login.mdx b/apps/docs/content/guides/auth/phone-login.mdx index f0b72198d94..e8baaf31ab3 100644 --- a/apps/docs/content/guides/auth/phone-login.mdx +++ b/apps/docs/content/guides/auth/phone-login.mdx @@ -5,7 +5,7 @@ description: 'Learn about logging in to your platform using SMS one-time passwor Phone Login is a method of authentication that allows users to log in to a website or application without using a password. The user authenticates through a one-time password (OTP) sent via a channel (SMS or WhatsApp). - + At this time, `WhatsApp` is only supported as a channel for the Twilio and Twilio Verify Providers. diff --git a/apps/docs/content/guides/auth/quickstarts/nextjs.mdx b/apps/docs/content/guides/auth/quickstarts/nextjs.mdx index 6db4230d2ac..9bb001cb9af 100644 --- a/apps/docs/content/guides/auth/quickstarts/nextjs.mdx +++ b/apps/docs/content/guides/auth/quickstarts/nextjs.mdx @@ -72,7 +72,7 @@ hideToc: true Start the development server, go to http://localhost:3000 in a browser, and you should see the contents of `app/page.tsx`. - To sign up a new user, navigate to http://localhost:3000/login, and click `Sign Up Now`. + To sign up a new user, navigate to http://localhost:3000/sign-up, and click `Sign up`. *NOTE: .env.example must be renamed to .env.local before this route becomes available* diff --git a/apps/docs/content/guides/auth/server-side/creating-a-client.mdx b/apps/docs/content/guides/auth/server-side/creating-a-client.mdx index bc43bb8ba16..10a38de1292 100644 --- a/apps/docs/content/guides/auth/server-side/creating-a-client.mdx +++ b/apps/docs/content/guides/auth/server-side/creating-a-client.mdx @@ -376,7 +376,7 @@ export const handle: Handle = async ({ event, resolve }) => { -Page components can get access to the supabase client from the `data` object due to this load function. +Page components can get access to the Supabase client from the `data` object due to this load function. ```ts +layout.ts import { PUBLIC_SUPABASE_ANON_KEY, PUBLIC_SUPABASE_URL } from '$env/static/public' diff --git a/apps/docs/content/guides/auth/server-side/sveltekit.mdx b/apps/docs/content/guides/auth/server-side/sveltekit.mdx index c7c6fecebaf..303eae39ee8 100644 --- a/apps/docs/content/guides/auth/server-side/sveltekit.mdx +++ b/apps/docs/content/guides/auth/server-side/sveltekit.mdx @@ -163,12 +163,13 @@ To prevent TypeScript errors, add type definitions for the new `event.locals` pr ```ts src/app.d.ts import type { Session, SupabaseClient, User } from '@supabase/supabase-js' +import type { Database } from './database.types.ts' // import generated types declare global { namespace App { // interface Error {} interface Locals { - supabase: SupabaseClient + supabase: SupabaseClient safeGetSession: () => Promise<{ session: Session | null; user: User | null }> session: Session | null user: User | null diff --git a/apps/docs/content/guides/auth/social-login/auth-apple.mdx b/apps/docs/content/guides/auth/social-login/auth-apple.mdx index 5463db69e7d..2d9223f0010 100644 --- a/apps/docs/content/guides/auth/social-login/auth-apple.mdx +++ b/apps/docs/content/guides/auth/social-login/auth-apple.mdx @@ -63,7 +63,7 @@ When developing with Expo, you can test Sign in with Apple via the Expo Go app, 6. Create a signing **Key** in the [Keys](https://developer.apple.com/account/resources/authkeys/list) section of the Apple Developer Console. You can use this key to generate a secret key using the tool below, which is added to your Supabase project's Auth configuration. Make sure you safely store the `AuthKey_XXXXXXXXXX.p8` file. If you ever lose access to it, or make it public accidentally please revoke it from the Apple Developer Console and create a new one immediately. You will have to generate a new secret key using this file every 6 months, so make sure you schedule a recurring meeting in your calendar! 7. Finally, add the information you configured above to the [Apple provider configuration in the Supabase dashboard](https://supabase.com/dashboard/project/_/auth/providers). - + Use this tool to generate a new Apple client secret. No keys leave your browser! Be aware that this tool does not currently work in Safari, so please use Firefox or a Chrome-based browser instead. @@ -280,7 +280,7 @@ When developing with Expo, you can test Sign in with Apple via the Expo Go app, 6. Create a signing **Key** in the [Keys](https://developer.apple.com/account/resources/authkeys/list) section of the Apple Developer Console. You can use this key to generate a secret key using the tool below, which is added to your Supabase project's Auth configuration. Make sure you safely store the `AuthKey_XXXXXXXXXX.p8` file. If you ever lose access to it, or make it public accidentally please revoke it from the Apple Developer Console and create a new one immediately. You will have to generate a new secret key using this file every 6 months, so make sure you schedule a recurring reminder in your calendar! 7. Finally, add the information you configured above to the [Apple provider configuration in the Supabase dashboard](https://supabase.com/dashboard/project/_/auth/providers). - + Use this tool to generate a new Apple client secret. No keys leave your browser! Be aware that this tool does not currently work in Safari, so please use Firefox or a Chrome-based browser instead. @@ -348,12 +348,12 @@ When developing with Expo, you can test Sign in with Apple via the Expo Go app, - + - + ## Using native sign in with Apple in Kotlin - - When using [Compose Multiplatform](https://github.com/JetBrains/compose-multiplatform/), you can use the [compose-auth](https://supabase.com/docs/reference/kotlin/installing) plugin. On iOS it uses Native Apple Login automatically and on other platforms it uses `gotrue.signInWith(Apple)`. + + When using [Compose Multiplatform](https://github.com/JetBrains/compose-multiplatform/), you can use the [compose-auth](https://supabase.com/docs/reference/kotlin/installing) plugin. On iOS it uses Native Apple Login automatically and on other platforms it uses `gotrue.signInWith(Apple)`. **Initialize the Supabase Client** diff --git a/apps/docs/content/guides/auth/social-login/auth-azure.mdx b/apps/docs/content/guides/auth/social-login/auth-azure.mdx index 6e9161a7d7e..f31c269492a 100644 --- a/apps/docs/content/guides/auth/social-login/auth-azure.mdx +++ b/apps/docs/content/guides/auth/social-login/auth-azure.mdx @@ -126,7 +126,7 @@ Supabase Auth requires that Azure returns a valid email address. Therefore you m -When your user signs in, call [signInWithOAuth()](/docs/reference/javascript/auth-signinwithoauth) with `azure` as the `provider`: +When your user signs in, call [`signInWithOAuth()`](/docs/reference/javascript/auth-signinwithoauth) with `azure` as the `provider`: ```js async function signInWithAzure() { @@ -142,7 +142,7 @@ async function signInWithAzure() { -When your user signs in, call [signInWithOAuth()](/docs/reference/dart/auth-signinwithoauth) with `azure` as the `provider`: +When your user signs in, call [`signInWithOAuth()`](/docs/reference/dart/auth-signinwithoauth) with `azure` as the `provider`: ```dart Future signInWithAzure() async { diff --git a/apps/docs/content/guides/auth/social-login/auth-bitbucket.mdx b/apps/docs/content/guides/auth/social-login/auth-bitbucket.mdx index 7c3d394a00c..a690d3cf9aa 100644 --- a/apps/docs/content/guides/auth/social-login/auth-bitbucket.mdx +++ b/apps/docs/content/guides/auth/social-login/auth-bitbucket.mdx @@ -4,7 +4,7 @@ title: 'Login with Bitbucket' description: 'Add Bitbucket OAuth to your Supabase project' --- -To enable Bitbucket Auth for your project, you need to set up a BitBucket OAuth application and add the application credentials to your Supabase Dashboard. +To enable Bitbucket Auth for your project, you need to set up a Bitbucket OAuth application and add the application credentials to your Supabase Dashboard. ## Overview @@ -57,7 +57,7 @@ Setting up Bitbucket logins for your application consists of 3 parts: -When your user signs in, call [signInWithOAuth()](/docs/reference/javascript/auth-signinwithoauth) with `bitbucket` as the `provider`: +When your user signs in, call [`signInWithOAuth()`](/docs/reference/javascript/auth-signinwithoauth) with `bitbucket` as the `provider`: ```js async function signInWithBitbucket() { @@ -70,7 +70,7 @@ async function signInWithBitbucket() { -When your user signs in, call [signInWithOAuth()](/docs/reference/dart/auth-signinwithoauth) with `bitbucket` as the `provider`: +When your user signs in, call [`signInWithOAuth()`](/docs/reference/dart/auth-signinwithoauth) with `bitbucket` as the `provider`: ```dart Future signInWithBitbucket() async { diff --git a/apps/docs/content/guides/auth/social-login/auth-discord.mdx b/apps/docs/content/guides/auth/social-login/auth-discord.mdx index 6dd8161b465..9712a6c76ae 100644 --- a/apps/docs/content/guides/auth/social-login/auth-discord.mdx +++ b/apps/docs/content/guides/auth/social-login/auth-discord.mdx @@ -56,7 +56,7 @@ Setting up Discord logins for your application consists of 3 parts: -When your user signs in, call [signInWithOAuth()](/docs/reference/javascript/auth-signinwithoauth) with `discord` as the `provider`: +When your user signs in, call [`signInWithOAuth()`](/docs/reference/javascript/auth-signinwithoauth) with `discord` as the `provider`: ```js async function signInWithDiscord() { @@ -69,7 +69,7 @@ async function signInWithDiscord() { -When your user signs in, call [signInWithOAuth()](/docs/reference/dart/auth-signinwithoauth) with `discord` as the `provider`: +When your user signs in, call [`signInWithOAuth()`](/docs/reference/dart/auth-signinwithoauth) with `discord` as the `provider`: ```dart Future signInWithDiscord() async { diff --git a/apps/docs/content/guides/auth/social-login/auth-facebook.mdx b/apps/docs/content/guides/auth/social-login/auth-facebook.mdx index 420bf7511d0..419533140e0 100644 --- a/apps/docs/content/guides/auth/social-login/auth-facebook.mdx +++ b/apps/docs/content/guides/auth/social-login/auth-facebook.mdx @@ -73,7 +73,7 @@ Under `Build Your App`, click on `Use Cases` screen. From there, do the followin -When your user signs in, call [signInWithOAuth()](/docs/reference/javascript/auth-signinwithoauth) with `facebook` as the `provider`: +When your user signs in, call [`signInWithOAuth()`](/docs/reference/javascript/auth-signinwithoauth) with `facebook` as the `provider`: ```js async function signInWithFacebook() { @@ -86,7 +86,7 @@ async function signInWithFacebook() { -When your user signs in, call [signInWithOAuth()](/docs/reference/dart/auth-signinwithoauth) with `facebook` as the `provider`: +When your user signs in, call [`signInWithOAuth()`](/docs/reference/dart/auth-signinwithoauth) with `facebook` as the `provider`: ```dart Future signInWithFacebook() async { @@ -102,7 +102,7 @@ Future signInWithFacebook() async { -When your user signs in, call [signInWithOAuth()](/docs/reference/swift/auth-signinwithoauth) with `facebook` as the `provider`: +When your user signs in, call [`signInWithOAuth()`](/docs/reference/swift/auth-signinwithoauth) with `facebook` as the `provider`: ```swift func signInWithFacebook() async throws { diff --git a/apps/docs/content/guides/auth/social-login/auth-figma.mdx b/apps/docs/content/guides/auth/social-login/auth-figma.mdx index ea3f979930a..fc5b9859acc 100644 --- a/apps/docs/content/guides/auth/social-login/auth-figma.mdx +++ b/apps/docs/content/guides/auth/social-login/auth-figma.mdx @@ -57,7 +57,7 @@ Setting up Figma logins for your application consists of 3 parts: -When your user signs in, call [signInWithOAuth()](/docs/reference/javascript/auth-signinwithoauth) with `figma` as the `provider`: +When your user signs in, call [`signInWithOAuth()`](/docs/reference/javascript/auth-signinwithoauth) with `figma` as the `provider`: ```js async function signInWithFigma() { @@ -70,7 +70,7 @@ async function signInWithFigma() { -When your user signs in, call [signInWithOAuth()](/docs/reference/flutter/auth-signinwithoauth) with `figma` as the `provider`: +When your user signs in, call [`signInWithOAuth()`](/docs/reference/flutter/auth-signinwithoauth) with `figma` as the `provider`: ```dart Future signInWithFigma() async { diff --git a/apps/docs/content/guides/auth/social-login/auth-github.mdx b/apps/docs/content/guides/auth/social-login/auth-github.mdx index 715b2a7b98c..8b86db5c659 100644 --- a/apps/docs/content/guides/auth/social-login/auth-github.mdx +++ b/apps/docs/content/guides/auth/social-login/auth-github.mdx @@ -51,7 +51,7 @@ Copy your new OAuth credentials -When your user signs in, call [signInWithOAuth()](/docs/reference/javascript/auth-signinwithoauth) with `github` as the `provider`: +When your user signs in, call [`signInWithOAuth()`](/docs/reference/javascript/auth-signinwithoauth) with `github` as the `provider`: ```js async function signInWithGithub() { @@ -64,7 +64,7 @@ async function signInWithGithub() { -When your user signs in, call [signInWithOAuth()](/docs/reference/dart/auth-signinwithoauth) with `github` as the `provider`: +When your user signs in, call [`signInWithOAuth()`](/docs/reference/dart/auth-signinwithoauth) with `github` as the `provider`: ```dart Future signInWithGithub() async { @@ -80,7 +80,7 @@ Future signInWithGithub() async { -When your user signs in, call [signInWithOAuth](/docs/reference/swift/auth-signinwithoauth) with `.github` as the `Provider`: +When your user signs in, call [`signInWithOAuth`](/docs/reference/swift/auth-signinwithoauth) with `.github` as the `Provider`: ```swift func signInWithGithub() async throws { diff --git a/apps/docs/content/guides/auth/social-login/auth-gitlab.mdx b/apps/docs/content/guides/auth/social-login/auth-gitlab.mdx index 067bbaeab92..7b68d6a8b84 100644 --- a/apps/docs/content/guides/auth/social-login/auth-gitlab.mdx +++ b/apps/docs/content/guides/auth/social-login/auth-gitlab.mdx @@ -54,7 +54,7 @@ Setting up GitLab logins for your application consists of 3 parts: -When your user signs in, call [signInWithOAuth()](/docs/reference/javascript/auth-signinwithoauth) with `gitlab` as the `provider`: +When your user signs in, call [`signInWithOAuth()`](/docs/reference/javascript/auth-signinwithoauth) with `gitlab` as the `provider`: ```js async function signInWithGitLab() { @@ -67,7 +67,7 @@ async function signInWithGitLab() { -When your user signs in, call [signInWithOAuth()](/docs/reference/dart/auth-signinwithoauth) with `gitlab` as the `provider`: +When your user signs in, call [`signInWithOAuth()`](/docs/reference/dart/auth-signinwithoauth) with `gitlab` as the `provider`: ```dart Future signInWithGitLab() async { diff --git a/apps/docs/content/guides/auth/social-login/auth-google.mdx b/apps/docs/content/guides/auth/social-login/auth-google.mdx index f9c814c232a..ca2f562edef 100644 --- a/apps/docs/content/guides/auth/social-login/auth-google.mdx +++ b/apps/docs/content/guides/auth/social-login/auth-google.mdx @@ -27,7 +27,7 @@ To support Sign In with Google, you need to configure the Google provider for yo For web applications, you can set up your signin button two different ways: - Use your own [application code](#application-code) for the button. -- Use [Google's pre-built sign-in or OneTap flows](#google-pre-built). +- Use [Google's pre-built sign-in or One Tap flows](#google-pre-built). ### Application code configuration @@ -78,7 +78,7 @@ To use Google's pre-built signin buttons: 1. Configure OAuth credentials for your Google Cloud project in the [Credentials](https://console.cloud.google.com/apis/credentials) page of the console. When creating a new OAuth client ID, choose _Android_ or _iOS_ depending on the mobile operating system your app is built for. - For Android, use the instructions on screen to provide the SHA-1 certificate fingerprint used to sign your Android app. - You will have a different set of SHA-1 certificate fingerprint for testing locally and going to production. Make sure to add both to the Google Cloud Console. and add all of the Client IDs to Supabase dashboard. - - For iOS, use the instructions on screen to provide the app Bundle ID, and App Store ID and Team ID if the app is already published on the Apple AppStore. + - For iOS, use the instructions on screen to provide the app Bundle ID, and App Store ID and Team ID if the app is already published on the Apple App Store. 2. Configure the [OAuth Consent Screen](https://console.cloud.google.com/apis/credentials/consent). This information is shown to the user when giving consent to your app. In particular, make sure you have set up links to your app's privacy policy and terms of service. 3. Finally, add the client ID from step 1 in the [Google provider on the Supabase Dashboard](https://supabase.com/dashboard/project/_/auth/providers), under _Client IDs_. @@ -94,7 +94,7 @@ To use Google's pre-built signin buttons: - For both Android and iOS, you will need to create a Web client ID in the [Google Cloud Console](https://console.cloud.google.com/apis/credentials). - For Android, use the instructions on screen to provide the SHA-1 certificate fingerprint used to sign your Android app. - You will have a different set of SHA-1 certificate fingerprint for testing locally and going to production. Make sure to add both to the Google Cloud Console. and add all of the Client IDs to Supabase dashboard. - - For iOS, use the instructions on screen to provide the app Bundle ID, and App Store ID and Team ID if the app is already published on the Apple AppStore. + - For iOS, use the instructions on screen to provide the app Bundle ID, and App Store ID and Team ID if the app is already published on the Apple App Store. 2. Configure the [OAuth Consent Screen](https://console.cloud.google.com/apis/credentials/consent). This information is shown to the user when giving consent to your app. In particular, make sure you have set up links to your app's privacy policy and terms of service. 3. Add only the web client ID from step 1 in the [Google provider on the Supabase Dashboard](https://supabase.com/dashboard/project/_/auth/providers), under _Client IDs_. If you need iOS support, enable the `Skip nonce check` option. 4. For iOS, add the `CFBundleURLTypes` attributes below into the `/ios/Runner/Info.plist` file. @@ -129,7 +129,7 @@ To use Google's pre-built signin buttons: - Google sign-in with Supabase is done through the [GoogleSignIn-iOS](https://github.com/google/GoogleSignIn-iOS) package. + Google sign-in with Supabase is done through the [`GoogleSignIn-iOS`](https://github.com/google/GoogleSignIn-iOS) package. When the user provides consent, Google issues an identity token (commonly abbreviated as ID token) that is then sent to your project's Supabase Auth server. When valid, a new user session is started by issuing an access and refresh token from Supabase Auth. @@ -187,7 +187,7 @@ To use Google's pre-built signin buttons: 1. Configure OAuth credentials for your Google Cloud project in the [Credentials](https://console.cloud.google.com/apis/credentials) page of the console. When creating a new OAuth client ID, choose _Android_ or _iOS_ depending on the mobile operating system your app is built for. - For Android, use the instructions on screen to provide the SHA-1 certificate fingerprint used to sign your Android app. - - For iOS, use the instructions on screen to provide the app Bundle ID, and App Store ID and Team ID if the app is already published on the Apple AppStore. + - For iOS, use the instructions on screen to provide the app Bundle ID, and App Store ID and Team ID if the app is already published on the Apple App Store. 2. Configure the [OAuth Consent Screen](https://console.cloud.google.com/apis/credentials/consent). This information is shown to the user when giving consent to your app. In particular, make sure you have set up links to your app's privacy policy and terms of service. 3. Finally, add the client ID from step 1 in the [Google provider on the Supabase Dashboard](https://supabase.com/dashboard/project/_/auth/providers), under _Client IDs_. @@ -548,7 +548,7 @@ Future _nativeGoogleSignIn() async { ### Web, macOS, Windows, and Linux -Google sign-in with Supabase on Web, macOS, Windows, and Linux is done through the [signInWithOAuth](docs/reference/dart/auth-signinwithoauth) method. +Google sign-in with Supabase on Web, macOS, Windows, and Linux is done through the [`signInWithOAuth`](docs/reference/dart/auth-signinwithoauth) method. This method of signing in is web based, and will open a browser window to perform the sign in. For non-web platforms, the user is brought back to the app via [deep linking](/docs/guides/auth/native-mobile-deep-linking?platform=flutter). @@ -595,7 +595,7 @@ implementation ("com.google.android.libraries.identity.googleid:googleid:") ``` -Add the following proguard rules to your `proguard-rules.pro` file: +Add the following ProGuard rules to your `proguard-rules.pro` file: ```proguard -if class androidx.credentials.CredentialManager diff --git a/apps/docs/content/guides/auth/social-login/auth-kakao.mdx b/apps/docs/content/guides/auth/social-login/auth-kakao.mdx index ca54ed737dc..9b678996d95 100644 --- a/apps/docs/content/guides/auth/social-login/auth-kakao.mdx +++ b/apps/docs/content/guides/auth/social-login/auth-kakao.mdx @@ -52,7 +52,7 @@ This will serve as the `client_id` when you make API calls to authenticate the u - Go to `Product settings` > `Kakao Login` > `Security`. - Click on the `Kakao Login` switch to enable Kakao Login. -- Click on `generate code` at the bottom to generate the `Client secret code` -- this will serve as a `client_secret` for your supabase project. +- Click on `generate code` at the bottom to generate the `Client secret code` -- this will serve as a `client_secret` for your Supabase project. - Make sure you enabled `Client secret code` by selecting `enable` from the `Activation state` section. ## Additional configurations on Kakao Developers portal @@ -79,7 +79,7 @@ This will serve as the `client_id` when you make API calls to authenticate the u -When your user signs in, call [signInWithOAuth()](/docs/reference/javascript/auth-signinwithoauth) with `kakao` as the `provider`: +When your user signs in, call [`signInWithOAuth()`](/docs/reference/javascript/auth-signinwithoauth) with `kakao` as the `provider`: ```js async function signInWithKakao() { @@ -92,7 +92,7 @@ async function signInWithKakao() { -When your user signs in, call [signInWithOAuth()](/docs/reference/dart/auth-signinwithoauth) with `kakao` as the `provider`: +When your user signs in, call [`signInWithOAuth()`](/docs/reference/dart/auth-signinwithoauth) with `kakao` as the `provider`: ```dart Future signInWithKakao() async { diff --git a/apps/docs/content/guides/auth/social-login/auth-keycloak.mdx b/apps/docs/content/guides/auth/social-login/auth-keycloak.mdx index 06750243cb2..43b21bac65e 100644 --- a/apps/docs/content/guides/auth/social-login/auth-keycloak.mdx +++ b/apps/docs/content/guides/auth/social-login/auth-keycloak.mdx @@ -10,16 +10,16 @@ To enable Keycloak Auth for your project, you need to set up an Keycloak OAuth a To get started with Keycloak, you can run it in a docker container with: `docker run -p 8080:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin quay.io/keycloak/keycloak:latest start-dev` -This guide will be assuming that you are running keycloak in a docker container as described in the command above. +This guide will be assuming that you are running Keycloak in a docker container as described in the command above. Keycloak OAuth consists of five broad steps: -- Create a new client in your specified keycloak realm. +- Create a new client in your specified Keycloak realm. - Obtain the `issuer` from the "OpenID Endpoint Configuration". This will be used as the `Keycloak URL`. -- Ensure that the new client has the "Client Protocol" set to "openid-connect" and the "Access Type" is set to "confidential". +- Ensure that the new client has the "Client Protocol" set to `openid-connect` and the "Access Type" is set to "confidential". - The `Client ID` of the client created will be used as the `client id`. - Obtain the `Secret` from the credentials tab which will be used as the `client secret`. -- Add the callback url of your application to your allowlist. +- Add the callback URL of your application to your allowlist. ## Access your Keycloak admin console @@ -43,7 +43,7 @@ The "Client ID" of the created client will serve as the `client_id` when you mak After you've created the client successfully, ensure that you set the following settings: -1. The "Client Protocol" should be set to "openid-connect". +1. The "Client Protocol" should be set to `openid-connect`. 2. The "Access Type" should be set to "confidential". 3. The "Valid Redirect URIs" should be set to: `https://.supabase.co/auth/v1/callback`. @@ -72,7 +72,7 @@ Since Keycloak version 22, the `openid` scope must be passed. Add this to the [` -When your user signs in, call [signInWithOAuth()](/docs/reference/javascript/auth-signinwithoauth) with `keycloak` as the `provider`: +When your user signs in, call [`signInWithOAuth()`](/docs/reference/javascript/auth-signinwithoauth) with `keycloak` as the `provider`: ```js async function signInWithKeycloak() { @@ -88,7 +88,7 @@ async function signInWithKeycloak() { -When your user signs in, call [signInWithOAuth()](/docs/reference/dart/auth-signinwithoauth) with `keycloak` as the `provider`: +When your user signs in, call [`signInWithOAuth()`](/docs/reference/dart/auth-signinwithoauth) with `keycloak` as the `provider`: ```dart Future signInWithKeycloak() async { @@ -163,5 +163,5 @@ suspend fun signOut() { ## Resources -- You can find the keycloak openid endpoint configuration under the realm settings. +- You can find the Keycloak OpenID endpoint configuration under the realm settings. ![Keycloak OpenID Endpoint Configuration](/docs/img/guides/auth-keycloak/keycloak-openid-endpoint-config.png) diff --git a/apps/docs/content/guides/auth/social-login/auth-linkedin.mdx b/apps/docs/content/guides/auth/social-login/auth-linkedin.mdx index d581605f2ef..0aa636294e1 100644 --- a/apps/docs/content/guides/auth/social-login/auth-linkedin.mdx +++ b/apps/docs/content/guides/auth/social-login/auth-linkedin.mdx @@ -59,7 +59,7 @@ Ensure that the appropriate scopes have been added under OAuth 2.0 Scopes at the -When your user signs in, call [signInWithOAuth()](/docs/reference/javascript/auth-signinwithoauth) with `linkedin_oidc` as the `provider`: +When your user signs in, call [`signInWithOAuth()`](/docs/reference/javascript/auth-signinwithoauth) with `linkedin_oidc` as the `provider`: ```js async function signInWithLinkedIn() { @@ -72,7 +72,7 @@ async function signInWithLinkedIn() { -When your user signs in, call [signInWithOAuth()](/docs/reference/dart/auth-signinwithoauth) with `linkedin_oidc` as the `provider`: +When your user signs in, call [`signInWithOAuth()`](/docs/reference/dart/auth-signinwithoauth) with `linkedin_oidc` as the `provider`: ```dart Future signInWithLinkedIn() async { diff --git a/apps/docs/content/guides/auth/social-login/auth-notion.mdx b/apps/docs/content/guides/auth/social-login/auth-notion.mdx index 91674a09e1a..c542db2e633 100644 --- a/apps/docs/content/guides/auth/social-login/auth-notion.mdx +++ b/apps/docs/content/guides/auth/social-login/auth-notion.mdx @@ -23,7 +23,7 @@ Setting up Notion logins for your application consists of 3 parts: - Once logged in, go to [notion.so/my-integrations](https://notion.so/my-integrations) and create a new integration. - When creating your integration, ensure that you select "Public integration" under "Integration type" and "Read user information including email addresses" under "Capabilities". -- You will need to add a redirect uri, see [Add the redirect uri](#add-the-redirect-uri) +- You will need to add a redirect URI, see [Add the redirect URI](#add-the-redirect-uri) - Once you've filled in the necessary fields, click "Submit" to finish creating the integration. ![notion.so](/docs/img/guides/auth-notion/notion-developer.png) @@ -57,7 +57,7 @@ Setting up Notion logins for your application consists of 3 parts: -When your user signs in, call [signInWithOAuth()](/docs/reference/javascript/auth-signinwithoauth) with `notion` as the `provider`: +When your user signs in, call [`signInWithOAuth()`](/docs/reference/javascript/auth-signinwithoauth) with `notion` as the `provider`: ```js async function signInWithNotion() { @@ -70,7 +70,7 @@ async function signInWithNotion() { -When your user signs in, call [signInWithOAuth()](/docs/reference/dart/auth-signinwithoauth) with `notion` as the `provider`: +When your user signs in, call [`signInWithOAuth()`](/docs/reference/dart/auth-signinwithoauth) with `notion` as the `provider`: ```dart Future signInWithNotion() async { diff --git a/apps/docs/content/guides/auth/social-login/auth-slack.mdx b/apps/docs/content/guides/auth/social-login/auth-slack.mdx index ea858493257..40d4bdce6ed 100644 --- a/apps/docs/content/guides/auth/social-login/auth-slack.mdx +++ b/apps/docs/content/guides/auth/social-login/auth-slack.mdx @@ -75,7 +75,7 @@ Under `Scopes`: -When your user signs in, call [signInWithOAuth()](/docs/reference/javascript/auth-signinwithoauth) with `slack_oidc` as the `provider`: +When your user signs in, call [`signInWithOAuth()`](/docs/reference/javascript/auth-signinwithoauth) with `slack_oidc` as the `provider`: ```js async function signInWithSlack() { @@ -88,7 +88,7 @@ async function signInWithSlack() { -When your user signs in, call [signInWithOAuth()](/docs/reference/dart/auth-signinwithoauth) with `slack` as the `provider`: +When your user signs in, call [`signInWithOAuth()`](/docs/reference/dart/auth-signinwithoauth) with `slack` as the `provider`: ```dart Future signInWithSlack() async { diff --git a/apps/docs/content/guides/auth/social-login/auth-spotify.mdx b/apps/docs/content/guides/auth/social-login/auth-spotify.mdx index 987492dde18..fe0df251278 100644 --- a/apps/docs/content/guides/auth/social-login/auth-spotify.mdx +++ b/apps/docs/content/guides/auth/social-login/auth-spotify.mdx @@ -69,7 +69,7 @@ The following outlines the steps to sign in using Spotify with Supabase Auth. -When your user signs in, call [signInWithOAuth()](/docs/reference/javascript/auth-signinwithoauth) with `spotify` as the `provider`: +When your user signs in, call [`signInWithOAuth()`](/docs/reference/javascript/auth-signinwithoauth) with `spotify` as the `provider`: ```js async function signInWithSpotify() { @@ -82,7 +82,7 @@ async function signInWithSpotify() { -When your user signs in, call [signInWithOAuth()](/docs/reference/dart/auth-signinwithoauth) with `spotify` as the `provider`: +When your user signs in, call [`signInWithOAuth()`](/docs/reference/dart/auth-signinwithoauth) with `spotify` as the `provider`: ```dart Future signInWithSpotify() async { diff --git a/apps/docs/content/guides/auth/social-login/auth-twitch.mdx b/apps/docs/content/guides/auth/social-login/auth-twitch.mdx index 97f17026a45..a1c2590143b 100644 --- a/apps/docs/content/guides/auth/social-login/auth-twitch.mdx +++ b/apps/docs/content/guides/auth/social-login/auth-twitch.mdx @@ -41,7 +41,7 @@ Setting up Twitch logins for your application consists of 3 parts: - Enter the name of your application. - Type or paste your `OAuth Redirect URL` (the callback URL from the previous step.) - Select a category for your app. -- Check the Captcha box and click `Create`. +- Check the CAPTCHA box and click `Create`. ## Retrieve your Twitch OAuth client ID and client secret @@ -72,7 +72,7 @@ Setting up Twitch logins for your application consists of 3 parts: -When your user signs in, call [signInWithOAuth()](/docs/reference/javascript/auth-signinwithoauth) with `twitch` as the `provider`: +When your user signs in, call [`signInWithOAuth()`](/docs/reference/javascript/auth-signinwithoauth) with `twitch` as the `provider`: ```js async function signInWithTwitch() { @@ -85,7 +85,7 @@ async function signInWithTwitch() { -When your user signs in, call [signInWithOAuth()](/docs/reference/dart/auth-signinwithoauth) with `twitch` as the `provider`: +When your user signs in, call [`signInWithOAuth()`](/docs/reference/dart/auth-signinwithoauth) with `twitch` as the `provider`: ```dart Future signInWithTwitch() async { diff --git a/apps/docs/content/guides/auth/social-login/auth-twitter.mdx b/apps/docs/content/guides/auth/social-login/auth-twitter.mdx index 0cd3fe30edb..cf8e34c7531 100644 --- a/apps/docs/content/guides/auth/social-login/auth-twitter.mdx +++ b/apps/docs/content/guides/auth/social-login/auth-twitter.mdx @@ -63,7 +63,7 @@ Setting up Twitter logins for your application consists of 3 parts: -When your user signs in, call [signInWithOAuth()](/docs/reference/javascript/auth-signinwithoauth) with `twitter` as the `provider`: +When your user signs in, call [`signInWithOAuth()`](/docs/reference/javascript/auth-signinwithoauth) with `twitter` as the `provider`: ```js async function signInWithTwitter() { @@ -76,7 +76,7 @@ async function signInWithTwitter() { -When your user signs in, call [signInWithOAuth()](/docs/reference/dart/auth-signinwithoauth) with `twitter` as the `provider`: +When your user signs in, call [`signInWithOAuth()`](/docs/reference/dart/auth-signinwithoauth) with `twitter` as the `provider`: ```dart Future signInWithTwitter() async { diff --git a/apps/docs/content/guides/auth/social-login/auth-workos.mdx b/apps/docs/content/guides/auth/social-login/auth-workos.mdx index 5e75b3c3e94..1abd6df0c0b 100644 --- a/apps/docs/content/guides/auth/social-login/auth-workos.mdx +++ b/apps/docs/content/guides/auth/social-login/auth-workos.mdx @@ -50,7 +50,7 @@ On the redirects page, enter your Supabase project's `Callback URL (for OAuth)` ## Step 5. Add login code to your client app -When a user signs in, call signInWithOAuth with `workos` as the provider. +When a user signs in, call `signInWithOAuth` with `workos` as the provider. ```javascript async function signInWithWorkOS() { diff --git a/apps/docs/content/guides/auth/social-login/auth-zoom.mdx b/apps/docs/content/guides/auth/social-login/auth-zoom.mdx index e10102550cc..6c3b9f33531 100644 --- a/apps/docs/content/guides/auth/social-login/auth-zoom.mdx +++ b/apps/docs/content/guides/auth/social-login/auth-zoom.mdx @@ -70,7 +70,7 @@ Under `Scopes` -When your user signs in, call [signInWithOAuth()](/docs/reference/javascript/auth-signinwithoauth) with `zoom` as the `provider`: +When your user signs in, call [`signInWithOAuth()`](/docs/reference/javascript/auth-signinwithoauth) with `zoom` as the `provider`: ```js async function signInWithZoom() { @@ -83,7 +83,7 @@ async function signInWithZoom() { -When your user signs in, call [signInWithOAuth()](/docs/reference/dart/auth-signinwithoauth) with `zoom` as the `provider`: +When your user signs in, call [`signInWithOAuth()`](/docs/reference/dart/auth-signinwithoauth) with `zoom` as the `provider`: ```dart Future signInWithZoom() async { diff --git a/apps/docs/content/guides/auth/third-party/firebase-auth.mdx b/apps/docs/content/guides/auth/third-party/firebase-auth.mdx index 0bf453afd5d..92d89657837 100644 --- a/apps/docs/content/guides/auth/third-party/firebase-auth.mdx +++ b/apps/docs/content/guides/auth/third-party/firebase-auth.mdx @@ -36,6 +36,26 @@ Make sure the all users in your application have the `role: 'authenticated'` [cu + + +Creating a client for the Web is as easy as passing the `accessToken` async function. This function should [return the Firebase Auth JWT of the current user](https://firebase.google.com/docs/auth/admin/verify-id-tokens) (or null if no such user) is found. + +```dart +await Supabase.initialize( + url: supabaseUrl, + anonKey: supabaseKey, + debug: false, + accessToken: () async { + final token = await FirebaseAuth.instance.currentUser?.getIdToken(); + return token; + }, +); +``` + +Make sure the all users in your application have the `role: 'authenticated'` [custom claim](https://firebase.google.com/docs/auth/admin/custom-claims) set. If you're using the `onCreate` Cloud Function to add this custom claim to newly signed up users, you will need to call `getIdToken(/* forceRefresh */ true)` immediately after sign up as the `onCreate` function does not run synchronously. + + + ```swift diff --git a/apps/docs/content/guides/auth/third-party/overview.mdx b/apps/docs/content/guides/auth/third-party/overview.mdx index 041be2090b4..792858dbf58 100644 --- a/apps/docs/content/guides/auth/third-party/overview.mdx +++ b/apps/docs/content/guides/auth/third-party/overview.mdx @@ -12,7 +12,7 @@ Supabase has first-class support for these third-party authentication providers: You can use these providers alongside Supabase Auth, or on their own, to access the [Data API (REST and GraphQL)](/docs/guides/database), [Storage](/docs/guides/storage), [Realtime](/docs/guides/storage) and [Functions](/docs/guides/functions) from your existing apps. -If you already have production apps using one of these authentication providers, and would like to use a Supabase feature, you no longer need to migrate your users to Supabase Auth or use work-arounds like translating JWTs into the Supabase Auth format and using your project's signing secret. +If you already have production apps using one of these authentication providers, and would like to use a Supabase feature, you no longer need to migrate your users to Supabase Auth or use workarounds like translating JWTs into the Supabase Auth format and using your project's signing secret. ## How does it work? @@ -27,3 +27,9 @@ There are some limitations you should be aware of when using third-party authent 1. The third-party provider must use asymmetrically signed JWTs (exposed as an OIDC Issuer Discovery URL by the third-party authentication provider). Using symmetrically signed JWTs is not possible at this time. 2. The JWT signing keys from the third-party provider are stored in the configuration of your project, and are checked for changes periodically. If you are rotating your keys (when supported) allow up to 30 minutes for the change to be picked up. 3. It is not possible to disable Supabase Auth at this time. + +## Pricing + + + +For a detailed breakdown of how charges are calculated, refer to [Manage Monthly Active Third-Party Users usage](/docs/guides/platform/manage-your-usage/monthly-active-users-third-party). diff --git a/apps/docs/content/guides/auth/users.mdx b/apps/docs/content/guides/auth/users.mdx index 98786de022f..2b537566047 100644 --- a/apps/docs/content/guides/auth/users.mdx +++ b/apps/docs/content/guides/auth/users.mdx @@ -50,7 +50,7 @@ An identity describes the authentication method that a user can use to sign in. -A user with an email or phone identity will be able to sign in with either a password or passwordless method (e.g. use a one-time password (OTP) or magiclink). By default, a user with an unverified email or phone number will not be able to sign in. +A user with an email or phone identity will be able to sign in with either a password or passwordless method (e.g. use a one-time password (OTP) or magic link). By default, a user with an unverified email or phone number will not be able to sign in. diff --git a/apps/docs/content/guides/cron/install.mdx b/apps/docs/content/guides/cron/install.mdx index 736b8870c28..478bf9eec70 100644 --- a/apps/docs/content/guides/cron/install.mdx +++ b/apps/docs/content/guides/cron/install.mdx @@ -37,7 +37,7 @@ Uninstall Supabase Cron by disabling the `pg_cron` extension: drop extension if exists pg_cron; ``` - + Disabling the `pg_cron` extension will permanently delete all Jobs. diff --git a/apps/docs/content/guides/cron/quickstart.mdx b/apps/docs/content/guides/cron/quickstart.mdx index 782303b9d57..0ea18729a6a 100644 --- a/apps/docs/content/guides/cron/quickstart.mdx +++ b/apps/docs/content/guides/cron/quickstart.mdx @@ -204,7 +204,7 @@ select cron.alter_job( select cron.unschedule('permanent-cron-job-name'); ``` - + Unscheduling a Job will permanently delete the Job from `cron.job` table but its run history remain in `cron.job_run_details` table. @@ -246,7 +246,7 @@ order by start_time desc limit 10; ``` - + The records in the `cron.job_run_details` table are not cleaned up automatically. They are also not removed when jobs are unscheduled, which will take up disk space in your database. diff --git a/apps/docs/content/guides/database/arrays.mdx b/apps/docs/content/guides/database/arrays.mdx index a3b87c9248d..2fc92f3f019 100644 --- a/apps/docs/content/guides/database/arrays.mdx +++ b/apps/docs/content/guides/database/arrays.mdx @@ -132,9 +132,11 @@ supabase.from_('arraytest').insert( You should see: +``` | id | textarray | | --- | ----------------------- | | 1 | ["Harry","Larry","Moe"] | +``` @@ -145,9 +147,11 @@ select * from arraytest; You should see: +``` | id | textarray | | --- | ----------------------- | | 1 | ["Harry","Larry","Moe"] | +``` @@ -173,9 +177,11 @@ SELECT textarray[1], array_length(textarray, 1) FROM arraytest; returns: +``` | textarray | array_length | | --------- | ------------ | | Harry | 3 | +``` diff --git a/apps/docs/content/guides/database/connecting-to-postgres.mdx b/apps/docs/content/guides/database/connecting-to-postgres.mdx index 0dbb2ec3fd6..7f88e92a951 100644 --- a/apps/docs/content/guides/database/connecting-to-postgres.mdx +++ b/apps/docs/content/guides/database/connecting-to-postgres.mdx @@ -10,7 +10,8 @@ How you connect to your database depends on where you're connecting from: - For frontend applications, use the [Data API](#data-apis-and-client-libraries) - For Postgres clients, use a connection string - - For persistent clients (for example, long-running servers or database GUIs) use the [direct connection string](#direct-connection) if your environment supports IPv6, and [Supavisor session mode](#supavisor-session-mode) if not + - For single sessions (for example, database GUIs) or Postgres native commands (for example, using client applications like [pg_dump](https://www.postgresql.org/docs/current/app-pgdump.html) or specifying connections for [replication](/docs/guides/database/postgres/setup-replication-external)) use the [direct connection string](#direct-connection) if your environment supports IPv6 + - For persistent clients, and support for both IPv4 and IPv6, use [Supavisor session mode](#supavisor-session-mode) - For temporary clients (for example, serverless or edge functions) use [Supavisor transaction mode](#supavisor-transaction-mode) ## Quickstarts @@ -66,7 +67,7 @@ The direct connection string connects directly to your Postgres instance. It is -Direct connections use IPv6 by default. If your environment doesn't support IPv6, use [Supavisor session mode](#supavisor-session-mode) or get the [IPv4 addon](/docs/guides/platform/ipv4-address). +Direct connections use IPv6 by default. If your environment doesn't support IPv6, use [Supavisor session mode](#supavisor-session-mode) or get the [IPv4 add-on](/docs/guides/platform/ipv4-address). diff --git a/apps/docs/content/guides/database/connection-management.mdx b/apps/docs/content/guides/database/connection-management.mdx index 720f87700f3..4a13b4aa4e5 100644 --- a/apps/docs/content/guides/database/connection-management.mdx +++ b/apps/docs/content/guides/database/connection-management.mdx @@ -17,7 +17,7 @@ You can change how many database connections Supavisor can manage by altering th The general rule is that if you are heavily using the PostgREST database API, you should be conscientious about raising your pool size past 40%. Otherwise, you can commit 80% to the pool. This leaves adequate room for the Authentication server and other utilities. -These numbers are generalizations and depends on other Supabase products that you use and the extent of their usage. The actual values depend on your peak connection usage. For instance, if you were only using 80 connections in a week period and your database max connections is set to 500, then realistically you could allocate the difference of 420 (minus a reasonable buffer) to service more demand. +These numbers are generalizations and depends on other Supabase products that you use and the extent of their usage. The actual values depend on your concurrent peak connection usage. For instance, if you were only using 80 connections in a week period and your database max connections is set to 500, then realistically you could allocate the difference of 420 (minus a reasonable buffer) to service more demand. ## Monitoring connections @@ -53,27 +53,27 @@ ON pg_stat_ssl.pid = pg_stat_activity.pid; Interpreting the query: -| Column | Description | -| ---------------- | ------------------------------------------------- | -| connection_id | connection id | -| ssl | Indicates if SSL is in use | -| database | Name of the connected database (usually postgres) | -| usename | Role of the connected user | -| application_name | Name of the connecting application | -| client_addr | IP address of the connecting server | -| query | Last query executed by the connection | -| query_start | Time when the last query was executed | -| state | Querying state: active or idle | -| backend_start | Timestamp of the connection's establishment | +| Column | Description | +| ------------------ | --------------------------------------------------- | +| `connection_id` | connection id | +| `ssl` | Indicates if SSL is in use | +| `database` | Name of the connected database (usually `postgres`) | +| `usename` | Role of the connected user | +| `application_name` | Name of the connecting application | +| `client_addr` | IP address of the connecting server | +| `query` | Last query executed by the connection | +| `query_start` | Time when the last query was executed | +| `state` | Querying state: active or idle | +| `backend_start` | Timestamp of the connection's establishment | -The usename can be used to identify the source: +The username can be used to identify the source: | Role | API/Tool | | ---------------------------- | ------------------------------------------------------------------------- | -| supabase_admin | Used by Supabase for monitoring and by Realtime | -| authenticator | Data API (PostgREST) | -| supabase_auth_admin | Auth | -| supabase_storage_admin | Storage | -| supabase_replication_admin | Synchronizes Read Replicas | -| postgres | Supabase Dashboard and External Tools (e.g., Prisma, SQLAlchemy, PSQL...) | +| `supabase_admin` | Used by Supabase for monitoring and by Realtime | +| `authenticator` | Data API (PostgREST) | +| `supabase_auth_admin` | Auth | +| `supabase_storage_admin` | Storage | +| `supabase_replication_admin` | Synchronizes Read Replicas | +| `postgres` | Supabase Dashboard and External Tools (e.g., Prisma, SQLAlchemy, PSQL...) | | Custom roles defined by user | External Tools (e.g., Prisma, SQLAlchemy, PSQL...) | diff --git a/apps/docs/content/guides/database/custom-postgres-config.mdx b/apps/docs/content/guides/database/custom-postgres-config.mdx index b5137339fd2..a4d84b971e1 100644 --- a/apps/docs/content/guides/database/custom-postgres-config.mdx +++ b/apps/docs/content/guides/database/custom-postgres-config.mdx @@ -61,7 +61,7 @@ Some settings can only be modified by a superuser. Supabase pre-enables the [`su | `session_replication_role` | Sets the session's behavior for triggers and rewrite rules. | | `track_io_timing` | Collects timing statistics for database I/O activity. | -For example, to enable `log_nested_statements` for the postgres role, execute: +For example, to enable `log_nested_statements` for the `postgres` role, execute: ```sql alter role "postgres" set "auto_explain.log_nested_statements" to 'on'; diff --git a/apps/docs/content/guides/database/extensions/http.mdx b/apps/docs/content/guides/database/extensions/http.mdx index e3a1e510cd7..0ee06be4321 100644 --- a/apps/docs/content/guides/database/extensions/http.mdx +++ b/apps/docs/content/guides/database/extensions/http.mdx @@ -46,7 +46,7 @@ You can use the `http` extension to make these network requests from Postgres. 1. Go to the [Database](https://supabase.com/dashboard/project/_/database/tables) page in the Dashboard. 2. Click on **Extensions** in the sidebar. -3. Search for "http" and enable the extension. +3. Search for `http` and enable the extension. diff --git a/apps/docs/content/guides/database/extensions/pg_hashids.mdx b/apps/docs/content/guides/database/extensions/pg_hashids.mdx index e53700420e1..07383d98c83 100644 --- a/apps/docs/content/guides/database/extensions/pg_hashids.mdx +++ b/apps/docs/content/guides/database/extensions/pg_hashids.mdx @@ -4,7 +4,7 @@ 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. +[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 identifiers. ## Enable the extension @@ -43,7 +43,7 @@ It's good practice to create the extension within a separate schema (like `exten ## 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. +Suppose we have a table that stores order information, and we want to give customers a unique identifier without exposing the sequential `id` column. To do this, we can use `pg_hashid`'s `id_encode` function. ```sql create table orders ( diff --git a/apps/docs/content/guides/database/extensions/pg_net.mdx b/apps/docs/content/guides/database/extensions/pg_net.mdx index 8855704fe94..bd477e4cdbf 100644 --- a/apps/docs/content/guides/database/extensions/pg_net.mdx +++ b/apps/docs/content/guides/database/extensions/pg_net.mdx @@ -435,7 +435,7 @@ More examples can be seen on the [Extension's GitHub page](https://github.com/su - Can only make POST requests with JSON data. No other data formats are supported - Intended to handle at most 200 requests per second. Increasing the rate can introduce instability - Does not have support for PATCH/PUT requests -- Can only work with one database at a time. It defaults to the postgres database. +- Can only work with one database at a time. It defaults to the `postgres` database. ## Resources diff --git a/apps/docs/content/guides/database/extensions/pg_stat_monitor.mdx b/apps/docs/content/guides/database/extensions/pg_stat_monitor.mdx deleted file mode 100644 index 843a7332e8e..00000000000 --- a/apps/docs/content/guides/database/extensions/pg_stat_monitor.mdx +++ /dev/null @@ -1,84 +0,0 @@ ---- -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://supabase.com/dashboard/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) diff --git a/apps/docs/content/guides/database/extensions/pg_stat_statements.mdx b/apps/docs/content/guides/database/extensions/pg_stat_statements.mdx index 7c724c3199d..2259a0e62a5 100644 --- a/apps/docs/content/guides/database/extensions/pg_stat_statements.mdx +++ b/apps/docs/content/guides/database/extensions/pg_stat_statements.mdx @@ -6,16 +6,16 @@ description: 'Track planning and execution statistics of all SQL statements exec `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 Name | 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) | +| Column Name | 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). diff --git a/apps/docs/content/guides/database/extensions/pgaudit.mdx b/apps/docs/content/guides/database/extensions/pgaudit.mdx index 50d890eabbf..6ea6bf78f9f 100644 --- a/apps/docs/content/guides/database/extensions/pgaudit.mdx +++ b/apps/docs/content/guides/database/extensions/pgaudit.mdx @@ -48,7 +48,7 @@ PGAudit can be configured with different levels of precision. **PGAudit logging precision:** - **[Session](#session-logging):** Logs activity within a connection, such as a [psql](https://supabase.com/docs/guides/database/connecting-to-postgres#connecting-with-psql) connection. -- **[User](#user-logging):** Logs activity by a particular database user (for example, anon or postgres). +- **[User](#user-logging):** Logs activity by a particular database user (for example, `anon` or `postgres`). - **[Global](#global-logging):** Logs activity across the entire database. - **[Object](#object-logging):** Logs events related to specific database objects (for example, the auth.users table). @@ -58,15 +58,15 @@ Although Session, User, and Global modes differ in their precision, they're all These modes can monitor predefined categories of database operations: -| Category | What it Logs | Description | -| -------- | --------------------------------------------------------------------- | -------------------------------------------------------------------------- | -| read | Data retrieval (SELECT, COPY) | Tracks what data is being accessed. | -| write | Data modification (INSERT, DELETE, UPDATE, TRUNCATE, COPY) | Tracks changes made to your database. | -| function | FUNCTION, PROCEDURE, and DO/END block executions | Tracks routine/function executions | -| role | User management actions (CREATE, DROP, ALTER on users and privileges) | Tracks changes to user permissions and access. | -| ddl | Schema changes (CREATE, DROP, ALTER statements) | Monitors modifications to your database structure (tables, indexes, etc.). | -| misc | Less common commands (FETCH, CHECKPOINT) | Captures obscure actions for deeper analysis if needed. | -| all | Everything above | Comprehensive logging for complete audit trails. | +| Category | What it Logs | Description | +| ---------- | --------------------------------------------------------------------- | -------------------------------------------------------------------------- | +| `read` | Data retrieval (SELECT, COPY) | Tracks what data is being accessed. | +| `write` | Data modification (INSERT, DELETE, UPDATE, TRUNCATE, COPY) | Tracks changes made to your database. | +| `function` | FUNCTION, PROCEDURE, and DO/END block executions | Tracks routine/function executions | +| `role` | User management actions (CREATE, DROP, ALTER on users and privileges) | Tracks changes to user permissions and access. | +| `ddl` | Schema changes (CREATE, DROP, ALTER statements) | Monitors modifications to your database structure (tables, indexes, etc.). | +| `misc` | Less common commands (FETCH, CHECKPOINT) | Captures obscure actions for deeper analysis if needed. | +| `all` | Everything above | Comprehensive logging for complete audit trails. | Below is a limited example of how to assign PGAudit to monitor specific categories. @@ -148,13 +148,13 @@ Use global logging cautiously. It can generate many logs and make it difficult t -The below SQL configures PGAudit to record all events associated with the "postgres" role. Since it has extensive privileges, this effectively monitors all database activity. +The below SQL configures PGAudit to record all events associated with the `postgres` role. Since it has extensive privileges, this effectively monitors all database activity. ```sql alter role "postgres" set pgaudit.log to 'all'; ``` -To check if the postgres role is auditing, execute the following command: +To check if the `postgres` role is auditing, execute the following command: ```sql select @@ -279,7 +279,7 @@ API requests are already recorded in the [API Edge Network](https://supabase.com -To monitor all writes initiated by the Postgrest API roles: +To monitor all writes initiated by the PostgREST API roles: ```sql alter role "authenticator" set pgaudit.log to 'write'; @@ -383,7 +383,7 @@ PGAudit allows settings to be applied to 3 different database scopes: | Database | Specific database | ALTER DATABASE commands | | Role | Specific user/role | ALTER ROLE commands | -Supabase limits full privileges for file system and database variables, meaning PGAudit modifications can only occur at the role level. Assigning PGAudit to the 'postgres' role grants it nearly complete visibility into the database, making role-level adjustments a practical alternative to configuring at the database or system level. +Supabase limits full privileges for file system and database variables, meaning PGAudit modifications can only occur at the role level. Assigning PGAudit to the `postgres` role grants it nearly complete visibility into the database, making role-level adjustments a practical alternative to configuring at the database or system level. PGAudit's [official documentation](https://www.pgaudit.org) focuses on system and database level configs, but its docs officially supports role level configs, too. diff --git a/apps/docs/content/guides/database/extensions/postgis.mdx b/apps/docs/content/guides/database/extensions/postgis.mdx index d83e1066472..40da5901fe3 100644 --- a/apps/docs/content/guides/database/extensions/postgis.mdx +++ b/apps/docs/content/guides/database/extensions/postgis.mdx @@ -11,7 +11,7 @@ tocVideo: 'agFsGDJxjwA' While you may be able to store simple lat/long geographic coordinates as a set of decimals, it does not scale very well when you try to query through a large data set. PostGIS comes with special data types that are efficient, and indexable for high scalability. -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). +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). ## Enable the extension @@ -28,7 +28,7 @@ You can get started with PostGIS by enabling the PostGIS extension in your Supab 1. Go to the [Database](https://supabase.com/dashboard/project/_/database/tables) page in the Dashboard. 2. Click on **Extensions** in the sidebar. -3. Search for "postgis" and enable the extension. +3. Search for `postgis` and enable the extension. 4. In the confirmation prompt select "Create a new schema" and name it `gis` for example. @@ -85,12 +85,16 @@ You can insert geographical data through SQL or through our API.

Restaurants

+{/* supa-mdx-lint-disable Rule003Spelling */} + | id | name | location | | --- | ----------- | -------------------------------- | | 1 | Supa Burger | lat: 40.807416, long: -73.946823 | | 2 | Supa Pizza | lat: 40.807475, long: -73.94581 | | 3 | Supa Taco | lat: 40.80629, long: -73.945826 | +{/* supa-mdx-lint-enable Rule003Spelling */} +
@@ -438,7 +442,7 @@ The [official PostGIS documentation](https://postgis.net/documentation/tips/tip- -As of PostGIS 2.3 or newer, the PostGIS extension is no longer relocatable from one schema to another. If you need to move it from one schema to another for any reason (Eg. from the public schema to the extensions schema for security reasons), you would normally run a ALTER EXTENSION to relocate the schema. However, you will now to do the following steps: +As of PostGIS 2.3 or newer, the PostGIS extension is no longer relocatable from one schema to another. If you need to move it from one schema to another for any reason (e.g. from the public schema to the extensions schema for security reasons), you would normally run a ALTER EXTENSION to relocate the schema. However, you will now to do the following steps: 1. Backup your Database to prevent data loss - You can do this through the [CLI](https://supabase.com/docs/reference/cli/supabase-db-dump) or Postgres backup tools such as [pg_dumpall](https://www.postgresql.org/docs/current/backup-dump.html#BACKUP-DUMP-ALL) diff --git a/apps/docs/content/guides/database/extensions/rum.mdx b/apps/docs/content/guides/database/extensions/rum.mdx index c95943e0202..4185efebd3b 100644 --- a/apps/docs/content/guides/database/extensions/rum.mdx +++ b/apps/docs/content/guides/database/extensions/rum.mdx @@ -4,7 +4,7 @@ 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](https://github.com/postgrespro/rum) is an extension which adds a RUM index to Postgres. 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: @@ -18,10 +18,10 @@ combinations or phrases. Main operators for ordering are: -tsvector `<=>` tsquery | float4 | Distance between tsvector and tsquery. -value `<=>` value | float8 | Distance between two values. +`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 +Where value is `timestamp`, `timestamptz`, `int2`, `int4`, `int8`, `float4`, `float8`, `money` and `oid` ## Usage @@ -120,7 +120,7 @@ SELECT id, d, d `<=>` '2016-05-16 14:21:25' FROM tsts WHERE t @@ 'wr&qh' ORDER B `rum_anyarray_ops` -This operator class stores anyarray elements with length of the array. It supports operators `&&`, `@>`, `<@`, `=`, `%` operators. It also supports ordering by `<=>` operator. +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[]); @@ -143,7 +143,7 @@ SELECT * FROM test_array WHERE i && '{1}' ORDER BY i `<=>` '{1}' ASC; `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 +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 diff --git a/apps/docs/content/guides/database/extensions/timescaledb.mdx b/apps/docs/content/guides/database/extensions/timescaledb.mdx index a97dd8260af..53277248488 100644 --- a/apps/docs/content/guides/database/extensions/timescaledb.mdx +++ b/apps/docs/content/guides/database/extensions/timescaledb.mdx @@ -4,7 +4,7 @@ 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`](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. @@ -27,7 +27,7 @@ Supabase projects come with [TimescaleDB Apache 2 Edition](https://docs.timescal 1. Go to the [Database](https://supabase.com/dashboard/project/_/database/tables) page in the Dashboard. 2. Click on **Extensions** in the sidebar. -3. Search for "timescaledb" and enable the extension. +3. Search for `timescaledb` and enable the extension. diff --git a/apps/docs/content/guides/database/extensions/uuid-ossp.mdx b/apps/docs/content/guides/database/extensions/uuid-ossp.mdx index 4fd6e9e3495..48e8ce2945e 100644 --- a/apps/docs/content/guides/database/extensions/uuid-ossp.mdx +++ b/apps/docs/content/guides/database/extensions/uuid-ossp.mdx @@ -27,7 +27,7 @@ Currently `uuid-ossp` extension is enabled by default and cannot be disabled. 1. Go to the [Database](https://supabase.com/dashboard/project/_/database/tables) page in the Dashboard. 2. Click on **Extensions** in the sidebar. -3. Search for "uuid-ossp" and enable the extension. +3. Search for `uuid-ossp` and enable the extension. diff --git a/apps/docs/content/guides/database/full-text-search.mdx b/apps/docs/content/guides/database/full-text-search.mdx index 0975aa2fe04..004d8fe9b1f 100644 --- a/apps/docs/content/guides/database/full-text-search.mdx +++ b/apps/docs/content/guides/database/full-text-search.mdx @@ -30,7 +30,7 @@ For this guide we'll use the following example data: > -{/* */} +{/* supa-mdx-lint-disable Rule003Spelling */} | id | title | author | description | | --- | ----------------------------------- | ---------------------- | ------------------------------------------------------------------ | @@ -40,7 +40,7 @@ For this guide we'll use the following example data: | 4 | Green Eggs and Ham | Dr. Seuss | Sam has changing food preferences and eats unusually colored food. | | 5 | Harry Potter and the Goblet of Fire | J.K. Rowling | Fourth year of school starts, big drama ensues. | -{/* */} +{/* supa-mdx-lint-enable Rule003Spelling */} @@ -301,14 +301,14 @@ data = supabase.from_('books').select().text_search('description', "'big'").exec -{/* */} +{/* supa-mdx-lint-disable Rule003Spelling */} | id | title | author | description | | --- | ----------------------------------- | ----------------- | ----------------------------------------------- | | 3 | Tootle | Gertrude Crampton | Little toy train has big dreams. | | 5 | Harry Potter and the Goblet of Fire | J.K. Rowling | Fourth year of school starts, big drama ensues. | -{/* */} +{/* supa-mdx-lint-enable Rule003Spelling */} @@ -420,14 +420,14 @@ data = supabase.from_('books').select().text_search('title_description', "little -{/* */} +{/* supa-mdx-lint-disable Rule003Spelling */} | id | title | author | description | | --- | --------------------- | ---------------------- | ------------------------------------------- | | 1 | The Poky Little Puppy | Janette Sebring Lowrey | Puppy is slower than other, bigger animals. | | 3 | Tootle | Gertrude Crampton | Little toy train has big dreams. | -{/* */} +{/* supa-mdx-lint-enable Rule003Spelling */} @@ -507,13 +507,13 @@ data = supabase.from_('books').select().text_search('description', "'little' & ' -{/* */} +{/* supa-mdx-lint-disable Rule003Spelling */} | id | title | author | description | | --- | ------ | ----------------- | -------------------------------- | | 3 | Tootle | Gertrude Crampton | Little toy train has big dreams. | -{/* */} +{/* supa-mdx-lint-enable Rule003Spelling */} @@ -593,14 +593,14 @@ response = client.from_('books').select().text_search('description', "'little' |
-{/* */} +{/* supa-mdx-lint-disable Rule003Spelling */} | id | title | author | description | | --- | --------------------- | ---------------------- | ------------------------------------------- | | 1 | The Poky Little Puppy | Janette Sebring Lowrey | Puppy is slower than other, bigger animals. | | 3 | Tootle | Gertrude Crampton | Little toy train has big dreams. | -{/* */} +{/* supa-mdx-lint-enable Rule003Spelling */} @@ -699,7 +699,7 @@ select * from search_books_by_title_prefix('Little+Puppy'); ## Creating indexes -Now that we have Full Text Search working, let's create an `index`. This will allow Postgres to "build" the documents pre-emptively so that they +Now that we have Full Text Search working, let's create an `index`. This will allow Postgres to "build" the documents preemptively so that they don't need to be created at the time we execute the query. This will make our queries much faster. ### Searchable columns @@ -734,8 +734,7 @@ from books; -{/* */} - +``` | id | fts | | --- | --------------------------------------------------------------------------------------------------------------- | | 1 | 'anim':7 'bigger':6 'littl':10 'poki':9 'puppi':1,11 'slower':3 | @@ -743,8 +742,7 @@ from books; | 3 | 'big':5 'dream':6 'littl':1 'tootl':7 'toy':2 'train':3 | | 4 | 'chang':3 'color':9 'eat':7 'egg':12 'food':4,10 'green':11 'ham':14 'prefer':5 'sam':1 'unus':8 | | 5 | 'big':6 'drama':7 'ensu':8 'fire':15 'fourth':1 'goblet':13 'harri':9 'potter':10 'school':4 'start':5 'year':2 | - -{/* */} +``` @@ -820,13 +818,13 @@ data = client.from_('books').select().text_search('fts', "'little' & 'big'").exe -{/* */} +{/* supa-mdx-lint-disable Rule003Spelling */} | id | title | author | description | fts | | --- | ------ | ----------------- | -------------------------------- | ------------------------------------------------------- | | 3 | Tootle | Gertrude Crampton | Little toy train has big dreams. | 'big':5 'dream':6 'littl':1 'tootl':7 'toy':2 'train':3 | -{/* */} +{/* supa-mdx-lint-enable Rule003Spelling */} diff --git a/apps/docs/content/guides/database/functions.mdx b/apps/docs/content/guides/database/functions.mdx index eb2b1a6b84e..58883b17e81 100644 --- a/apps/docs/content/guides/database/functions.mdx +++ b/apps/docs/content/guides/database/functions.mdx @@ -82,7 +82,7 @@ select hello_world(); const { data, error } = await supabase.rpc('hello_world') ``` -Reference: [rpc()](../../reference/javascript/rpc) +Reference: [`rpc()`](../../reference/javascript/rpc) @@ -92,7 +92,7 @@ final data = await supabase .rpc('hello_world'); ``` -Reference: [rpc()](../../reference/dart/rpc) +Reference: [`rpc()`](../../reference/dart/rpc) @@ -101,7 +101,7 @@ Reference: [rpc()](../../reference/dart/rpc) try await supabase.rpc("hello_world").execute() ``` -Reference: [rpc()](../../reference/swift/rpc) +Reference: [`rpc()`](../../reference/swift/rpc) @@ -110,7 +110,7 @@ Reference: [rpc()](../../reference/swift/rpc) val data = supabase.postgrest.rpc("hello_world") ``` -Reference: [rpc()](../../reference/kotlin/rpc) +Reference: [`rpc()`](../../reference/kotlin/rpc) @@ -119,7 +119,7 @@ Reference: [rpc()](../../reference/kotlin/rpc) data = supabase.rpc('hello_world').execute() ``` -Reference: [rpc()](../../reference/python/rpc) +Reference: [`rpc()`](../../reference/python/rpc) @@ -141,20 +141,24 @@ For example, if we had a database with some Star Wars data inside:

Planets

+``` | id | name | | --- | -------- | | 1 | Tatooine | | 2 | Alderaan | | 3 | Kashyyyk | +```

People

+``` | id | name | planet_id | | --- | ---------------- | --------- | | 1 | Anakin Skywalker | 1 | | 2 | Luke Skywalker | 1 | | 3 | Princess Leia | 2 | | 4 | Chewbacca | 3 | +``` diff --git a/apps/docs/content/guides/database/inspect.mdx b/apps/docs/content/guides/database/inspect.mdx index 4552a1697e5..44d95c0bf20 100644 --- a/apps/docs/content/guides/database/inspect.mdx +++ b/apps/docs/content/guides/database/inspect.mdx @@ -1,9 +1,22 @@ --- -id: 'json' title: 'Debugging and monitoring' description: 'Inspecting your Postgres database for common issues around disk, query performance, index, locks, and more using the terminal.' --- +Database performance is a large topic and many factors can contribute. Some of the most common causes of poor performance include: + +- An inefficiently designed schema +- Inefficiently designed queries +- A lack of indexes causing slower than required queries over large tables +- Unused indexes causing slow `INSERT`, `UPDATE` and `DELETE` operations +- Not enough compute resources, such as memory, causing your database to go to disk for results too often +- Lock contention from multiple queries operating on highly utilized tables +- Large amount of bloat on your tables causing poor query planning + +You can examine your database and queries for these issues using either the [Supabase CLI](/docs/guides/local-development/cli/getting-started) or SQL. + +## Using the CLI + The Supabase CLI comes with a range of tools to help inspect your Postgres instances for potential issues. The CLI gets the information from Postgres internals. Therefore, most tools provided are compatible with any Postgres databases regardless if they are a Supabase project or not. You can find installation instructions for the the Supabase CLI here. @@ -47,9 +60,9 @@ supabase link --project-ref Then the CLI will automatically connect to your Supabase project whenever you are in the project folder and you no longer need to provide `—db-url`. -## Inspection commands +### Inspection commands -Below are the db inspection commands provided, grouped by different use cases. +Below are the `db` inspection commands provided, grouped by different use cases. @@ -57,7 +70,7 @@ Some commands might require `pg_stat_statements` to be enabled or a specific Pos -### Disk storage +#### Disk storage These commands are handy if you are running low on disk storage: @@ -68,7 +81,7 @@ These commands are handy if you are running low on disk storage: - [index-sizes](/docs/reference/cli/supabase-inspect-db-index-sizes) - shows the sizes of individual index - [table-index-sizes](/docs/reference/cli/supabase-inspect-db-table-index-sizes) - shows the sizes of indexes for each table -### Query performance +#### Query performance The commands below are useful if your Postgres database consumes a lot of resources like CPU, RAM or Disk IO. You can also use them to investigate slow queries. @@ -79,17 +92,17 @@ The commands below are useful if your Postgres database consumes a lot of resour - [long-running-queries](/docs/reference/cli/supabase-inspect-db-long-running-queries) - shows long running queries that are executing right now - [outliers](/docs/reference/cli/supabase-inspect-db-outliers) - shows queries with high execution time but low call count and queries with high proportion of execution time spent on synchronous I/O -### Locks +#### Locks - [locks](/docs/reference/cli/supabase-inspect-db-locks) - shows statements which have taken out an exclusive lock on a relation - [blocking](/docs/reference/cli/supabase-inspect-db-blocking) - shows statements that are waiting for locks to be released -### Connections +#### Connections - [role-connections](/docs/reference/cli/supabase-inspect-db-role-connections) - shows number of active connections for all database roles (Supabase-specific command) - [replication-slots](/docs/reference/cli/supabase-inspect-db-replication-slots) - shows information about replication slots on the database -## Notes on `pg_stat_statements` +### Notes on `pg_stat_statements` Following commands require `pg_stat_statements` to be enabled: calls, locks, cache-hit, blocking, unused-indexes, index-usage, bloat, outliers, table-record-counts, replication-slots, seq-scans, vacuum-stats, long-running-queries. @@ -97,6 +110,155 @@ When using `pg_stat_statements` also take note that it only stores the latest 5, Learn more about pg_stats [here](https://supabase.com/docs/guides/database/extensions/pg_stat_statements). -## Acknowledgements +## Using SQL -Supabase CLI's inspect commands are heavily inspired by the pg-extras tools. + + +If you're seeing an `insufficient privilege` error when viewing the Query Performance page from the dashboard, run this command: + +```shell +$ grant pg_read_all_stats to postgres; +``` + + + +### Postgres cumulative statistics system + +Postgres collects data about its own operations using the [cumulative statistics system](https://www.postgresql.org/docs/current/monitoring-stats.html). In addition to this, every Supabase project has the [pg_stat_statements extension](/docs/guides/database/extensions/pg_stat_statements) enabled by default. This extension records query execution performance details and is the best way to find inefficient queries. This information can be combined with the Postgres query plan analyzer to develop more efficient queries. + +Here are some example queries to get you started. + +### Most frequently called queries + +```sql +select + auth.rolname, + statements.query, + statements.calls, + -- -- Postgres 13, 14, 15 + statements.total_exec_time + statements.total_plan_time as total_time, + statements.min_exec_time + statements.min_plan_time as min_time, + statements.max_exec_time + statements.max_plan_time as max_time, + statements.mean_exec_time + statements.mean_plan_time as mean_time, + -- -- Postgres <= 12 + -- total_time, + -- min_time, + -- max_time, + -- mean_time, + statements.rows / statements.calls as avg_rows +from + pg_stat_statements as statements + inner join pg_authid as auth on statements.userid = auth.oid +order by statements.calls desc +limit 100; +``` + +This query shows: + +- query statistics, ordered by the number of times each query has been executed +- the role that ran the query +- the number of times it has been called +- the average number of rows returned +- the cumulative total time the query has spent running +- the min, max and mean query times. + +This provides useful information about the queries you run most frequently. Queries that have high `max_time` or `mean_time` times and are being called often can be good candidates for optimization. + +### Slowest queries by execution time + +```sql +select + auth.rolname, + statements.query, + statements.calls, + -- -- Postgres 13, 14, 15 + statements.total_exec_time + statements.total_plan_time as total_time, + statements.min_exec_time + statements.min_plan_time as min_time, + statements.max_exec_time + statements.max_plan_time as max_time, + statements.mean_exec_time + statements.mean_plan_time as mean_time, + -- -- Postgres <= 12 + -- total_time, + -- min_time, + -- max_time, + -- mean_time, + statements.rows / statements.calls as avg_rows +from + pg_stat_statements as statements + inner join pg_authid as auth on statements.userid = auth.oid +order by max_time desc +limit 100; +``` + +This query will show you statistics about queries ordered by the maximum execution time. It is similar to the query above ordered by calls, but this one highlights outliers that may have high executions times. Queries which have high or mean execution times are good candidates for optimization. + +### Most time consuming queries + +```sql +select + auth.rolname, + statements.query, + statements.calls, + statements.total_exec_time + statements.total_plan_time as total_time, + to_char( + ( + (statements.total_exec_time + statements.total_plan_time) / sum( + statements.total_exec_time + statements.total_plan_time + ) over () + ) * 100, + 'FM90D0' + ) || '%' as prop_total_time +from + pg_stat_statements as statements + inner join pg_authid as auth on statements.userid = auth.oid +order by total_time desc +limit 100; +``` + +This query will show you statistics about queries ordered by the cumulative total execution time. It shows the total time the query has spent running as well as the proportion of total execution time the query has taken up. + +Queries which are the most time consuming are not necessarily bad, you may have a very efficient and frequently ran queries that end up taking a large total % time, but it can be useful to help spot queries that are taking up more time than they should. + +### Hit rate + +Generally for most applications a small percentage of data is accessed more regularly than the rest. To make sure that your regularly accessed data is available, Postgres tracks your data access patterns and keeps this in its [shared_buffers](https://www.postgresql.org/docs/15/runtime-config-resource.html#RUNTIME-CONFIG-RESOURCE-MEMORY) cache. + +Applications with lower cache hit rates generally perform more poorly since they have to hit the disk to get results rather than serving them from memory. Very poor hit rates can also cause you to burst past your [Disk IO limits](./compute-add-ons#disk-io) causing significant performance issues. + +You can view your cache and index hit rate by executing the following query: + +```sql +select + 'index hit rate' as name, + (sum(idx_blks_hit)) / nullif(sum(idx_blks_hit + idx_blks_read), 0) * 100 as ratio +from pg_statio_user_indexes +union all +select + 'table hit rate' as name, + sum(heap_blks_hit) / nullif(sum(heap_blks_hit) + sum(heap_blks_read), 0) * 100 as ratio +from pg_statio_user_tables; +``` + +This shows the ratio of data blocks fetched from the Postgres [shared_buffers](https://www.postgresql.org/docs/15/runtime-config-resource.html#RUNTIME-CONFIG-RESOURCE-MEMORY) cache against the data blocks that were read from disk/OS cache. + +If either of your index or table hit rate are < 99% then this can indicate your compute plan is too small for your current workload and you would benefit from more memory. [Upgrading your compute](./compute-add-ons) is easy and can be done from your [project dashboard](https://supabase.com/dashboard/project/_/settings/compute-and-disk). + +### Optimizing poor performing queries + +Postgres has built in tooling to help you optimize poorly performing queries. You can use the [query plan analyzer](https://www.postgresql.org/docs/current/sql-explain.html) on any expensive queries that you have identified: + +```sql +explain analyze ; +``` + +When you include `analyze` in the explain statement, the database attempts to execute the query and provides a detailed query plan along with actual execution times. So, be careful using `explain analyze` with `insert`/`update`/`delete` queries, because the query will actually run, and could have unintended side-effects. + +If you run just `explain` without the `analyze` keyword, the database will only perform query planning without actually executing the query. This approach can be beneficial when you want to inspect the query plan without affecting the database or if you encounter timeouts in your queries. + +Using the query plan analyzer to optimize your queries is a large topic, with a number of online resources available: + +- [Official docs.](https://www.postgresql.org/docs/current/using-explain.html) +- [The Art of PostgreSQL.](https://theartofpostgresql.com/explain-plan-visualizer/) +- [Postgres Wiki.](https://wiki.postgresql.org/wiki/Using_EXPLAIN) +- [Enterprise DB.](https://www.enterprisedb.com/blog/postgresql-query-optimization-performance-tuning-with-explain-analyze) + +You can pair the information available from `pg_stat_statements` with the detailed system metrics available [via your metrics endpoint](../platform/metrics) to better understand the behavior of your DB and the queries you're executing against it. diff --git a/apps/docs/content/guides/database/json.mdx b/apps/docs/content/guides/database/json.mdx index a1195bfe912..5a067043f46 100644 --- a/apps/docs/content/guides/database/json.mdx +++ b/apps/docs/content/guides/database/json.mdx @@ -117,6 +117,8 @@ values 2. Select the `books` table in the sidebar. 3. Click **+ Insert row** and add 5 rows with the following properties: +{/* supa-mdx-lint-disable Rule003Spelling */} + | id | title | author | metadata | | --- | ----------------------------------- | ---------------------- | --------------------------------------------------------------------------------------------------------------------- | | 1 | The Poky Little Puppy | Janette Sebring Lowrey | `json {"ages":[3,6],"price":5.95,"description":"Puppy is slower than other, bigger animals."}` | @@ -125,6 +127,8 @@ values | 4 | Green Eggs and Ham | Dr. Seuss | `json {"ages":[4,8],"price":7.49,"description":"Sam has changing food preferences and eats unusually colored food."}` | | 5 | Harry Potter and the Goblet of Fire | J.K. Rowling | `json {"ages":[10,99],"price":24.95,"description":"Fourth year of school starts, big drama ensues."}` | +{/* supa-mdx-lint-enable Rule003Spelling */} + diff --git a/apps/docs/content/guides/database/orioledb.mdx b/apps/docs/content/guides/database/orioledb.mdx index ba8e3279502..a83faabf945 100644 --- a/apps/docs/content/guides/database/orioledb.mdx +++ b/apps/docs/content/guides/database/orioledb.mdx @@ -15,7 +15,7 @@ OrioleDB addresses PostgreSQL's scalability limitations by removing bottlenecks zoomable /> - + OrioleDB is in active development and currently has [certain limitations](https://www.orioledb.com/docs/usage/getting-started#current-limitations). Currently, only B-tree indexes are supported, so features like pg_vector's HNSW indexes are not yet available. An Index Access Method bridge to unlock support for all index types used with heap storage is under active development. In the Supabase OrioleDB image the default storage method has been updated to use OrioleDB, granting better performance out of the box. @@ -78,7 +78,7 @@ create table blog_post ( OrioleDB tables always have a primary key. If it wasn't defined explicitly, a hidden primary key is created using the `ctid` column. Additionally you can create secondary indexes. - + Currently, only B-tree indexes are supported, so features like pg_vector's HNSW indexes are not yet available. diff --git a/apps/docs/content/guides/database/pgadmin.mdx b/apps/docs/content/guides/database/pgadmin.mdx index 99297520d89..7608ad02112 100644 --- a/apps/docs/content/guides/database/pgadmin.mdx +++ b/apps/docs/content/guides/database/pgadmin.mdx @@ -70,7 +70,7 @@ hideToc: true Download your SSL certificate from Dashboard's [`Database Settings`](https://supabase.com/dashboard/project/_/settings/database). - In pgAdmin, navigate to the Parameters tab and select connection parameter as Root Certificate. Next navigate to the Root certificate input, it will open up a file-picker modal. Select the certificate you downloaded earlier and save the server details. PgAdmin should now be able to connect to your Postgres via SSL. + In pgAdmin, navigate to the Parameters tab and select connection parameter as Root Certificate. Next navigate to the Root certificate input, it will open up a file-picker modal. Select the certificate you downloaded earlier and save the server details. pgAdmin should now be able to connect to your Postgres via SSL. diff --git a/apps/docs/content/guides/database/postgres/custom-claims-and-role-based-access-control-rbac.mdx b/apps/docs/content/guides/database/postgres/custom-claims-and-role-based-access-control-rbac.mdx index a63886037f3..e5ff8ea539b 100644 --- a/apps/docs/content/guides/database/postgres/custom-claims-and-role-based-access-control-rbac.mdx +++ b/apps/docs/content/guides/database/postgres/custom-claims-and-role-based-access-control-rbac.mdx @@ -267,7 +267,7 @@ const { subscription: authListener } = supabase.auth.onAuthStateChange(async (ev }) ``` -For server-side logic you can use packages like [express-jwt](https://github.com/auth0/express-jwt), [koa-jwt](https://github.com/stiang/koa-jwt), [PyJWT](https://github.com/jpadilla/pyjwt), [dart_jsonwebtoken](https://pub.dev/packages/dart_jsonwebtoken), [Microsoft.AspNetCore.Authentication.JwtBearer](https://www.nuget.org/packages/Microsoft.AspNetCore.Authentication.JwtBearer), etc. +For server-side logic you can use packages like [`express-jwt`](https://github.com/auth0/express-jwt), [`koa-jwt`](https://github.com/stiang/koa-jwt), [`PyJWT`](https://github.com/jpadilla/pyjwt), [dart_jsonwebtoken](https://pub.dev/packages/dart_jsonwebtoken), [Microsoft.AspNetCore.Authentication.JwtBearer](https://www.nuget.org/packages/Microsoft.AspNetCore.Authentication.JwtBearer), etc. ## Conclusion diff --git a/apps/docs/content/guides/database/postgres/indexes.mdx b/apps/docs/content/guides/database/postgres/indexes.mdx index c8d96b6cbb9..acbbff3daf4 100644 --- a/apps/docs/content/guides/database/postgres/indexes.mdx +++ b/apps/docs/content/guides/database/postgres/indexes.mdx @@ -26,7 +26,7 @@ create table persons ( ); ``` - + All the queries in this guide can be run using the [SQL Editor](https://supabase.com/dashboard/project/_/sql) in the Supabase Dashboard, or via `psql` if you're [connecting directly to the database](/docs/guides/database/connecting-to-postgres#direct-connections). diff --git a/apps/docs/content/guides/database/postgres/roles.mdx b/apps/docs/content/guides/database/postgres/roles.mdx index 7a36f2761d7..009df3a5684 100644 --- a/apps/docs/content/guides/database/postgres/roles.mdx +++ b/apps/docs/content/guides/database/postgres/roles.mdx @@ -38,13 +38,13 @@ Your Postgres database is the core of your Supabase project, so it's important t ### Special symbols in passwords -If you use special symbols in your postgres password, you must remember to [percent-encode](https://en.wikipedia.org/wiki/Percent-encoding) your password later if using the postgres connection string, for example, `postgresql://postgres.projectref:p%3Dword@aws-0-us-east-1.pooler.supabase.com:6543/postgres` +If you use special symbols in your Postgres password, you must remember to [percent-encode](https://en.wikipedia.org/wiki/Percent-encoding) your password later if using the Postgres connection string, for example, `postgresql://postgres.projectref:p%3Dword@aws-0-us-east-1.pooler.supabase.com:6543/postgres` ### Changing your project password When you created your project you were also asked to enter a password. This is actually the password for the `postgres` role in your database. You can update this from the Dashboard under the [database settings](https://supabase.com/dashboard/project/_/settings/database) page. You should _never_ give this to third-party service unless you absolutely trust them. Instead, we recommend that you create a new user for every service that you want to give access too. This will also help you with debugging - you can see every query that each role is executing in your database within `pg_stat_statements`. -Changing the password does not result in any downtime. All connected services, such as postgrest, pgbouncer, and other Supabase managed services, are automatically updated to use the latest password to ensure availability. However, if you have any external services connecting to the Supabase database using hardcoded username/password credentials, a manual update will be required. +Changing the password does not result in any downtime. All connected services, such as PostgREST, PgBouncer, and other Supabase managed services, are automatically updated to use the latest password to ensure availability. However, if you have any external services connecting to the Supabase database using hardcoded username/password credentials, a manual update will be required. ## Granting permissions @@ -72,7 +72,7 @@ create role "child_role_name" inherit "parent_role_name"; ### Preventing inheritance -In some cases, you might want to prevent a role from having a child relationship (typically superuser roles). You can prevent inheritance relations using NOINHERIT: +In some cases, you might want to prevent a role from having a child relationship (typically superuser roles). You can prevent inheritance relations using `NOINHERIT`: ```sql alter role "child_role_name" noinherit; @@ -117,7 +117,7 @@ For running commands via the Supabase UI. ### `supabase_admin` -Supabase Administrative role for maintaining your database. +An internal role Supabase uses for administrative tasks, such as running upgrades and automations. ## Resources diff --git a/apps/docs/content/guides/database/postgres/row-level-security.mdx b/apps/docs/content/guides/database/postgres/row-level-security.mdx index 26ced6b8455..d2090a1ffb7 100644 --- a/apps/docs/content/guides/database/postgres/row-level-security.mdx +++ b/apps/docs/content/guides/database/postgres/row-level-security.mdx @@ -9,16 +9,25 @@ When you need granular authorization rules, nothing beats Postgres's [Row Level ## Row Level Security in Supabase + + +Supabase allows convenient and secure data access from the browser, as long as you enable RLS. + +RLS _must_ always be enabled on any tables stored in an exposed schema. By default, this is the `public` schema. + +RLS is enabled by default on tables created with the Table Editor in the dashboard. If you create one in raw SQL or with the SQL editor, remember to enable RLS yourself: + +```sql +alter table . +enable row level security; +``` + + + RLS is incredibly powerful and flexible, allowing you to write complex SQL rules that fit your unique business needs. RLS can be [combined with Supabase Auth](/docs/guides/database/postgres/row-level-security) for end-to-end user security from the browser to the database. RLS is a Postgres primitive and can provide "[defense in depth]()" to protect your data from malicious actors even when accessed through third-party tooling. - - -You should _always_ enable RLS on tables created in a public schema. This is done for you when you create a table with the Table Editor. If you create one in raw SQL or with the SQL Editor, remember to enable RLS yourself. - - - ## Policies [Policies](https://www.postgresql.org/docs/current/sql-createpolicy.html) are Postgres's rule engine. Policies are easy to understand once you get the hang of them. Each policy is attached to a table, and the policy is executed every time a table is accessed. diff --git a/apps/docs/content/guides/database/postgres/setup-replication-external.mdx b/apps/docs/content/guides/database/postgres/setup-replication-external.mdx index 5418c47051a..8621755ff4f 100644 --- a/apps/docs/content/guides/database/postgres/setup-replication-external.mdx +++ b/apps/docs/content/guides/database/postgres/setup-replication-external.mdx @@ -25,7 +25,7 @@ select pg_create_logical_replication_slot('example_slot', 'pgoutput'); 3. Now we will connect to our **external database** and subscribe to our `publication` Note: ): - + This will need a direct connection to your database and you can find the connection info in the [Dashboard](https://supabase.com/dashboard/project/_/settings/database). @@ -42,7 +42,7 @@ PUBLICATION example_pub WITH (copy_data = true, create_slot=false, slot_name=example_slot); ``` - + `create_slot` is set to `false` because `slot_name` is provided and the slot was already created in Step 2. To copy data from before the slot was created, set `copy_data` to `true`. diff --git a/apps/docs/content/guides/database/postgres/timeouts.mdx b/apps/docs/content/guides/database/postgres/timeouts.mdx index 9033debd47f..88d7fdc359a 100644 --- a/apps/docs/content/guides/database/postgres/timeouts.mdx +++ b/apps/docs/content/guides/database/postgres/timeouts.mdx @@ -71,8 +71,7 @@ alter role example_role set statement_timeout = '10min'; -- could also use secon ``` - If you are changing the timeout for the Supabase Client API calls, you will need to reload - postgREST to reflect the timeout changes by running the following script: + If you are changing the timeout for the Supabase Client API calls, you will need to reload PostgREST to reflect the timeout changes by running the following script: ```sql NOTIFY pgrst, 'reload config'; @@ -161,16 +160,16 @@ Go to the [Query Performance page](/dashboard/project/_/advisors/query-performan Each API server uses a designated user for connecting to the database: -| Role | API/Tool | -| -------------------------- | ------------------------------------------------------------------------- | -| supabase_admin | Used by Supabase to configure projects and for monitoring | -| authenticator | PostgREST | -| supabase_auth_admin | Auth | -| supabase_storage_admin | Storage | -| supabase_realtime_admin | Realtime | -| supabase_replication_admin | Synchronizes Read Replicas | -| postgres | Supabase Dashboard and External Tools (e.g., Prisma, SQLAlchemy, PSQL...) | -| Custom roles | External Tools (e.g., Prisma, SQLAlchemy, PSQL...) | +| Role | API/Tool | +| ---------------------------- | ------------------------------------------------------------------------- | +| `supabase_admin` | Used by Supabase to configure projects and for monitoring | +| `authenticator` | PostgREST | +| `supabase_auth_admin` | Auth | +| `supabase_storage_admin` | Storage | +| `supabase_realtime_admin` | Realtime | +| `supabase_replication_admin` | Synchronizes Read Replicas | +| `postgres` | Supabase Dashboard and External Tools (e.g., Prisma, SQLAlchemy, PSQL...) | +| Custom roles | External Tools (e.g., Prisma, SQLAlchemy, PSQL...) | Filter by the `parsed.user_name` field to only retrieve logs made by specific users: diff --git a/apps/docs/content/guides/database/prisma.mdx b/apps/docs/content/guides/database/prisma.mdx index 2f95850a507..dbb1606d7d3 100644 --- a/apps/docs/content/guides/database/prisma.mdx +++ b/apps/docs/content/guides/database/prisma.mdx @@ -17,7 +17,7 @@ If you plan to solely use Prisma instead of the Supabase Data API (PostgREST), t - - In the [SQL Editor](https://supabase.com/dashboard/project/_/sql/new), create a Prisma db-user with full privileges on the public schema. + - In the [SQL Editor](https://supabase.com/dashboard/project/_/sql/new), create a Prisma DB user with full privileges on the public schema. - This gives you better control over Prisma's access and makes it easier to monitor using Supabase tools like the [Query Performance Dashboard](https://supabase.com/dashboard/project/_/advisors/query-performance) and [Log Explorer](https://supabase.com/dashboard/project/_/logs/explorer). @@ -56,7 +56,7 @@ If you plan to solely use Prisma instead of the Supabase Data API (PostgREST), t - Create a new prisma Project on your computer + Create a new Prisma Project on your computer @@ -98,11 +98,11 @@ If you plan to solely use Prisma instead of the Supabase Data API (PostgREST), t ```bash yarn init -y - yarn install prisma typescript ts-node @types/node --save-dev + yarn add prisma typescript ts-node @types/node --save-dev - yarn dlx tsc --init + npx tsc --init - yarn dlx prisma init + npx prisma init ``` @@ -166,7 +166,7 @@ If you plan to solely use Prisma instead of the Supabase Data API (PostgREST), t postgres://prisma.[PROJECT-REF]... ``` - In your schema.prisma file, edit your datasource db configs to reference your DIRECT_URL + In your schema.prisma file, edit your `datasource db` configs to reference your DIRECT_URL ```text schema.prisma datasource db { provider = "postgresql" @@ -241,7 +241,7 @@ If you plan to solely use Prisma instead of the Supabase Data API (PostgREST), t ```bash - yarn dlx prisma migrate dev --name first_prisma_migration + npx prisma migrate dev --name first_prisma_migration ``` @@ -269,7 +269,7 @@ If you plan to solely use Prisma instead of the Supabase Data API (PostgREST), t npx prisma db pull ``` - Create a migraton file + Create a migration file ```bash mkdir -p prisma/migrations/0_init_supabase ``` @@ -295,7 +295,7 @@ If you plan to solely use Prisma instead of the Supabase Data API (PostgREST), t pnpx prisma db pull ``` - Create a migraton file + Create a migration file ```bash mkdir -p prisma/migrations/0_init_supabase ``` @@ -318,17 +318,17 @@ If you plan to solely use Prisma instead of the Supabase Data API (PostgREST), t ```bash - yarn dlx prisma db pull + npx prisma db pull ``` - Create a migraton file + Create a migration file ```bash mkdir -p prisma/migrations/0_init_supabase ``` Synchronize the migrations ```bash - yarn dlx prisma migrate diff \ + npx prisma migrate diff \ --from-empty \ --to-schema-datamodel prisma/schema.prisma \ --script > prisma/migrations/0_init_supabase/migration.sql @@ -338,7 +338,7 @@ If you plan to solely use Prisma instead of the Supabase Data API (PostgREST), t ```bash - yarn dlx prisma migrate resolve --applied 0_init_supabase + npx prisma migrate resolve --applied 0_init_supabase ``` @@ -347,7 +347,7 @@ If you plan to solely use Prisma instead of the Supabase Data API (PostgREST), t bunx prisma db pull ``` - Create a migraton file + Create a migration file ```bash mkdir -p prisma/migrations/0_init_supabase ``` @@ -378,7 +378,7 @@ If you plan to solely use Prisma instead of the Supabase Data API (PostgREST), t - Install the prisma client and generate its model + Install the Prisma client and generate its model @@ -403,8 +403,8 @@ If you plan to solely use Prisma instead of the Supabase Data API (PostgREST), t ```sh - yarn install @prisma/client - yarn dlx prisma generate + yarn add @prisma/client + npx prisma generate ``` diff --git a/apps/docs/content/guides/database/supavisor.mdx b/apps/docs/content/guides/database/supavisor.mdx index 007e57e7dcf..1ee0aedd6df 100644 --- a/apps/docs/content/guides/database/supavisor.mdx +++ b/apps/docs/content/guides/database/supavisor.mdx @@ -5,13 +5,13 @@ subtitle: Troubleshooting Supavisor errors Supavisor logs are available under [Pooler Logs](/dashboard/project/_/logs/pooler-logs) in the Dashboard. The following are common errors and their solutions: -| Error Type | Description | Resolution Link | -| ------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | -| Max client connections reached | This error happens when the number of connections to Supavisor is more than [the allowed limit of your compute add-on](https://supabase.com/docs/guides/platform/compute-add-ons). | Follow this [guide](https://github.com/orgs/supabase/discussions/22305) to resolve. | -| Connection failed \{:error, :eaddrnotavail} to 'db.xxx.supabase.co':5432 | Supavisor cannot connect to the customer database. This is usually caused if the target database is unable to respond. | N/A | -| Connection failed \{:error, :nxdomain} to 'db.xxx.supabase.co':5432 | Supavisor cannot connect to the customer database. This is usually caused if the target database is unable to respond. | N/A | -| Connection closed when state was authentication | This error happens when either the database doesn’t exist or if the user doesn't have the right credentials. | N/A | -| Subscribe error: \{:error, :worker_not_found} | This log event is emitted when the client tries to connect to the database, but Supavisor does not have the necessary information to route the connection. Try reconnecting to the database as it can take some time for the project information to propagate to Supavisor. | N/A | -| Subscribe error: \{:error, \{:badrpc, \{:error, \{:erpc, :timeout}}}} | This is a timeout error when the communication between different Supavisor nodes takes longer than expected. Try reconnecting to the database. | N/A | -| Terminating with reason :client_termination when state was :busy | This error happens when the client terminates the connection before the connection with the database is completed. | N/A | -| Error: received invalid response to GSSAPI negotiation: S | This error happens due to "gssencmode" parameter not set to disabled. | Follow this [guide](https://github.com/orgs/supabase/discussions/30173) to resolve. | +| Error Type | Description | Resolution Link | +| ------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | +| Max client connections reached | This error happens when the number of connections to Supavisor is more than [the allowed limit of your compute add-on](https://supabase.com/docs/guides/platform/compute-add-ons). | Follow this [guide](https://github.com/orgs/supabase/discussions/22305) to resolve. | +| Connection failed `{:error, :eaddrnotavail}` to 'db.xxx.supabase.co':5432 | Supavisor cannot connect to the customer database. This is usually caused if the target database is unable to respond. | N/A | +| Connection failed `{:error, :nxdomain}` to 'db.xxx.supabase.co':5432 | Supavisor cannot connect to the customer database. This is usually caused if the target database is unable to respond. | N/A | +| Connection closed when state was authentication | This error happens when either the database doesn’t exist or if the user doesn't have the right credentials. | N/A | +| Subscribe error: `{:error, :worker_not_found}` | This log event is emitted when the client tries to connect to the database, but Supavisor does not have the necessary information to route the connection. Try reconnecting to the database as it can take some time for the project information to propagate to Supavisor. | N/A | +| Subscribe error: `{:error, {:badrpc, {:error, {:erpc, :timeout}}}}` | This is a timeout error when the communication between different Supavisor nodes takes longer than expected. Try reconnecting to the database. | N/A | +| Terminating with reason :client_termination when state was :busy | This error happens when the client terminates the connection before the connection with the database is completed. | N/A | +| Error: received invalid response to GSSAPI negotiation: S | This error happens due to `gssencmode` parameter not set to disabled. | Follow this [guide](https://github.com/orgs/supabase/discussions/30173) to resolve. | diff --git a/apps/docs/content/guides/database/tables.mdx b/apps/docs/content/guides/database/tables.mdx index 71a2d2c8fcd..285e33feceb 100644 --- a/apps/docs/content/guides/database/tables.mdx +++ b/apps/docs/content/guides/database/tables.mdx @@ -10,6 +10,8 @@ Tables are where you store your data. Tables are similar to excel spreadsheets. They contain columns and rows. For example, this table has 3 "columns" (`id`, `name`, `description`) and 4 "rows" of data: +{/* supa-mdx-lint-disable Rule003Spelling */} + | `id` | `name` | `description` | | ---- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 1 | The Phantom Menace | Two Jedi escape a hostile blockade to find allies and come across a young boy who may bring balance to the Force. | @@ -17,6 +19,8 @@ For example, this table has 3 "columns" (`id`, `name`, `description`) and 4 "row | 3 | Revenge of the Sith | As Obi-Wan pursues a new threat, Anakin acts as a double agent between the Jedi Council and Palpatine and is lured into a sinister plan to rule the galaxy. | | 4 | Star Wars | Luke Skywalker joins forces with a Jedi Knight, a cocky pilot, a Wookiee and two droids to save the galaxy from the Empire's world-destroying battle station. | +{/* supa-mdx-lint-enable Rule003Spelling */} + There are a few important differences from a spreadsheet, but it's a good starting point if you're new to Relational databases. ## Creating tables @@ -91,51 +95,51 @@ Every column is a predefined type. PostgreSQL provides many [default types](http
Show/Hide default data types -| `Name` | `Aliases` | `Description` | -| --------------------------------- | ----------- | ---------------------------------------------------------------- | -| bigint | int8 | signed eight-byte integer | -| bigserial | serial8 | autoincrementing eight-byte integer | -| bit | | fixed-length bit string | -| bit varying | varbit | variable-length bit string | -| boolean | bool | logical Boolean (true/false) | -| box | | rectangular box on a plane | -| bytea | | binary data (“byte array”) | -| character | char | fixed-length character string | -| character varying | varchar | variable-length character string | -| cidr | | IPv4 or IPv6 network address | -| circle | | circle on a plane | -| date | | calendar date (year, month, day) | -| double precision | float8 | double precision floating-point number (8 bytes) | -| inet | | IPv4 or IPv6 host address | -| integer | int, int4 | signed four-byte integer | -| interval \[ fields \] | | time span | -| json | | textual JSON data | -| jsonb | | binary JSON data, decomposed | -| line | | infinite line on a plane | -| lseg | | line segment on a plane | -| macaddr | | MAC (Media Access Control) address | -| macaddr8 | | MAC (Media Access Control) address (EUI-64 format) | -| money | | currency amount | -| numeric | decimal | exact numeric of selectable precision | -| path | | geometric path on a plane | -| pg_lsn | | PostgreSQL Log Sequence Number | -| pg_snapshot | | user-level transaction ID snapshot | -| point | | geometric point on a plane | -| polygon | | closed geometric path on a plane | -| real | float4 | single precision floating-point number (4 bytes) | -| smallint | int2 | signed two-byte integer | -| smallserial | serial2 | autoincrementing two-byte integer | -| serial | serial4 | autoincrementing four-byte integer | -| text | | variable-length character string | -| time \[ without time zone \] | | time of day (no time zone) | -| time with time zone | timetz | time of day, including time zone | -| timestamp \[ without time zone \] | | date and time (no time zone) | -| timestamp with time zone | timestamptz | date and time, including time zone | -| tsquery | | text search query | -| tsvector | | text search document | -| txid_snapshot | | user-level transaction ID snapshot (deprecated; see pg_snapshot) | -| uuid | | universally unique identifier | -| xml | | XML data | +| `Name` | `Aliases` | `Description` | +| --------------------------------- | ------------- | ---------------------------------------------------------------- | +| `bigint` | `int8` | signed eight-byte integer | +| `bigserial` | `serial8` | autoincrementing eight-byte integer | +| `bit` | | fixed-length bit string | +| `bit varying` | `varbit` | variable-length bit string | +| `boolean` | `bool` | logical Boolean (true/false) | +| `box` | | rectangular box on a plane | +| `bytea` | | binary data (“byte array”) | +| `character` | `char` | fixed-length character string | +| `character varying` | `varchar` | variable-length character string | +| `cidr` | | IPv4 or IPv6 network address | +| `circle` | | circle on a plane | +| `date` | | calendar date (year, month, day) | +| `double precision` | `float8` | double precision floating-point number (8 bytes) | +| `inet` | | IPv4 or IPv6 host address | +| `integer` | `int`, `int4` | signed four-byte integer | +| `interval [ fields ]` | | time span | +| `json` | | textual JSON data | +| `jsonb` | | binary JSON data, decomposed | +| `line` | | infinite line on a plane | +| `lseg` | | line segment on a plane | +| `macaddr` | | MAC (Media Access Control) address | +| `macaddr8` | | MAC (Media Access Control) address (EUI-64 format) | +| `money` | | currency amount | +| `numeric` | `decimal` | exact numeric of selectable precision | +| `path` | | geometric path on a plane | +| `pg_lsn` | | PostgreSQL Log Sequence Number | +| `pg_snapshot` | | user-level transaction ID snapshot | +| `point` | | geometric point on a plane | +| `polygon` | | closed geometric path on a plane | +| `real` | `float4` | single precision floating-point number (4 bytes) | +| `smallint` | `int2` | signed two-byte integer | +| `smallserial` | `serial2` | autoincrementing two-byte integer | +| `serial` | `serial4` | autoincrementing four-byte integer | +| `text` | | variable-length character string | +| `time [ without time zone ]` | | time of day (no time zone) | +| `time with time zone` | `timetz` | time of day, including time zone | +| `timestamp [ without time zone ]` | | date and time (no time zone) | +| `timestamp with time zone` | `timestamptz` | date and time, including time zone | +| `tsquery` | | text search query | +| `tsvector` | | text search document | +| `txid_snapshot` | | user-level transaction ID snapshot (deprecated; see pg_snapshot) | +| `uuid` | | universally unique identifier | +| `xml` | | XML data |
@@ -300,7 +304,7 @@ supabase ### Bulk data loading When inserting large data sets it's best to use PostgreSQL's [COPY](https://www.postgresql.org/docs/current/sql-copy.html) command. -This loads data directly from a file into a table. There are several file formats available for copying data: text, csv, binary, JSON, etc. +This loads data directly from a file into a table. There are several file formats available for copying data: text, CSV, binary, JSON, etc. For example, if you wanted to load a CSV file into your movies table: @@ -442,12 +446,16 @@ Say we have the following tables from a database of a university: **`students`** +{/* supa-mdx-lint-disable Rule003Spelling */} + | id | name | type | | --- | ---------------- | ------------- | | 1 | Princess Leia | undergraduate | | 2 | Yoda | graduate | | 3 | Anakin Skywalker | graduate | +{/* supa-mdx-lint-enable Rule003Spelling */} + **`courses`** | id | title | code | diff --git a/apps/docs/content/guides/database/testing.mdx b/apps/docs/content/guides/database/testing.mdx index be1a9a06696..8054ecc2b7f 100644 --- a/apps/docs/content/guides/database/testing.mdx +++ b/apps/docs/content/guides/database/testing.mdx @@ -18,7 +18,7 @@ You can use the Supabase CLI to test your database. The minimum required version ## Creating a test -Create a tests folder inside the supabase folder: +Create a tests folder inside the `supabase` folder: ```bash mkdir -p ./supabase/tests/database diff --git a/apps/docs/content/guides/deployment/branching.mdx b/apps/docs/content/guides/deployment/branching.mdx index 1eec46d5754..7f9555d4651 100644 --- a/apps/docs/content/guides/deployment/branching.mdx +++ b/apps/docs/content/guides/deployment/branching.mdx @@ -50,7 +50,7 @@ Preview Branch instances contain no data by default. You must include a seed fil ## Git providers -To manage code changes, your Supabase project must be connected to a Git repository. At this stage, we only support [GitHub](#branching-with-github). If you are interested in other Git providers, join the [discussion](https://github.com/orgs/supabase/discussions/18936) for GitLab, BitBucket, and non-Git based Branching. +To manage code changes, your Supabase project must be connected to a Git repository. At this stage, we only support [GitHub](#branching-with-github). If you are interested in other Git providers, join the [discussion](https://github.com/orgs/supabase/discussions/18936) for GitLab, Bitbucket, and non-Git based Branching. ### Branching with GitHub @@ -337,7 +337,7 @@ The Supabase CLI provides two options: [manual migrations](https://supabase.com/ - Start supabase locally: + Start Supabase locally: diff --git a/apps/docs/content/guides/deployment/database-migrations.mdx b/apps/docs/content/guides/deployment/database-migrations.mdx index 11059b17bd8..2502d360ae2 100644 --- a/apps/docs/content/guides/deployment/database-migrations.mdx +++ b/apps/docs/content/guides/deployment/database-migrations.mdx @@ -135,7 +135,7 @@ supabase migration up Finally, you should see the `department` column added to your `employees` table in the local Dashboard. - + View the [complete code](https://github.com/supabase/supabase/tree/master/examples/database/employees) for this example on GitHub. diff --git a/apps/docs/content/guides/deployment/going-into-prod.mdx b/apps/docs/content/guides/deployment/going-into-prod.mdx index dba01cb4e90..97d8ea94fb3 100644 --- a/apps/docs/content/guides/deployment/going-into-prod.mdx +++ b/apps/docs/content/guides/deployment/going-into-prod.mdx @@ -42,7 +42,7 @@ After developing your project and deciding it's Production Ready, you should run - Tools like [k6](https://k6.io/) can simulate traffic from many different users. - Upgrade your database if you require more resources. If you need anything beyond what is listed, contact enterprise@supabase.io. - If you are expecting a surge in traffic (for a big launch) and are on a Team or Enterprise Plan, [contact support](https://supabase.com/dashboard/support/new) with more details about your launch and we'll help keep an eye on your project. -- If you expect your database size to be > 4 GB, [enable](https://supabase.com/dashboard/project/_/settings/addons?panel=pitr) the Point in Time Recovery (PITR) addon. Daily backups can take up resources from your database when the backup is in progress. PITR is more resource efficient, since only the changes to the database are backed up. +- If you expect your database size to be > 4 GB, [enable](https://supabase.com/dashboard/project/_/settings/addons?panel=pitr) the Point in Time Recovery (PITR) add-on. Daily backups can take up resources from your database when the backup is in progress. PITR is more resource efficient, since only the changes to the database are backed up. - Check and review issues in your database using [Performance Advisor](https://supabase.com/dashboard/project/_/database/performance-advisor). ## Availability @@ -75,7 +75,7 @@ After developing your project and deciding it's Production Ready, you should run | ------------------------------------------------ | -------------------------------------------------------------- | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | All endpoints that send emails | `/auth/v1/signup` `/auth/v1/recover` `/auth/v1/user`[^1] | Sum of combined requests | As of 3 Sep 2024, this has been updated to auth.rate_limits.email.inbuilt_smtp_per_hour.value emails per hour. You can only change this with your own [custom SMTP setup](/docs/guides/auth/auth-smtp). | | All endpoints that send One-Time-Passwords (OTP) | `/auth/v1/otp` | Sum of combined requests | Defaults to 360 OTPs per hour. Is customizable. | -| Send OTPs or magiclinks | `/auth/v1/otp` | Last request | Defaults to 60 seconds window before a new request is allowed. Is customizable. | +| Send OTPs or magic links | `/auth/v1/otp` | Last request | Defaults to 60 seconds window before a new request is allowed. Is customizable. | | Signup confirmation request | `/auth/v1/signup` | Last request | Defaults to 60 seconds window before a new request is allowed. Is customizable. | | Password Reset Request | `/auth/v1/recover` | Last request | Defaults to 60 seconds window before a new request is allowed. Is customizable. | | Verification requests | `/auth/v1/verify` | IP Address | 360 requests per hour (with bursts up to 30 requests) | @@ -97,7 +97,7 @@ After developing your project and deciding it's Production Ready, you should run - When working with enterprise systems, email scanners may scan and make a `GET` request to the reset password link or sign up link in your email. Since links in Supabase Auth are single use, a user who opens an email post-scan to click on a link will receive an error. To get around this problem, consider altering the email template to replace the original magic link with a link to a domain you control. The domain can present the user with a "Sign-in" button which redirect the user to the original magic link URL when clicked. -- When using a custom SMTP service, some services might have link tracking enabled which may overwrite or malform the email confirmation links sent by Supabase Auth. To prevent this from happening, we recommend that you disable link tracking when using a custom SMTP service. +- When using a custom SMTP service, some services might have link tracking enabled which may overwrite or disform the email confirmation links sent by Supabase Auth. To prevent this from happening, we recommend that you disable link tracking when using a custom SMTP service. ## Next steps diff --git a/apps/docs/content/guides/deployment/managing-environments.mdx b/apps/docs/content/guides/deployment/managing-environments.mdx index bde4fefce16..b4b90e37676 100644 --- a/apps/docs/content/guides/deployment/managing-environments.mdx +++ b/apps/docs/content/guides/deployment/managing-environments.mdx @@ -146,7 +146,7 @@ Commit the new migration script to git and you are ready to deploy. -Alternatively, you may pass in the `--use-migra` experimental flag to generate a more concise migration using [migra](https://github.com/djrobstep/migra). +Alternatively, you may pass in the `--use-migra` experimental flag to generate a more concise migration using [`migra`](https://github.com/djrobstep/migra). Without the `-f` file flag, the output is written to stdout by default. @@ -360,7 +360,7 @@ Once pushed, check that the migration version is up to date for both local and r supabase migration list ``` -### Permission denied on db pull +### Permission denied on `db pull` If you have been using Supabase hosted projects for a long time, you might encounter the following permission error when executing `db pull`. @@ -378,7 +378,7 @@ grant all on all functions in schema graphql to postgres, anon, authenticated, s grant all on all sequences in schema graphql to postgres, anon, authenticated, service_role; ``` -### Permission denied on db push +### Permission denied on `db push` If you created a table through Supabase dashboard, and your new migration script contains `ALTER TABLE` statements, you might run into permission error when applying them on staging or production databases. diff --git a/apps/docs/content/guides/deployment/shared-responsibility-model.mdx b/apps/docs/content/guides/deployment/shared-responsibility-model.mdx index 1e57ad99edf..a4d8ed0fb54 100644 --- a/apps/docs/content/guides/deployment/shared-responsibility-model.mdx +++ b/apps/docs/content/guides/deployment/shared-responsibility-model.mdx @@ -61,7 +61,7 @@ Supabase offers a lot of opportunities for flexibly integrating with third-party - Calls to external APIs within Postgres functions or triggers - Calls to external APIs within Edge Functions -You are free to use and integrate with any service, but you're also responsible for ensuring that the performance, availability, and security of the services you use match up with your application's requirements. We do not monitor for outages or performance issues within integrations with third-party services. Depending on the implementation, an issue with such an integration could also result in performance degradataion or an outage for your Supabase project. +You are free to use and integrate with any service, but you're also responsible for ensuring that the performance, availability, and security of the services you use match up with your application's requirements. We do not monitor for outages or performance issues within integrations with third-party services. Depending on the implementation, an issue with such an integration could also result in performance degradation or an outage for your Supabase project. If your application architecture relies on such integrations, you should monitor the relevant logs and metrics to ensure optimal performance. diff --git a/apps/docs/content/guides/functions/ai-models.mdx b/apps/docs/content/guides/functions/ai-models.mdx index 60b9df79bcd..dcf300e1d04 100644 --- a/apps/docs/content/guides/functions/ai-models.mdx +++ b/apps/docs/content/guides/functions/ai-models.mdx @@ -165,7 +165,7 @@ curl --get "http://localhost:54321/functions/v1/ollama-test" \
-Follow the [Llamafile Quickstart](https://github.com/Mozilla-Ocho/llamafile?tab=readme-ov-file#quickstart) to download an run a llamafile locally on your machine. +Follow the [Llamafile Quickstart](https://github.com/Mozilla-Ocho/llamafile?tab=readme-ov-file#quickstart) to download an run a Llamafile locally on your machine. Since Llamafile provides an OpenAI API compatible server, you can either use it with `@supabase/functions-js` or with the official OpenAI Deno SDK. @@ -190,7 +190,7 @@ Create a new function with the following code supabase functions new llamafile-test ``` - + Note that the model parameter doesn't have any effect here! The model depends on which Llamafile is currently running! @@ -246,7 +246,7 @@ Create a new function with the following code supabase functions new llamafile-test ``` - + Note that the model parameter doesn't have any effect here! The model depends on which Llamafile is currently running! diff --git a/apps/docs/content/guides/functions/compression.mdx b/apps/docs/content/guides/functions/compression.mdx index 77964769461..c735106fb99 100644 --- a/apps/docs/content/guides/functions/compression.mdx +++ b/apps/docs/content/guides/functions/compression.mdx @@ -43,7 +43,7 @@ Deno.serve(async (req) => { }) ``` - + Edge functions have a runtime memory limit of 150MB. Overly large compressed payloads may result in an out-of-memory error. diff --git a/apps/docs/content/guides/functions/dependencies.mdx b/apps/docs/content/guides/functions/dependencies.mdx index b9abf701bf6..d13bd7fa187 100644 --- a/apps/docs/content/guides/functions/dependencies.mdx +++ b/apps/docs/content/guides/functions/dependencies.mdx @@ -34,7 +34,7 @@ Learn more about npm specifiers and Node built-in APIs in [Deno's documentation] ### JSR -You can import JS modules published to [JSR](https://jsr.io/) (eg: Deno's standard library), using the `jsr:` specifier: +You can import JS modules published to [JSR](https://jsr.io/) (e.g.: Deno's standard library), using the `jsr:` specifier: ```ts import path from 'jsr:@std/path@1.0.8' @@ -57,7 +57,7 @@ There are two ways to manage your dependencies in Supabase Edge Functions: ### Using deno.json (recommended) - + This feature requires Supabase CLI version 1.215.0 or higher. diff --git a/apps/docs/content/guides/functions/ephemeral-storage.mdx b/apps/docs/content/guides/functions/ephemeral-storage.mdx index 55bc58780ec..4d567230977 100644 --- a/apps/docs/content/guides/functions/ephemeral-storage.mdx +++ b/apps/docs/content/guides/functions/ephemeral-storage.mdx @@ -14,7 +14,7 @@ Ephemeral storage will reset on each function invocation. This means the files y Here are some use cases where ephemeral storage can be useful: - Unzip an archive of CSVs and then add them as records to the DB -- Custom image manipulation workflows (using [MagickWasm](https://supabase.com/docs/guides/functions/examples/image-manipulation)) +- Custom image manipulation workflows (using [`magick-wasm`](https://supabase.com/docs/guides/functions/examples/image-manipulation)) You can use [Background Tasks](https://supabase.com/docs/guides/functions/background-tasks) to handle slow file processing outside of a request. @@ -45,9 +45,9 @@ Deno.serve(async (req) => { ### Unavailable APIs -Currently, the synchronous APIs (eg: `Deno.writeFileSync` or `Deno.mkdirSync`) for creating or writing files are not supported. +Currently, the synchronous APIs (e.g. `Deno.writeFileSync` or `Deno.mkdirSync`) for creating or writing files are not supported. -You can use sync variations of read APIs (eg: `Deno.readFileSync`). +You can use sync variations of read APIs (e.g. `Deno.readFileSync`). ### Limits diff --git a/apps/docs/content/guides/functions/examples/auth-send-email-hook-react-email-resend.mdx b/apps/docs/content/guides/functions/examples/auth-send-email-hook-react-email-resend.mdx index f7c7ef537bf..3ea96f95d88 100644 --- a/apps/docs/content/guides/functions/examples/auth-send-email-hook-react-email-resend.mdx +++ b/apps/docs/content/guides/functions/examples/auth-send-email-hook-react-email-resend.mdx @@ -6,7 +6,7 @@ tocVideo: 'tlA7BomSCgU' Use the [send email hook](/docs/guides/auth/auth-hooks/send-email-hook?queryGroups=language&language=http) to send custom auth emails with [React Email](https://react.email/) and [Resend](https://resend.com/) in Supabase Edge Functions. - + Prefer to jump straight to the code? [Check out the example on GitHub](https://github.com/supabase/supabase/tree/master/examples/edge-functions/supabase/functions/auth-hook-react-email-resend). @@ -251,7 +251,7 @@ const code = { } ``` - + You can find a selection of React Email templates in the [React Email Examples](https://react.email/examples). @@ -282,7 +282,7 @@ RESEND_API_KEY=your_resend_api_key SEND_EMAIL_HOOK_SECRET= ``` - + You can generate the secret in the [Auth Hooks](/dashboard/project/_/auth/hooks) section of the Supabase dashboard. Make sure to remove the `v1,whsec_` prefix! diff --git a/apps/docs/content/guides/functions/examples/image-manipulation.mdx b/apps/docs/content/guides/functions/examples/image-manipulation.mdx index 0c9ac6c99bf..551da2e9d81 100644 --- a/apps/docs/content/guides/functions/examples/image-manipulation.mdx +++ b/apps/docs/content/guides/functions/examples/image-manipulation.mdx @@ -6,9 +6,9 @@ description: 'How to optimize and transform images using Edge Functions.' Supabase Storage has [out-of-the-box support](https://supabase.com/docs/guides/storage/serving/image-transformations?queryGroups=language&language=js) for the most common image transformations and optimizations you need. If you need to do anything custom beyond what Supabase Storage provides, you can use Edge Functions to write custom image manipulation scripts. -In this example, we will use [magick-wasm](https://github.com/dlemstra/magick-wasm) to perform image manipulations. `magick-wasm` is the WebAssembly port of the popular ImageMagick library and supports processing over 100 file formats. +In this example, we will use [`magick-wasm`](https://github.com/dlemstra/magick-wasm) to perform image manipulations. `magick-wasm` is the WebAssembly port of the popular ImageMagick library and supports processing over 100 file formats. - + Edge Functions currently doesn't support image processing libraries such as `Sharp`, which depend on native libraries. Only WASM-based libraries are supported. @@ -69,7 +69,7 @@ supabase functions deploy image-blur ``` - + Hosted Edge Functions have [limits](https://supabase.com/docs/guides/functions/limits) on memory and CPU usage. diff --git a/apps/docs/content/guides/functions/examples/push-notifications.mdx b/apps/docs/content/guides/functions/examples/push-notifications.mdx index 994c7e4267d..cb157e8230f 100644 --- a/apps/docs/content/guides/functions/examples/push-notifications.mdx +++ b/apps/docs/content/guides/functions/examples/push-notifications.mdx @@ -22,7 +22,7 @@ Push notifications are an important part of any mobile app. They allow you to se - [Create a new Supabase project](https://database.new). - Link your project: `supabase link --project-ref your-supabase-project-ref` - - Start supabase locally: `supabase start` + - Start Supabase locally: `supabase start` - Push up the schema: `supabase db push` (schema is defined in [supabase/migrations](https://github.com/supabase/supabase/blob/master/examples/user-management/expo-push-notifications/supabase/migrations/)) ## Expo setup @@ -123,7 +123,7 @@ Push notifications are an important part of any mobile app. They allow you to se Firebase Cloud Messaging (FCM) is a push notification service offered by Google that allows you to send push notifications to your users' devices on iOS, Android, and Web. - + This guide will show you how to send push notifications to your app when a new row is inserted into a table using FCM, Supabase Edge Functions, and database web hooks. ## Supabase setup diff --git a/apps/docs/content/guides/functions/examples/screenshots.mdx b/apps/docs/content/guides/functions/examples/screenshots.mdx index 6fb9f4e0d2a..2e346f9ec9e 100644 --- a/apps/docs/content/guides/functions/examples/screenshots.mdx +++ b/apps/docs/content/guides/functions/examples/screenshots.mdx @@ -12,6 +12,6 @@ description: 'Take screenshots in Edge Functions with Puppeteer and Browserless. >
-[Puppeteer](https://pptr.dev/) is a handy tool to programmatically take screenshots and generate PDFs. However, trying to do so in Edge Functions can be challenging due to the size restrictions. Luckily there is a [serverless browser offering available](https://www.browserless.io/) that we can connect to via websockets. +[Puppeteer](https://pptr.dev/) is a handy tool to programmatically take screenshots and generate PDFs. However, trying to do so in Edge Functions can be challenging due to the size restrictions. Luckily there is a [serverless browser offering available](https://www.browserless.io/) that we can connect to via WebSockets. Find the code on [GitHub](https://github.com/supabase/supabase/tree/master/examples/edge-functions/supabase/functions/puppeteer). diff --git a/apps/docs/content/guides/functions/examples/semantic-search.mdx b/apps/docs/content/guides/functions/examples/semantic-search.mdx index 7534d6bafae..21e88052359 100644 --- a/apps/docs/content/guides/functions/examples/semantic-search.mdx +++ b/apps/docs/content/guides/functions/examples/semantic-search.mdx @@ -13,7 +13,7 @@ Since Supabase Edge Runtime [v1.36.0](https://github.com/supabase/edge-runtime/r In this tutorial you're implementing three parts: 1. A [`generate-embedding`](https://github.com/supabase/supabase/tree/master/examples/ai/edge-functions/supabase/functions/generate-embedding/index.ts) database webhook edge function which generates embeddings when a content row is added (or updated) in the [`public.embeddings`](https://github.com/supabase/supabase/tree/master/examples/ai/edge-functions/supabase/migrations/20240408072601_embeddings.sql) table. -2. A [`query_embeddings` Postgres function](https://github.com/supabase/supabase/tree/master/examples/ai/edge-functions/supabase/migrations/20240410031515_vector-search.sql) which allows us to perform similarity search from an egde function via [Remote Procedure Call (RPC)](https://supabase.com/docs/guides/database/functions?language=js). +2. A [`query_embeddings` Postgres function](https://github.com/supabase/supabase/tree/master/examples/ai/edge-functions/supabase/migrations/20240410031515_vector-search.sql) which allows us to perform similarity search from an Edge Function via [Remote Procedure Call (RPC)](https://supabase.com/docs/guides/database/functions?language=js). 3. A [`search` edge function](https://github.com/supabase/supabase/tree/master/examples/ai/edge-functions/supabase/functions/search/index.ts) which generates the embedding for the search term, performs the similarity search via RPC function call, and returns the result. You can find the complete example code on [GitHub](https://github.com/supabase/supabase/tree/master/examples/ai/edge-functions) diff --git a/apps/docs/content/guides/functions/examples/send-emails.mdx b/apps/docs/content/guides/functions/examples/send-emails.mdx index 4a28a8285ef..8dc64ae0683 100644 --- a/apps/docs/content/guides/functions/examples/send-emails.mdx +++ b/apps/docs/content/guides/functions/examples/send-emails.mdx @@ -77,7 +77,7 @@ Deploy function to Supabase: supabase functions deploy resend --no-verify-jwt ``` - + When you deploy to Supabase, make sure that your `RESEND_API_KEY` is set in [Edge Function Secrets Management](https://supabase.com/dashboard/project/_/settings/functions) diff --git a/apps/docs/content/guides/functions/limits.mdx b/apps/docs/content/guides/functions/limits.mdx index 9a41065188b..829482dd7ef 100644 --- a/apps/docs/content/guides/functions/limits.mdx +++ b/apps/docs/content/guides/functions/limits.mdx @@ -23,4 +23,4 @@ subtitle: "Limits applied Edge Functions in Supabase's hosted platform." - Outgoing connections to ports `25` and `587` are not allowed. - Serving of HTML content is only supported with [custom domains](/docs/reference/cli/supabase-domains) (Otherwise `GET` requests that return `text/html` will be rewritten to `text/plain`). - Web Worker API (or Node `vm` API) are not available. -- Node Libraries that require multithreading are not supported. Examples: [libvips](https://github.com/libvips/libvips), [sharp](https://github.com/lovell/sharp). +- Node Libraries that require multithreading are not supported. Examples: [`libvips`](https://github.com/libvips/libvips), [sharp](https://github.com/lovell/sharp). diff --git a/apps/docs/content/guides/functions/local-development.mdx b/apps/docs/content/guides/functions/local-development.mdx index 1da210dd2b6..8b8a12f9e96 100644 --- a/apps/docs/content/guides/functions/local-development.mdx +++ b/apps/docs/content/guides/functions/local-development.mdx @@ -13,7 +13,7 @@ You can follow the [Deno guide](https://deno.com/manual@v1.32.5/getting_started/ ## Deno with Visual Studio Code -When using VSCode, you should install both the Deno CLI and the the Deno language server [via this link](vscode:extension/denoland.vscode-deno) or by browsing the extensions in vscode and choosing to install the _Deno_ extension. +When using VSCode, you should install both the Deno CLI and the the Deno language server [via this link](vscode:extension/denoland.vscode-deno) or by browsing the extensions in VSCode and choosing to install the _Deno_ extension. diff --git a/apps/docs/content/guides/functions/pricing.mdx b/apps/docs/content/guides/functions/pricing.mdx new file mode 100644 index 00000000000..90634c298ed --- /dev/null +++ b/apps/docs/content/guides/functions/pricing.mdx @@ -0,0 +1,8 @@ +--- +id: 'functions-pricing' +title: 'Pricing' +--- + + + +For a detailed explanation of how charges are calculated, refer to [Manage Edge Function Invocations usage](/docs/guides/platform/manage-your-usage/edge-function-invocations). diff --git a/apps/docs/content/guides/functions/quickstart.mdx b/apps/docs/content/guides/functions/quickstart.mdx index 19aedd7811c..a7ef88b6e70 100644 --- a/apps/docs/content/guides/functions/quickstart.mdx +++ b/apps/docs/content/guides/functions/quickstart.mdx @@ -30,7 +30,7 @@ If you're using VS code you can have the CLI automatically create helpful Deno s -If you're using an Intellj IDEA editor such as WebStorm, you can use the `--with-intellij-settings` flag with `supabase init` to create an auto generated deno cofig. +If you're using an IntelliJ IDEA editor such as WebStorm, you can use the `--with-intellij-settings` flag with `supabase init` to create an auto generated Deno config. @@ -190,9 +190,9 @@ import_map = './import_map.json' When you create a new Edge Function, it will use TypeScript by default. However, it is possible to write and deploy Edge Functions using pure JavaScript. -Save your Function as a JavaScript file (eg: `index.js`) and then update the `supabase/config.toml` as follows: +Save your Function as a JavaScript file (e.g. `index.js`) and then update the `supabase/config.toml` as follows: - + `entrypoint` is available only in Supabase CLI version 1.215.0 or higher. diff --git a/apps/docs/content/guides/functions/routing.mdx b/apps/docs/content/guides/functions/routing.mdx index 2944dacf500..023e284739f 100644 --- a/apps/docs/content/guides/functions/routing.mdx +++ b/apps/docs/content/guides/functions/routing.mdx @@ -5,8 +5,8 @@ description: 'How to handle custom routing within Edge Functions.' subtitle: 'How to handle custom routing within Edge Functions.' --- -Usually, an Edge Function is written to perform a single action (eg: write a record to the database). However, if your app's logic is split into multiple Edge Functions requests to each action may seem slower. -This is because each Edge Function needs to be booted before serving a request (known as cold starts). If an action is performed less frequently (eg: deleting a record), there is a high-chance of that function experiencing a cold-start. +Usually, an Edge Function is written to perform a single action (e.g. write a record to the database). However, if your app's logic is split into multiple Edge Functions requests to each action may seem slower. +This is because each Edge Function needs to be booted before serving a request (known as cold starts). If an action is performed less frequently (e.g. deleting a record), there is a high-chance of that function experiencing a cold-start. One way to reduce the cold starts and increase performance of your app is to combine multiple actions into a single Edge Function. This way only one instance of the Edge Function needs to be booted and it can handle multiple requests to different actions. For example, we can use a single Edge Function to create a typical CRUD API (create, read, update, delete records). @@ -160,10 +160,10 @@ We should see a response printing `Hello Foo!`. ## Using route parameters -We can use route parameters to capture values at specific URL segments (eg: `/tasks/:taskId/notes/:noteId`). +We can use route parameters to capture values at specific URL segments (e.g. `/tasks/:taskId/notes/:noteId`). Here's an example Edge Function implemented using the Framework for managing tasks using route parameters. -Keep in mind paths must be prefixed by function name (ie. `tasks` in this example). Route parameters can only be used after the function name prefix. +Keep in mind paths must be prefixed by function name (i.e. `tasks` in this example). Route parameters can only be used after the function name prefix. { ## URL patterns API -If you prefer not to use a web framework, you can directly use [URLPattern API](https://developer.mozilla.org/en-US/docs/Web/API/URL_Pattern_API) within your Edge Functions to implement routing. +If you prefer not to use a web framework, you can directly use [URL Pattern API](https://developer.mozilla.org/en-US/docs/Web/API/URL_Pattern_API) within your Edge Functions to implement routing. This is ideal for small apps with only couple of routes and you want to have a custom matching algorithm. Here is an example Edge Function using URL Patterns API: https://github.com/supabase/supabase/blob/master/examples/edge-functions/supabase/functions/restful-tasks/index.ts diff --git a/apps/docs/content/guides/functions/status-codes.mdx b/apps/docs/content/guides/functions/status-codes.mdx index 5925a2ad3bb..dfe712f6d3d 100644 --- a/apps/docs/content/guides/functions/status-codes.mdx +++ b/apps/docs/content/guides/functions/status-codes.mdx @@ -33,7 +33,7 @@ Edge Functions only support these HTTP methods: 'POST', 'GET', 'PUT', 'PATCH', ' ### 500 Internal Server Error -Edge Function throwed an uncaught exception (`WORKER_ERROR`). Check Edge Function logs to find the cause. +Edge Function threw an uncaught exception (`WORKER_ERROR`). Check Edge Function logs to find the cause. ### 503 Service Unavailable diff --git a/apps/docs/content/guides/functions/unit-test.mdx b/apps/docs/content/guides/functions/unit-test.mdx index 5dc3e942fb2..d36ac8f5549 100644 --- a/apps/docs/content/guides/functions/unit-test.mdx +++ b/apps/docs/content/guides/functions/unit-test.mdx @@ -105,7 +105,7 @@ This test case consists of two parts. The first part tests the client library an - The `testHelloWorld` function tests the "Hello-world" Edge Function by invoking it using the Supabase client's `functions.invoke` method. It checks if the response message matches the expected greeting. - We run the tests using the `Deno.test` function, providing a descriptive name for each test case and the corresponding test function. - + Please make sure to replace the placeholders (`supabaseUrl`, `supabaseKey`, `my_table`) with the actual values relevant to your Supabase setup. diff --git a/apps/docs/content/guides/functions/wasm.mdx b/apps/docs/content/guides/functions/wasm.mdx index af0bf32a0f5..b1c26e34de2 100644 --- a/apps/docs/content/guides/functions/wasm.mdx +++ b/apps/docs/content/guides/functions/wasm.mdx @@ -7,7 +7,7 @@ subtitle: 'How to use WebAssembly in Edge Functions.' Edge Functions supports running [WebAssembly (Wasm)](https://developer.mozilla.org/en-US/docs/WebAssembly) modules. WebAssembly is useful if you want to optimize code that's slower to run in JavaScript or require low-level manipulation. -It also gives you the option to port existing libraries written in other languages to be used with JavaScript. For example, [MagickWasm](https://supabase.com/docs/guides/functions/examples/image-manipulation), which does image manipulation and transforms, is a port of an existing C library to WebAssembly. +It also gives you the option to port existing libraries written in other languages to be used with JavaScript. For example, [magick-wasm](https://supabase.com/docs/guides/functions/examples/image-manipulation), which does image manipulation and transforms, is a port of an existing C library to WebAssembly. ### Writing a Wasm module @@ -62,7 +62,7 @@ lines={[[1, -1]]} meta="index.ts" /> - + Supabase Edge Functions currently use Deno 1.46. From [Deno 2.1, importing Wasm modules](https://deno.com/blog/v2.1) will require even less boilerplate code. @@ -71,7 +71,7 @@ meta="index.ts" Before deploying the Edge Function, we need to ensure it bundles the Wasm module with it. We can do this by defining it in the `static_files` for the function in `superbase/config.toml`. - + You will need update Supabase CLI to 2.7.0 or higher for the `static_files` support. diff --git a/apps/docs/content/guides/functions/websockets.mdx b/apps/docs/content/guides/functions/websockets.mdx index 0c0c82201e8..c1c1c214319 100644 --- a/apps/docs/content/guides/functions/websockets.mdx +++ b/apps/docs/content/guides/functions/websockets.mdx @@ -1,5 +1,5 @@ --- -id: 'function-websockets' +id: 'function-WebSockets' title: 'Handling WebSockets' description: 'How to handle WebSocket connections in Edge Functions' subtitle: 'How to handle WebSocket connections in Edge Functions' @@ -25,8 +25,8 @@ Here are some basic examples of setting up WebSocket servers using Deno and Node Deno.serve(req => { const upgrade = req.headers.get("upgrade") || ""; - if (upgrade.toLowerCase() != "websocket") { - return new Response("request isn't trying to upgrade to websocket.", { status: 400 }); + if (upgrade.toLowerCase() != "WebSocket") { + return new Response("request isn't trying to upgrade to WebSocket.", { status: 400 }); } const { socket, response } = Deno.upgradeWebSocket(req); @@ -130,8 +130,8 @@ Deno.env.get("SUPABASE_SERVICE_ROLE_KEY"), Deno.serve(req => { const upgrade = req.headers.get("upgrade") || ""; - if (upgrade.toLowerCase() != "websocket") { - return new Response("request isn't trying to upgrade to websocket.", { status: 400 }); + if (upgrade.toLowerCase() != "WebSocket") { + return new Response("request isn't trying to upgrade to WebSocket.", { status: 400 }); } // Please be aware query params may be logged in some logging systems. @@ -179,8 +179,8 @@ const supabase = createClient( Deno.serve(req => { const upgrade = req.headers.get("upgrade") || ""; - if (upgrade.toLowerCase() != "websocket") { - return new Response("request isn't trying to upgrade to websocket.", { status: 400 }); + if (upgrade.toLowerCase() != "WebSocket") { + return new Response("request isn't trying to upgrade to WebSocket.", { status: 400 }); } // Sec-WebScoket-Protocol may return multiple protocol values `jwt-TOKEN, value1, value 2` diff --git a/apps/docs/content/guides/getting-started.mdx b/apps/docs/content/guides/getting-started.mdx index 0586275ec12..3e50b8ff57b 100644 --- a/apps/docs/content/guides/getting-started.mdx +++ b/apps/docs/content/guides/getting-started.mdx @@ -111,10 +111,10 @@ hideToc: true hasLightIcon: true, }, { - title: 'NuxtJS', + title: 'Nuxt', href: '/guides/getting-started/quickstarts/nuxtjs', description: - 'Learn how to create a Supabase project, add some sample data to your database, and query the data from a NuxtJS app.', + 'Learn how to create a Supabase project, add some sample data to your database, and query the data from a Nuxt app.', icon: '/docs/img/icons/nuxt-icon', }, { diff --git a/apps/docs/content/guides/getting-started/ai-prompts.mdx b/apps/docs/content/guides/getting-started/ai-prompts.mdx index 412364c2bfb..706a56033ba 100644 --- a/apps/docs/content/guides/getting-started/ai-prompts.mdx +++ b/apps/docs/content/guides/getting-started/ai-prompts.mdx @@ -3,13 +3,13 @@ title: AI Prompts subtitle: Prompts for working with Supabase using AI-powered IDE tools --- -We've curated a selection of prompts to help you work with Supabase using your favorite AI-powered IDE tools, such as GitHub Copilot or Cursor. +We've curated a selection of prompts to help you work with Supabase using your favorite AI-powered IDE tools, such as Cursor or GitHub Copilot. ## How to use Copy the prompt to a file in your repo. -Use the "include file" feature from your AI tool to include the prompt when chatting with your AI assistant. For example, with GitHub Copilot, use `#`, in Cursor, use `@Files`, and in Zed, use `/file`. +Use the "include file" feature from your AI tool to include the prompt when chatting with your AI assistant. For example, in Cursor, add them as [project rules](https://docs.cursor.com/context/rules-for-ai#project-rules-recommended), with GitHub Copilot, use `#`, and in Zed, use `/file`. ## Prompts diff --git a/apps/docs/content/guides/getting-started/architecture.mdx b/apps/docs/content/guides/getting-started/architecture.mdx index 531cce08e21..f2da781b4de 100644 --- a/apps/docs/content/guides/getting-started/architecture.mdx +++ b/apps/docs/content/guides/getting-started/architecture.mdx @@ -66,7 +66,7 @@ We use this with our [`pg_graphql`](https://github.com/supabase/pg_graphql) exte ### Realtime (API & multiplayer) -A scalable websocket engine for managing user Presence, broadcasting messages, and streaming database changes. +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) @@ -80,7 +80,7 @@ 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 +- Language: Node.js / TypeScript ### Deno (Edge Functions) @@ -98,7 +98,7 @@ A RESTful API for managing your Postgres. Fetch tables, add roles, and run queri - 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 +- Language: Node.js / TypeScript ### Supavisor diff --git a/apps/docs/content/guides/getting-started/features.mdx b/apps/docs/content/guides/getting-started/features.mdx index d9861810518..80a3a939245 100644 --- a/apps/docs/content/guides/getting-started/features.mdx +++ b/apps/docs/content/guides/getting-started/features.mdx @@ -78,11 +78,11 @@ Login to the Supabase dashboard via SSO. [Docs](/docs/guides/platform/sso). ### Postgres changes -Receive your database changes through websockets. [Docs](/docs/guides/realtime/postgres-changes). +Receive your database changes through WebSockets. [Docs](/docs/guides/realtime/postgres-changes). ### Broadcast -Send messages between connected users through websockets. [Docs](/docs/guides/realtime/broadcast). +Send messages between connected users through WebSockets. [Docs](/docs/guides/realtime/broadcast). ### Presence @@ -110,9 +110,9 @@ Build passwordless logins via magic links for your application or website. [Docs Control the data each user can access with Postgres Policies. [Docs](/docs/guides/database/postgres/row-level-security). -### Captcha protection +### CAPTCHA protection -Add Captcha to your sign-in, sign-up, and password reset forms. [Docs](/docs/guides/auth/auth-captcha). +Add CAPTCHA to your sign-in, sign-up, and password reset forms. [Docs](/docs/guides/auth/auth-captcha). ### Server-Side Auth @@ -235,7 +235,7 @@ In addition to the Beta requirements, features in GA are covered by the [uptime | Auth | Passwordless login | `GA` | ✅ | | Auth | SSO with SAML | `GA` | ✅ | | Auth | Authorization via RLS | `GA` | ✅ | -| Auth | Captcha protection | `GA` | ✅ | +| Auth | CAPTCHA protection | `GA` | ✅ | | Auth | Server-side Auth | `beta` | ✅ | | CLI | | `GA` | ✅ Works with self-hosted | | Management API | | `GA` | N/A | diff --git a/apps/docs/content/guides/getting-started/mcp.mdx b/apps/docs/content/guides/getting-started/mcp.mdx new file mode 100644 index 00000000000..49691b960a7 --- /dev/null +++ b/apps/docs/content/guides/getting-started/mcp.mdx @@ -0,0 +1,153 @@ +--- +id: 'ai-tools-mcp' +title: 'Model context protocol (MCP)' +description: 'Connect AI tools to Supabase using MCP' +sidebar_label: 'Model context protocol (MCP)' +--- + +The [Model Context Protocol](https://modelcontextprotocol.io/introduction) (MCP) is a standard for connecting Large Language Models (LLMs) to external services. This guide will walk you through how to connect AI tools to Supabase using MCP. + +There are a number of popular AI tools that support MCP, including: + +- [Cursor](https://www.cursor.com/) +- [Claude desktop](https://claude.ai/download) +- [Cline](https://github.com/cline/cline) (VS Code extension) +- [Windsurf](https://docs.codeium.com/windsurf) (Codium) + +Connecting these tools to Supabase will allow you to query your database and perform other SQL operations using natural language commands. + +## Connect to Supabase using MCP + +We will use the [Postgres MCP server](https://github.com/modelcontextprotocol/servers/tree/main/src/postgres) to connect AI tools to Supabase. + +### Step 1: Find your database connection string + +To get started, you will need to retrieve your database connection string. These will differ depending on whether you are using a local or hosted instance of Supabase. + +#### For a local Supabase instance + +When running a local instance of Supabase via the [CLI](/docs/reference/cli/introduction), you can find your connection string by running: + +```shell +supabase status +``` + +or if you are using `npx`: + +```shell +npx supabase status +``` + +This will output a list of details about your local Supabase instance. Copy the `DB URL` field in the output. + +#### For a hosted Supabase instance + +When running a hosted instance of Supabase, you can find your connection string by: + +1. Navigating to your project's [Connection settings](/dashboard/project/_/settings/database?showConnect=true) +1. Copying the connection string found under **Session pooler**. + +### Step 2: Configure in your AI tool + +All MCP compatible tools can connect to Supabase using the [Postgres MCP server](https://github.com/modelcontextprotocol/servers/tree/main/src/postgres). Pass the following CLI command to your tool: + +```shell +npx -y @modelcontextprotocol/server-postgres +``` + +Replace `` with the connection string you retrieved in Step 1. + + + +This assumes you have Node.js and npx installed. If you don't have Node.js or prefer to connect to the server using Docker, you can follow the instructions in the [Postgres MCP server README](https://github.com/modelcontextprotocol/servers/tree/main/src/postgres#docker). + + + +Below are some ways to connect to the Postgres MCP server using popular AI tools: + +#### Cursor + +1. Open Cursor and navigate to **Cursor Settings**. +1. Under the **Features** tab, tap **+ Add new MCP server** under the **MCP Servers** section. +1. Enter the following details: + + - **Name**: Supabase (or your project name) + - **Type**: command + - **Command**: `npx -y @modelcontextprotocol/server-postgres ` + + (replace `` with your connection string) + +1. You should see a green active status after the server is successfully connected. + +#### Claude desktop + +1. Open Claude desktop and navigate to **Settings**. +1. Under the **Developer** tab, tap **Edit Config** to open the configuration file. +1. Add the following configuration: + + ```json + { + "mcpServers": { + "supabase": { + "command": "npx", + "args": ["-y", "@modelcontextprotocol/server-postgres", ""] + } + } + } + ``` + + Replace `` with your connection string. + +1. Save the configuration file and restart Claude desktop. + +1. From the new chat screen, you should see a hammer (MCP) icon appear with the new MCP server available. + +#### Cline + +1. Open the Cline extension in VS Code and tap the **MCP Servers** icon. +1. Tap **Configure MCP Servers** to open the configuration file. +1. Add the following configuration: + + ```json + { + "mcpServers": { + "supabase": { + "command": "npx", + "args": ["-y", "@modelcontextprotocol/server-postgres", ""] + } + } + } + ``` + + Replace `` with your connection string. + +1. Save the configuration file. Cline should automatically reload the configuration. + +1. You should see a green active status after the server is successfully connected. + +#### Windsurf + +1. Open Windsurf and navigate to the Cascade assistant. +1. Tap on the hammer (MCP) icon, then **Configure** to open the configuration file. +1. Add the following configuration: + + ```json + { + "mcpServers": { + "supabase": { + "command": "npx", + "args": ["-y", "@modelcontextprotocol/server-postgres", ""] + } + } + } + ``` + + Replace `` with your connection string. + +1. Save the configuration file and reload by tapping **Refresh** in the Cascade assistant. + +1. You should see a green active status after the server is successfully connected. + +## Next steps + +You are now connected to Supabase using MCP! You can now interact with your database using natural language commands. Try asking your AI tool to query your database, create a new table, or perform other SQL operations. diff --git a/apps/docs/content/guides/getting-started/quickstarts/flutter.mdx b/apps/docs/content/guides/getting-started/quickstarts/flutter.mdx index 6863daee724..29211b1338e 100644 --- a/apps/docs/content/guides/getting-started/quickstarts/flutter.mdx +++ b/apps/docs/content/guides/getting-started/quickstarts/flutter.mdx @@ -8,7 +8,7 @@ hideToc: true - + @@ -155,7 +155,7 @@ hideToc: true Run your app on a platform of your choosing! By default an app should launch in your web browser. Note that `supabase_flutter` is compatible with web, iOS, Android, macOS, and Windows apps. - Running the app on MacOS requires additional configuration to [set the entitlements](https://docs.flutter.dev/development/platform-integration/macos/building#setting-up-entitlements). + Running the app on macOS requires additional configuration to [set the entitlements](https://docs.flutter.dev/development/platform-integration/macos/building#setting-up-entitlements). diff --git a/apps/docs/content/guides/getting-started/quickstarts/hono.mdx b/apps/docs/content/guides/getting-started/quickstarts/hono.mdx index 3a818e69f83..be46310e9b3 100644 --- a/apps/docs/content/guides/getting-started/quickstarts/hono.mdx +++ b/apps/docs/content/guides/getting-started/quickstarts/hono.mdx @@ -11,7 +11,7 @@ hideToc: true - Boostrap the Hono example app from the Supabase Samples using the CLI. + Bootstrap the Hono example app from the Supabase Samples using the CLI. diff --git a/apps/docs/content/guides/getting-started/quickstarts/ios-swiftui.mdx b/apps/docs/content/guides/getting-started/quickstarts/ios-swiftui.mdx index 4889133195e..40b6e910092 100644 --- a/apps/docs/content/guides/getting-started/quickstarts/ios-swiftui.mdx +++ b/apps/docs/content/guides/getting-started/quickstarts/ios-swiftui.mdx @@ -8,7 +8,7 @@ hideToc: true - + @@ -39,7 +39,7 @@ hideToc: true - Create a new `Supabase.swift` file add a new supabase instance using your project URL and public API (anon) key: + Create a new `Supabase.swift` file add a new Supabase instance using your project URL and public API (anon) key: diff --git a/apps/docs/content/guides/getting-started/quickstarts/laravel.mdx b/apps/docs/content/guides/getting-started/quickstarts/laravel.mdx index 011d956bac5..cf2afe249aa 100644 --- a/apps/docs/content/guides/getting-started/quickstarts/laravel.mdx +++ b/apps/docs/content/guides/getting-started/quickstarts/laravel.mdx @@ -73,7 +73,7 @@ hideToc: true - By default Laravel uses the `public` schema. We recommend changing this as supabase exposes the `public` schema as a [data API](/docs/guides/api). + By default Laravel uses the `public` schema. We recommend changing this as Supabase exposes the `public` schema as a [data API](/docs/guides/api). You can change the schema of your Laravel application by modifying the `search_path` variable `app/config/database.php`. diff --git a/apps/docs/content/guides/getting-started/quickstarts/nuxtjs.mdx b/apps/docs/content/guides/getting-started/quickstarts/nuxtjs.mdx index 4ba45b41a18..6e39dff869d 100644 --- a/apps/docs/content/guides/getting-started/quickstarts/nuxtjs.mdx +++ b/apps/docs/content/guides/getting-started/quickstarts/nuxtjs.mdx @@ -1,6 +1,6 @@ --- -title: 'Use Supabase with NuxtJS' -subtitle: 'Learn how to create a Supabase project, add some sample data to your database, and query the data from a NuxtJS app.' +title: 'Use Supabase with Nuxt' +subtitle: 'Learn how to create a Supabase project, add some sample data to your database, and query the data from a Nuxt app.' breadcrumb: 'Framework Quickstarts' hideToc: true --- @@ -8,16 +8,16 @@ hideToc: true - + - + - Create a Nuxt.js app using the `npx nuxi` command. + Create a Nuxt app using the `npx nuxi` command. @@ -34,9 +34,9 @@ hideToc: true - 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 NuxtJS 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 Nuxt app. - Navigate to the NuxtJS app and install `supabase-js`. + Navigate to the Nuxt app and install `supabase-js`. diff --git a/apps/docs/content/guides/getting-started/quickstarts/redwoodjs.mdx b/apps/docs/content/guides/getting-started/quickstarts/redwoodjs.mdx index 2ca3ceee5a9..5bae8cd6a65 100644 --- a/apps/docs/content/guides/getting-started/quickstarts/redwoodjs.mdx +++ b/apps/docs/content/guides/getting-started/quickstarts/redwoodjs.mdx @@ -11,7 +11,7 @@ hideToc: true [Create a new project](https://supabase.com/dashboard) in the Supabase Dashboard. - + Be sure to make note of the Database Password you used as you will need this later to connect to your database. diff --git a/apps/docs/content/guides/getting-started/quickstarts/refine.mdx b/apps/docs/content/guides/getting-started/quickstarts/refine.mdx index c061ecf8156..f85b656d936 100644 --- a/apps/docs/content/guides/getting-started/quickstarts/refine.mdx +++ b/apps/docs/content/guides/getting-started/quickstarts/refine.mdx @@ -140,7 +140,7 @@ hideToc: true Add routes for the `list`, `create`, `show`, and `edit` pages. - + You should remove the `index` route for the Welcome page presented with the `` component. diff --git a/apps/docs/content/guides/getting-started/tutorials/with-angular.mdx b/apps/docs/content/guides/getting-started/tutorials/with-angular.mdx index da7aa2b7d1c..1fc7ee77539 100644 --- a/apps/docs/content/guides/getting-started/tutorials/with-angular.mdx +++ b/apps/docs/content/guides/getting-started/tutorials/with-angular.mdx @@ -47,7 +47,7 @@ export const environment = { } ``` -Now that we have the API credentials in place, let's create a **SupabaseService** with `ng g s supabase` to initialize the Supabase client and implement functions to communicate with the Supabase API. +Now that we have the API credentials in place, let's create a `SupabaseService` with `ng g s supabase` to initialize the Supabase client and implement functions to communicate with the Supabase API. ```ts src/app/supabase.service.ts import { Injectable } from '@angular/core' @@ -130,7 +130,7 @@ Optionally, update [src/styles.css](https://raw.githubusercontent.com/supabase/s ### Set up a login component Let's set up an Angular component to manage logins and sign ups. We'll use Magic Links, so users can sign in with their email without using passwords. -Create an **AuthComponent** with `ng g c auth` Angular CLI command. +Create an `AuthComponent` with `ng g c auth` Angular CLI command. ```ts src/app/auth/auth.component.ts import { Component } from '@angular/core' @@ -202,7 +202,7 @@ export class AuthComponent { ### Account page Users also need a way to edit their profile details and manage their accounts after signing in. -Create an **AccountComponent** with the `ng g c account` Angular CLI command. +Create an `AccountComponent` with the `ng g c account` Angular CLI command. ```ts src/app/account/account.component.ts import { Component, Input, OnInit } from '@angular/core' @@ -326,7 +326,7 @@ export class AccountComponent implements OnInit { ### Launch! -Now that we have all the components in place, let's update **AppComponent**: +Now that we have all the components in place, let's update `AppComponent`: ```ts src/app/app.component.ts import { Component, OnInit } from '@angular/core' @@ -397,7 +397,7 @@ Every Supabase project is configured with [Storage](/docs/guides/storage) for ma ### Create an upload widget Let's create an avatar for the user so that they can upload a profile photo. -Create an **AvatarComponent** with `ng g c avatar` Angular CLI command. +Create an `AvatarComponent` with `ng g c avatar` Angular CLI command. ```ts src/app/avatar/avatar.component.ts import { Component, EventEmitter, Input, Output } from '@angular/core' @@ -492,7 +492,7 @@ export class AvatarComponent { ### Add the new widget -And then we can add the widget on top of the **AccountComponent** HTML template: +And then we can add the widget on top of the `AccountComponent` HTML template: ```html src/app/account.component.html @@ -501,7 +501,7 @@ And then we can add the widget on top of the **AccountComponent** HTML template: ``` -And add an `updateAvatar` function along with an `avatarUrl` getter to the **AccountComponent** typescript file: +And add an `updateAvatar` function along with an `avatarUrl` getter to the `AccountComponent` typescript file: ```ts src/app/account.component.ts @Component({ diff --git a/apps/docs/content/guides/getting-started/tutorials/with-flutter.mdx b/apps/docs/content/guides/getting-started/tutorials/with-flutter.mdx index 64b1e2e1a06..3bedc966dc8 100644 --- a/apps/docs/content/guides/getting-started/tutorials/with-flutter.mdx +++ b/apps/docs/content/guides/getting-started/tutorials/with-flutter.mdx @@ -63,7 +63,7 @@ That is it on Supabase's end and the rest are platform specific settings: Edit the `ios/Runner/Info.plist` file. -Add CFBundleURLTypes to enable deep linking: +Add `CFBundleURLTypes` to enable deep linking: ```xml ios/Runner/Info.plist" diff --git a/apps/docs/content/guides/getting-started/tutorials/with-ionic-angular.mdx b/apps/docs/content/guides/getting-started/tutorials/with-ionic-angular.mdx index b22f2f867d3..09c3b87a039 100644 --- a/apps/docs/content/guides/getting-started/tutorials/with-ionic-angular.mdx +++ b/apps/docs/content/guides/getting-started/tutorials/with-ionic-angular.mdx @@ -48,7 +48,7 @@ export const environment = { } ``` -Now that we have the API credentials in place, let's create a **SupabaseService** with `ionic g s supabase` to initialize the Supabase client and implement functions to communicate with the Supabase API. +Now that we have the API credentials in place, let's create a `SupabaseService` with `ionic g s supabase` to initialize the Supabase client and implement functions to communicate with the Supabase API. ```ts src/app/supabase.service.ts import { Injectable } from '@angular/core' @@ -136,7 +136,7 @@ export class SupabaseService { ### Set up a login route Let's set up a route to manage logins and signups. We'll use Magic Links so users can sign in with their email without using passwords. -Create a **LoginPage** with the `ionic g page login` Ionic CLI command. +Create a `LoginPage` with the `ionic g page login` Ionic CLI command. @@ -204,7 +204,7 @@ export class LoginPage { ### Account page After a user is signed in, we can allow them to edit their profile details and manage their account. -Create an **AccountComponent** with `ionic g page account` Ionic CLI command. +Create an `AccountComponent` with `ionic g page account` Ionic CLI command. ```ts src/app/account.page.ts import { Component, OnInit } from '@angular/core' @@ -310,7 +310,7 @@ export class AccountPage implements OnInit { ### Launch! -Now that we have all the components in place, let's update **AppComponent**: +Now that we have all the components in place, let's update `AppComponent`: ```ts src/app/app.component.ts import { Component } from '@angular/core' @@ -341,7 +341,7 @@ export class AppComponent { } ``` -Then update the **AppRoutingModule** +Then update the `AppRoutingModule` ```ts src/app/app-routing.module.ts" import { NgModule } from '@angular/core' @@ -393,7 +393,7 @@ First, install two packages in order to interact with the user's camera. npm install @ionic/pwa-elements @capacitor/camera ``` -[CapacitorJS](https://capacitorjs.com) is a cross-platform native runtime from Ionic that enables web apps to be deployed through the app store and provides access to native device API. +[Capacitor](https://capacitorjs.com) is a cross-platform native runtime from Ionic that enables web apps to be deployed through the app store and provides access to native device API. Ionic PWA elements is a companion package that will polyfill certain browser APIs that provide no user interface with custom Ionic UI. @@ -417,7 +417,7 @@ platformBrowserDynamic() .catch((err) => console.log(err)) ``` -Then create an **AvatarComponent** with this Ionic CLI command: +Then create an `AvatarComponent` with this Ionic CLI command: ```bash ionic g component avatar --module=/src/app/account/account.module.ts --create-module @@ -536,7 +536,7 @@ export class AvatarComponent { ### Add the new widget -And then, we can add the widget on top of the **AccountComponent** HTML template: +And then, we can add the widget on top of the `AccountComponent` HTML template: ```ts src/app/account.component.ts template: ` diff --git a/apps/docs/content/guides/getting-started/tutorials/with-ionic-react.mdx b/apps/docs/content/guides/getting-started/tutorials/with-ionic-react.mdx index cbfc4db3f49..5ad2106479f 100644 --- a/apps/docs/content/guides/getting-started/tutorials/with-ionic-react.mdx +++ b/apps/docs/content/guides/getting-started/tutorials/with-ionic-react.mdx @@ -363,7 +363,7 @@ First install two packages in order to interact with the user's camera. npm install @ionic/pwa-elements @capacitor/camera ``` -[CapacitorJS](https://capacitorjs.com) is a cross platform native runtime from Ionic that enables web apps to be deployed through the app store and provides access to native device API. +[Capacitor](https://capacitorjs.com) is a cross platform native runtime from Ionic that enables web apps to be deployed through the app store and provides access to native device API. Ionic PWA elements is a companion package that will polyfill certain browser APIs that provide no user interface with custom Ionic UI. @@ -390,7 +390,7 @@ serviceWorkerRegistration.unregister() reportWebVitals() ``` -Then create an **AvatarComponent**. +Then create an `AvatarComponent`. ```jsx src/components/Avatar.tsx import { IonIcon } from '@ionic/react'; diff --git a/apps/docs/content/guides/getting-started/tutorials/with-ionic-vue.mdx b/apps/docs/content/guides/getting-started/tutorials/with-ionic-vue.mdx index 00b85b6aa6f..58388b7796c 100644 --- a/apps/docs/content/guides/getting-started/tutorials/with-ionic-vue.mdx +++ b/apps/docs/content/guides/getting-started/tutorials/with-ionic-vue.mdx @@ -396,7 +396,7 @@ First install two packages in order to interact with the user's camera. npm install @ionic/pwa-elements @capacitor/camera ``` -[CapacitorJS](https://capacitorjs.com) is a cross platform native runtime from Ionic that enables web apps to be deployed through the app store and provides access to native device API. +[Capacitor](https://capacitorjs.com) is a cross-platform native runtime from Ionic that enables web apps to be deployed through the app store and provides access to native device API. Ionic PWA elements is a companion package that will polyfill certain browser APIs that provide no user interface with custom Ionic UI. @@ -423,7 +423,7 @@ router.isReady().then(() => { }) ``` -Then create an **AvatarComponent**. +Then create an `AvatarComponent`. ```html src/components/Avatar.vue