diff --git a/README.md b/README.md index 70298a09e54..491a08b1009 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ You can also [self-host](https://supabase.com/docs/guides/hosting/overview) and - [pg_graphql](http://github.com/supabase/pg_graphql/) a PostgreSQL extension that exposes a GraphQL API - [Storage](https://github.com/supabase/storage-api) provides a RESTful interface for managing Files stored in S3, using Postgres to manage permissions. - [postgres-meta](https://github.com/supabase/postgres-meta) is a RESTful API for managing your Postgres, allowing you to fetch tables, add roles, and run queries, etc. -- [GoTrue](https://github.com/supabase/gotrue) is an JWT based API for managing users and issuing JWT tokens. +- [GoTrue](https://github.com/supabase/gotrue) is a JWT based API for managing users and issuing JWT tokens. - [Kong](https://github.com/Kong/kong) is a cloud-native API gateway. #### Client libraries diff --git a/apps/docs/components/Extensions.tsx b/apps/docs/components/Extensions.tsx index b49c5ad83d3..8740be67870 100644 --- a/apps/docs/components/Extensions.tsx +++ b/apps/docs/components/Extensions.tsx @@ -1,7 +1,7 @@ import Link from 'next/link' import React, { useState } from 'react' -import { GlassPanel, IconLink, IconX, Input } from 'ui' -import extensions from '../data/extensions.json' +import { extensions } from 'shared-data' +import { GlassPanel, IconX, Input } from 'ui' type Extension = { name: string diff --git a/apps/docs/components/MDX/storage_management.mdx b/apps/docs/components/MDX/storage_management.mdx index ce28a1684c8..94aaa57a999 100644 --- a/apps/docs/components/MDX/storage_management.mdx +++ b/apps/docs/components/MDX/storage_management.mdx @@ -61,13 +61,16 @@ as $$ declare status int; content text; + avatar_name text; begin if coalesce(old.avatar_url, '') <> '' and (tg_op = 'DELETE' or (old.avatar_url <> new.avatar_url)) then + -- extract avatar name + avatar_name := substring(old.avatar_url from '/([^\/]+)\?.*$'); select into status, content result.status, result.content - from public.delete_avatar(old.avatar_url) as result; + from public.delete_avatar(avatar_name) as result; if status <> 200 then raise warning 'Could not delete avatar: % %', status, content; end if; diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts index 488226925ee..3e26e339ec4 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts @@ -449,6 +449,10 @@ export const auth = { name: 'Overview', url: '/guides/auth', }, + { + name: 'Redirect URLs', + url: '/guides/auth/concepts/redirect-urls', + }, { name: 'Quickstarts', items: [ @@ -495,22 +499,36 @@ export const auth = { { name: 'Managing User Data', url: '/guides/auth/managing-user-data' }, { name: 'Multi-Factor Authentication', url: '/guides/auth/auth-mfa' }, { name: 'Row Level Security', url: '/guides/auth/row-level-security' }, - { name: 'Server-side Rendering', url: '/guides/auth/server-side-rendering' }, ], }, { - name: 'Auth Helpers', + name: 'Server-side Auth', url: undefined, items: [ { name: 'Overview', url: '/guides/auth/auth-helpers' }, - { name: 'Auth UI', url: '/guides/auth/auth-helpers/auth-ui' }, - { name: 'Flutter Auth UI', url: '/guides/auth/auth-helpers/flutter-auth-ui' }, { name: 'Next.js', url: '/guides/auth/auth-helpers/nextjs', }, { name: 'Remix', url: '/guides/auth/auth-helpers/remix' }, { name: 'SvelteKit', url: '/guides/auth/auth-helpers/sveltekit' }, + { name: 'Server-side Rendering', url: '/guides/auth/server-side-rendering' }, + { + name: 'Email Auth with PKCE flow for SSR', + url: '/guides/auth/server-side/email-based-auth-with-pkce-flow-for-ssr', + }, + { + name: 'OAuth with PKCE flow for SSR', + url: '/guides/auth/server-side/oauth-with-pkce-flow-for-ssr', + }, + ], + }, + { + name: 'Auth UI', + url: undefined, + items: [ + { name: 'Auth UI', url: '/guides/auth/auth-helpers/auth-ui' }, + { name: 'Flutter Auth UI', url: '/guides/auth/auth-helpers/flutter-auth-ui' }, ], }, { @@ -923,7 +941,18 @@ export const storage: NavMenuConstant = { ], } -export const ai: NavMenuConstant = { +export const vectorIndexItems = [ + { + name: 'HNSW indexes', + url: '/guides/ai/vector-indexes/hnsw-indexes', + }, + { + name: 'IVFFlat indexes', + url: '/guides/ai/vector-indexes/ivf-indexes', + }, +] + +export const ai = { icon: 'ai', title: 'AI & Vectors', url: '/guides/ai', @@ -935,61 +964,34 @@ export const ai: NavMenuConstant = { url: '/guides/ai/structured-unstructured', }, { - name: 'Quickstarts', + name: 'Learn', url: undefined, items: [ - { name: 'Developing locally with Vecs', url: '/guides/ai/vecs-python-client' }, - { name: 'Creating and managing collections', url: '/guides/ai/quickstarts/hello-world' }, - { - name: 'Generate Embeddings', - url: '/guides/ai/quickstarts/generate-text-embeddings', - }, - { name: 'Text Deduplication', url: '/guides/ai/quickstarts/text-deduplication' }, - { name: 'Face similarity search', url: '/guides/ai/quickstarts/face-similarity' }, - ], - }, - { - name: 'Python Client', - url: undefined, - items: [ - { name: 'API', url: '/guides/ai/python/api' }, - { name: 'Collections', url: '/guides/ai/python/collections' }, - { name: 'Indexes', url: '/guides/ai/python/indexes' }, - { name: 'Metadata', url: '/guides/ai/python/metadata' }, - ], - }, - { - name: 'Guides', - url: undefined, - items: [ - { name: 'Managing collections', url: '/guides/ai/managing-collections' }, - { name: 'Managing indexes', url: '/guides/ai/managing-indexes' }, { name: 'Vector columns', url: '/guides/ai/vector-columns' }, + { name: 'Vector indexes', url: '/guides/ai/vector-indexes', items: vectorIndexItems }, { name: 'Engineering for scale', url: '/guides/ai/engineering-for-scale' }, { name: 'Choosing Compute Add-on', url: '/guides/ai/choosing-compute-addon' }, { name: 'Going to Production', url: '/guides/ai/going-to-prod' }, ], }, { - name: 'Examples', + name: 'JavaScript Examples', url: undefined, items: [ { name: 'OpenAI completions using Edge Functions', url: '/guides/ai/examples/openai', }, - { - name: 'Image search with OpenAI CLIP', - url: '/guides/ai/examples/image-search-openai-clip', - }, + { name: 'Generate image captions using Hugging Face', url: '/guides/ai/examples/huggingface-image-captioning', }, { - name: 'Building ChatGPT Plugins', - url: '/guides/ai/examples/building-chatgpt-plugins', + name: 'Generate Embeddings', + url: '/guides/ai/quickstarts/generate-text-embeddings', }, + { name: 'Adding generative Q&A to your documentation', url: '/guides/ai/examples/headless-vector-search', @@ -1000,6 +1002,36 @@ export const ai: NavMenuConstant = { }, ], }, + { + name: 'Python Client', + url: undefined, + items: [ + { name: 'Choosing a Client', url: '/guides/ai/python-clients' }, + { name: 'API', url: '/guides/ai/python/api' }, + { name: 'Collections', url: '/guides/ai/python/collections' }, + { name: 'Indexes', url: '/guides/ai/python/indexes' }, + { name: 'Metadata', url: '/guides/ai/python/metadata' }, + ], + }, + { + name: 'Python Examples', + url: undefined, + items: [ + { name: 'Developing locally with Vecs', url: '/guides/ai/vecs-python-client' }, + { name: 'Creating and managing collections', url: '/guides/ai/quickstarts/hello-world' }, + + { name: 'Text Deduplication', url: '/guides/ai/quickstarts/text-deduplication' }, + { name: 'Face similarity search', url: '/guides/ai/quickstarts/face-similarity' }, + { + name: 'Image search with OpenAI CLIP', + url: '/guides/ai/examples/image-search-openai-clip', + }, + { + name: 'Building ChatGPT Plugins', + url: '/guides/ai/examples/building-chatgpt-plugins', + }, + ], + }, { name: 'Third-Party Tools', url: undefined, diff --git a/apps/docs/docs/ref/kotlin/installing.mdx b/apps/docs/docs/ref/kotlin/installing.mdx index 72e10be54fd..07125a7c0ac 100644 --- a/apps/docs/docs/ref/kotlin/installing.mdx +++ b/apps/docs/docs/ref/kotlin/installing.mdx @@ -49,7 +49,7 @@ custom_edit_url: https://github.com/supabase/supabase/edit/master/web/spec/supab - The available modules are: **gotrue-kt**, **realtime-kt**, **storage-kt**, **functions-kt**, **postgrest-kt** and **apollo-graphql** + The available modules are: **gotrue-kt**, **realtime-kt**, **storage-kt**, **functions-kt**, **postgrest-kt**, **apollo-graphql**, [**compose-auth**](https://github.com/supabase-community/supabase-kt/tree/master/plugins/ComposeAuth) and [**compose-auth-ui**](https://github.com/supabase-community/supabase-kt/tree/master/plugins/ComposeAuthUI) When using multiple modules, you can also use the BOM dependency to ensure that all modules use the same version: diff --git a/apps/docs/docs/ref/kotlin/introduction.mdx b/apps/docs/docs/ref/kotlin/introduction.mdx index 914310ba347..35570b4d54f 100644 --- a/apps/docs/docs/ref/kotlin/introduction.mdx +++ b/apps/docs/docs/ref/kotlin/introduction.mdx @@ -16,23 +16,43 @@ hideTitle: true This reference documents every object and method available in Supabase's Kotlin Multiplatform library, [supabase-kt](https://github.com/supabase-community/supabase-kt). You can use supabase-kt to interact with your Postgres database, listen to database changes, invoke Deno Edge Functions, build login and user management functionality, and manage large files. -Supported Kotlin targets: +Supported targets: -| | **GoTrue** | **Realtime** | **Postgrest** | **Storage** | **Functions** | **Apollo-GraphQL** | -| ------------------------------------------------------------------ | ---------- | ------------ | ------------- | ----------- | ------------- | ------------------ | -| **JVM** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| **Android** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| **JS** _(Browser, NodeJS)_ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| **IOS** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| **tvOS** _(tvosArm64, tvosX64, tvosSimulatorArm64)_ 🚧 | ☑️ | ✅ | ✅ | ✅ | ✅ | ✅ | -| **watchOS** _(watchosArm64, watchosX64, watchosSimulatorArm64)_ 🚧 | ☑️ | ✅ | ✅ | ✅ | ✅ | ✅ | -| **MacOS** _(macosX64 & macosArm64)_ 🚧 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| **Windows** _(mingwX64)_ 🚧 | ☑️ | ✅ | ✅ | ✅ | ✅ | ❌ | -| **Linux** _(linuxX64)_ 🚧 | ☑️ | ✅ | ✅ | ✅ | ✅ | ❌ | +| | **GoTrue** | **Realtime** | **Postgrest** | **Storage** | **Functions** | **Apollo-GraphQL** | **Compose Auth 🚧** | **Compose Auth UI 🚧** | +| ----------- | ---------- | ------------ | ------------- | ----------- | ------------- | ------------------ | ------------------- | ---------------------- | +| **JVM** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ☑️ | ✅ | +| **Android** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| **JS** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ☑️ | ✅ | +| **IOS** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| **tvOS** | ☑️ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | +| **watchOS** | ☑️ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | +| **MacOS** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | +| **Windows** | ☑️ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | +| **Linux** | ☑️ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | + +
+ +In-depth Kotlin targets + +**iOS:** iosArm64, iosSimulatorArm64, iosX64 + +**JS**: Browser, NodeJS + +**tvOS**: tvosArm64, tvosX64, tvosSimulatorArm64 + +**watchOS**: watchosArm64, watchosX64, watchosSimulatorArm64 + +**MacOS**: macosX64, macosArm64 + +**Windows**: mingwX64 + +**Linux**: linuxX64 + +
✅ = full support -☑️ = partial support: no built-in OAuth/OTP link handling. Linux also has no persistent storage. +☑️ = partial support: no built-in OAuth/OTP link handling. Linux also has no support for persistent storage. For Compose Auth, it relies on GoTrue as fallback. 🚧 = experimental/needs feedback diff --git a/apps/docs/layouts/SiteLayout.tsx b/apps/docs/layouts/SiteLayout.tsx index 94cd0ae0631..1b0f0a75df1 100644 --- a/apps/docs/layouts/SiteLayout.tsx +++ b/apps/docs/layouts/SiteLayout.tsx @@ -244,7 +244,7 @@ const Container = memo(function Container(props) { className={[ // 'overflow-x-auto', 'w-full h-screen transition-all ease-out', - 'absolute lg:relative', + // 'absolute lg:relative', mobileMenuOpen ? '!w-auto ml-[75%] sm:ml-[50%] md:ml-[33%] overflow-hidden' : 'overflow-auto', diff --git a/apps/docs/pages/guides/ai/choosing-compute-addon.mdx b/apps/docs/pages/guides/ai/choosing-compute-addon.mdx index 4ab4e13e808..fb7bf5c9fa6 100644 --- a/apps/docs/pages/guides/ai/choosing-compute-addon.mdx +++ b/apps/docs/pages/guides/ai/choosing-compute-addon.mdx @@ -17,7 +17,122 @@ You have two options for scaling your vector workload: ## Dimensionality -The number of dimensions in your embeddings is the most important factor in choosing the right Compute Add-on. In general, the lower the dimensionality the better the performance. We've provided guidance for some of the more common embedding dimensions below. For each benchmark, we used [Vecs](https://github.com/supabase/vecs) to create a collection, upload the embeddings to a single table, and create an `inner-product` index for the embedding column. We then ran a series of queries to measure the performance of different compute add-ons: +The number of dimensions in your embeddings is the most important factor in choosing the right Compute Add-on. In general, the lower the dimensionality the better the performance. We've provided guidance for some of the more common embedding dimensions below. For each benchmark, we used [Vecs](https://github.com/supabase/vecs) to create a collection, upload the embeddings to a single table, and create both the `IVFFlat` and `HNSW` indexes for `inner-product` distance measure for the embedding column. We then ran a series of queries to measure the performance of different compute add-ons: + +## HNSW + +### 1536 Dimensions + +This benchmark uses the [dbpedia-entities-openai-1M](https://huggingface.co/datasets/KShivendu/dbpedia-entities-openai-1M) dataset, which contains 1,000,000 embeddings of text. And 224,482 embeddings from [Wikipedia articles](https://huggingface.co/datasets/Supabase/wikipedia-en-embeddings) for compute add-ons `large` and below. Each embedding is 1536 dimensions created with the [OpenAI Embeddings API](https://platform.openai.com/docs/guides/embeddings). + + + + +| Plan | Vectors | m | ef_construction | ef_search | QPS | Latency Mean | Latency p95 | RAM Usage | RAM | +| ------ | --------- | --- | --------------- | --------- | ---- | ------------ | ----------- | ------------------ | ------ | +| Free | 15,000 | 16 | 40 | 40 | 480 | 0.011 sec | 0.016 sec | 1 GB + 200 Mb Swap | 1 GB | +| Small | 50,000 | 32 | 64 | 100 | 175 | 0.031 sec | 0.051 sec | 2 GB + 200 Mb Swap | 2 GB | +| Medium | 100,000 | 32 | 64 | 100 | 240 | 0.083 sec | 0.126 sec | 4 GB | 4 GB | +| Large | 224,482 | 32 | 64 | 100 | 280 | 0.017 sec | 0.028 sec | 8 GB | 8 GB | +| XL | 500,000 | 24 | 56 | 100 | 360 | 0.055 sec | 0.135 sec | 13 GB | 16 GB | +| 2XL | 1,000,000 | 24 | 56 | 250 | 560 | 0.036 sec | 0.058 sec | 32 GB | 32 GB | +| 4XL | 1,000,000 | 24 | 56 | 250 | 950 | 0.021 sec | 0.033 sec | 39 GB | 64 GB | +| 8XL | 1,000,000 | 24 | 56 | 250 | 1650 | 0.016 sec | 0.023 sec | 40 GB | 128 GB | +| 12XL | 1,000,000 | 24 | 56 | 250 | 1900 | 0.015 sec | 0.021 sec | 38 GB | 192 GB | +| 16XL | 1,000,000 | 24 | 56 | 250 | 2200 | 0.015 sec | 0.020 sec | 40 GB | 256 GB | + +Accuracy was 0.99 for benchmarks. + +QPS can also be improved by increasing [`m` and `ef_construction`](/docs/guides/ai/going-to-prod#hnsw-understanding-efconstruction--efsearch--and-m). This will allow you to use a smaller value for `ef_search` and increase QPS. For example, increasing `m` to 32 and `ef_construction` to 80 for 4XL will increase QPS to 1280. + + + + + + +It is possible to upload more vectors to a single table if Memory allows it (for example, 4XL plan and higher for OpenAI embeddings). But it will affect the performance of the queries: QPS will be lower, and latency will be higher. Scaling should be almost linear, but it is recommended to benchmark your workload to find the optimal number of vectors per table and per database instance. + + + +## IVFFlat + +### 512 Dimensions + +This benchmark uses the [GloVe Reddit comments](https://nlp.stanford.edu/projects/glove/) dataset, which contains 1,623,397 embeddings of text. Each embedding is 512 dimensions. Random vectors were generated for queries. + + + + +| Plan | Vectors | Lists | QPS | Latency Mean | Latency p95 | RAM Usage | RAM | +| ------ | --------- | ----- | ---- | ------------ | ----------- | ------------------ | ------ | +| Free | 100,000 | 100 | 250 | 0.395 sec | 0.432 sec | 1 GB + 300 Mb Swap | 1 GB | +| Small | 250,000 | 250 | 440 | 0.223 sec | 0.250 sec | 2 GB + 200 Mb Swap | 2 GB | +| Medium | 500,000 | 500 | 425 | 0.116 sec | 0.143 sec | 3.7 GB | 4 GB | +| Large | 1,000,000 | 1000 | 515 | 0.096 sec | 0.116 sec | 7.5 GB | 8 GB | +| XL | 1,623,397 | 1275 | 465 | 0.212 sec | 0.272 sec | 14 GB | 16 GB | +| 2XL | 1,623,397 | 1275 | 1400 | 0.061 sec | 0.075 sec | 22 GB | 32 GB | +| 4XL | 1,623,397 | 1275 | 1800 | 0.027 sec | 0.043 sec | 20 GB | 64 GB | +| 8XL | 1,623,397 | 1275 | 2850 | 0.032 sec | 0.049 sec | 21 GB | 128 GB | +| 12XL | 1,623,397 | 1275 | 3700 | 0.020 sec | 0.036 sec | 26 GB | 192 GB | +| 16XL | 1,623,397 | 1275 | 3700 | 0.025 sec | 0.042 sec | 29 GB | 256 GB | + + + + +| Plan | Vectors | Lists | QPS | Latency Mean | Latency p95 | RAM Usage | RAM | +| ------ | --------- | ----- | --- | ------------ | ----------- | --------- | ------ | +| Free | 100,000 | 100 | - | - | - | - | 1 GB | +| Small | 250,000 | 250 | - | - | - | - | 2 GB | +| Medium | 500,000 | 500 | 75 | 0.656 sec | 0.750 sec | 3.7 GB | 4 GB | +| Large | 1,000,000 | 1000 | 102 | 0.488 sec | 0.580 sec | 7.5 GB | 8 GB | +| XL | 1,000,000 | 1000 | 188 | 0.525 sec | 0.596 sec | 14 GB | 16 GB | +| XL | 1,623,397 | 1275 | 75 | 0.679 sec | 0.798 sec | 14 GB | 16 GB | +| 2XL | 1,623,397 | 1275 | 160 | 0.314 sec | 0.384 sec | 22 GB | 32 GB | +| 4XL | 1,623,397 | 1275 | 300 | 0.083 sec | 0.113 sec | 20 GB | 64 GB | +| 8XL | 1,623,397 | 1275 | 565 | 0.105 sec | 0.141 sec | 21 GB | 128 GB | +| 12XL | 1,623,397 | 1275 | 840 | 0.093 sec | 0.124 sec | 26 GB | 192 GB | +| 16XL | 1,623,397 | 1275 | 940 | 0.084 sec | 0.108 sec | 29 GB | 256 GB | + + + + +### 960 Dimensions + +This benchmark uses the [gist-960-angular](http://corpus-texmex.irisa.fr/) dataset, which contains 1,000,000 embeddings of images. Each embedding is 960 dimensions. + + + + +| Plan | Vectors | Lists | QPS | Latency Mean | Latency p95 | RAM Usage | RAM | +| ------ | --------- | ----- | ---- | ------------ | ----------- | ------------------ | ------ | +| Free | 30,000 | 30 | 75 | 0.065 sec | 0.088 sec | 1 GB + 100 Mb Swap | 1 GB | +| Small | 100,000 | 100 | 78 | 0.064 sec | 0.092 sec | 1.8 GB | 2 GB | +| Medium | 250,000 | 250 | 58 | 0.085 sec | 0.129 sec | 3.2 GB | 4 GB | +| Large | 500,000 | 500 | 55 | 0.088 sec | 0.140 sec | 5 GB | 8 GB | +| XL | 1,000,000 | 1000 | 110 | 0.046 sec | 0.070 sec | 14 GB | 16 GB | +| 2XL | 1,000,000 | 1000 | 235 | 0.083 sec | 0.136 sec | 10 GB | 32 GB | +| 4XL | 1,000,000 | 1000 | 420 | 0.071 sec | 0.106 sec | 11 GB | 64 GB | +| 8XL | 1,000,000 | 1000 | 815 | 0.072 sec | 0.106 sec | 13 GB | 128 GB | +| 12XL | 1,000,000 | 1000 | 1150 | 0.052 sec | 0.078 sec | 15.5 GB | 192 GB | +| 16XL | 1,000,000 | 1000 | 1345 | 0.072 sec | 0.106 sec | 17.5 GB | 256 GB | + + + ### 1536 Dimensions @@ -31,7 +146,7 @@ This benchmark uses the [dbpedia-entities-openai-1M](https://huggingface.co/data > -| Plan | Vectors | Lists | RPS | Latency Mean | Latency p95 | RAM Usage | RAM | +| Plan | Vectors | Lists | QPS | Latency Mean | Latency p95 | RAM Usage | RAM | | ------ | --------- | ----- | ---- | ------------ | ----------- | ------------------ | ------ | | Free | 20,000 | 40 | 135 | 0.372 sec | 0.412 sec | 1 GB + 200 Mb Swap | 1 GB | | Small | 50,000 | 100 | 140 | 0.357 sec | 0.398 sec | 1.8 GB | 2 GB | @@ -44,12 +159,12 @@ This benchmark uses the [dbpedia-entities-openai-1M](https://huggingface.co/data | 12XL | 1,000,000 | 2000 | 1600 | 0.030 sec | 0.052 sec | 41 GB | 192 GB | | 16XL | 1,000,000 | 2000 | 1790 | 0.029 sec | 0.051 sec | 45 GB | 256 GB | -For 1,000,000 vectors 10 probes results to precision of 0.91. And for 500,000 vectors and below 10 probes results to precision in the range of 0.95 - 0.99. To increase precision, you need to increase the number of probes. +For 1,000,000 vectors 10 probes results to accuracy of 0.91. And for 500,000 vectors and below 10 probes results to accuracy in the range of 0.95 - 0.99. To increase accuracy, you need to increase the number of probes. -| Plan | Vectors | Lists | RPS | Latency Mean | Latency p95 | RAM Usage | RAM | +| Plan | Vectors | Lists | QPS | Latency Mean | Latency p95 | RAM Usage | RAM | | ------ | --------- | ----- | --- | ------------ | ----------- | --------- | ------ | | Free | 20,000 | 40 | - | - | - | - | 1 GB | | Small | 50,000 | 100 | - | - | - | - | 2 GB | @@ -62,7 +177,7 @@ For 1,000,000 vectors 10 probes results to precision of 0.91. And for 500,000 ve | 12XL | 1,000,000 | 2000 | 600 | 0.085 sec | 0.132 sec | 41 GB | 192 GB | | 16XL | 1,000,000 | 2000 | 670 | 0.081 sec | 0.129 sec | 45 GB | 256 GB | -For 1,000,000 vectors 40 probes results to precision of 0.98. Note that exact values may vary depending on the dataset and queries, we recommend to run benchmarks with your own data to get precise results. Use this table as a reference. +For 1,000,000 vectors 40 probes results to accuracy of 0.98. Note that exact values may vary depending on the dataset and queries, we recommend to run benchmarks with your own data to get precise results. Use this table as a reference. @@ -80,82 +195,9 @@ For 1,000,000 vectors 40 probes results to precision of 0.98. Note that exact va /> -### 960 Dimensions - -This benchmark uses the [gist-960-angular](http://corpus-texmex.irisa.fr/) dataset, which contains 1,000,000 embeddings of images. Each embedding is 960 dimensions. - - - - -| Plan | Vectors | Lists | RPS | Latency Mean | Latency p95 | RAM Usage | RAM | -| ------ | --------- | ----- | ---- | ------------ | ----------- | ------------------ | ------ | -| Free | 30,000 | 30 | 75 | 0.065 sec | 0.088 sec | 1 GB + 100 Mb Swap | 1 GB | -| Small | 100,000 | 100 | 78 | 0.064 sec | 0.092 sec | 1.8 GB | 2 GB | -| Medium | 250,000 | 250 | 58 | 0.085 sec | 0.129 sec | 3.2 GB | 4 GB | -| Large | 500,000 | 500 | 55 | 0.088 sec | 0.140 sec | 5 GB | 8 GB | -| XL | 1,000,000 | 1000 | 110 | 0.046 sec | 0.070 sec | 14 GB | 16 GB | -| 2XL | 1,000,000 | 1000 | 235 | 0.083 sec | 0.136 sec | 10 GB | 32 GB | -| 4XL | 1,000,000 | 1000 | 420 | 0.071 sec | 0.106 sec | 11 GB | 64 GB | -| 8XL | 1,000,000 | 1000 | 815 | 0.072 sec | 0.106 sec | 13 GB | 128 GB | -| 12XL | 1,000,000 | 1000 | 1150 | 0.052 sec | 0.078 sec | 15.5 GB | 192 GB | -| 16XL | 1,000,000 | 1000 | 1345 | 0.072 sec | 0.106 sec | 17.5 GB | 256 GB | - - - - -### 512 Dimensions - -This benchmark uses the [GloVe Reddit comments](https://nlp.stanford.edu/projects/glove/) dataset, which contains 1,623,397 embeddings of text. Each embedding is 512 dimensions. Random vectors were generated for queries. - - - - -| Plan | Vectors | Lists | RPS | Latency Mean | Latency p95 | RAM Usage | RAM | -| ------ | --------- | ----- | ---- | ------------ | ----------- | ------------------ | ------ | -| Free | 100,000 | 100 | 250 | 0.395 sec | 0.432 sec | 1 GB + 300 Mb Swap | 1 GB | -| Small | 250,000 | 250 | 440 | 0.223 sec | 0.250 sec | 2 GB + 200 Mb Swap | 2 GB | -| Medium | 500,000 | 500 | 425 | 0.116 sec | 0.143 sec | 3.7 GB | 4 GB | -| Large | 1,000,000 | 1000 | 515 | 0.096 sec | 0.116 sec | 7.5 GB | 8 GB | -| XL | 1,623,397 | 1275 | 465 | 0.212 sec | 0.272 sec | 14 GB | 16 GB | -| 2XL | 1,623,397 | 1275 | 1400 | 0.061 sec | 0.075 sec | 22 GB | 32 GB | -| 4XL | 1,623,397 | 1275 | 1800 | 0.027 sec | 0.043 sec | 20 GB | 64 GB | -| 8XL | 1,623,397 | 1275 | 2850 | 0.032 sec | 0.049 sec | 21 GB | 128 GB | -| 12XL | 1,623,397 | 1275 | 3700 | 0.020 sec | 0.036 sec | 26 GB | 192 GB | -| 16XL | 1,623,397 | 1275 | 3700 | 0.025 sec | 0.042 sec | 29 GB | 256 GB | - - - - -| Plan | Vectors | Lists | RPS | Latency Mean | Latency p95 | RAM Usage | RAM | -| ------ | --------- | ----- | --- | ------------ | ----------- | --------- | ------ | -| Free | 100,000 | 100 | - | - | - | - | 1 GB | -| Small | 250,000 | 250 | - | - | - | - | 2 GB | -| Medium | 500,000 | 500 | 75 | 0.656 sec | 0.750 sec | 3.7 GB | 4 GB | -| Large | 1,000,000 | 1000 | 102 | 0.488 sec | 0.580 sec | 7.5 GB | 8 GB | -| XL | 1,000,000 | 1000 | 188 | 0.525 sec | 0.596 sec | 14 GB | 16 GB | -| XL | 1,623,397 | 1275 | 75 | 0.679 sec | 0.798 sec | 14 GB | 16 GB | -| 2XL | 1,623,397 | 1275 | 160 | 0.314 sec | 0.384 sec | 22 GB | 32 GB | -| 4XL | 1,623,397 | 1275 | 300 | 0.083 sec | 0.113 sec | 20 GB | 64 GB | -| 8XL | 1,623,397 | 1275 | 565 | 0.105 sec | 0.141 sec | 21 GB | 128 GB | -| 12XL | 1,623,397 | 1275 | 840 | 0.093 sec | 0.124 sec | 26 GB | 192 GB | -| 16XL | 1,623,397 | 1275 | 940 | 0.084 sec | 0.108 sec | 29 GB | 256 GB | - - - - -It is possible to upload more vectors to a single table if Memory allows it (for example, 4XL plan and higher for OpenAI embeddings). But it will affect the performance of the queries: RPS will be lower, and latency will be higher. Scaling should be almost linear, but it is recommended to benchmark your workload to find the optimal number of vectors per table and per database instance. +It is possible to upload more vectors to a single table if Memory allows it (for example, 4XL plan and higher for OpenAI embeddings). But it will affect the performance of the queries: QPS will be lower, and latency will be higher. Scaling should be almost linear, but it is recommended to benchmark your workload to find the optimal number of vectors per table and per database instance. diff --git a/apps/docs/pages/guides/ai/engineering-for-scale.mdx b/apps/docs/pages/guides/ai/engineering-for-scale.mdx index 871d0a5059c..0c4fff2fb26 100644 --- a/apps/docs/pages/guides/ai/engineering-for-scale.mdx +++ b/apps/docs/pages/guides/ai/engineering-for-scale.mdx @@ -53,7 +53,7 @@ const { data, error } = await supabase ## Enterprise workloads -As you move into production, we recommend running splitting your collections into separate projects. This is because it allows your vector stores to scale independently of your production data. Vectors typically grow faster than operational data, and they have different resource requirements. Running them on separate databases removes the single-point-of-failure. +As you move into production, we recommend splitting your collections into separate projects. This is because it allows your vector stores to scale independently of your production data. Vectors typically grow faster than operational data, and they have different resource requirements. Running them on separate databases removes the single-point-of-failure.
+ dbpedia embeddings comparing ivfflat and hnsw queries-per-second using the 4XL compute addon (light) + dbpedia embeddings comparing ivfflat and hnsw queries-per-second using the 4XL compute addon (dark) +
+ +## HNSW, understanding `ef_construction`, `ef_search`, and `m` + +Index build parameters: + +- `m` is the number of bi-directional links created for every new element during construction. Higher `m` is suitable for datasets with high dimensionality and/or high accuracy requirements. Reasonable values for `m` are between 2 and 100. Range 12-48 is a good starting point for most use cases (16 is the default value). + +- `ef_construction` is the size of the dynamic list for the nearest neighbors (used during the construction algorithm). Higher `ef_construction` will result in better index quality and higher accuracy, but it will also increase the time required to build the index. `ef_construction` has to be at least 2 \* `m` (64 is the default value). At some point, increasing `ef_construction` does not improve the quality of the index. You can measure accuracy when `ef_search`=`ef_construction`: if accuracy is lower than 0.9, then there is room for improvement. + +Search parameters: + +- `ef_search` is the size of the dynamic list for the nearest neighbors (used during the search). Increasing `ef_search` will result in better accuracy, but it will also increase the time required to execute a query (40 is the default value). + +
+ dbpedia embeddings comparing hnsw queries-per-second using different build parameters (light) + dbpedia embeddings comparing hnsw queries-per-second using different build parameters (dark) +
+ +## IVFFlat, understanding `probes` and `lists` Indexes used for approximate vector similarity search in pgvector divides a dataset into partitions. The number of these partitions is defined by the `lists` constant. The `probes` controls how many lists are going to be searched during a query. -The values of lists and probes directly affect precision and requests per second (RPS). +The values of lists and probes directly affect accuracy and queries per second (QPS). -- Higher `lists` means an index will be built slower, but you can achieve better RPS and precision. -- Higher `probes` means that select queries will be slower, but you can achieve better precision. -- `lists` and `probes` are not independent. Higher `lists` means that you will have to use higher `probes` to achieve the same precision. +- Higher `lists` means an index will be built slower, but you can achieve better QPS and accuracy. +- Higher `probes` means that select queries will be slower, but you can achieve better accuracy. +- `lists` and `probes` are not independent. Higher `lists` means that you will have to use higher `probes` to achieve the same accuracy. -You can find more examples of how `lists` and `probes` constants affect precision and RPS in [pgvector 0.4.0 performance](https://supabase.com/blog/pgvector-performance) blogpost. +You can find more examples of how `lists` and `probes` constants affect accuracy and QPS in [pgvector 0.4.0 performance](https://supabase.com/blog/pgvector-performance) blogpost.
- -The time required to create an index grows with the number of records and size of vectors. For a few thousand records expect sub-minute a response in under a minute. It may take a few minutes for larger collections. - - - -For an in-depth guide on vector indexes, see [Managing indexes](/docs/guides/ai/managing-indexes). - -### Query - -Be aware that indexes are essential for good performance. If you do not create an index, every query will return a warning that includes the `IndexMeasure` you should index. - -#### Basic - -The simplest form of search is to provide a query vector. - -```python -docs.query( - query_vector=[0.4,0.5,0.6], # required - limit=5, # number of records to return - filters={}, # metadata filters - measure="cosine_distance", # distance measure to use - include_value=False, # should distance measure values be returned? - include_metadata=False, # should record metadata be returned? -) -``` - -Which returns a list of vector record `ids`. - -#### Metadata Filtering - -The metadata that is associated with each record can also be filtered during a query. - -As an example, `{"year": {"$eq": 2005}}` filters a `year` metadata key to be equal to 2005 - -In context: - -```python -docs.query( - query_vector=[0.4,0.5,0.6], - filters={"year": {"$eq": 2012}}, # metadata filters -) -``` - -For a complete reference, see the [metadata guide](https://supabase.github.io/vecs/concepts_metadata/). - -## Resources - -- Official Vecs Documentation: https://supabase.github.io/vecs/api -- Source Code: https://github.com/supabase/vecs - -export const Page = ({ children }) => - -export default Page diff --git a/apps/docs/pages/guides/ai/python-clients.mdx b/apps/docs/pages/guides/ai/python-clients.mdx new file mode 100644 index 00000000000..6e6695d3592 --- /dev/null +++ b/apps/docs/pages/guides/ai/python-clients.mdx @@ -0,0 +1,18 @@ +import Layout from '~/layouts/DefaultGuideLayout' + +export const meta = { + id: 'ai-python-clients', + title: 'Choosing a Client', + description: 'Learn how to manage vectors using Python', + sidebar_label: 'Choosing a Client', +} + +As described in [Structured & Unstructured Embeddings](/docs/guides/ai/structured-unstructured), AI workloads come in many forms. + +For data science or ephemeral workloads, the [Supabase Vecs](https://supabase.github.io/vecs/) client gets you started quickly. All you need is a connection string and vecs handles setting up your database to store and query vectors with associated metadata. + +For production python applications with version controlled migrations, we recommend adding first class vector support to your toolchain by [registering the vector type with your ORM](https://github.com/pgvector/pgvector-python). pgvector provides bindings for the most commonly used SQL drivers/libraries including Django, SQLAlchemy, SQLModel, psycopg, asyncpg and Peewee. + +export const Page = ({ children }) => + +export default Page diff --git a/apps/docs/pages/guides/ai/vecs-python-client.mdx b/apps/docs/pages/guides/ai/vecs-python-client.mdx index bdcecb77c43..ce7e5a7517c 100644 --- a/apps/docs/pages/guides/ai/vecs-python-client.mdx +++ b/apps/docs/pages/guides/ai/vecs-python-client.mdx @@ -79,7 +79,7 @@ docs.query( ## Deep Dive -For a more in-depth guide on `vecs` collections, see [Managing collections](/docs/guides/ai/managing-collections). +For a more in-depth guide on `vecs` collections, see [API](/docs/guides/ai/python/api). ## Resources diff --git a/apps/docs/pages/guides/ai/vector-columns.mdx b/apps/docs/pages/guides/ai/vector-columns.mdx index b263cb9b619..8b4d29643a5 100644 --- a/apps/docs/pages/guides/ai/vector-columns.mdx +++ b/apps/docs/pages/guides/ai/vector-columns.mdx @@ -7,7 +7,7 @@ export const meta = { sidebar_label: 'Vector columns', } -Supabase offers a number of different ways to store and query vectors within Postgres. If you prefer to use Python to store and query your vectors using collections, see [Managing collections](/docs/guides/ai/managing-collections). If you want more control over vectors within your own Postgres tables or would like to interact with them using a different language like JavaScript, keep reading. +Supabase offers a number of different ways to store and query vectors within Postgres. The SQL included in this guide is applicable for clients in all programming languages. If you are a Python user see your [Python client options](/docs/guides/ai/python-clients) after reading the `Learn` section. Vectors in Supabase are enabled via [pgvector](https://github.com/pgvector/pgvector/), a PostgreSQL extension for storing and querying vectors in Postgres. It can be used to store [embeddings](/docs/guides/ai/concepts#what-are-embeddings). @@ -162,7 +162,7 @@ Vectors and embeddings can be used for much more than search. Learn more about e ### Indexes -Once your vector table starts to grow, you will likely want to add an index to speed up queries. See [Managing indexes](/docs/guides/ai/managing-indexes) to learn how vector indexes work and how to create them. +Once your vector table starts to grow, you will likely want to add an index to speed up queries. See [Vector indexes](/docs/guides/ai/vector-indexes) to learn how vector indexes work and how to create them. export const Page = ({ children }) => diff --git a/apps/docs/pages/guides/ai/vector-indexes.mdx b/apps/docs/pages/guides/ai/vector-indexes.mdx new file mode 100644 index 00000000000..2bb3a4e310e --- /dev/null +++ b/apps/docs/pages/guides/ai/vector-indexes.mdx @@ -0,0 +1,39 @@ +import Layout from '~/layouts/DefaultGuideLayout' + +export const meta = { + id: 'ai-vector-indexes', + title: 'Vector indexes', + description: 'Understanding vector indexes', + sidebar_label: 'Vector indexes', +} + +Once your vector table starts to grow, you will likely want to add an index to speed up queries. Without indexes, you'll be performing a sequential scan which can be a resource-intensive operation when you have many records. + +## Choosing an index + +Today `pgvector` supports two types of indexes: + +- [HNSW](/docs/guides/ai/vector-indexes/hnsw-indexes) +- [IVFFlat](/docs/guides/ai/vector-indexes/ivf-indexes) + +In general 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). + +## Distance operators + +Indexes can be used to improve performance of nearest neighbor search using various distance measures. `pgvector` includes 3 distance operators: + +| Operator | Description | [**Operator class**](https://www.postgresql.org/docs/current/sql-createopclass.html) | +| -------- | ---------------------- | ------------------------------------------------------------------------------------ | +| `<->` | Euclidean distance | `vector_l2_ops` | +| `<#>` | negative inner product | `vector_ip_ops` | +| `<=>` | cosine distance | `vector_cosine_ops` | + +Currently vectors with up to 2,000 dimensions can be indexed. + +## Resources + +Read more about indexing on `pgvector`'s [GitHub page](https://github.com/pgvector/pgvector#indexing). + +export const Page = ({ children }) => + +export default Page diff --git a/apps/docs/pages/guides/ai/vector-indexes/hnsw-indexes.mdx b/apps/docs/pages/guides/ai/vector-indexes/hnsw-indexes.mdx new file mode 100644 index 00000000000..75fb6d336e8 --- /dev/null +++ b/apps/docs/pages/guides/ai/vector-indexes/hnsw-indexes.mdx @@ -0,0 +1,101 @@ +import Layout from '~/layouts/DefaultGuideLayout' + +export const meta = { + id: 'ai-hnsw-indexes', + title: 'HNSW indexes', + description: 'Understanding HNSW indexes in pgvector', + sidebar_label: 'HNSW indexes', +} + +HNSW is an algorithm for approximate nearest neighbor search. It is a frequently used index type that can improve performance when querying highly-dimensional vectors, like those representing embeddings. + +## Usage + +The way you create an HNSW index depends on the distance operator you are using. `pgvector` includes 3 distance operators: + +| Operator | Description | [**Operator class**](https://www.postgresql.org/docs/current/sql-createopclass.html) | +| -------- | ---------------------- | ------------------------------------------------------------------------------------ | +| `<->` | Euclidean distance | `vector_l2_ops` | +| `<#>` | negative inner product | `vector_ip_ops` | +| `<=>` | cosine distance | `vector_cosine_ops` | + +Use the following SQL commands to create an HNSW index for the operator(s) used in your queries. + +### Euclidean L2 distance (`vector_l2_ops`) + +```sql +create index on items using hnsw (column_name vector_l2_ops); +``` + +### Inner product (`vector_ip_ops`) + +```sql +create index on items using hnsw (column_name vector_ip_ops); +``` + +### Cosine distance (`vector_cosine_ops`) + +```sql +create index on items using hnsw (column_name vector_cosine_ops); +``` + +Currently vectors with up to 2,000 dimensions can be indexed. + +## How does HNSW work? + +HNSW uses proximity graphs (graphs connecting nodes based on distance between them) to approximate nearest-neighbor search. To understand HNSW, we can break it down into 2 parts: + +- **Hierarchical (H):** The algorithm operates over multiple layers +- **Navigable Small World (NSW):** Each vector is a node within a graph and is connected to several other nodes + +### Hierarchical + +The hierarchical aspect of HNSW builds off of the idea of skip lists. + +Skip lists are multi-layer linked lists. The bottom layer is a regular linked list connecting an ordered sequence of elements. Each new layer above removes some elements from the underlying layer (based on a fixed probability), producing a sparser subsequence that “skips” over elements. + +
+ visual of an example skip list (light) + visual of an example skip list (dark) +
+ +When searching for an element, the algorithm begins at the top layer and traverses its linked list horizontally. If the target element is found, the algorithm stops and returns it. Otherwise if the next element in the list is greater than the target (or `NULL`), the algorithm drops down to the next layer below. Since each layer below is less sparse than the layer above (with the bottom layer connecting all elements), the target will eventually be found. Skip lists offer O(log n) average complexity for both search and insertion/deletion. + +### Navigable Small World + +A navigable small world (NSW) is a special type of proximity graph that also includes long-range connections between nodes. These long-range connections support the “small world” property of the graph, meaning almost every node can be reached from any other node within a few hops. Without these additional long-range connections, many hops would be required to reach a far-away node. + +visual of an example navigable small world graph + +The “navigable” part of NSW specifically refers to the ability to logarithmically scale the greedy search algorithm on the graph, an algorithm that attempts to make only the locally optimal choice at each hop. Without this property, the graph may still be considered a small world with short paths between far-away nodes, but the greedy algorithm tends to miss them. Greedy search is ideal for NSW because it is quick to navigate and has low computational costs. + +### **Hierarchical +** Navigable Small World + +HNSW combines these two concepts. From the hierarchical perspective, the bottom layer consists of a NSW made up of short links between nodes. Each layer above “skips” elements and creates longer links between nodes further away from each other. + +Just like skip lists, search starts at the top layer and works its way down until it finds the target element. However, instead of comparing a scalar value at each layer to determine whether or not to descend to the layer below, a multi-dimensional distance measure (such as Euclidean distance) is used. + +## When should you create HNSW indexes? + +HNSW should be your default choice when creating a vector index. Add the index when you don't need 100% accuracy and are willing to trade a small amount of accuracy for a lot of throughput. + +Unlike IVFFlat indexes, you are safe to build an HNSW index immediately after the table is created. HNSW indexes are based on graphs which inherently are not affected by the same limitations as IVFFlat. As new data is added to the table, the index will be filled automatically and the index structure will remain optimal. + +## Resources + +Read more about indexing on `pgvector`'s [GitHub page](https://github.com/pgvector/pgvector#indexing). + +export const Page = ({ children }) => + +export default Page diff --git a/apps/docs/pages/guides/ai/managing-indexes.mdx b/apps/docs/pages/guides/ai/vector-indexes/ivf-indexes.mdx similarity index 60% rename from apps/docs/pages/guides/ai/managing-indexes.mdx rename to apps/docs/pages/guides/ai/vector-indexes/ivf-indexes.mdx index 6269919badf..46795777c68 100644 --- a/apps/docs/pages/guides/ai/managing-indexes.mdx +++ b/apps/docs/pages/guides/ai/vector-indexes/ivf-indexes.mdx @@ -1,17 +1,58 @@ import Layout from '~/layouts/DefaultGuideLayout' export const meta = { - id: 'ai-managing-indexes', - title: 'Managing indexes', - description: 'Understanding vector indexes', - sidebar_label: 'Managing indexes', + id: 'ai-ivf-indexes', + title: 'IVFFlat indexes', + description: 'Understanding IVFFlat indexes in pgvector', + sidebar_label: 'IVFFlat indexes', } -Once your vector table starts to grow, you will likely want to add an index to speed up queries. Without indexes, you'll be performing a sequential scan which can be a resource-intensive operation when you have many records. +IVFFlat is a type of vector index for approximate nearest neighbor search. It is a frequently used index type that can improve performance when querying highly-dimensional vectors, like those representing embeddings. -## IVFFlat indexes +## Choosing an index -Today `pgvector` indexes use an algorithm called IVFFlat. IVF stands for 'inverted file indexes'. It works by clustering your vectors in order to reduce the similarity search scope. Rather than comparing a vector to every other vector, the vector is only compared against vectors within the same cell cluster (or nearby clusters, depending on your configuration). +Today `pgvector` supports two types of indexes: + +- [HNSW](/docs/guides/ai/vector-indexes/hnsw-indexes) +- [IVFFlat](/docs/guides/ai/vector-indexes/ivf-indexes) + +In general 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). If you have a special use case that requires IVFFlat instead, keep reading. + +## Usage + +The way you create an IVFFlat index depends on the distance operator you are using. `pgvector` includes 3 distance operators: + +| Operator | Description | [**Operator class**](https://www.postgresql.org/docs/current/sql-createopclass.html) | +| -------- | ---------------------- | ------------------------------------------------------------------------------------ | +| `<->` | Euclidean distance | `vector_l2_ops` | +| `<#>` | negative inner product | `vector_ip_ops` | +| `<=>` | cosine distance | `vector_cosine_ops` | + +Use the following SQL commands to create an IVFFlat index for the operator(s) used in your queries. + +### Euclidean L2 distance (`vector_l2_ops`) + +```sql +create index on items using ivfflat (column_name vector_l2_ops) with (lists = 100); +``` + +### Inner product (`vector_ip_ops`) + +```sql +create index on items using ivfflat (column_name vector_ip_ops) with (lists = 100); +``` + +### Cosine distance (`vector_cosine_ops`) + +```sql +create index on items using ivfflat (column_name vector_cosine_ops) with (lists = 100); +``` + +Currently vectors with up to 2,000 dimensions can be indexed. + +## How does IVFFlat work? + +IVF stands for 'inverted file indexes'. It works by clustering your vectors in order to reduce the similarity search scope. Rather than comparing a vector to every other vector, the vector is only compared against vectors within the same cell cluster (or nearby clusters, depending on your configuration). ### Inverted lists (cell clusters) @@ -48,43 +89,9 @@ If the number of probes is the same as the number of lists, exact nearest neighb One important note with IVF indexes is that nearest neighbor search is approximate, since exact search on high dimensional data can't be indexed efficiently. This means that similarity results will change (slightly) after you add an index (trading recall for speed). -## Distance operators +## When should you create IVFFlat indexes? -The type of index required depends on the distance operator you are using. `pgvector` includes 3 distance operators: - -| Operator | Description | [**Operator class**](https://www.postgresql.org/docs/current/sql-createopclass.html) | -| -------- | ---------------------- | ------------------------------------------------------------------------------------ | -| `<->` | Euclidean distance | `vector_l2_ops` | -| `<#>` | negative inner product | `vector_ip_ops` | -| `<=>` | cosine distance | `vector_cosine_ops` | - -Use the following SQL commands to create an index for the operator(s) used in your queries. - -### Euclidean L2 distance (`vector_l2_ops`) - -```sql -create index on items using ivfflat (column_name vector_l2_ops) with (lists = 100); -``` - -### Inner product (`vector_ip_ops`) - -```sql -create index on items using ivfflat (column_name vector_ip_ops) with (lists = 100); -``` - -### Cosine distance (`vector_cosine_ops`) - -```sql -create index on items using ivfflat (column_name vector_cosine_ops) with (lists = 100); -``` - -Currently vectors with up to 2,000 dimensions can be indexed. - -If you are using the `vecs` Python library, follow the instructions in [Managing collections](/docs/guides/ai/managing-collections#create-an-index) to create indexes. - -## When should you add indexes? - -`pgvector` recommends adding indexes only after the table has sufficient data, so that the internal IVFFlat cell clusters are based on your data's distribution. Anytime the distribution changes significantly, consider recreating indexes. +`pgvector` recommends building IVFFlat indexes only after the table has sufficient data, so that the internal IVFFlat cell clusters are based on your data's distribution. Anytime the distribution changes significantly, consider rebuilding indexes. ## Resources diff --git a/apps/docs/pages/guides/auth.mdx b/apps/docs/pages/guides/auth.mdx index b827086f831..2a516ff35ea 100644 --- a/apps/docs/pages/guides/auth.mdx +++ b/apps/docs/pages/guides/auth.mdx @@ -6,12 +6,10 @@ export const meta = { id: 'auth', title: 'Auth', description: 'Use Supabase to Authenticate and Authorize your users.', - sidebar_label: 'Overview', + subtitle: 'Use Supabase to authenticate and authorize your users.', video: 'https://www.youtube.com/v/6ow_jW4epf8', } -## Overview - There are two parts to every Auth system: - **Authentication:** should this person be allowed in? If yes, who are they? @@ -64,56 +62,13 @@ You can enable third-party providers with the click of a button by navigating to ### Redirect URLs and wildcards -When using third-party providers, the [Supabase client library](/docs/reference/javascript/auth-signinwithoauth#sign-in-using-a-third-party-provider-with-redirect) redirects the user to the provider. When the third-party provider successfully authenticates the user, the provider redirects the user to the Supabase Auth callback URL where they are further redirected to the URL specified in the `redirectTo` parameter. This parameter defaults to the [`SITE_URL`](/docs/reference/auth/config#site_url). You can modify the `SITE_URL` or add additional [redirect URLs](https://supabase.com/dashboard/project/_/auth/url-configuration). +We've moved the guide for setting up redirect URLs [here](/docs/guides/auth/concepts/redirect-urls). -You can use wildcard match patterns to support preview URLs from providers like Netlify and Vercel. See the [full list of supported patterns](https://pkg.go.dev/github.com/gobwas/glob#Compile). Use [this tool](https://www.digitalocean.com/community/tools/glob?comments=true&glob=http%3A%2F%2Flocalhost%3A3000%2F%2A%2A&matches=false&tests=http%3A%2F%2Flocalhost%3A3000&tests=http%3A%2F%2Flocalhost%3A3000%2F&tests=http%3A%2F%2Flocalhost%3A3000%2F%3Ftest%3Dtest&tests=http%3A%2F%2Flocalhost%3A3000%2Ftest-test%3Ftest%3Dtest&tests=http%3A%2F%2Flocalhost%3A3000%2Ftest%2Ftest%3Ftest%3Dtest) to test your patterns. +#### [Netlify preview URLs](/docs/guides/auth/concepts/redirect-urls#netlify-preview-urls) - +#### [Vercel preview URLs](/docs/guides/auth/concepts/redirect-urls#vercel-preview-urls) -While the "globstar" (`**`) is useful for local development and preview URLs, we recommend setting the exact redirect URL path for your site URL in production. - - - -#### Netlify preview URLs - -For deployments with Netlify, set the `SITE_URL` to your official site URL. Add the following additional redirect URLs for local development and deployment previews: - -- `http://localhost:3000/**` -- `https://**--my_org.netlify.app/**` - -#### Vercel preview URLs - -For deployments with Vercel, set the `SITE_URL` to your official site URL. Add the following additional redirect URLs for local development and deployment previews: - -- `http://localhost:3000/**` -- `https://*-username.vercel.app/**` - -Vercel provides an environment variable for the URL of the deployment called `NEXT_PUBLIC_VERCEL_URL`. See the [Vercel docs](https://vercel.com/docs/concepts/projects/environment-variables#system-environment-variables) for more details. You can use this variable to dynamically redirect depending on the environment. You should also set the value of the environment variable called NEXT_PUBLIC_SITE_URL, this should be set to your site URL in production environment to ensure that redirects function correctly. - -```js -const getURL = () => { - let url = - process?.env?.NEXT_PUBLIC_SITE_URL ?? // Set this to your site URL in production env. - process?.env?.NEXT_PUBLIC_VERCEL_URL ?? // Automatically set by Vercel. - 'http://localhost:3000/' - // Make sure to include `https://` when not localhost. - url = url.includes('http') ? url : `https://${url}` - // Make sure to include a trailing `/`. - url = url.charAt(url.length - 1) === '/' ? url : `${url}/` - return url -} - -const { data, error } = await supabase.auth.signInWithOAuth({ - provider: 'github', - options: { - redirectTo: getURL(), - }, -}) -``` - -#### Mobile deep linking URIs - -For mobile applications you can use deep linking URIs. For example for your `SITE_URL` you can specify something like `com.supabase://login-callback/` and for additional redirect URLs something like `com.supabase.staging://login-callback/` if needed. +#### [Mobile deep linking URIs](/docs/guides/auth/concepts/redirect-urls#mobile-deep-linking-uris) ## Authorization diff --git a/apps/docs/pages/guides/auth/auth-helpers.mdx b/apps/docs/pages/guides/auth/auth-helpers.mdx index 26b798b820b..041df7fa54b 100644 --- a/apps/docs/pages/guides/auth/auth-helpers.mdx +++ b/apps/docs/pages/guides/auth/auth-helpers.mdx @@ -2,31 +2,15 @@ import Layout from '~/layouts/DefaultGuideLayout' export const meta = { id: 'index', - title: 'Auth Helpers Overview', - description: 'A collection of framework-specific Auth utilities for working with Supabase.', + title: 'Server-Side Auth Overview', + description: 'Server-Side Auth guides and utilities for working with Supabase.', sidebar_label: 'Overview', } -A collection of framework-specific Auth utilities for working with Supabase. +Working with server-side frameworks is slightly different to client-side frameworks. In this section we cover the various ways of handling server-side authentication and demonstrate how to use the Supabase helper-libraries to make the process more seamless.
- {/* Auth UI */} -
- -
- {/* Flutter Auth UI */} -
- -
{/* Next.js */}
diff --git a/apps/docs/pages/guides/auth/concepts/redirect-urls.mdx b/apps/docs/pages/guides/auth/concepts/redirect-urls.mdx new file mode 100644 index 00000000000..371df16ed15 --- /dev/null +++ b/apps/docs/pages/guides/auth/concepts/redirect-urls.mdx @@ -0,0 +1,87 @@ +import Layout from '~/layouts/DefaultGuideLayout' + +export const meta = { + id: 'redirect-urls', + title: 'Redirect URLs', + description: 'Set up redirect urls with Supabase Auth.', + subtitle: 'Set up redirect urls with Supabase Auth.', +} + +## Overview + +When using [passwordless sign-ins](/docs/reference/javascript/auth-signinwithotp) or [third-party providers](/docs/reference/javascript/auth-signinwithoauth#sign-in-using-a-third-party-provider-with-redirect), the Supabase client library methods provide a `redirectTo` parameter to specify where to redirect the user to after authentication. By default, the user will be redirected to the [`SITE_URL`](/docs/reference/auth/config#site_url) but you can modify the `SITE_URL` or add additional redirect URLs to the [allow list](https://supabase.com/dashboard/project/_/auth/url-configuration). Once you've added necessary URLs to the allow list, you can specify the URL you want the user to be redirected to in the `redirectTo` parameter. + +## Use wildcards in redirect URLs + +Supabase allows you to specify wildcards when adding redirect URLs to the [allow list](https://supabase.com/dashboard/project/_/auth/url-configuration). You can use wildcard match patterns to support preview URLs from providers like Netlify and Vercel. + +| Wildcard | Description | +| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------ | +| `*` | matches any sequence of non-separator characters | +| `**` | matches any sequence of characters | +| `?` | matches any single non-separator character | +| `c` | matches character c (c != `*`, `**`, `?`, `\`, `[`, `{`, `}`) | +| `\c` | matches character c | +| `[!{ character-range }]` | matches any sequence of characters not in the `{ character-range }`. For example, `[!a-z]` will not match any characters ranging from a-z. | + +The separator characters in a URL are defined as `.` and `/`. Use [this tool](https://www.digitalocean.com/community/tools/glob?comments=true&glob=http%3A%2F%2Flocalhost%3A3000%2F%2A%2A&matches=false&tests=http%3A%2F%2Flocalhost%3A3000&tests=http%3A%2F%2Flocalhost%3A3000%2F&tests=http%3A%2F%2Flocalhost%3A3000%2F%3Ftest%3Dtest&tests=http%3A%2F%2Flocalhost%3A3000%2Ftest-test%3Ftest%3Dtest&tests=http%3A%2F%2Flocalhost%3A3000%2Ftest%2Ftest%3Ftest%3Dtest) to test your patterns. + + + +While the "globstar" (`**`) is useful for local development and preview URLs, we recommend setting the exact redirect URL path for your site URL in production. + + + +### Redirect URL examples with wildcards + +| Redirect URL | Description | +| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `http://localhost:3000/*` | matches `http://localhost:3000/foo`, `http://localhost:3000/bar` but not `http://localhost:3000/foo/bar` or `http://localhost:3000/foo/` (note the trailing slash) | +| `http://localhost:3000/**` | matches `http://localhost:3000/foo`, `http://localhost:3000/bar` and `http://localhost:3000/foo/bar` | +| `http://localhost:3000/?` | matches `http://localhost:3000/a` but not `http://localhost:3000/foo` | +| `http://localhost:3000/[!a-z]` | matches `http://localhost:3000/1` but not `http://localhost:3000/a` | + +## Netlify preview URLs + +For deployments with Netlify, set the `SITE_URL` to your official site URL. Add the following additional redirect URLs for local development and deployment previews: + +- `http://localhost:3000/**` +- `https://**--my_org.netlify.app/**` + +## Vercel preview URLs + +For deployments with Vercel, set the `SITE_URL` to your official site URL. Add the following additional redirect URLs for local development and deployment previews: + +- `http://localhost:3000/**` +- `https://*-username.vercel.app/**` + +Vercel provides an environment variable for the URL of the deployment called `NEXT_PUBLIC_VERCEL_URL`. See the [Vercel docs](https://vercel.com/docs/concepts/projects/environment-variables#system-environment-variables) for more details. You can use this variable to dynamically redirect depending on the environment. You should also set the value of the environment variable called NEXT_PUBLIC_SITE_URL, this should be set to your site URL in production environment to ensure that redirects function correctly. + +```js +const getURL = () => { + let url = + process?.env?.NEXT_PUBLIC_SITE_URL ?? // Set this to your site URL in production env. + process?.env?.NEXT_PUBLIC_VERCEL_URL ?? // Automatically set by Vercel. + 'http://localhost:3000/' + // Make sure to include `https://` when not localhost. + url = url.includes('http') ? url : `https://${url}` + // Make sure to include a trailing `/`. + url = url.charAt(url.length - 1) === '/' ? url : `${url}/` + return url +} + +const { data, error } = await supabase.auth.signInWithOAuth({ + provider: 'github', + options: { + redirectTo: getURL(), + }, +}) +``` + +## Mobile deep linking URIs + +For mobile applications you can use deep linking URIs. For example, for your `SITE_URL` you can specify something like `com.supabase://login-callback/` and for additional redirect URLs something like `com.supabase.staging://login-callback/` if needed. + +export const Page = ({ children }) => + +export default Page diff --git a/apps/docs/pages/guides/auth/phone-login/messagebird.mdx b/apps/docs/pages/guides/auth/phone-login/messagebird.mdx index 273b4870944..ad70cc72d52 100644 --- a/apps/docs/pages/guides/auth/phone-login/messagebird.mdx +++ b/apps/docs/pages/guides/auth/phone-login/messagebird.mdx @@ -111,7 +111,7 @@ curl -X POST 'https://cvwawazfelidkloqmbma.supabase.co/auth/v1/signup' \ The user will now receive an SMS with a 6-digit pin that you will need to receive from them within 60-seconds before they can login to their account. -You should present a form to the user so they can input the 6 digit pin, then send it along with the phone number to `verifyOTP`: +You should present a form to the user so they can input the 6 digit pin, then send it along with the phone number to `verifyOtp`: ```js -let { session, error } = await supabase.auth.verifyOTP({ +let { session, error } = await supabase.auth.verifyOtp({ phone: '+13334445555', token: '123456', }) @@ -237,7 +237,7 @@ The second step is the same as the previous section, you need to collect the 6-d ```js -let { session, error } = await supabase.auth.verifyOTP({ +let { session, error } = await supabase.auth.verifyOtp({ phone: '+13334445555', token: '123456', }) diff --git a/apps/docs/pages/guides/auth/phone-login/twilio.mdx b/apps/docs/pages/guides/auth/phone-login/twilio.mdx index 70dd119c164..7489c644267 100644 --- a/apps/docs/pages/guides/auth/phone-login/twilio.mdx +++ b/apps/docs/pages/guides/auth/phone-login/twilio.mdx @@ -157,7 +157,7 @@ curl -X POST 'https://cvwawazfelidkloqmbma.supabase.co/auth/v1/signup' \ The user will now receive an SMS with a 6-digit pin that you will need to receive from them within 60-seconds before they can login to their account. -You should present a form to the user so they can input the 6 digit pin, then send it along with the phone number to `verifyOTP`: +You should present a form to the user so they can input the 6 digit pin, then send it along with the phone number to `verifyOtp`: ```js -let { session, error } = await supabase.auth.verifyOTP({ +let { session, error } = await supabase.auth.verifyOtp({ phone: '491512223334444', token: '123456', }) @@ -230,7 +230,7 @@ The second step is the same as the previous section, you need to collect the 6-d ```js -let { session, error } = await supabase.auth.verifyOTP({ +let { session, error } = await supabase.auth.verifyOtp({ phone: '491512223334444', token: '123456', }) diff --git a/apps/docs/pages/guides/auth/server-side/email-based-auth-with-pkce-flow-for-ssr.mdx b/apps/docs/pages/guides/auth/server-side/email-based-auth-with-pkce-flow-for-ssr.mdx new file mode 100644 index 00000000000..9e41fe99ede --- /dev/null +++ b/apps/docs/pages/guides/auth/server-side/email-based-auth-with-pkce-flow-for-ssr.mdx @@ -0,0 +1,258 @@ +import Layout from '~/layouts/DefaultGuideLayout' + +export const meta = { + title: 'Email Auth with PKCE flow for SSR', + description: + 'Learn how to configure email authentication in your server-side rendering (SSR) application to work with the PKCE flow.', + subtitle: + 'Learn how to configure email authentication in your server-side rendering (SSR) application to work with the PKCE flow.', +} + +### Install Supabase Auth Helpers + +The Auth Helpers will assist you in implementing user authentication within your server-side rendering (SSR) framework. + + + + + +```bash +npm install @supabase/auth-helpers-nextjs @supabase/supabase-js +``` + + + + + +```bash +npm install @supabase/auth-helpers-sveltekit @supabase/supabase-js +``` + + + + + +### Set environment variables + +Create an `.env.local` file in your project root directory. You can get your `SITE_URL` and `ANON_KEY` from inside of the [dashboard](https://supabase.com/dashboard/project/_/settings/api). + + + + + +```bash .env.local +NEXT_PUBLIC_SUPABASE_URL=your_supabase_project_url +NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key +``` + + + + +```bash .env.local +PUBLIC_SUPABASE_URL=your_supabase_project_url +PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key +``` + + + + +### Setting up the Auth Helpers + +When using the Supabase client on the server, you must perform extra steps to ensure the user's auth session remains active. Since the user's session is tracked in a cookie, we need to read this cookie and update it if necessary. + + + +Next.js Server Components allow you to read a cookie but not write back to it. Middleware on the other hand allow you to both read and write to cookies. + +Next.js [Middleware](https://nextjs.org/docs/app/building-your-application/routing/middleware) runs immediately before each route is rendered. We'll use Middleware to refresh the user's session before loading Server Component routes. + +Create a new `middleware.js` file in the root of your project and populate with the following: + +```js middleware.js +import { createMiddlewareClient } from '@supabase/auth-helpers-nextjs' +import { NextResponse } from 'next/server' + +export async function middleware(req) { + const res = NextResponse.next() + const supabase = createMiddlewareClient({ req, res }) + await supabase.auth.getSession() + return res +} +``` + + + +Create a new `hooks.server.js` file in the root of your project and populate with the following: + +```ts src/hooks.server.js +import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY } from '$env/static/public' +import { createSupabaseServerClient } from '@supabase/auth-helpers-sveltekit' +import type { Handle } from '@sveltejs/kit' + +export const handle: Handle = async ({ event, resolve }) => { + event.locals.supabase = createSupabaseServerClient({ + supabaseUrl: PUBLIC_SUPABASE_URL, + supabaseKey: PUBLIC_SUPABASE_ANON_KEY, + event, + }) + + event.locals.getSession = async () => { + const { + data: { session }, + } = await event.locals.supabase.auth.getSession() + return session + } + + return resolve(event, { + filterSerializedResponseHeaders(name) { + return name === 'content-range' + }, + }) +} +``` + + + + +### Create API endpoint for handling `token_hash` + +In order to use the updated email links we will need to setup a endpoint for verifying the `token_hash` along with the `type` to exchange `token_hash` for the user's `session`, which is set as a cookie for future requests made to Supabase. + + + +Create a new file at `app/auth/confirm/route.js` and populate with the following: + +```js app/auth/confirm/route.js +import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs' +import { cookies } from 'next/headers' +import { NextResponse } from 'next/server' + +export async function GET(req) { + const { searchParams } = new URL(req.url) + const token_hash = searchParams.get('token_hash') + const type = searchParams.get('type') + const next = searchParams.get('next') ?? '/' + + if (token_hash && type) { + const supabase = createRouteHandlerClient({ cookies }) + const { error } = await supabase.auth.verifyOtp({ type, token_hash }) + if (!error) { + return NextResponse.redirect(new URL(`/${next.slice(1)}`, req.url)) + } + } + + // return the user to an error page with some instructions + return NextResponse.redirect(new URL('/auth/auth-code-error', req.url)) +} +``` + + + +Create a new file at `src/routes/auth/confirm/+server.js` and populate with the following: + +```js src/routes/auth/confirm/+server.js +import { redirect } from '@sveltejs/kit'; + +export const GET = async (event) => { + const { + url, + locals: { supabase } + } = event; + const token_hash = url.searchParams.get('token_hash') as string; + const type = url.searchParams.get('type') as string; + const next = url.searchParams.get('next') ?? '/'; + + if (token_hash && type) { + const { error } = await supabase.auth.verifyOtp({ token_hash, type }); + if (!error) { + throw redirect(303, `/${next.slice(1)}`); + } + } + + // return the user to an error page with some instructions + throw redirect(303, '/auth/auth-code-error'); +}; +``` + + + + +### Update email templates with URL for API endpoint + +Let's update the URL in our email templates to point to our new confirmation endpoint for the user to get confirmed. + +**Confirm signup template** + +```html +

Confirm your signup

+ +

Follow this link to confirm your user:

+

+ Confirm your email +

+``` + +**Invite user template** + +```html +

You have been invited

+ +

+ You have been invited to create a user on {{ .SiteURL }}. Follow this link to accept the invite: +

+ +

+ Accept the invite +

+``` + +**Magic Link template** + +```html +

Magic Link

+ +

Follow this link to login:

+

Log In

+``` + +**Change Email Address template** + +```html +

Confirm Change of Email

+ +

Follow this link to confirm the update of your email from {{ .Email }} to {{ .NewEmail }}:

+

Change Email

+``` + +**Reset Password template** + +```html +

Reset Password

+ +

Follow this link to reset the password for your user:

+

+ Reset Password +

+``` + +export const Page = ({ children }) => + +export default Page diff --git a/apps/docs/pages/guides/auth/server-side/oauth-with-pkce-flow-for-ssr.mdx b/apps/docs/pages/guides/auth/server-side/oauth-with-pkce-flow-for-ssr.mdx new file mode 100644 index 00000000000..60f6fb53a1c --- /dev/null +++ b/apps/docs/pages/guides/auth/server-side/oauth-with-pkce-flow-for-ssr.mdx @@ -0,0 +1,207 @@ +import Layout from '~/layouts/DefaultGuideLayout' + +export const meta = { + title: 'OAuth with PKCE flow for SSR', + description: + 'Learn how to configure OAuth authentication in your server-side rendering (SSR) application to work with the PKCE flow.', + subtitle: + 'Learn how to configure OAuth authentication in your server-side rendering (SSR) application to work with the PKCE flow.', +} + +### Install Supabase Auth Helpers + +The Auth Helpers assist with user authentication within server-side rendering (SSR) frameworks. + + + + +```bash +npm install @supabase/auth-helpers-nextjs @supabase/supabase-js +``` + + + + +```bash +npm install @supabase/auth-helpers-sveltekit @supabase/supabase-js +``` + + + + +### Set environment variables + +Create an `.env.local` file in your project root directory. You can get your `SITE_URL` and `ANON_KEY` from inside of the [dashboard](https://supabase.com/dashboard/project/_/settings/api). + + + + +```bash .env.local +NEXT_PUBLIC_SUPABASE_URL=your_supabase_project_url +NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key +``` + + + + +```bash .env.local +PUBLIC_SUPABASE_URL=your_supabase_project_url +PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key +``` + + + + +### Setting up the Auth Helpers + +For SSR, the Supabase client requires extra steps to ensure the user's auth session remains active. Since the user's session is tracked in a cookie, we need to read this cookie and update it if necessary. + + + +Next.js Server Components allow you to read a cookie but not write back to it. Middleware on the other hand allow you to both read and write to cookies. + +Next.js [Middleware](https://nextjs.org/docs/app/building-your-application/routing/middleware) runs immediately before each route is rendered. We'll use Middleware to refresh the user's session before loading Server Component routes. + +Create a new `middleware.js` file in the root of your project and populate with the following: + +```js middleware.js +import { createMiddlewareClient } from '@supabase/auth-helpers-nextjs' +import { NextResponse } from 'next/server' + +export async function middleware(req) { + const res = NextResponse.next() + const supabase = createMiddlewareClient({ req, res }) + await supabase.auth.getSession() + return res +} +``` + + + +Create a new `hooks.server.js` file in the root of your project and populate with the following: + +```ts src/hooks.server.js +import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY } from '$env/static/public' +import { createSupabaseServerClient } from '@supabase/auth-helpers-sveltekit' +import type { Handle } from '@sveltejs/kit' + +export const handle: Handle = async ({ event, resolve }) => { + event.locals.supabase = createSupabaseServerClient({ + supabaseUrl: PUBLIC_SUPABASE_URL, + supabaseKey: PUBLIC_SUPABASE_ANON_KEY, + event, + }) + + event.locals.getSession = async () => { + const { + data: { session }, + } = await event.locals.supabase.auth.getSession() + return session + } + + return resolve(event, { + filterSerializedResponseHeaders(name) { + return name === 'content-range' + }, + }) +} +``` + + + + +### Create API endpoint for handling the `code` exchange + +In order to use OAuth we will need to setup a endpoint for the `code` exchange, to exchange an auth `code` for the user's `session`, which is set as a cookie for future requests made to Supabase. + + + +Create a new file at `app/auth/callback/route.js` and populate with the following: + +```js app/auth/callback/route.js +import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs' +import { cookies } from 'next/headers' +import { NextResponse } from 'next/server' + +export async function GET(req) { + const { searchParams } = new URL(req.url) + const code = searchParams.get('code') + const next = searchParams.get('next') ?? '/' + + if (code) { + const supabase = createRouteHandlerClient({ cookies: () => cookies() }) + const { error } = await supabase.auth.exchangeCodeForSession(code) + if (!error) { + return NextResponse.redirect(new URL(`/${next.slice(1)}`, req.url)) + } + } + + // return the user to an error page with instructions + return NextResponse.redirect(new URL('/auth/auth-code-error', req.url)) +} +``` + + + +Create a new file at `src/routes/auth/callback/+server.js` and populate with the following: + +```js src/routes/auth/callback/+server.js +import { redirect } from '@sveltejs/kit'; + +export const GET = async (event) => { + const { + url, + locals: { supabase } + } = event; + const code = url.searchParams.get('code') as string; + const next = url.searchParams.get('next') ?? '/'; + + if (code) { + const { error } = await supabase.auth.exchangeCodeForSession(code) + if (!error) { + throw redirect(303, `/${next.slice(1)}`); + } + } + + // return the user to an error page with instructions + throw redirect(303, '/auth/auth-code-error'); +}; +``` + + + + +Let's point our `.signInWithOAuth` method's redirect to the callback route we create above: + +```js +await supabase.auth.signInWithOAuth({ + email, + options: { + redirectTo: `http://example.com/auth/callback`, + }, +}) +``` + +export const Page = ({ children }) => + +export default Page diff --git a/apps/docs/pages/guides/database/column-encryption.mdx b/apps/docs/pages/guides/database/column-encryption.mdx index 1734d88a6af..0f6d7e599ee 100644 --- a/apps/docs/pages/guides/database/column-encryption.mdx +++ b/apps/docs/pages/guides/database/column-encryption.mdx @@ -11,7 +11,7 @@ export const meta = { Supabase provides a secure method for encrypting data using [Vault](/docs/guides/database/vault), our Postgres secrets manager. Vault is a Postgres extension with an [integrated UI](https://app.supabase.com/project/_/settings/vault/secrets) intended to act as a secure global secrets management for your project. -In addition to the Vault secret storage table, Supabase also enables an advanced feature called Transparent Column Encryption (TCE) which provides a safe way to encrypt columns in your own tables so that they doesn't leak into logs and backups. It can also provide row-level authenticated encryption. +In addition to the Vault secret storage table, Supabase also enables an advanced feature called Transparent Column Encryption (TCE) which provides a safe way to encrypt columns in your own tables so that they don't leak into logs and backups. It can also provide row-level authenticated encryption. Column Encryption comes with tradeoffs that need to be considered before using it. diff --git a/apps/docs/pages/guides/database/extensions/index_advisor.mdx b/apps/docs/pages/guides/database/extensions/index_advisor.mdx index d155c36a49b..988d2f315b2 100644 --- a/apps/docs/pages/guides/database/extensions/index_advisor.mdx +++ b/apps/docs/pages/guides/database/extensions/index_advisor.mdx @@ -31,7 +31,7 @@ Features: ## Installation -index_advisor is a trusted language extension, which means it is directly installable by users from the [database.dev](database.dev) SQL package repository. +index_advisor is a trusted language extension, which means it is directly installable by users from the [database.dev](https://database.dev/) SQL package repository. To get started, enable the dbdev client by executing the [setup SQL script](https://database.dev/installer). diff --git a/apps/docs/pages/guides/database/postgres/triggers.mdx b/apps/docs/pages/guides/database/postgres/triggers.mdx index 58b8aca96f4..bd3a48106c2 100644 --- a/apps/docs/pages/guides/database/postgres/triggers.mdx +++ b/apps/docs/pages/guides/database/postgres/triggers.mdx @@ -49,7 +49,7 @@ $$; create trigger salary_update_trigger after update on employees for each row -exectute function update_salary_log(); +execute function update_salary_log(); ``` ### Trigger variables diff --git a/apps/docs/pages/guides/platform/compute-add-ons.mdx b/apps/docs/pages/guides/platform/compute-add-ons.mdx index d3bd4ad528a..e809e93476a 100644 --- a/apps/docs/pages/guides/platform/compute-add-ons.mdx +++ b/apps/docs/pages/guides/platform/compute-add-ons.mdx @@ -8,21 +8,23 @@ export const meta = { Every project on the Supabase Platform comes with its own dedicated Postgres instance running inside a virtual machine (VM). The following table describes the base instance with additional compute add-ons available if you need extra performance when scaling up Supabase. -| Plan | Pricing | CPU | Memory | Connections: Direct | Connections: Pooler | -| --------------- | ------- | ----------------------- | ------ | ------------------- | ------------------- | -| Free (Included) | $0 | 2-core ARM (shared) | 1 GB | 60 | 200 | -| Small | $5 | 2-core ARM (shared) | 2 GB | 90 | 200 | -| Medium | $50 | 2-core ARM (shared) | 4 GB | 120 | 200 | -| Large | $100 | 2-core ARM (dedicated) | 8 GB | 160 | 300 | -| XL | $200 | 4-core ARM (dedicated) | 16 GB | 240 | 700 | -| 2XL | $400 | 8-core ARM (dedicated) | 32 GB | 380 | 1500 | -| 4XL | $950 | 16-core ARM (dedicated) | 64 GB | 480 | 3000 | -| 8XL | $1,860 | 32-core ARM (dedicated) | 128 GB | 490 | 6000 | -| 12XL | $2,790 | 48-core ARM (dedicated) | 192 GB | 500 | 9000 | -| 16XL | $3,720 | 64-core ARM (dedicated) | 256 GB | 500 | 12,000 | +| Plan | Hourly Price USD | Monthly Price USD | CPU | Memory | Connections: Direct | Connections: Pooler | +| ------- | ---------------- | ----------------- | ----------------------- | ------ | ------------------- | ------------------- | +| Starter | $0.01344 | ~$10 | 2-core ARM (shared) | 1 GB | 60 | 200 | +| Small | $0.0206 | ~$15 | 2-core ARM (shared) | 2 GB | 90 | 200 | +| Medium | $0.0822 | ~$60 | 2-core ARM (shared) | 4 GB | 120 | 200 | +| Large | $0.1517 | ~$110 | 2-core ARM (dedicated) | 8 GB | 160 | 300 | +| XL | $0.2877 | ~$210 | 4-core ARM (dedicated) | 16 GB | 240 | 700 | +| 2XL | $0.562 | ~$410 | 8-core ARM (dedicated) | 32 GB | 380 | 1500 | +| 4XL | $1.32 | ~$960 | 16-core ARM (dedicated) | 64 GB | 480 | 3000 | +| 8XL | $2.562 | ~$1,870 | 32-core ARM (dedicated) | 128 GB | 490 | 6000 | +| 12XL | $3.836 | ~$2,800 | 48-core ARM (dedicated) | 192 GB | 500 | 9000 | +| 16XL | $5.12 | ~$3,730 | 64-core ARM (dedicated) | 256 GB | 500 | 12,000 | Number of connections above are recommended values. +We charge hourly for additional compute based on your usage. Read more about [usage-based billing for compute](/docs/guides/platform/org-based-billing#usage-based-billing-for-compute). + [Contact us](https://supabase.com/contact/enterprise) if you require a custom plan. ## Dedicated vs. shared CPU @@ -37,18 +39,18 @@ When considering compute upgrades, assess whether your bottlenecks are hardware- SSD Disks are attached to your servers and the disk performance depends on the compute add-on of your instance. Disk IO refers to two metrics: throughput (Megabits per Second) and IOPS (Input/Output Operations per Second). -| Plan | Pricing | Max Disk Throughput | Baseline Disk Throughput | Max IOPS | Baseline IOPS | -| --------------- | ------- | ------------------- | ------------------------ | ----------- | ------------- | -| Free (Included) | $0 | 2,085 Mbps | 87 Mbps | 11,800 IOPS | 500 IOPS | -| Small | $5 | 2,085 Mbps | 174 Mbps | 11,800 IOPS | 1,000 IOPS | -| Medium | $50 | 2,085 Mbps | 347 Mbps | 11,800 IOPS | 2,000 IOPS | -| Large | $100 | 4,750 Mbps | 630 Mbps | 20,000 IOPS | 3,600 IOPS | -| XL | $200 | 4,750 Mbps | 1,188 Mbps | 20,000 IOPS | 6,000 IOPS | -| 2XL | $400 | 4,750 Mbps | 2,375 Mbps | 20,000 IOPS | 12,000 IOPS | -| 4XL | $950 | 4,750 Mbps | 4,750 Mbps | 20,000 IOPS | 20,000 IOPS | -| 8XL | $1,860 | 9,500 Mbps | 9,500 Mbps | 40,000 IOPS | 40,000 IOPS | -| 12XL | $2,790 | 14,250 Mbps | 14,250 Mbps | 50,000 IOPS | 50,000 IOPS | -| 16XL | $3,720 | 19,000 Mbps | 19,000 Mbps | 80,000 IOPS | 80,000 IOPS | +| Plan | Max Disk Throughput | Baseline Disk Throughput | Max IOPS | Baseline IOPS | +| ------- | ------------------- | ------------------------ | ----------- | ------------- | +| Starter | 2,085 Mbps | 87 Mbps | 11,800 IOPS | 500 IOPS | +| Small | 2,085 Mbps | 174 Mbps | 11,800 IOPS | 1,000 IOPS | +| Medium | 2,085 Mbps | 347 Mbps | 11,800 IOPS | 2,000 IOPS | +| Large | 4,750 Mbps | 630 Mbps | 20,000 IOPS | 3,600 IOPS | +| XL | 4,750 Mbps | 1,188 Mbps | 20,000 IOPS | 6,000 IOPS | +| 2XL | 4,750 Mbps | 2,375 Mbps | 20,000 IOPS | 12,000 IOPS | +| 4XL | 4,750 Mbps | 4,750 Mbps | 20,000 IOPS | 20,000 IOPS | +| 8XL | 9,500 Mbps | 9,500 Mbps | 40,000 IOPS | 40,000 IOPS | +| 12XL | 14,250 Mbps | 14,250 Mbps | 50,000 IOPS | 50,000 IOPS | +| 16XL | 19,000 Mbps | 19,000 Mbps | 80,000 IOPS | 80,000 IOPS | [Contact us](https://supabase.com/contact/enterprise) if you require a custom plan. diff --git a/apps/docs/pages/guides/platform/migrating-and-upgrading-projects.mdx b/apps/docs/pages/guides/platform/migrating-and-upgrading-projects.mdx index f425d5d260d..38272bbf5c8 100644 --- a/apps/docs/pages/guides/platform/migrating-and-upgrading-projects.mdx +++ b/apps/docs/pages/guides/platform/migrating-and-upgrading-projects.mdx @@ -132,6 +132,10 @@ const NEW_PROJECT_SERVICE_KEY = 'new-project-service-key-yyy' })() ``` +### Transfer to a different organization + +Note that project migration is for transferring your projects to different regions. If you need to move your project to a different organization without touching the infrastrusture, see [project transfers](/docs/guides/platform/project-transfer). + export const Page = ({ children }) => export default Page diff --git a/apps/docs/pages/guides/platform/org-based-billing.mdx b/apps/docs/pages/guides/platform/org-based-billing.mdx index a63986576c4..2aff36341a6 100644 --- a/apps/docs/pages/guides/platform/org-based-billing.mdx +++ b/apps/docs/pages/guides/platform/org-based-billing.mdx @@ -3,7 +3,7 @@ import Layout from '~/layouts/DefaultGuideLayout' export const meta = { id: 'organization-based-billing', title: 'How billing works', - description: 'Learn how organzation-based billing works in Supabase.', + description: 'Learn how organization-based billing works in Supabase.', subtitle: 'Learn how organzation-based billing works in Supabase.', } @@ -123,7 +123,7 @@ If you launch a second or third instance on your paid plan, we add the additiona | 12XL | $3.836 | ~$2800 | | 16XL | $5.12 | ~$3730 | -With Legacy Billing, when you upgraded the [Compute Add-On](/docs/guides/platform/compute-add-ons), you were immediately charged the prorated amount (days left in your current billing cycle) and when your billing cycle reset you were charged upfront for the entire month. When you downgraded, you got the appropriate credits for unused time. +With Legacy Billing, when you upgraded the [Compute Add-On](/docs/guides/platform/compute-add-ons), you were immediately charged the prorated amount (days remaining in your current billing cycle) and when your billing cycle reset you were charged upfront for the entire month. When you downgraded, you got the appropriate credits for unused time. ### Free plan @@ -315,6 +315,15 @@ If you head over to your [organizations' billing settings](https://supabase.com/ infrastructure. +
+ + Where do I change my project add-ons such as PITR, Compute and Custom Domain? + + +Head over to your project [Add-ons page](https://supabase.com/dashboard/project/_/settings/addons) to change your compute size, Point-In-Time-Recovery or custom domain. + +
+
I have additional questions/concerns, how do I get help? diff --git a/apps/docs/pages/guides/realtime/realtime-with-nextjs.mdx b/apps/docs/pages/guides/realtime/realtime-with-nextjs.mdx index e60afb4f74a..fd9e8d98675 100644 --- a/apps/docs/pages/guides/realtime/realtime-with-nextjs.mdx +++ b/apps/docs/pages/guides/realtime/realtime-with-nextjs.mdx @@ -7,8 +7,8 @@ export const meta = { sidebar_label: 'Videos', } -In this guide we explore the best ways to receive realtime Postgres changes with your Next.js application. -We'll show both client and serverside updates, and explore the which option is best. +In this guide, we explore the best ways to receive real-time Postgres changes with your Next.js application. +We'll show both client and server side updates, and explore which option is best.