From b623a4c403811266a10379b4e763ef8f6eca2d8a Mon Sep 17 00:00:00 2001 From: David Date: Wed, 4 Dec 2024 12:45:22 -0500 Subject: [PATCH 1/6] Saving schema changes to migration file in backup docs (#30901) added -f flag to diff on backup --- .../content/guides/local-development/cli/getting-started.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/docs/content/guides/local-development/cli/getting-started.mdx b/apps/docs/content/guides/local-development/cli/getting-started.mdx index 6319a53ddf5a3..fa4e927ca853e 100644 --- a/apps/docs/content/guides/local-development/cli/getting-started.mdx +++ b/apps/docs/content/guides/local-development/cli/getting-started.mdx @@ -131,7 +131,7 @@ If you have any Supabase containers running locally, stop them and delete their Remember to save any local schema and data changes before stopping because the `--no-backup` flag will delete them. ```sh -supabase db diff my_schema +supabase db diff -f my_schema supabase db dump --local --data-only > supabase/seed.sql supabase stop --no-backup ``` From 0ecd2446ffcbccba309d915f690c71d34745495f Mon Sep 17 00:00:00 2001 From: lanye <50891500+lanye74@users.noreply.github.com> Date: Wed, 4 Dec 2024 13:12:53 -0500 Subject: [PATCH 2/6] docs: migrate sveltekit server-side auth to svelte 5 (#30445) * basic migration pass * update leftover slot * format: style --- .../guides/auth/server-side/sveltekit.mdx | 156 +++++++++--------- 1 file changed, 80 insertions(+), 76 deletions(-) diff --git a/apps/docs/content/guides/auth/server-side/sveltekit.mdx b/apps/docs/content/guides/auth/server-side/sveltekit.mdx index cb8b48079e19b..bd41b4276c1ca 100644 --- a/apps/docs/content/guides/auth/server-side/sveltekit.mdx +++ b/apps/docs/content/guides/auth/server-side/sveltekit.mdx @@ -276,24 +276,24 @@ Set up a listener for Auth events on the client, to handle session refreshes and ```svelte src/routes/+layout.svelte - +{@render children()} ``` @@ -327,15 +327,15 @@ export const load: PageServerLoad = async ({ locals: { supabase } }) => { ```svelte src/routes/+page.svelte

Welcome to Supabase!

``` @@ -408,26 +408,31 @@ export const actions: Actions = { ```svelte src/routes/auth/+page.svelte
- - - - + + + +
``` ```svelte src/routes/auth/+layout.svelte + +
- +
- + +{@render children()} ``` ```svelte src/routes/auth/error/+page.svelte @@ -512,25 +517,25 @@ To ensure that `hooks.server.ts` runs for every nested path, put a `+layout.serv ```svelte src/routes/private/+layout.svelte
- - + +
- + {@render children()}
``` @@ -561,55 +566,54 @@ using ((select auth.uid()) = user_id); ``` ```svelte src/routes/private/+page.server.ts -import type { PageServerLoad } from './$types'; +import type { PageServerLoad } from './$types' export const load: PageServerLoad = async ({ depends, locals: { supabase } }) => { - depends('supabase:db:notes'); - const { data: notes } = await supabase.from('notes').select('id,note').order('id'); - return { notes: notes ?? [] }; -}; + depends('supabase:db:notes') + const { data: notes } = await supabase.from('notes').select('id,note').order('id') + return { notes: notes ?? [] } +} ``` ```svelte src/routes/private/+page.svelte

Private page for user: {user?.email}

Notes

    - {#each notes as note} -
  • {note.note}
  • - {/each} + {#each notes as note} +
  • {note.note}
  • + {/each}
-
- + +
``` From 4cecae1e09a5c80b5101ad6ef13a423dec1e3863 Mon Sep 17 00:00:00 2001 From: Charis <26616127+charislam@users.noreply.github.com> Date: Wed, 4 Dec 2024 15:37:49 -0500 Subject: [PATCH 3/6] ci(docs): linter bump version + expand scope (#30860) --- .github/workflows/docs-lint-v2.yml | 12 +- .../docs/content/guides/auth/auth-captcha.mdx | 12 +- .../content/guides/database/extensions.mdx | 2 +- .../guides/database/extensions/rum.mdx | 4 +- .../postgres/column-level-security.mdx | 6 +- .../guides/database/postgres/roles.mdx | 2 +- .../guides/database/postgres/timeouts.mdx | 4 +- .../prisma/prisma-troubleshooting.mdx | 4 +- .../guides/database/query-optimization.mdx | 6 +- ...uth-send-email-hook-react-email-resend.mdx | 4 +- .../guides/functions/examples/discord-bot.mdx | 4 +- .../functions/examples/push-notifications.mdx | 8 +- .../functions/examples/semantic-search.mdx | 2 +- .../functions/examples/sentry-monitoring.mdx | 2 +- .../content/guides/functions/import-maps.mdx | 10 +- .../docs/content/guides/functions/logging.mdx | 6 +- .../docs/content/guides/functions/routing.mdx | 6 +- .../content/guides/functions/status-codes.mdx | 2 + .../guides/functions/troubleshooting.mdx | 10 +- .../content/guides/functions/websockets.mdx | 2 +- supa-mdx-lint.config.toml | 123 +---------- supa-mdx-lint/Rule001HeadingCase.toml | 200 ++++++++++++++++++ supa-mdx-lint/Rule003Spelling.toml | 191 +++++++++++++++++ 23 files changed, 462 insertions(+), 160 deletions(-) create mode 100644 supa-mdx-lint/Rule001HeadingCase.toml create mode 100644 supa-mdx-lint/Rule003Spelling.toml diff --git a/.github/workflows/docs-lint-v2.yml b/.github/workflows/docs-lint-v2.yml index 0aabb9ce7faef..f967f56e94366 100644 --- a/.github/workflows/docs-lint-v2.yml +++ b/.github/workflows/docs-lint-v2.yml @@ -16,8 +16,10 @@ jobs: steps: - uses: actions/checkout@v3 with: + fetch-depth: 0 sparse-checkout: | supa-mdx-lint.config.toml + supa-mdx-lint apps/docs/content - name: cache cargo id: cache-cargo @@ -28,17 +30,21 @@ jobs: ~/.cargo/registry/index/ ~/.cargo/registry/cache/ ~/.cargo/git/db/ - key: 2d535367b06fe62354035464cf8334929bafca85 + key: 6435a4cd1eeea7c2bbd343731de7e8a5127cb2d1 - 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 2d535367b06fe62354035464cf8334929bafca85 + run: cargo install --locked --git https://github.com/supabase-community/supa-mdx-lint --rev 6435a4cd1eeea7c2bbd343731de7e8a5127cb2d1 - name: install reviewdog uses: reviewdog/action-setup@3f401fe1d58fe77e10d665ab713057375e39b887 # v1.3.0 with: reviewdog_version: v0.20.2 - name: run linter env: + BASE_REF: ${{ github.base_ref }} REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | set -o pipefail - supa-mdx-lint apps/docs/content/guides/getting-started apps/docs/content/guides/ai apps/docs/content/guides/auth --format rdf | tee >(cat) | reviewdog -f=rdjsonl -reporter=github-pr-review + git diff --name-only origin/$BASE_REF HEAD \ + | grep -E "^apps/docs/content/guides/(getting-started|ai|api|auth|database|deployment|functions)/" \ + | xargs -r supa-mdx-lint --format rdf \ + | reviewdog -f=rdjsonl -reporter=github-pr-review diff --git a/apps/docs/content/guides/auth/auth-captcha.mdx b/apps/docs/content/guides/auth/auth-captcha.mdx index 127640ae1925d..a55b4e46ab248 100644 --- a/apps/docs/content/guides/auth/auth-captcha.mdx +++ b/apps/docs/content/guides/auth/auth-captcha.mdx @@ -1,13 +1,13 @@ --- id: 'auth-captcha' -title: 'Enable Captcha Protection' -description: 'Add Captcha Protection to your Supabase project' +title: 'Enable CAPTCHA Protection' +description: 'Add CAPTCHA Protection to your Supabase project' tocVideo: 'em1cpOAXknM' --- -Supabase provides you with the option of adding captcha to your sign-in, sign-up, and password reset forms. This keeps your website safe from bots and malicious scripts. Supabase authentication has support for [hCaptcha](https://www.hcaptcha.com/) and [Cloudflare Turnstile](https://www.cloudflare.com/products/turnstile/). +Supabase provides you with the option of adding CAPTCHA to your sign-in, sign-up, and password reset forms. This keeps your website safe from bots and malicious scripts. Supabase authentication has support for [hCaptcha](https://www.hcaptcha.com/) and [Cloudflare Turnstile](https://www.cloudflare.com/products/turnstile/). -## Sign up for Captcha +## Sign up for CAPTCHA -## Enable Captcha protection for your Supabase project +## 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. Select your CAPTCHA provider from the dropdown, enter your Captcha **Secret key**, and click **Save**. -## Add the Captcha frontend component +## Add the CAPTCHA frontend component The frontend requires some changes to provide the captcha on-screen for the user. This example uses React and the corresponding Captcha React component, but both Captcha providers can be used with any JavaScript framework. diff --git a/apps/docs/content/guides/database/extensions.mdx b/apps/docs/content/guides/database/extensions.mdx index d5fb2834e26be..3bdbb3db71f9a 100644 --- a/apps/docs/content/guides/database/extensions.mdx +++ b/apps/docs/content/guides/database/extensions.mdx @@ -54,7 +54,7 @@ In addition to the pre-configured extensions, you can also install your own SQL -### Upgrade Extensions +### Upgrade extensions If a new version of an extension becomes available on Supabase, you need to initiate a software upgrade in the [Infrastructure Settings](https://supabase.com/dashboard/project/_/settings/infrastructure) to access it. Software upgrades can also be initiated by restarting your server in the [General Settings](https://supabase.com/dashboard/project/_/settings/general). diff --git a/apps/docs/content/guides/database/extensions/rum.mdx b/apps/docs/content/guides/database/extensions/rum.mdx index e6fbe850a6137..c95943e020216 100644 --- a/apps/docs/content/guides/database/extensions/rum.mdx +++ b/apps/docs/content/guides/database/extensions/rum.mdx @@ -58,7 +58,7 @@ drop extension if exists rum; ### Syntax -#### For type: tsvector +#### For type: `tsvector` To understand the following you may need first to see [Official PostgreSQL documentation on text search](https://www.postgresql.org/docs/current/functions-textsearch.html) @@ -116,7 +116,7 @@ SELECT id, d, d `<=>` '2016-05-16 14:21:25' FROM tsts WHERE t @@ 'wr&qh' ORDER B (5 rows) ``` -#### For type: anyarray +#### For type: `anyarray` `rum_anyarray_ops` diff --git a/apps/docs/content/guides/database/postgres/column-level-security.mdx b/apps/docs/content/guides/database/postgres/column-level-security.mdx index 6ed691e326c64..a6731d1a281e2 100644 --- a/apps/docs/content/guides/database/postgres/column-level-security.mdx +++ b/apps/docs/content/guides/database/postgres/column-level-security.mdx @@ -12,7 +12,7 @@ PostgreSQL's [Row Level Security (RLS)](https://www.postgresql.org/docs/current/ roles. -## Policies at the Row Level +## Policies at the row level Policies in Row Level Security (RLS) are used to restrict access to rows in a table. Think of them like adding a `WHERE` clause to every query. @@ -35,7 +35,7 @@ update However, this gives the post owner full access to update the row, including all of the columns. -## Privileges at the Column Level +## Privileges at the column level To restrict access to columns, you can use [Privileges](https://www.postgresql.org/docs/current/ddl-priv.html). @@ -80,7 +80,7 @@ You can view and edit the privileges in the [Supabase Studio](https://supabase.c ![Column level privileges](/docs/img/guides/privileges/column-level-privileges-2.png) -## Manage column privileges in Migrations +## Manage column privileges in migrations While you can manage privileges directly from the Dashboard, as your project grows you may want to manage them in your migrations. Read about database migrations in the [Local Development](https://supabase.com/docs/guides/getting-started/local-development#database-migrations) guide. diff --git a/apps/docs/content/guides/database/postgres/roles.mdx b/apps/docs/content/guides/database/postgres/roles.mdx index b72ab8c1ad20b..7a36f2761d7f3 100644 --- a/apps/docs/content/guides/database/postgres/roles.mdx +++ b/apps/docs/content/guides/database/postgres/roles.mdx @@ -46,7 +46,7 @@ When you created your project you were also asked to enter a password. This is a 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 +## Granting permissions Roles can be granted various permissions on database objects using the `GRANT` command. Permissions include `SELECT`, `INSERT`, `UPDATE`, and `DELETE`. You can configure access to almost any object inside your database - including tables, views, functions, and triggers. diff --git a/apps/docs/content/guides/database/postgres/timeouts.mdx b/apps/docs/content/guides/database/postgres/timeouts.mdx index 8a735c9d7c10b..c89c3def47ace 100644 --- a/apps/docs/content/guides/database/postgres/timeouts.mdx +++ b/apps/docs/content/guides/database/postgres/timeouts.mdx @@ -124,9 +124,9 @@ language sql; The Supabase Dashboard contains tools to help you identify timed-out and long-running queries. -### Using the Log Explorer +### Using the Logs Explorer -Go to the [Log Explorer](/dashboard/project/_/logs/explorer), and run the following query to identify timed-out events (`statement timeout`) and queries that successfully run for longer than 10 seconds (`duration`). +Go to the [Logs Explorer](/dashboard/project/_/logs/explorer), and run the following query to identify timed-out events (`statement timeout`) and queries that successfully run for longer than 10 seconds (`duration`). ```sql select diff --git a/apps/docs/content/guides/database/prisma/prisma-troubleshooting.mdx b/apps/docs/content/guides/database/prisma/prisma-troubleshooting.mdx index 851ea07ef6c63..974528af32241 100644 --- a/apps/docs/content/guides/database/prisma/prisma-troubleshooting.mdx +++ b/apps/docs/content/guides/database/prisma/prisma-troubleshooting.mdx @@ -27,6 +27,8 @@ connection_string.../postgres?KEY1=VALUE&KEY2=VALUE&KEY3=VALUE # Errors +{/* supa-mdx-lint-disable-next-line Rule001HeadingCase */} + ## ... prepared statement already exists Supavisor in transaction mode (port 6543) does not support [prepared statements](https://www.postgresql.org/docs/current/sql-prepare.html), which Prisma will try to create in the background. @@ -162,7 +164,7 @@ An alternative strategy to reference these tables is to duplicate values into Pr
Show/Hide Details - + ```sql table_in_public -- Create the 'profiles' table in the 'public' schema create table public.profiles ( diff --git a/apps/docs/content/guides/database/query-optimization.mdx b/apps/docs/content/guides/database/query-optimization.mdx index d1e2d88690d6a..f34e8ebf2bdd5 100644 --- a/apps/docs/content/guides/database/query-optimization.mdx +++ b/apps/docs/content/guides/database/query-optimization.mdx @@ -34,7 +34,7 @@ limit 10; In this query, there are several parts that indexes could likely help in optimizing the performance: -### `where` Clause: +### `where` clause: The `where` clause filters rows based on certain conditions, and indexing the columns involved can improve this process: @@ -47,7 +47,7 @@ create index idx_customers_sign_up_date on customers (sign_up_date); create index idx_orders_status on orders (status); ``` -### `join` Columns +### `join` columns Indexes on the columns used for joining tables can help Postgres avoid scanning tables in their entirety when connecting tables. @@ -58,7 +58,7 @@ Indexes on the columns used for joining tables can help Postgres avoid scanning create index idx_orders_customer_id on orders (customer_id); ``` -### `order by` Clause +### `order by` clause Sorting can also be optimized by indexing: 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 908d9808ee77b..f7c7ef537bf30 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 @@ -114,7 +114,7 @@ Deno.serve(async (req) => { }) ``` -### 3. Create React Email Templates +### 3. Create React Email templates Create a new folder `_templates` and create a new file `magic-link.tsx` with the following code: @@ -296,7 +296,7 @@ supabase secrets set --env-file supabase/functions/.env That's it, now your Supabase Edge Function will be triggered anytime an Auth Email needs to be send to the user! -## More Resources +## More resources - [Send Email Hooks](/docs/guides/auth/auth-hooks/send-email-hook) - [Auth Hooks](/docs/guides/auth/auth-hooks) diff --git a/apps/docs/content/guides/functions/examples/discord-bot.mdx b/apps/docs/content/guides/functions/examples/discord-bot.mdx index bfe4a4e511474..48b0481bbc3af 100644 --- a/apps/docs/content/guides/functions/examples/discord-bot.mdx +++ b/apps/docs/content/guides/functions/examples/discord-bot.mdx @@ -138,7 +138,7 @@ function hexToUint8Array(hex: string) { } ``` -## Deploy the Slash Command handler +## Deploy the slash command handler ```bash supabase functions deploy discord-bot --no-verify-jwt @@ -154,7 +154,7 @@ Navigate to your Function details in the Supabase Dashboard to get your Endpoint The application is now ready. Let's proceed to the next section to install it. -## Install the Slash Command on your Discord server +## Install the slash command on your Discord server So to use the `hello` Slash Command, we need to install our Greeter application on our Discord server. Here are the steps: diff --git a/apps/docs/content/guides/functions/examples/push-notifications.mdx b/apps/docs/content/guides/functions/examples/push-notifications.mdx index b59ad8879a9ff..994c7e4267dcc 100644 --- a/apps/docs/content/guides/functions/examples/push-notifications.mdx +++ b/apps/docs/content/guides/functions/examples/push-notifications.mdx @@ -18,14 +18,14 @@ Push notifications are an important part of any mobile app. They allow you to se Find the example code on [GitHub](https://github.com/supabase/supabase/blob/master/examples/user-management/expo-push-notifications/). - ## Supabase Setup + ## Supabase setup - [Create a new Supabase project](https://database.new). - Link your project: `supabase link --project-ref your-supabase-project-ref` - 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 + ## Expo setup To utilize Expo's push notification service, you must configure your app by installing a set of libraries, implementing functions to handle notifications, and setting up credentials for Android and iOS. Follow the official [Expo Push Notifications Setup Guide](https://docs.expo.dev/push-notifications/push-notifications-setup/) to get the credentials for Android and iOS. This project uses [Expo's EAS build](https://docs.expo.dev/build/introduction/) service to simplify this part. @@ -126,7 +126,7 @@ Push notifications are an important part of any mobile app. They allow you to se 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 + ## Supabase setup We will create two tables. One to store the user's FCM token and a `notifications` table. The edge function will be triggered when a new row is inserted into the `notifications` table and sends a push notification to the user. @@ -258,7 +258,7 @@ Push notifications are an important part of any mobile app. They allow you to se } ``` - ## FCM Setup + ## FCM setup 1. Follow the official [FCM Setup Guide](https://firebase.google.com/docs/cloud-messaging) to set up FCM for your client side application. 1. Generate a new service account private key from the Firebase console `Project Settings > Service Accounts > Generate new private key`. diff --git a/apps/docs/content/guides/functions/examples/semantic-search.mdx b/apps/docs/content/guides/functions/examples/semantic-search.mdx index b42077dd395b0..7534d6bafaea0 100644 --- a/apps/docs/content/guides/functions/examples/semantic-search.mdx +++ b/apps/docs/content/guides/functions/examples/semantic-search.mdx @@ -61,7 +61,7 @@ Deno.serve(async (req) => { }) ``` -## Create a Postgres Function and RPC +## Create a Database Function and RPC With the embeddings now stored in your Postgres database table, you can query them from Supabase Edge Functions by utilizing [Remote Procedure Calls (RPC)](/docs/guides/database/functions?language=js). diff --git a/apps/docs/content/guides/functions/examples/sentry-monitoring.mdx b/apps/docs/content/guides/functions/examples/sentry-monitoring.mdx index ec522e0a892e9..29bf251619383 100644 --- a/apps/docs/content/guides/functions/examples/sentry-monitoring.mdx +++ b/apps/docs/content/guides/functions/examples/sentry-monitoring.mdx @@ -79,7 +79,7 @@ supabase functions deploy sentryfied --no-verify-jwt Find the complete example on [GitHub](https://github.com/supabase/supabase/tree/master/examples/edge-functions/supabase/functions/sentryfied/index.ts). -## Working With Scopes +## Working with scopes Sentry Deno SDK currently do not support `Deno.serve` instrumentation, which means that there is no scope separation between requests. Because of that, when the Edge Functions runtime is reused between multiple requests, all globally captured breadcrumbs and contextual data diff --git a/apps/docs/content/guides/functions/import-maps.mdx b/apps/docs/content/guides/functions/import-maps.mdx index 15e7bfdc9aba8..176aa73d4f272 100644 --- a/apps/docs/content/guides/functions/import-maps.mdx +++ b/apps/docs/content/guides/functions/import-maps.mdx @@ -12,7 +12,7 @@ Developing with Edge Functions is similar to developing with Node.js, but with a There are two ways to manage your dependencies in Supabase Edge Functions: -### Using deno.json (Recommended) +### Using deno.json (recommended) @@ -50,7 +50,7 @@ The recommended file structure when using `deno.json`: └── config.toml ``` -### Using Import Maps (Legacy) +### Using import maps (legacy) Import Maps are a legacy way to manage dependencies, similar to a `package.json` file. While still supported, we recommend using `deno.json`. If both exist, `deno.json` takes precedence. @@ -112,7 +112,7 @@ Supabase Edge Functions support several ways to import dependencies: - Built-in [Node APIs](https://docs.deno.com/runtime/manual/node/compatibility) - Third party modules published to [JSR](https://jsr.io/) or [deno.land/x](https://deno.land/x) -### NPM Modules +### NPM modules You can import npm modules using the `npm:` specifier: @@ -120,7 +120,7 @@ You can import npm modules using the `npm:` specifier: import { createClient } from 'npm:@supabase/supabase-js@2' ``` -### Node.js Built-ins +### Node.js built-ins For Node.js built-in APIs, use the `node:` specifier: @@ -130,7 +130,7 @@ import process from 'node:process' Learn more about npm specifiers and Node built-in APIs in [Deno's documentation](https://docs.deno.com/runtime/manual/node/npm_specifiers). -### Importing from Private Registries +### Importing from private registries diff --git a/apps/docs/content/guides/functions/logging.mdx b/apps/docs/content/guides/functions/logging.mdx index 4f0e66879cf3f..62a5cc3cd7093 100644 --- a/apps/docs/content/guides/functions/logging.mdx +++ b/apps/docs/content/guides/functions/logging.mdx @@ -61,9 +61,9 @@ Deno.serve(async (req) => { }) ``` -## Logging Tips +## Logging tips -### Logging Request Headers +### Logging request headers When debugging Edge Functions, a common mistake is to try to log headers to the developer console via code like this: @@ -88,7 +88,7 @@ The reason behind this behavior is that [Headers](https://developer.mozilla.org/ However, `Headers` objects are iterable. You can utilize this feature to craft a couple of succinct one-liners for debugging and printing headers. -### Convert headers into an object with Object.fromEntries: +### Convert headers into an object with `Object.fromEntries`: You can use `Object.fromEntries` which is a call to convert the headers into an object: diff --git a/apps/docs/content/guides/functions/routing.mdx b/apps/docs/content/guides/functions/routing.mdx index f13eb21eeed52..2944dacf5000c 100644 --- a/apps/docs/content/guides/functions/routing.mdx +++ b/apps/docs/content/guides/functions/routing.mdx @@ -15,7 +15,7 @@ To combine multiple endpoints into a single Edge Function, you can use web appli Let's dive into some examples. -## Routing with Frameworks +## Routing with frameworks Here's a simple hello world example using some popular web frameworks. @@ -158,7 +158,7 @@ curl --request POST 'https://.supabase.co/functions/v1/hello-world' We should see a response printing `Hello Foo!`. -## Using Route Parameters +## Using route parameters We can use route parameters to capture values at specific URL segments (eg: `/tasks/:taskId/notes/:noteId`). @@ -415,7 +415,7 @@ Deno.serve(async (req) => { -## URL Patterns API +## 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. This is ideal for small apps with only couple of routes and you want to have a custom matching algorithm. diff --git a/apps/docs/content/guides/functions/status-codes.mdx b/apps/docs/content/guides/functions/status-codes.mdx index 29f7a4307f6d6..5925a2ad3bb8b 100644 --- a/apps/docs/content/guides/functions/status-codes.mdx +++ b/apps/docs/content/guides/functions/status-codes.mdx @@ -5,6 +5,8 @@ description: 'Edge Functions can return following status codes.' subtitle: 'Edge Functions can return following status codes.' --- +{/* supa-mdx-lint-disable Rule001HeadingCase */} + ## 2XX Success A successful Edge Function Response diff --git a/apps/docs/content/guides/functions/troubleshooting.mdx b/apps/docs/content/guides/functions/troubleshooting.mdx index cdeddcbe9c66c..2d69c1e0867df 100644 --- a/apps/docs/content/guides/functions/troubleshooting.mdx +++ b/apps/docs/content/guides/functions/troubleshooting.mdx @@ -30,6 +30,8 @@ If your Edge Function takes too long to respond or times out: - If the boot times are similar, it’s likely an issue with your function’s code, such as a large dependency, a slow API call, or a complex computation. You can try to optimize your code, reduce the size of your dependencies, or use caching techniques to improve the performance of your function. - If only some of the `booted` events are slow, find the affected `region` in the metadata and submit a support request via the "Help" button at the top. +{/* supa-mdx-lint-disable-next-line Rule001HeadingCase */} + ### Receiving 546 Error Response The 546 error response might occur because: @@ -47,7 +49,7 @@ The 546 error response might occur because: ## Advanced techniques -### Monitoring Edge Function Resource Usage +### Monitoring Edge Function resource usage To determine how much memory and CPU your Edge Function consumes, follow these steps: @@ -62,7 +64,7 @@ To determine how much memory and CPU your Edge Function consumes, follow these s resources. -### Understanding CPU soft and hard Limits +### Understanding CPU soft and hard limits An isolate is like a worker that can handle multiple requests for a function. It works until a time limit of 400 seconds is reached. Now, there are two types of limits for the CPU. @@ -89,7 +91,7 @@ When analyzing dependencies for your Supabase Edge Functions, it's essential to By selectively importing only the required submodules, you can effectively reduce the size of your function's dependencies and optimize its performance. Before finalizing your imports, ensure to review both Deno and NPM dependencies, checking for any unnecessary or redundant dependencies that can be removed. Additionally, check for outdated dependencies and update to the latest versions if possible. -#### Deno Dependencies +#### Deno dependencies Run `deno info`, providing the path to your input map if you use one. Review the dependencies listed in the output. Pay attention to any significantly large dependencies, as they can contribute to increased bundle size and potential boot time issues. @@ -99,7 +101,7 @@ Examine if there are any unnecessary or redundant dependencies that can be remov deno info --import-map=/path/to/import_map.json /path/to/function/index.ts ``` -#### NPM Dependencies +#### NPM dependencies Additionally, if you utilize NPM modules in your Edge Functions, it's crucial to be mindful of their size and impact on the overall bundle size. While importing NPM modules, consider using the notation `import { submodule } from 'npm:package/submodule'` to selectively import specific submodules rather than importing the entire package. This approach can help minimize unnecessary overhead and streamline the execution of your function. diff --git a/apps/docs/content/guides/functions/websockets.mdx b/apps/docs/content/guides/functions/websockets.mdx index 48fd6fff028c0..0c0c82201e88e 100644 --- a/apps/docs/content/guides/functions/websockets.mdx +++ b/apps/docs/content/guides/functions/websockets.mdx @@ -88,7 +88,7 @@ server.listen(8080); -### Outbound Websockets +### Outbound WebSockets You can also establish an outbound WebSocket connection to another server from an Edge Function. diff --git a/supa-mdx-lint.config.toml b/supa-mdx-lint.config.toml index b85581f17a6bb..fc3403b3048ec 100644 --- a/supa-mdx-lint.config.toml +++ b/supa-mdx-lint.config.toml @@ -1,115 +1,14 @@ ignore_patterns = ["**/_*.mdx"] # Heading should be sentence case -[Rule001HeadingCase] -# Words that may be uppercased even if they are not the first word in the sentence. -# Can also specify a regex that is compatible with the [Rust regex crate](https://docs.rs/regex/latest/regex/). -may_uppercase = [ - "[A-Z0-9]{2,5}", - "APIs", - "Android", - "Angular", - "Apple", - "Auth", - "Auth0", - "Auth0 Actions?", - "Azure", - "Azure Developers?", - "Bitbucket", - "Boolean", - "Captcha", - "ChatGPT", - "Chrome", - "Code Exchange", - "Content Delivery Network", - "Dart", - "Discord", - "Edge Functions?", - "Expo", - "Facebook", - "Facebook Developers?", - "Figma", - "Figma Developers?", - "Firebase", - "Firebase Authentication", - "Flutter", - "GitHub", - "GitHub Actions", - "GitLab", - "GoTrue", - "Google", - "GraphQL", - "Hooks?", - "Hugging Face", - "I", - "IVFFlat", - "Inbucket", - "Ionic Angular", - "Ionic React", - "Ionic Vue", - "JavaScript", - "JWTs", - "Kakao", - "Kakao Developers?", - "Kakao Login", - "Keycloak", - "Kotlin", - "Kotlin Multiplatform", - "LinkedIn", - "LinkedIn Developers?", - "Linux", - "Magic Link", - "Navigable Small World", - "Next.js", - "Notion", - "Nuxt", - "OAuth", - "OpenAI", - "Open ID Connect", - "Poetry", - "Postgres", - "PostgreSQL", - "PostgREST", - "Python", - "React", - "React Native", - "Reciprocal Ranked Fusion", - "RedwoodJS", - "Remix", - "Retrieval Plugin", - "Roboflow Inference", - "Row Level Security", - "Server-Side Auth", - "Server-Side Rendering", - "Single Sign-On", - "Slack", - "Slack Developers?", - "SolidJS", - "Spotify", - "Spotify Developers?", - "Supabase", - "Svelte", - "SvelteKit", - "Swift", - "SwiftUI", - "Third-Party Auth", - "Twitch", - "Twitch Developers?", - "Twitter", - "Twitter Developers?", - "TypeScript", - "URIs", - "URLs", - "Xcode", - "Vecs", - "Vue", - "Web", - "Windows", - "WorkOS", - "Wrappers", - "Zoom", - "Zoom Developers?", -] -# Words that may be lowercased even if they are the first word in the sentence. -# Can also specify a regex that is compatible with the [Rust regex crate](https://docs.rs/regex/latest/regex/). -may_lowercase = ["iOS"] +Rule001HeadingCase = "include('supa-mdx-lint/Rule001HeadingCase.toml')" + +Rule003Spelling = false +# Check spelling +# Error message: "Word not found in dictionary" +# Rule003Spelling = "include('supa-mdx-lint/Rule003Spelling.toml')" + +Rule002AdmonitionTypes = false +# [Rule002AdmonitionTypes] +# Allowed admonition types are: +# admonition_types = ["note", "tip", "caution", "deprecation", "danger"] diff --git a/supa-mdx-lint/Rule001HeadingCase.toml b/supa-mdx-lint/Rule001HeadingCase.toml new file mode 100644 index 0000000000000..48d7b7a019342 --- /dev/null +++ b/supa-mdx-lint/Rule001HeadingCase.toml @@ -0,0 +1,200 @@ +# Heading should be sentence case + +# Words that may be uppercased even if they are not the first word in the sentence. +# Can also specify a regex that is compatible with the [Rust regex crate](https://docs.rs/regex/latest/regex/). +may_uppercase = [ + "[A-Z0-9]{2,5}s?", + "Amazon RDS", + "APIs", + "Analytics", + "Android", + "Angular", + "Apple", + "Audit Logs?", + "Auth", + "Auth0", + "Auth0 Actions?", + "Azure", + "Azure Developers?", + "BigQuery", + "Bitbucket", + "Bitbucket Pipelines", + "Boolean", + "Broadcast", + "CAPTCHA", + "Channel", + "ChatGPT", + "Chrome", + "Chrome Developer Tools", + "Cloudflare", + "Cloudflare Workers?", + "Code Exchange", + "Colab", + "Content Delivery Network", + "Cron Jobs?", + "Data API", + "DataDog", + "Dart", + "Dashboard", + "Database Functions?", + "Deno", + "DigitalOcean", + "Discord", + "Discord Developers?", + "Django", + "Docker", + "Drizzle", + "Edge Functions?", + "Enterprise Plan", + "Expo", + "Facebook", + "Facebook Developers?", + "Figma", + "Figma Developers?", + "Firebase", + "Firebase Authentication", + "Firestore", + "Flutter", + "Functions?", + "Free Plan", + "Git", + "GitHub", + "GitHub Actions", + "GitLab", + "GoTrue", + "Google", + "Grafana", + "GraphQL", + "Heroku", + "Homebrew", + "Hooks?", + "Hugging Face", + "I", + "IPv4", + "IPv6", + "IVFFlat", + "IdP", + "Inbucket", + "Index Advisor", + "IntelliJ", + "Ionic Angular", + "Ionic React", + "Ionic Vue", + "JavaScript", + "JSON Web Tokens?", + "JWTs", + "Kakao", + "Kakao Developers?", + "Kakao Login", + "Keycloak", + "Kotlin", + "Kotlin Multiplatform", + "Kysely", + "Large Language Models?", + "LinkedIn", + "LinkedIn Developers?", + "Linux", + "LlamaIndex", + "Llamafile", + "Logs Explorer", + "Magic Link", + "Mixpeek", + "Mixpeek Embed", + "MySQL", + "Navigable Small World", + "Neon", + "Next.js", + "Node.js", + "Notion", + "Nuxt", + "OAuth", + "Okta", + "Ollama", + "OpenAI", + "Open ID Connect", + "OrbStack", + "OrioleDB", + "PGAudit", + "Phoenix", + "Pro Plan", + "Podman", + "Poetry", + "Postgres", + "Postgres Changes", + "PostgreSQL", + "PostgREST", + "Presence", + "Prometheus", + "Python", + "Query Performance", + "React", + "React Email", + "React Native", + "Reciprocal Ranked Fusion", + "Redis", + "RedwoodJS", + "Remix", + "Render", + "Retrieval Plugin", + "Roboflow Inference", + "Row Level Security", + "Send Email Hook", + "SendGrid", + "Sentry", + "Server-Side Auth", + "Server-Side Rendering", + "Single Sign-On", + "Slack", + "Slack Developers?", + "SolidJS", + "Spotify", + "Spotify Developers?", + "Sqitch", + "Storage", + "Studio", + "Supabase", + "Supabase Marketplace", + "Supavisor('s)?", + "Svelte", + "SvelteKit", + "Swift", + "SwiftUI", + "Team Plan", + "Third-Party Auth", + "TimescaleDB", + "Transformers.js", + "Twilio", + "Twitch", + "Twitch Developers?", + "Twitter", + "Twitter Developers?", + "TypeScript", + "Uppy", + "Upstash", + "URIs", + "URLs", + "Unsplash", + "Xcode", + "Vault", + "VSCode", + "Vecs", + "Vercel", + "Vercel Marketplace", + "Visual Studio Code", + "Vue", + "Web", + "WebAssembly", + "WebP", + "WebSockets?", + "WebStorm", + "Windows", + "WorkOS", + "Wrappers", + "Write-Ahead Log(s|ging)?", + "Zoom", + "Zoom Developers?", +] + +# Words that may be lowercased even if they are the first word in the sentence. +# Can also specify a regex that is compatible with the [Rust regex crate](https://docs.rs/regex/latest/regex/). +may_lowercase = ["iOS"] diff --git a/supa-mdx-lint/Rule003Spelling.toml b/supa-mdx-lint/Rule003Spelling.toml new file mode 100644 index 0000000000000..1eeb1ed2ffe98 --- /dev/null +++ b/supa-mdx-lint/Rule003Spelling.toml @@ -0,0 +1,191 @@ +# Check spelling +# Error message: "Word not found in dictionary" +# +# Allow list: Spellings that are actually correct, though they aren't in the +# dictionary. +# +# Prefixes: Strings that are not standalone words, but that can be used in a +# prefix before a hyphen, such as "pre" or "bi". +# +# Before adding a new word to the allow list, double check that it is in fact +# the correct casing for the word! Especially for styling of company and product +# names, the "official" casing might not be what you think. + +allow_list = [ + "[A-Za-z0-9_-]+(\\.[A-Z-a-z0-9_-]+)+(\\/[A-Za-z0-9_-]+)*", + "[KMG]iB", + "\\[#[A-Za-z0-9-]+\\]", + "\\$\\$.+?\\$\\$", + "\\S+\\.json", + "\\S+\\.toml", + "\\S+\\.yaml", + "[A-Z]{2,5}s?", + "[Aa]utovacuum(s|ing|ed)?", + "[Bb]ackend", + "[Bb]uilt-ins?", + "[Cc]onfigs?", + "[Cc]ooldowns?", + "[Cc]ron", + "[Dd]atasets?", + "[Dd]evs?", + "[Dd]ropdown", + "[Ee]nums?", + "[Ee]nv", + "[Ff]rontend", + "[Gg]zip(s|ed)?", + "[Mm]iddlewares?", + "[Mm]ultimodal", + "[Mm]ultipart", + "[Mm]ultithreading", + "[Nn]onces?", + "[Nn]ullable", + "[Pp]arams?", + "[Pp]laintext", + "[Pp]olyfill(s|ed)?", + "[Pp]ooler", + "[Qq]uickstarts?", + "[Rr]ealtime", + "[Rr]ebas(e|ed|es|ing)", + "[Rr]epos?", + "[Rr]untimes?", + "[Ss]erverless", + "[Ss]ubcommands?", + "[Ss]ubdomains?", + "[Ss]ubfolders?", + "[Ss]ubmodules?", + "[Tt]odos?", + "[Tt]radeoffs?", + "[Uu]pserts?", + "[Uu]ptime", + "[Ww]aitlists?", + "[Ww]ebhooks?", + "AndroidX", + "AsyncStorage", + "BigQuery", + "Bitbucket", + "CAPTCHA", + "ChatGPT", + "Clippy", + "Cloudflare", + "Colab", + "Database Functions?", + "DataDog", + "DevTools", + "DDoS", + "Deno", + "Django", + "Docker", + "Drizzle", + "Erlang", + "Firestore", + "GDScript", + "Git", + "GitHub", + "GitLab", + "GoTrue", + "Grafana", + "GraphQL", + "Heroku", + "Homebrew", + "Hono", + "IdP", + "ImageMagick", + "Inbucket", + "IntelliJ", + "IVFFlat", + "Jupyter", + "JWTs", + "Kotlin", + "Ktor", + "Kysely", + "LangChain", + "Laravel", + "LlamaIndex", + "Llamafile", + "Lua", + "Mixpeek", + "MySQL", + "Nano", + "Netlify", + "Next.js", + "NoSQL", + "Node.js", + "Nuxt", + "OAuth", + "Okta", + "Ollama", + "OpenAI", + "OrbStack", + "PascalCase", + "Podman", + "PostgREST", + "Postgres", + # We prefer Postgres, but check for vocabulary preference in a separate rule + "PostgreSQL", + "PubSub", + "Prisma", + "Redis", + "RedwoodJS", + "Roboflow", + "SDKs", + "SQLAlchemy", + "SQLite", + "SendGrid", + "Snaplet", + "SolidJS", + "Supavisor", + "SvelteKit", + "SwiftUI", + "Supabase", + "TimescaleDB", + "Transformers.js", + "Twilio", + "Undici", + "Unsplash", + "Uppy", + "Upstash", + "VSCode", + "Vecs", + "Vercel", + "Vite", + "Vue", + "WebAssembly", + "WebP", + "WebSockets?", + "WebStorm", + "Xcode", + "bcrypt", + "camelCase", + "deno-postgres", + "e.g.", + "gte-small", + "https?:\\/\\/\\S+", + "i.e.", + "iOS", + "localhost", + "macOS", + "npm", + "npmrc", + "pgAudit", + "pgsodium", + "pgvector", + "psql", + "scrypt", + "stdin", + "stdout", + "supabase-auth-ui", + "supabase-csharp", + "supabase-flutter", + "supabase-gdscript", + "supabase-go", + "supabase-js", + "supabase-kt", + "supabase-management-js", + "supabase-py", + "supabase-rb", + "supabase-swift", + "supautils", + "vecs", +] + +prefixes = ["bi", "pre"] From f9ccf16e7427642cf1cd7cf83efa40f10d1fbd97 Mon Sep 17 00:00:00 2001 From: TheOtherBrian1 <91111415+TheOtherBrian1@users.noreply.github.com> Date: Wed, 4 Dec 2024 15:50:32 -0500 Subject: [PATCH 4/6] added warning to timeout docs (#30869) * added warning to timeout docs * Update timeouts.mdx --------- Co-authored-by: Brian Brennglass --- apps/docs/content/guides/database/postgres/timeouts.mdx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/docs/content/guides/database/postgres/timeouts.mdx b/apps/docs/content/guides/database/postgres/timeouts.mdx index c89c3def47ace..0afd32c6cfc11 100644 --- a/apps/docs/content/guides/database/postgres/timeouts.mdx +++ b/apps/docs/content/guides/database/postgres/timeouts.mdx @@ -3,9 +3,11 @@ title: Timeouts subtitle: Extend database timeouts to execute longer transactions --- -Requests made through the Supabase API and Dashboard have a timeout of 60 seconds. The database also has a global default timeout of 2 minutes. + -To execute longer transactions, connect to your database using [Supavisor](/docs/guides/database/connecting-to-postgres#connection-pooler) or the [direct connection string](/docs/guides/database/connecting-to-postgres#direct-connections), and change the timeout settings. +Dashboard and [Client](/docs/guides/api/rest/client-libs) queries have a max-configurable timeout of 60 seconds. For longer transactions, use [Supavisor or direct connections](/docs/guides/database/connecting-to-postgres#quick-summary). + + ## Change Postgres timeout From 068a7d930fe02af78e8a62a40b653c30adc52b07 Mon Sep 17 00:00:00 2001 From: Wen Bo Xie <5532241+w3b6x9@users.noreply.github.com> Date: Wed, 4 Dec 2024 16:05:38 -0500 Subject: [PATCH 5/6] feat: add orioledb to www features page (#30805) * feat: add orioledb to www features page * Update apps/www/data/features.tsx Co-authored-by: Oliver Rice * Update apps/www/data/features.tsx Co-authored-by: Oliver Rice * update lw13 build stage descriptions --------- Co-authored-by: Oliver Rice --- .../13/Releases/data/lw13_build_stage.tsx | 5 ++- apps/www/data/features.tsx | 31 ++++++++++++++++++ .../public/images/features/orioledb-light.png | Bin 0 -> 35815 bytes apps/www/public/images/features/orioledb.png | Bin 0 -> 37809 bytes 4 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 apps/www/public/images/features/orioledb-light.png create mode 100644 apps/www/public/images/features/orioledb.png diff --git a/apps/www/components/LaunchWeek/13/Releases/data/lw13_build_stage.tsx b/apps/www/components/LaunchWeek/13/Releases/data/lw13_build_stage.tsx index b825a3827d308..a03f0f6d2531c 100644 --- a/apps/www/components/LaunchWeek/13/Releases/data/lw13_build_stage.tsx +++ b/apps/www/components/LaunchWeek/13/Releases/data/lw13_build_stage.tsx @@ -26,7 +26,7 @@ export interface AdventLink { export const days: AdventDay[] = [ { title: 'OrioleDB Public Alpha', - description: 'Launching OrioleDB Public Alpha', + description: 'A better Postgres storage engine replacing Heap storage', id: 'orioledb', is_shipped: true, links: [ @@ -65,8 +65,7 @@ export const days: AdventDay[] = [ }, { title: 'Supabase CLI v2: Config as Code', - description: - 'Commit the configuration for all of your Projects and Branches into version control.', + description: 'Version control the configuration of your Projects and Branches', id: 'cli', is_shipped: true, links: [ diff --git a/apps/www/data/features.tsx b/apps/www/data/features.tsx index 9be05c703786f..66553f20f1c79 100644 --- a/apps/www/data/features.tsx +++ b/apps/www/data/features.tsx @@ -2067,4 +2067,35 @@ Supabase Cron is a Postgres module designed to schedule recurring Jobs with cron availableOnSelfHosted: true, }, }, + { + title: 'OrioleDB', + subtitle: "New Postgres storage engine that's better than Heap storage.", + description: ` +OrioleDB is a PostgreSQL storage extension built on its pluggable storage framework. Serving as a direct replacement for PostgreSQL's Heap storage, it addresses scalability challenges while harnessing the full power of modern hardware. Designed to integrate effortlessly with PostgreSQL, OrioleDB enhances performance, efficiency, and scalability, all while maintaining the reliability and robustness PostgreSQL users depend on. + +## Key benefits: +1. Fully Integrated: A drop-in replacement for PostgreSQL’s Heap storage, enabling easy adoption without major changes to existing workflows. +2. Enhanced Scalability: Eliminates buffer mapping bottlenecks and utilizes lock-less page reading, significantly improving vertical scalability and hardware utilization. +3. Superior Performance: Proven to outperform PostgreSQL Heap by up to 5.5x in benchmarks, particularly under high-load and large-scale scenarios. +4. Reduced Maintenance Overhead: Undo log-based MVCC eliminates storage bloat and removes the need for VACUUM, preventing common performance degradation. +5. Efficient Storage Management: Built-in compression reduces storage requirements by up to 5x, enabling more cost-effective data handling. +6. Modern Write-Ahead Logging (WAL): Row-level WAL supports parallelism and is designed for future active-active multi-master configurations. +7. Optimized for Large Datasets: Index-organized tables improve data locality, reducing disk I/O for workloads exceeding memory cache capacity. + +## Roadmap Features: +1. Decoupled storage and compute with S3 integration for unlimited scalability. +2. Planned columnar indexes to enable hybrid OLTP and OLAP workloads on the same system. +3. Multi-master replication for better availability and fault tolerance. +`, + icon: Database, + products: [PRODUCT_SHORTNAMES.DATABASE, ADDITIONAL_PRODUCTS.PLATFORM], + heroImage: '/images/features/orioledb.png', + heroImageLight: '/images/features/orioledb-light.png', + docsUrl: 'https://supabase.com/docs/guides/database/orioledb', + slug: 'orioledb', + status: { + stage: PRODUCT_STAGES.PUBLIC_ALPHA, + availableOnSelfHosted: true, + }, + }, ] diff --git a/apps/www/public/images/features/orioledb-light.png b/apps/www/public/images/features/orioledb-light.png new file mode 100644 index 0000000000000000000000000000000000000000..578f74ca9b4817574df95385b6b0f5863a7a2263 GIT binary patch literal 35815 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 Date: Wed, 4 Dec 2024 18:01:57 -0400 Subject: [PATCH 6/6] ai assistant feature (#30906) * ai assistant feature * add ai assistant stage and self-host * add openai api key requirement --------- Co-authored-by: Wen Bo Xie --- apps/www/data/features.tsx | 36 +++++++++++++++++++++--------------- apps/www/lib/redirects.js | 7 +++++++ 2 files changed, 28 insertions(+), 15 deletions(-) diff --git a/apps/www/data/features.tsx b/apps/www/data/features.tsx index 66553f20f1c79..7be5528e54ee1 100644 --- a/apps/www/data/features.tsx +++ b/apps/www/data/features.tsx @@ -1710,29 +1710,35 @@ By building on Supabase's SOC 2 compliant platform, you gain a significant advan }, // Studio { - title: 'AI-Powered Query Assistance', - subtitle: 'Get help crafting complex SQL queries effortlessly.', + title: 'Supabase AI Assistant', + subtitle: 'Your intelligent companion for managing Postgres databases.', description: ` -The AI-Powered Query Assistance feature in Supabase provides users with intelligent suggestions for writing SQL queries based on natural language input. This tool is designed to enhance productivity and streamline the development process, making it easier for both novice and experienced developers to interact with their databases. By transforming user queries into optimized SQL commands, it reduces the time spent on query formulation and minimizes errors. +Supabase AI Assistant is integrated into the Supabase Dashboard to drastically improve your database management experience. This tool helps streamline your workflow, significantly reducing the time it takes to transition from concept to product. -## Key benefits -1. User-Friendly: Simplifies the query-writing process by allowing users to express their needs in plain language. -2. Time-Saving: Accelerates development by quickly generating accurate SQL queries. -3. Learning Tool: Acts as a learning resource for new developers to understand SQL syntax and best practices. -4. Error Reduction: Minimizes common mistakes in query formulation through intelligent suggestions. -5. Integration: Seamlessly integrates with existing Supabase projects, enhancing the overall user experience. +The AI Assistant can save you hours of work by automating repetitive tasks and providing context-aware support. For instance, when designing a new database schema, it can generate all necessary SQL queries based on your specifications, allowing you to focus on the bigger picture rather than getting bogged down in details. If you encounter an error in your SQL code, the Assistant can quickly analyze the issue and suggest solutions, enabling you to resolve problems efficiently without losing your train of thought. Additionally, while working on a specific table, the Assistant can provide instant feedback and recommendations tailored to that context, ensuring that you always have relevant information at your fingertips. -This feature is especially beneficial for teams looking to improve their database interaction efficiency while ensuring that all members can contribute effectively, regardless of their SQL expertise. +Key benefits: +1. Schema Design Assistance: Receive guidance on structuring your database and generating SQL queries. +2. Enhanced Query Writing: Get precise suggestions for writing SQL queries based on your schema. +3. Error Debugging: Identify and resolve SQL errors directly within the SQL Editor. +4. Data Insights Discovery: Execute queries using natural language and view results in a clear format. +5. RLS Policies Management: Create and modify Row Level Security (RLS) Policies according to your requirements. +6. Functions and Triggers Support: Easily suggest, create, or update Postgres Functions and Triggers. + +With Supabase AI Assistant, you gain a powerful ally in your development process, helping you work more efficiently and effectively while keeping you focused on your goals. `, icon: Brain, - products: [ADDITIONAL_PRODUCTS.STUDIO], - heroImage: 'https://www.youtube-nocookie.com/embed/hu2SQjvCXIw', - docsUrl: - 'https://supabase.com/blog/studio-introducing-assistant#introducing-the-supabase-assistant', - slug: 'ai-query-assistance', + products: [ADDITIONAL_PRODUCTS.PLATFORM], + heroImage: 'https://www.youtube-nocookie.com/embed/_fdP-aaTHgw', + docsUrl: '/blog/supabase-ai-assistant-v2', + slug: 'ai-assistant', status: { stage: PRODUCT_STAGES.PUBLIC_ALPHA, availableOnSelfHosted: true, + selfHostedTooling: { + label: 'OpenAI API Key', + link: 'https://platform.openai.com/docs/quickstart#create-and-export-an-api-key://platform.openai.com/api-keys', + }, }, }, { diff --git a/apps/www/lib/redirects.js b/apps/www/lib/redirects.js index 7fe17daad1e4f..6deae59b20a27 100644 --- a/apps/www/lib/redirects.js +++ b/apps/www/lib/redirects.js @@ -2852,4 +2852,11 @@ module.exports = [ source: '/changelogpod', destination: 'https://forms.supabase.com/changelog-podcast-sponsorship', }, + + // features + { + permanent: true, + source: '/features/ai-query-assistance', + destination: '/features/ai-assistant', + }, ]