diff --git a/.gitignore b/.gitignore index 88297ef381c..de5d7122e93 100644 --- a/.gitignore +++ b/.gitignore @@ -125,9 +125,12 @@ apps/new-docs/* # UI tokens packages/ui/tokens/**/*.json +# UI Storybook build +packages/ui/storybook-static + # For self-hosted logs: https://github.com/supabase/supabase/blob/86e3ab20abfdb9c3e666334d3d2f8efeef9ccf2c/docker/docker-compose-logging.yml#L101 gcloud.json # sitemaps # apps/www/public/*.xml -# apps/docs/public/*.xml \ No newline at end of file +# apps/docs/public/*.xml diff --git a/DEVELOPERS.md b/DEVELOPERS.md index 1a4e1a1e27c..505029ef2e6 100644 --- a/DEVELOPERS.md +++ b/DEVELOPERS.md @@ -69,11 +69,11 @@ To contribute code to [Supabase](https://supabase.com), you must fork the [Supab Then visit, and edit, any of the following sites: -| Site | Directory | Scope name | Description | Local development server | -| ---------------------------------------------- | ------------ | ---------- | ------------------------------------ | -------------------------- | -| [supabase.com](https://supabase.com) | `/apps/www` | www | The main website | http://localhost:3000 | -| [app.supabase.com](https://app.supabase.com) | `/studio` | studio | Studio dashboard | http://localhost:8082 | -| [supabase.com/docs](https://supabase.com/docs) | `/apps/docs` | docs | Guides and Reference (Next.js based) | http://localhost:3001/docs | +| Site | Directory | Scope name | Description | Local development server | +| -------------------------------------------------------- | ------------ | ---------- | ------------------------------------ | -------------------------- | +| [supabase.com](https://supabase.com) | `/apps/www` | www | The main website | http://localhost:3000 | +| [supabase.com/dashboard](https://supabase.com/dashboard) | `/studio` | studio | Studio dashboard | http://localhost:8082 | +| [supabase.com/docs](https://supabase.com/docs) | `/apps/docs` | docs | Guides and Reference (Next.js based) | http://localhost:3001/docs | #### Running sites individually @@ -132,6 +132,26 @@ Create a new entry in the [`redirects.js`](https://github.com/supabase/supabase/ --- +### Federated docs + +We support "federating" docs, meaning doc content can come directly from external repos other than [`supabase/supabase`](https://github.com/supabase/supabase). + +- It's great for things like client libs who have their own set of docs that we don't want to duplicate on the official Supabase docs (eg. [`supabase/vecs`](https://github.com/supabase/vecs)). +- No duplication or manual steps required - fetches and generates automatically as part of the docs build pipeline +- It's flexible - you can "embed" external docs nearly anywhere at any level in Supabase docs, but they will feel native +- If you are maintaining a repo containing docs that you think could also live in Supabase docs, feel free to create an issue and we can work together to integrate + +Federated docs work using Next.js's build pipeline. We use `getStaticProps()` to fetch remote documentation (ie. markdown) at build time which is processed and passed to the respective page within the docs. + +See the [Vecs Python source code](https://github.com/supabase/supabase/blob/master/apps/docs/pages/guides/ai/python/%5Bslug%5D.tsx) to see how we do this for [`supabase/vecs`](https://github.com/supabase/vecs). Use this as a starting point for federating other docs. + +Some things to consider: + +- Links will often need to be transformed. For example if you are bringing in external markdown content, they may contain relative links that may not translate 1-to-1 after rendering in the Supabase docs. Use the [Link Transform](https://github.com/supabase/supabase/blob/master/apps/docs/lib/mdx/plugins/rehypeLinkTransform.ts) rehype plugin to transform links. +- External markdown may contain syntax extensions that Supabase docs don't understand by default (eg. [mkdocs-material extensions](https://squidfunk.github.io/mkdocs-material/setup/extensions/python-markdown)). We've built a few remark plugins to support these extensions (eg. [MkDocs Admonition](https://github.com/supabase/supabase/blob/master/apps/docs/lib/mdx/plugins/remarkAdmonition.ts)). If there is a markdown extension that you need that isn't built yet, feel free to open an issue and we can work together to create it. + +--- + ## Community channels If you are stuck somewhere or have any questions, join our [Discord Community Server](https://discord.supabase.com/) or the [Github Discussions](https://github.com/supabase/supabase/discussions). We are here to help! diff --git a/README.md b/README.md index 2e3acce6815..da83a9e1e4e 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ To see how to Contribute, visit [Getting Started](./DEVELOPERS.md) ## Status - [x] Alpha: We are testing Supabase with a closed set of customers -- [x] Public Alpha: Anyone can sign up over at [app.supabase.com](https://app.supabase.com). But go easy on us, there are a few kinks +- [x] Public Alpha: Anyone can sign up over at [supabase.com/dashboard](https://supabase.com/dashboard). But go easy on us, there are a few kinks - [x] Public Beta: Stable enough for most non-enterprise use-cases - [ ] Public: General Availability [[status](https://supabase.com/docs/guides/getting-started/features#feature-status)] @@ -56,7 +56,7 @@ Supabase is a combination of open source tools. We’re building the features of **Architecture** -Supabase is a [hosted platform](https://app.supabase.com). You can sign up and start using Supabase without installing anything. +Supabase is a [hosted platform](https://supabase.com/dashboard). You can sign up and start using Supabase without installing anything. You can also [self-host](https://supabase.com/docs/guides/hosting/overview) and [develop locally](https://supabase.com/docs/guides/local-development). ![Architecture](https://github.com/supabase/supabase/blob/master/apps/docs/public/img/supabase-architecture.png) @@ -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/netlify/gotrue) is an SWT based API for managing users and issuing SWT tokens. +- [GoTrue](https://github.com/supabase/gotrue) is an 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/AppleSecretGenerator.tsx b/apps/docs/components/AppleSecretGenerator.tsx index 7bfeed67dcc..4029b1d8770 100644 --- a/apps/docs/components/AppleSecretGenerator.tsx +++ b/apps/docs/components/AppleSecretGenerator.tsx @@ -1,6 +1,5 @@ import { useState } from 'react' -import { Input, Button } from 'ui' -import Admonition from '~/components/Admonition' +import { Admonition, Button, Input } from 'ui' function base64URL(value: string) { return globalThis.btoa(value).replace(/[=]/g, '').replace(/[+]/g, '-').replace(/[\/]/g, '_') diff --git a/apps/docs/components/Extensions.tsx b/apps/docs/components/Extensions.tsx index b7b7260cd64..b49c5ad83d3 100644 --- a/apps/docs/components/Extensions.tsx +++ b/apps/docs/components/Extensions.tsx @@ -3,14 +3,25 @@ import React, { useState } from 'react' import { GlassPanel, IconLink, IconX, Input } from 'ui' import extensions from '../data/extensions.json' -type extension = { +type Extension = { name: string comment: string tags: string[] - link?: string + link: string } -function getUniqueTags(json: extension[]) { +type LinkTarget = React.ComponentProps<'a'>['target'] + +function getLinkTarget(link: string): LinkTarget { + // Link is relative, open in the same tab + if (link.startsWith('/')) { + return '_self' + } + // Link is external, open in a new tab + return '_blank' +} + +function getUniqueTags(json: Extension[]): string[] { const tags = [] for (const item of json) { if (item.tags) { @@ -21,12 +32,12 @@ function getUniqueTags(json: extension[]) { } export default function Extensions() { - const [searchTerm, setSearchTerm] = useState('') - const [filters, setFilters] = useState([]) + const [searchTerm, setSearchTerm] = useState('') + const [filters, setFilters] = useState([]) const tags = getUniqueTags(extensions) - function handleChecked(tag) { + function handleChecked(tag: string) { if (filters.includes(tag)) { setFilters(filters.filter((x) => x !== tag)) } else { @@ -94,15 +105,8 @@ export default function Extensions() { filters.length === 0 ? x : x.tags.some((item) => filters.includes(item)) ) .map((extension) => ( - - + +

{extension.comment.charAt(0).toUpperCase() + extension.comment.slice(1)} diff --git a/apps/docs/components/Frameworks.tsx b/apps/docs/components/Frameworks.tsx index 12e38b7910d..2b88f604748 100644 --- a/apps/docs/components/Frameworks.tsx +++ b/apps/docs/components/Frameworks.tsx @@ -93,6 +93,14 @@ const Frameworks = () => { }, href: '/guides/with-vue-3', }, + { + name: 'refine', + logo: { + light: '/docs/img/icons/refine-icon.svg', + dark: '/docs/img/icons/refine-icon.svg', + }, + href: '/guides/getting-started/tutorials/with-refine', + }, ] return (

diff --git a/apps/docs/components/HomePageCover.tsx b/apps/docs/components/HomePageCover.tsx index 3bf27c6b2a0..e760c411dc3 100644 --- a/apps/docs/components/HomePageCover.tsx +++ b/apps/docs/components/HomePageCover.tsx @@ -48,6 +48,11 @@ const HomePageCover = (props) => { icon: '/docs/img/icons/nuxt-icon', href: '/guides/getting-started/quickstarts/nuxtjs', }, + { + tooltip: 'refine', + icon: '/docs/img/icons/refine-icon', + href: '/guides/getting-started/quickstarts/refine', + }, ] const GettingStarted = () => ( @@ -72,7 +77,7 @@ const HomePageCover = (props) => { Discover how to set up a database to an app making queries in just a few minutes.

-
+
{frameworks.map((framework, i) => ( @@ -93,7 +98,7 @@ const HomePageCover = (props) => { return (
-
+

{props.meta?.title}

@@ -103,7 +108,7 @@ const HomePageCover = (props) => {

-
+
diff --git a/apps/docs/components/MDX/database_setup.mdx b/apps/docs/components/MDX/database_setup.mdx index 2a6e853f9c2..c54b9b32f84 100644 --- a/apps/docs/components/MDX/database_setup.mdx +++ b/apps/docs/components/MDX/database_setup.mdx @@ -12,7 +12,7 @@ Your database will be available in less than a minute. **Finding your credentials:** -You can find your project credentials inside the project [settings](https://app.supabase.com/project/_/settings/), including: +You can find your project credentials inside the project [settings](https://supabase.com/dashboard/project/_/settings/), including: -- [Database credentials](https://app.supabase.com/project/_/settings/database): connection strings and connection pooler details. -- [API credentials](https://app.supabase.com/project/_/settings/database): your serverless API URL and `anon` / `service_role` keys. +- [Database credentials](https://supabase.com/dashboard/project/_/settings/database): connection strings and connection pooler details. +- [API credentials](https://supabase.com/dashboard/project/_/settings/database): your serverless API URL and `anon` / `service_role` keys. diff --git a/apps/docs/components/MDX/project_setup.mdx b/apps/docs/components/MDX/project_setup.mdx index 2003a3a2220..d7b095e1288 100644 --- a/apps/docs/components/MDX/project_setup.mdx +++ b/apps/docs/components/MDX/project_setup.mdx @@ -8,7 +8,7 @@ Before we start building we're going to set up our Database and API. This is as ### Create a project -1. [Create a new project](https://app.supabase.com) in the Supabase Dashboard. +1. [Create a new project](https://supabase.com/dashboard) in the Supabase Dashboard. 1. Enter your project details. 1. Wait for the new database to launch. @@ -24,7 +24,7 @@ Now we are going to set up the database schema. We can use the "User Management > -1. Go to the [SQL Editor](https://app.supabase.com/project/_/sql) page in the Dashboard. +1. Go to the [SQL Editor](https://supabase.com/dashboard/project/_/sql) page in the Dashboard. 2. Click **User Management Starter**. 3. Click **Run**. @@ -41,5 +41,5 @@ Now we are going to set up the database schema. We can use the "User Management Now that you've created some database tables, you are ready to insert data using the auto-generated API. We just need to get the Project URL and `anon` key from the API settings. -1. Go to the [API Settings](https://app.supabase.com/project/_/settings/api) page in the Dashboard. +1. Go to the [API Settings](https://supabase.com/dashboard/project/_/settings/api) page in the Dashboard. 1. Find your Project `URL`, `anon`, and `service_role` keys on this page. diff --git a/apps/docs/components/MDX/social_provider_settings_supabase.mdx b/apps/docs/components/MDX/social_provider_settings_supabase.mdx index 38e0d9e5bfe..394673df620 100644 --- a/apps/docs/components/MDX/social_provider_settings_supabase.mdx +++ b/apps/docs/components/MDX/social_provider_settings_supabase.mdx @@ -1,6 +1,6 @@ -- Go to your [Supabase Project Dashboard](https://app.supabase.com) +- Go to your [Supabase Project Dashboard](https://supabase.com/dashboard) - In the left sidebar, click the `Authentication` icon (near the top) -- Click on [`Providers`](https://app.supabase.com/project/_/auth/providers) under the Configuration section +- Click on [`Providers`](https://supabase.com/dashboard/project/_/auth/providers) under the Configuration section - Click on **{props.provider}** from the accordion list to expand and turn **{props.provider} Enabled** to ON - Enter your **{props.provider} Client ID** and **{props.provider} Client Secret** saved in the previous step - Click `Save` diff --git a/apps/docs/components/MDX/social_provider_setup.mdx b/apps/docs/components/MDX/social_provider_setup.mdx index 4e32c3bf401..e6c1fe7e6dc 100644 --- a/apps/docs/components/MDX/social_provider_setup.mdx +++ b/apps/docs/components/MDX/social_provider_setup.mdx @@ -2,7 +2,7 @@ The next step requires a callback URL, which looks like this: `https://.supabase.co/auth/v1/callback` -- Go to your [Supabase Project Dashboard](https://app.supabase.com) +- Go to your [Supabase Project Dashboard](https://supabase.com/dashboard) - Click on the `Authentication` icon in the left sidebar -- Click on [`Providers`](https://app.supabase.com/project/_/auth/providers) under the Configuration section +- Click on [`Providers`](https://supabase.com/dashboard/project/_/auth/providers) under the Configuration section - Click on **{props.provider}** from the accordion list to expand and you'll find your **Redirect URL**, you can click `Copy` to copy it to the clipboard diff --git a/apps/docs/components/MDX/storage_management.mdx b/apps/docs/components/MDX/storage_management.mdx index e8f0184b88e..ce28a1684c8 100644 --- a/apps/docs/components/MDX/storage_management.mdx +++ b/apps/docs/components/MDX/storage_management.mdx @@ -7,7 +7,7 @@ triggers. Note that it is not sufficient to delete the objects from the `storage.objects` table because that would orphan and leak the actual storage objects in the S3 backend. Instead, invoke the storage API within Postgres via the `http` extension. -Enable the [http extension for the `extensions` schema](https://app.supabase.com/project/_/database/extensions) in the Dashboard. +Enable the [http extension for the `extensions` schema](https://supabase.com/dashboard/project/_/database/extensions) in the Dashboard. Then, define the following SQL functions in the SQL Editor to delete storage objects via the API: diff --git a/apps/docs/components/Navigation/NavigationMenu/HomeMenuIcons.tsx b/apps/docs/components/Navigation/NavigationMenu/HomeMenuIcons.tsx index 6925319d68d..4d2e53f6ea2 100644 --- a/apps/docs/components/Navigation/NavigationMenu/HomeMenuIcons.tsx +++ b/apps/docs/components/Navigation/NavigationMenu/HomeMenuIcons.tsx @@ -1,3 +1,5 @@ +import { products } from 'shared-data' + type HomeMenuIcon = { width?: number height?: number @@ -50,7 +52,7 @@ export function IconMenuAuth({ width = 16, height = 16 }: HomeMenuIcon) { xmlns="http://www.w3.org/2000/svg" > = (props) => { > {item.examples && item.examples.map((example, exampleIndex) => { - const exampleString = '' + const exampleString = + '' + + (example.code && + example.code + .trim() + .replace(/^```.*/, '') + .replace(/```$/, '')) const codeBlockLang = example?.code?.startsWith('```js') ? 'js' @@ -133,7 +139,6 @@ const RefFunctionSection: React.FC = (props) => { // // Create a single supabase client for interacting with your database // const supabase = createClient('https://xyzcompany.supabase.co', 'public-anon-key') // ` - const currentExampleId = example.id const staticExample = item.examples[exampleIndex] const response = staticExample.response @@ -152,15 +157,7 @@ const RefFunctionSection: React.FC = (props) => { language={codeBlockLang} hideLineNumbers={true} > - {exampleString + - (example.code && - example.code - .replace(/```/g, '') - .replace('js', '') - .replace('ts', '') - .replace('dart', '') - .replace('c#', '') - .replace('kotlin', ''))} + {exampleString} {((tables && tables.length > 0) || sql) && ( diff --git a/apps/docs/data/authProviders.ts b/apps/docs/data/authProviders.ts index a1053a30772..9694f1daf12 100644 --- a/apps/docs/data/authProviders.ts +++ b/apps/docs/data/authProviders.ts @@ -79,6 +79,16 @@ const authProviders = [ selfHosted: true, authType: 'social', }, + { + name: 'Kakao', + logo: '/docs/img/icons/kakao-icon', + href: '/guides/auth/social-login/auth-kakao', + official: true, + supporter: 'Supabase', + platform: true, + selfHosted: true, + authType: 'social', + }, { name: 'Keycloak', logo: '/docs/img/icons/keycloak-icon', diff --git a/apps/docs/data/extensions.json b/apps/docs/data/extensions.json index c46542b2cfd..af7f7342fba 100644 --- a/apps/docs/data/extensions.json +++ b/apps/docs/data/extensions.json @@ -3,460 +3,414 @@ "name": "address_standardizer", "comment": "Used to parse an address into constituent elements. Generally used to support geocoding address normalization step.", "tags": ["Utility"], - "link": "http://supabase.com/docs/guides/extensions/etc/etc/etc" - }, - { - "name": "address_standardizer-3", - "comment": "Used to parse an address into constituent elements. Generally used to support geocoding address normalization step.", - "tags": ["Utility"] + "link": "https://postgis.net/docs/manual-2.5/Address_Standardizer.html" }, { "name": "address_standardizer_data_us", "comment": "Address Standardizer US dataset example", - "tags": ["Dataset"] - }, - { - "name": "address_standardizer_data_us-3", - "comment": "Address Standardizer US dataset example", - "tags": ["Dataset"] - }, - { - "name": "adminpack", - "comment": "administrative functions for PostgreSQL", - "tags": ["Admin", "Utility"] + "tags": ["Dataset"], + "link": "https://postgis.net/docs/manual-2.5/Address_Standardizer.html" }, { "name": "amcheck", "comment": "functions for verifying relation integrity", - "tags": ["Admin", "Utility"] + "tags": ["Admin", "Utility"], + "link": "https://www.postgresql.org/docs/current/amcheck.html" }, { "name": "autoinc", "comment": "functions for autoincrementing fields", - "tags": ["Utility"] + "tags": ["Utility"], + "link": "https://www.postgresql.org/docs/current/contrib-spi.html#id-1.11.7.50.6" }, { "name": "bloom", "comment": "bloom access method - signature file based index", - "tags": ["Index"] + "tags": ["Index"], + "link": "https://www.postgresql.org/docs/current/bloom.html" }, { "name": "btree_gin", "comment": "support for indexing common datatypes in GIN", - "tags": ["Index"] + "tags": ["Index"], + "link": "https://www.postgresql.org/docs/current/btree-gin.html" }, { "name": "btree_gist", "comment": "support for indexing common datatypes in GiST", - "tags": ["Index"] + "tags": ["Index"], + "link": "https://www.postgresql.org/docs/current/btree-gist.html" }, { "name": "citext", "comment": "data type for case-insensitive character strings", - "tags": ["Data Type"] + "tags": ["Data Type"], + "link": "https://www.postgresql.org/docs/current/citext.html" }, { "name": "cube", "comment": "data type for multidimensional cubes", - "tags": ["Data Type"] + "tags": ["Data Type"], + "link": "https://www.postgresql.org/docs/current/cube.html" }, { "name": "dblink", "comment": "connect to other PostgreSQL databases from within a database", - "tags": ["Admin", "Utility"] + "tags": ["Admin", "Utility"], + "link": "https://www.postgresql.org/docs/current/contrib-dblink-function.html" }, { "name": "dict_int", "comment": "text search dictionary template for integers", - "tags": ["Search"] + "tags": ["Search"], + "link": "https://www.postgresql.org/docs/current/dict-int.html" }, { "name": "dict_xsyn", "comment": "text search dictionary template for extended synonym processing", - "tags": ["Search"] + "tags": ["Search"], + "link": "https://www.postgresql.org/docs/current/dict-xsyn.html" }, { "name": "earthdistance", "comment": "calculate great-circle distances on the surface of the Earth", - "tags": ["Geo", "Utility"] - }, - { - "name": "file_fdw", - "comment": "foreign-data wrapper for flat file access", - "tags": ["Admin", "Utility"] + "tags": ["Geo", "Utility"], + "link": "https://www.postgresql.org/docs/current/earthdistance.html" }, { "name": "fuzzystrmatch", "comment": "determine similarities and distance between strings", - "tags": ["Search"] + "tags": ["Search"], + "link": "https://www.postgresql.org/docs/current/fuzzystrmatch.html" }, { "name": "hstore", "comment": "data type for storing sets of (key, value) pairs", - "tags": ["Data Type"] + "tags": ["Data Type"], + "link": "https://www.postgresql.org/docs/current/hstore.html" }, { "name": "hypopg", "comment": "Hypothetical indexes for PostgreSQL", - "link": "https://supabase.com/docs/guides/database/extensions/hypopg", - "tags": ["Admin", "Index"] + "tags": ["Admin", "Index"], + "link": "/guides/database/extensions/hypopg" }, { "name": "http", "comment": "HTTP client for PostgreSQL, allows web page retrieval inside the database.", - "link": "https://supabase.com/docs/guides/database/extensions/http", - "tags": ["Utility"] + "tags": ["Utility"], + "link": "/guides/database/extensions/http" }, { "name": "insert_username", "comment": "functions for tracking who changed a table", - "tags": ["Audit", "Utility"] + "tags": ["Audit", "Utility"], + "link": "https://www.postgresql.org/docs/current/contrib-spi.html#id-1.11.7.50.7" }, { "name": "old_snapshot", "comment": "utilities in support of old_snapshot_threshold", - "tags": ["Admin", "Utility"] - }, - { - "name": "intagg", - "comment": "integer aggregator and enumerator (obsolete)", - "tags": ["Utility"] + "tags": ["Admin", "Utility"], + "link": "https://www.postgresql.org/docs/current/oldsnapshot.html" }, { "name": "intarray", "comment": "functions, operators, and index support for 1-D arrays of integers", - "tags": ["Utility"] + "tags": ["Utility"], + "link": "https://www.postgresql.org/docs/current/intarray.html" }, { "name": "isn", "comment": "data types for international product numbering standards", - "tags": ["Data Type"] + "tags": ["Data Type"], + "link": "https://www.postgresql.org/docs/current/isn.html" }, { "name": "lo", "comment": "Large Object maintenance", - "tags": ["Data Type"] + "tags": ["Data Type"], + "link": "https://www.postgresql.org/docs/current/lo.html" }, { "name": "ltree", "comment": "data type for hierarchical tree-like structures", - "tags": ["Data Type"] + "tags": ["Data Type"], + "link": "https://www.postgresql.org/docs/current/ltree.html" }, { "name": "moddatetime", "comment": "functions for tracking last modification time", - "tags": ["Audit", "Utility"] - }, - { - "name": "pageinspect", - "comment": "inspect the contents of database pages at a low level", - "tags": ["Admin", "Utility"] - }, - { - "name": "pg_buffercache", - "comment": "examine the shared buffer cache", - "tags": ["Admin", "Utility"] + "tags": ["Audit", "Utility"], + "link": "https://www.postgresql.org/docs/current/contrib-spi.html#id-1.11.7.50.8" }, { "name": "pg_cron", "comment": "Job scheduler for PostgreSQL", - "link": "https://supabase.com/docs/guides/database/extensions/pgcron", - "tags": ["Utility"] + "tags": ["Utility"], + "link": "/guides/database/extensions/pg_cron" }, { "name": "pg_freespacemap", "comment": "examine the free space map (FSM)", - "tags": ["Admin", "Utility"] + "tags": ["Admin", "Utility"], + "link": "https://www.postgresql.org/docs/current/pgfreespacemap.html" }, { "name": "pg_graphql", "comment": "pg_graphql: GraphQL support", - "link": "https://supabase.com/docs/guides/database/extensions/pg_graphql", - "tags": ["Utility"] + "tags": ["Utility"], + "link": "/guides/database/extensions/pg_graphql" }, { "name": "pg_hashids", "comment": "pg_hashids", - "link": "https://supabase.com/docs/guides/database/extensions/pg_hashids", - "tags": ["Utility"] + "tags": ["Utility"], + "link": "/guides/database/extensions/pg_hashids" }, { "name": "pg_jsonschema", "comment": "pg_jsonschema", - "link": "https://supabase.com/docs/guides/database/extensions/pg_jsonschema", - "tags": ["Utility"] + "tags": ["Utility"], + "link": "/guides/database/extensions/pg_jsonschema" }, { "name": "pg_net", "comment": "Async HTTP", - "link": "https://supabase.com/docs/guides/database/extensions/pgnet", - "tags": ["Utility", "Notifications"] + "tags": ["Utility", "Notifications"], + "link": "/guides/database/extensions/pg_net" }, { "name": "pg_prewarm", "comment": "prewarm relation data", - "tags": ["Admin", "Utility"] + "tags": ["Admin", "Utility"], + "link": "https://www.postgresql.org/docs/current/pgprewarm.html" }, { "name": "pg_stat_monitor", "comment": "The pg_stat_monitor is a PostgreSQL Query Performance Monitoring tool, based on PostgreSQL contrib module pg_stat_statements. pg_stat_monitor provides aggregated statistics, client information, plan details including plan, and histogram information.", - "link": "https://supabase.com/docs/guides/database/extensions/pg_stat_monitor", - "tags": ["Utility"] + "tags": ["Utility"], + "link": "/guides/database/extensions/pg_stat_monitor" }, { "name": "pg_stat_statements", "comment": "track execution statistics of all SQL statements executed", - "link": "https://supabase.com/docs/guides/database/extensions/pg_stat_statements", - "tags": ["Admin", "Utility"] + "tags": ["Admin", "Utility"], + "link": "/guides/database/extensions/pg_stat_statements" }, { "name": "pg_surgery", "comment": "extension to perform surgery on a damaged relation", - "tags": ["Admin", "Utility"] + "tags": ["Admin", "Utility"], + "link": "https://www.postgresql.org/docs/current/pgsurgery.html" }, { "name": "pg_trgm", "comment": "text similarity measurement and index searching based on trigrams", - "tags": ["Search"] - }, - { - "name": "pg_visibility", - "comment": "examine the visibility map (VM) and page-level visibility info", - "tags": ["Admin", "Utility"] + "tags": ["Search"], + "link": "https://www.postgresql.org/docs/current/pgtrgm.html" }, { "name": "pgaudit", "comment": "provides auditing functionality", - "link": "https://supabase.com/docs/guides/database/extensions/pgaudit", - "tags": ["Audit", "Utility"] + "tags": ["Audit", "Utility"], + "link": "/guides/database/extensions/pgaudit" }, { "name": "pg_walinspect", "comment": "functions to inspect contents of PostgreSQL Write-Ahead Log", - "tags": ["Admin", "Utility"] + "tags": ["Admin", "Utility"], + "link": "https://www.postgresql.org/docs/current/pgwalinspect.html" }, { "name": "pgcrypto", "comment": "cryptographic functions", - "tags": ["Utility", "Cryptography"] + "tags": ["Utility", "Cryptography"], + "link": "https://www.postgresql.org/docs/current/pgcrypto.html" }, { "name": "pgjwt", "comment": "JSON Web Token API for Postgresql", - "link": "https://supabase.com/docs/guides/database/extensions/pgjwt", - "tags": ["Utility", "Cryptography"] + "tags": ["Utility", "Cryptography"], + "link": "/guides/database/extensions/pgjwt" }, { "name": "pgroonga", "comment": "Super fast and all languages supported full text search index based on Groonga", - "link": "https://supabase.com/docs/guides/database/extensions/pgroonga", - "tags": ["Search"] + "tags": ["Search"], + "link": "/guides/database/extensions/pgroonga" }, { "name": "pgroonga_database", "comment": "PGroonga database management module", - "tags": ["Admin"] + "tags": ["Admin"], + "link": "https://pgroonga.github.io/reference/modules/pgroonga-database.html" }, { "name": "pgrouting", "comment": "pgRouting Extension", - "link": "https://supabase.com/docs/guides/database/extensions/pgrouting", - "tags": ["Geo", "Utility"] + "tags": ["Geo", "Utility"], + "link": "/guides/database/extensions/pgrouting" }, { "name": "pgrowlocks", "comment": "show row-level locking information", - "tags": ["Admin", "Utility"] + "tags": ["Admin", "Utility"], + "link": "https://www.postgresql.org/docs/current/pgrowlocks.html" }, { "name": "pgsodium", "comment": "Postgres extension for libsodium functions", - "link": "https://supabase.com/docs/guides/database/extensions/pgsodium", - "tags": ["Utility", "Cryptography"] + "tags": ["Utility", "Cryptography"], + "link": "/guides/database/extensions/pgsodium" }, { "name": "pgstattuple", "comment": "show tuple-level statistics", - "tags": ["Admin", "Utility"] + "tags": ["Admin", "Utility"], + "link": "https://www.postgresql.org/docs/current/pgstattuple.html" }, { "name": "pgtap", "comment": "Unit testing for PostgreSQL", - "link": "https://supabase.com/docs/guides/database/extensions/pgtap", - "tags": ["Utility", "Testing"] + "tags": ["Utility", "Testing"], + "link": "/guides/database/extensions/pgtap" }, { "name": "plcoffee", "comment": "PL/CoffeeScript (v8) trusted procedural language", - "tags": ["Language"] + "tags": ["Language"], + "link": "https://github.com/plv8/plv8/blob/master/doc/plv8.md#coffeescript-extension" }, { "name": "pljava", "comment": "PL/Java procedural language (https://tada.github.io/pljava/)", - "tags": ["Language"] + "tags": ["Language"], + "link": "https://tada.github.io/pljava/" }, { "name": "plls", "comment": "PL/LiveScript (v8) trusted procedural language", - "tags": ["Language"] + "tags": ["Language"], + "link": "https://github.com/plv8/plv8/blob/master/doc/plv8.md#livescript-extension" }, { "name": "plpgsql", "comment": "PL/pgSQL procedural language", - "tags": ["Language"] + "tags": ["Language"], + "link": "https://www.postgresql.org/docs/current/plpgsql.html" }, { "name": "plpgsql_check", "comment": "extended check for plpgsql functions", - "href": "https://supabase.com/docs/guides/database/extensions/plpgsql_check", - "tags": ["Utility", "Testing"] + "tags": ["Utility", "Testing"], + "link": "/guides/database/extensions/plpgsql_check" }, { "name": "plv8", "comment": "PL/JavaScript (v8) trusted procedural language", - "link": "http://localhost:3001/docs/guides/database/extensions/plv8", - "tags": ["Language"] + "tags": ["Language"], + "link": "/guides/database/extensions/plv8" }, { "name": "postgis", "comment": "PostGIS geometry and geography spatial types and functions", - "link": "https://supabase.com/docs/guides/database/extensions/postgis", - "tags": ["Geo"] - }, - { - "name": "postgis-3", - "comment": "PostGIS geometry and geography spatial types and functions", - "tags": ["Geo"] - }, - { - "name": "postgis_raster", - "comment": "PostGIS raster types and functions", - "tags": ["Geo"] - }, - { - "name": "postgis_raster-3", - "comment": "PostGIS raster types and functions", - "tags": ["Geo"] - }, - { - "name": "postgis_sfcgal", - "comment": "PostGIS SFCGAL functions", - "tags": ["Geo"] - }, - { - "name": "postgis_sfcgal-3", - "comment": "PostGIS SFCGAL functions", - "tags": ["Geo"] - }, - { - "name": "postgis_tiger_geocoder", - "comment": "PostGIS tiger geocoder and reverse geocoder", - "tags": ["Geo"] - }, - { - "name": "postgis_tiger_geocoder-3", - "comment": "PostGIS tiger geocoder and reverse geocoder", - "tags": ["Geo"] - }, - { - "name": "postgis_topology", - "comment": "PostGIS topology spatial types and functions", - "tags": ["Geo"] - }, - { - "name": "postgis_topology-3", - "comment": "PostGIS topology spatial types and functions", - "tags": ["Geo"] + "tags": ["Geo"], + "link": "/guides/database/extensions/postgis" }, { "name": "postgres_fdw", "comment": "foreign-data wrapper for remote PostgreSQL servers", - "tags": ["Admin"] + "tags": ["Admin"], + "link": "https://www.postgresql.org/docs/current/postgres-fdw.html" }, { "name": "refint", "comment": "functions for implementing referential integrity (obsolete)", - "tags": ["Utility"] + "tags": ["Utility"], + "link": "https://www.postgresql.org/docs/current/contrib-spi.html#id-1.11.7.50.5" }, { "name": "rum", "comment": "GIN-like index for text search", - "link": "https://supabase.com/docs/guides/database/extensions/rum", - "tags": ["Index", "Search"] + "tags": ["Index", "Search"], + "link": "/guides/database/extensions/rum" }, { "name": "seg", "comment": "data type for representing line segments or floating-point intervals", - "tags": ["Utility"] + "tags": ["Utility"], + "link": "https://www.postgresql.org/docs/current/seg.html" }, { "name": "sslinfo", "comment": "information about SSL certificates", - "tags": ["Admin", "Utility"] + "tags": ["Admin", "Utility"], + "link": "https://www.postgresql.org/docs/current/sslinfo.html" }, { "name": "supautils", "comment": "Supabase standard library", - "tags": ["Utility"] + "tags": ["Utility"], + "link": "https://supabase.github.io/supautils/" }, { "name": "tablefunc", "comment": "functions that manipulate whole tables, including crosstab", - "tags": ["Utility"] + "tags": ["Utility"], + "link": "https://www.postgresql.org/docs/current/tablefunc.html" }, { "name": "tcn", "comment": "Triggered change notifications", - "tags": ["Utility", "Notifications"] + "tags": ["Utility", "Notifications"], + "link": "https://www.postgresql.org/docs/current/tcn.html" }, { "name": "timescaledb", "comment": "Enables scalable inserts and complex queries for time-series data", - "link": "https://supabase.com/docs/guides/database/extensions/timescaledb", - "tags": ["Time Series", "Data Type", "Utility"] + "tags": ["Time Series", "Data Type", "Utility"], + "link": "/guides/database/extensions/timescaledb" }, { "name": "tsm_system_rows", "comment": "TABLESAMPLE method which accepts number of rows as a limit", - "tags": ["Utility"] + "tags": ["Utility"], + "link": "https://www.postgresql.org/docs/current/tsm-system-rows.html" }, { "name": "tsm_system_time", "comment": "TABLESAMPLE method which accepts time in milliseconds as a limit", - "tags": ["Utility"] + "tags": ["Utility"], + "link": "https://www.postgresql.org/docs/current/tsm-system-time.html" }, { "name": "unaccent", "comment": "text search dictionary that removes accents", - "tags": ["Search"] + "tags": ["Search"], + "link": "https://www.postgresql.org/docs/current/unaccent.html" }, { "name": "uuid-ossp", "comment": "generate universally unique identifiers (UUIDs)", - "link": "https://supabase.com/docs/guides/database/extensions/uuid-ossp", - "tags": ["Utility", "Data Type"] + "tags": ["Utility", "Data Type"], + "link": "/guides/database/extensions/uuid-ossp" }, { "name": "pgvector", "comment": "vector data type with similarity search", - "link": "https://supabase.com/docs/guides/database/extensions/pgvector", - "tags": ["AI", "Data Type", "Search"] + "tags": ["AI", "Data Type", "Search"], + "link": "/guides/database/extensions/pgvector" }, { "name": "pg_repack", "comment": "Optimize physical storage and remove bloat from tables and indexes", - "link": "https://supabase.com/docs/guides/database/extensions/pgrepack", - "tags": ["Admin", "Utility"] + "tags": ["Admin", "Utility"], + "link": "/guides/database/extensions/pg_repack" }, { "name": "wrappers", "comment": "Foreign data wrappers developed by Supabase", - "link": "https://supabase.com/docs/guides/database/extensions/wrappers", - "tags": ["Admin", "Utility"] - }, - { - "name": "xml2", - "comment": "XPath querying and XSLT", - "tags": ["Utility"] + "tags": ["Admin", "Utility"], + "link": "/guides/database/extensions/wrappers" } ] diff --git a/apps/docs/docs/ref/api/api.mdx b/apps/docs/docs/ref/api/api.mdx index 371041af0bd..70bb75ee9f6 100644 --- a/apps/docs/docs/ref/api/api.mdx +++ b/apps/docs/docs/ref/api/api.mdx @@ -15,7 +15,7 @@ The Management API is in `beta`. It is usable in it's current state, but it's li ## Authentication All API requests require a Supabase Personal token to be included in the Authorization header: `Authorization Bearer -### Add KotlinX Serialization (required for PostgREST and Realtime serialization) +### Serialization + +supabase-kt provides several different ways to encode and decode your custom objects. +By default, [KotlinX Serialization](https://github.com/Kotlin/kotlinx.serialization) is used. - + - Add the serialization plugin + Use [KotlinX Serialization](https://github.com/Kotlin/kotlinx.serialization). + + + - - Now you can create serializable classes in your project: ```kotlin - @Serializable - data class Message( - val id: Int, - val content: String, - @SerialName("author_id") - val authorId: String - ) + val client = createSupabaseClient(supabaseUrl, supabaseKey) { + //Already the default serializer + } ``` - And use them in your Supabase queries: + + + + + + + Use [Moshi](https://github.com/square/moshi). + + + + + + + + ```kotlin + implementation("io.github.jan-tennert.supabase:serializer-moshi:VERSION") + ``` + + + + + ```groovy + implementation 'io.github.jan-tennert.supabase:serializer-moshi:VERSION' + ``` + + + + + ```xml + + io.github.jan-tennert.supabase + serializer-moshi + VERSION + + ``` + + + ```kotlin - val messages = client.postgrest["messages"].select().decodeList() + val client = createSupabaseClient(supabaseUrl, supabaseKey) { + defaultSerializer = MoshiSerializer() + } + ``` + + + + + + + + Use [Jackson](https://github.com/FasterXML/jackson-module-kotlin). + + + + + + + + ```kotlin + implementation("io.github.jan-tennert.supabase:serializer-jackson:VERSION") + ``` + + + + + ```groovy + implementation 'io.github.jan-tennert.supabase:serializer-jackson:VERSION' + ``` + + + + + ```xml + + io.github.jan-tennert.supabase + serializer-jackson + VERSION + + ``` + + + + ```kotlin + val client = createSupabaseClient(supabaseUrl, supabaseKey) { + defaultSerializer = JacksonSerializer() + } + ``` + + + + + + + + Use custom serializer. + + + + + ```kotlin + class CustomSerializer: SupabaseSerializer { + + override fun encode(type: KType, value: T): String { + //encode value to string + } + + override fun decode(type: KType, value: String): T { + //decode value + } + + } + ``` + + ```kotlin + val client = createSupabaseClient(supabaseUrl, supabaseKey) { + defaultSerializer = CustomSerializer() + } ``` diff --git a/apps/docs/docs/ref/kotlin/introduction.mdx b/apps/docs/docs/ref/kotlin/introduction.mdx index 36be6dd7a89..7f1f3784175 100644 --- a/apps/docs/docs/ref/kotlin/introduction.mdx +++ b/apps/docs/docs/ref/kotlin/introduction.mdx @@ -18,15 +18,28 @@ This reference documents every object and method available in Supabase's Kotlin Supported Kotlin targets: -- JVM -- Android -- iOS (Experimental) -- JS (Browser) +| **Module** | **GoTrue** | **Realtime** | **Postgrest** | **Storage** | **Functions** | **Apollo-GraphQL** | +| -------------------------------------- | ---------- | ------------ | ------------- | ----------- | ------------- | ------------------ | +| **JVM** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| **Android** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| **JS** _(Browser)_ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| **IOS** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| **MacOS** _(macosX64 & macosArm64)_ 🚧 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| **Windows** _(mingwX64)_ 🚧 | ☑️ | ✅ | ✅ | ✅ | ✅ | ❌ | +| **Linux** _(linuxX64)_ 🚧 | ☑️ | ✅ | ✅ | ✅ | ✅ | ❌ | + +✅ = full support + +☑️ = partial support: no built-in OAuth/OTP link handling. Linux also has no persistent storage. + +🚧 = experimental/needs feedback + +❌ = not supported
-

The Kotlin client library is created and maintained by the Supabase community, and is not an official library. Please be tolerant of areas where the library is still being developed, and — as with all the libraries — feel free to contribute wherever you find issues.

+

The Kotlin client library is created and maintained by the Supabase community, and is not an official library. Please be tolerant of areas where the library is still being developed, and — as with all the libraries — feel free to contribute wherever you find issues.

Huge thanks to official maintainer, [jan-tennert](https://github.com/jan-tennert).

diff --git a/apps/docs/docs/ref/self-hosting-analytics/introduction.mdx b/apps/docs/docs/ref/self-hosting-analytics/introduction.mdx index ccf1ee1c932..a1cb8d81c9a 100644 --- a/apps/docs/docs/ref/self-hosting-analytics/introduction.mdx +++ b/apps/docs/docs/ref/self-hosting-analytics/introduction.mdx @@ -122,6 +122,10 @@ Download your Google Cloud API JWT and store it under gcloud.json in your workin required. +### Storage Location + +Currently, all BigQuery datasets stored and managed by Analytics, whether via CLI or self-hosted, will default to the US region. + ## Production Recommendations To self-host in a production setting, we recommend performing the following for a better experience. diff --git a/apps/docs/docs/reference/api.mdx b/apps/docs/docs/reference/api.mdx index 371041af0bd..70bb75ee9f6 100644 --- a/apps/docs/docs/reference/api.mdx +++ b/apps/docs/docs/reference/api.mdx @@ -15,7 +15,7 @@ The Management API is in `beta`. It is usable in it's current state, but it's li ## Authentication All API requests require a Supabase Personal token to be included in the Authorization header: `Authorization Bearer + + +Emdeddings of 1536 dimensions by OpenAI for dbpedia dataset with 1,000,000 vectors. + +| Plan | Vectors | Lists | RPS | 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 | +| Medium | 100,000 | 200 | 130 | 0.383 sec | 0.446 sec | 3.7 GB | 4 GB | +| Large | 250,000 | 500 | 130 | 0.378 sec | 0.434 sec | 7 GB | 8 GB | +| XL | 500,000 | 1000 | 235 | 0.213 sec | 0.271 sec | 13.5 GB | 16 GB | +| 2XL | 1,000,000 | 2000 | 380 | 0.133 sec | 0.236 sec | 30 GB | 32 GB | +| 4XL | 1,000,000 | 2000 | 720 | 0.068 sec | 0.120 sec | 35 GB | 64 GB | +| 8XL | 1,000,000 | 2000 | 1250 | 0.039 sec | 0.066 sec | 38 GB | 128 GB | +| 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. + + + + +Emdeddings of 1536 dimensions by OpenAI for dbpedia dataset with 1,000,000 vectors. + +| Plan | Vectors | Lists | RPS | Latency Mean | Latency p95 | RAM Usage | RAM | +| ------ | --------- | ----- | --- | ------------ | ----------- | --------- | ------ | +| Free | 20,000 | 40 | - | - | - | - | 1 GB | +| Small | 50,000 | 100 | - | - | - | - | 2 GB | +| Medium | 100,000 | 200 | - | - | - | - | 4 GB | +| Large | 250,000 | 500 | - | - | - | - | 8 GB | +| XL | 500,000 | 1000 | - | - | - | - | 16 GB | +| 2XL | 1,000,000 | 2000 | 140 | 0.358 sec | 0.575 sec | 30 GB | 32 GB | +| 4XL | 1,000,000 | 2000 | 270 | 0.186 sec | 0.304 sec | 35 GB | 64 GB | +| 8XL | 1,000,000 | 2000 | 470 | 0.104 sec | 0.166 sec | 38 GB | 128 GB | +| 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. + + + + +Emdeddings of 960 dimensions from gist-960 dataset with 1,000,000 vectors. + +| 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 | + + + + +Emdeddings of 512 dimensions from GloVe Reddit comments dataset with 1,623,397 vectors. + +| 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 | + +Random vectors were generated for queries. + + + + +Emdeddings of 512 dimensions from GloVe Reddit comments dataset with 1,623,397 vectors. + +| 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 | + +Random vectors were generated for queries. + + + + + + +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. + + + +## Methodology + +We follow techniques outlined in the [ANN Benchmarks](https://github.com/erikbern/ann-benchmarks) methodology. A Python test runner is responsible for uploading the data, creating the index, and running the queries. The pgvector engine is implemented using [vecs](https://github.com/supabase/vecs), a Python client for pgvector. + +
+ multi database + multi database +
+ +Each test is run for a minimum of 30-40 minutes. They include a series of experiments executed at different concurrency levels to measure the engine's performance under different load types. The results are then averaged. + +As a general recommendation, we suggest using a concurrency level of 5 or more for most workloads and 30 or more for high-load workloads. + +export const Page = ({ children }) => + +export default Page diff --git a/apps/docs/pages/guides/ai/choosing-instance-type.mdx b/apps/docs/pages/guides/ai/choosing-instance-type.mdx deleted file mode 100644 index c6bb503c4aa..00000000000 --- a/apps/docs/pages/guides/ai/choosing-instance-type.mdx +++ /dev/null @@ -1,79 +0,0 @@ -import Layout from '~/layouts/DefaultGuideLayout' - -export const meta = { - id: 'ai-choosing-instance-type', - title: 'Choosing Instance Type', - description: 'Choosing the right instance type for your workload.', - subtitle: 'Choosing the right instance type for your workload.', - sidebar_label: 'Choosing Instance Type', -} - -This guide will help you choose the right instance type for your workload. We'll provide general guidance, as it is impossible to provide specific instructions for every possible use case. The goal is to give you a starting point from which you can make your own benchmarks and optimizations. - -For more information about engineering at scale, see our [Engineering for Scale](/docs/guides/ai/engineering-for-scale) guide. - -## Simple workloads - -We've run a set of benchmarks using the [gist-960-angular](http://corpus-texmex.irisa.fr/) dataset. This dataset contains 1,000,000 embeddings for images, with each embedding being 960 dimensions. - -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 instance types: - -### Results - -The number of vectors in `gist-960-angular` was cut to fit the instance size. - -| Plan | CPU | Memory | Vectors | RPS | Latency Mean | Latency p95 | CPU Usage - Max % | Memory Usage - Max | -| ------ | ------ | ------ | ------- | --- | ------------ | ----------- | ----------------- | ------------------ | -| Free | 2-core | 1 GB | 30,000 | 75 | 0.065 sec | 0.088 sec | 90% | 1 GB + 100 Mb Swap | -| Small | 2-core | 2 GB | 100,000 | 78 | 0.064 sec | 0.092 sec | 80% | 1.8 GB | -| Medium | 2-core | 4 GB | 250,000 | 58 | 0.085 sec | 0.129 sec | 90% | 3.2 GB | -| Large | 2-core | 8 GB | 500,000 | 55 | 0.088 sec | 0.140 sec | 90% | 5 GB | - -The full number of vectors in `gist-960-angular` dataset - 1,000,000. - -| Plan | CPU | Memory | Vectors | RPS | Latency Mean | Latency p95 | CPU Usage - Max % | Memory Usage - Max | -| ---- | ------- | ------ | --------- | ---- | ------------ | ----------- | ----------------- | ------------------ | -| XL | 4-core | 16 GB | 1,000,000 | 110 | 0.046 sec | 0.070 sec | 45% | 14 GB | -| 2XL | 8-core | 32 GB | 1,000,000 | 235 | 0.083 sec | 0.136 sec | 33% | 10 GB | -| 4XL | 16-core | 64 GB | 1,000,000 | 420 | 0.071 sec | 0.106 sec | 45% | 11 GB | -| 8XL | 32-core | 128 GB | 1,000,000 | 815 | 0.072 sec | 0.106 sec | 75% | 13 GB | -| 12XL | 48-core | 192 GB | 1,000,000 | 1150 | 0.052 sec | 0.078 sec | 70% | 15.5 GB | -| 16XL | 64-core | 256 GB | 1,000,000 | 1345 | 0.072 sec | 0.106 sec | 60% | 17.5 GB | - -- Lists set to `Number of vectors / 1000` -- Probes set to `10` - - - -It is possible to upload more than 1,000,000 vectors to a single table if Memory allows it (for example, 2XL instance and higher). 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 instance. - - - -## Methodology - -We follow techniques outlined in the [ANN Benchmarks](https://github.com/erikbern/ann-benchmarks) methodology. A Python test runner is responsible for uploading the data, creating the index, and running the queries. The pgvector engine is implemented using [vecs](https://github.com/supabase/vecs), a Python client for pgvector. - -
- multi database - multi database -
- -Each test is run for a minimum of 30-40 minutes. They include a series of experiments executed at different concurrency levels to measure the engine's performance under different load types. The results are then averaged. - -As a general recommendation, we suggest using a concurrency level of 5 or more for most workloads and 30 or more for high-load workloads. - -## Future benchmarks - -We'll continue to add more benchmarks on datasets consisting of different vector dimensions, number of `lists` in the index, and number of `probes` in the index. Stay tuned for more information about how it may affect the performance and precision of your queries. - -export const Page = ({ children }) => - -export default Page diff --git a/apps/docs/pages/guides/ai/examples/headless-vector-search.mdx b/apps/docs/pages/guides/ai/examples/headless-vector-search.mdx index 467d3788a38..31cabb29dcf 100644 --- a/apps/docs/pages/guides/ai/examples/headless-vector-search.mdx +++ b/apps/docs/pages/guides/ai/examples/headless-vector-search.mdx @@ -37,7 +37,7 @@ There are 3 steps to build similarity search inside your documentation: ### Prepare your database -To prepare, create a [new Supabase project](https://database.new) and store the database and API credentials, which you can find in the project [settings](https://app.supabase.com/_/settings). +To prepare, create a [new Supabase project](https://database.new) and store the database and API credentials, which you can find in the project [settings](https://supabase.com/dashboard/_/settings). Now we can use the [Headless Vector Search](https://github.com/supabase/headless-vector-search#set-up) instructions to set up the database: @@ -46,7 +46,7 @@ Now we can use the [Headless Vector Search](https://github.com/supabase/headless 3. Apply the database migrations: `supabase db push` 4. Set your OpenAI key as a secret: `supabase secrets set OPENAI_KEY=sk-xxx` 5. Deploy the Edge Functions: `supabase functions deploy --no-verify-jwt` -6. Expose `docs` schema via API in Supabase Dashboard [settings](https://app.supabase.com/project/_/settings/api) > `API Settings` > `Exposed schemas` +6. Expose `docs` schema via API in Supabase Dashboard [settings](https://supabase.com/dashboard/project/_/settings/api) > `API Settings` > `Exposed schemas` ### Ingest your documentation diff --git a/apps/docs/pages/guides/ai/examples/huggingface-image-captioning.mdx b/apps/docs/pages/guides/ai/examples/huggingface-image-captioning.mdx index 1f891655d85..163422ee325 100644 --- a/apps/docs/pages/guides/ai/examples/huggingface-image-captioning.mdx +++ b/apps/docs/pages/guides/ai/examples/huggingface-image-captioning.mdx @@ -20,15 +20,15 @@ We can combine Hugging Face with [Supabase Storage](https://supabase.com/storage ## Setup -- Open your Supabase project dashboard or [create a new project](https://app.supabase.com/projects). -- [Create a new bucket](https://app.supabase.com/project/_/storage/buckets) called `images`. +- Open your Supabase project dashboard or [create a new project](https://supabase.com/dashboard/projects). +- [Create a new bucket](https://supabase.com/dashboard/project/_/storage/buckets) called `images`. - Generate TypeScript types from remote Database. - Create a new Database table called `image_caption`. - Create `id` column of type `uuid` which references `storage.objects.id`. - Create a `caption` column of type `text`. - Regenerate TypeScript types to include new `image_caption` table. - Deploy the function to Supabase: `supabase functions deploy huggingface-image-captioning`. -- Create the Database Webhook in the [Supabase Dashboard](https://app.supabase.com/project/_/database/hooks) to trigger the `huggingface-image-captioning` function anytime a record is added to the `storage.objects` table. +- Create the Database Webhook in the [Supabase Dashboard](https://supabase.com/dashboard/project/_/database/hooks) to trigger the `huggingface-image-captioning` function anytime a record is added to the `storage.objects` table. ## Generate TypeScript Types diff --git a/apps/docs/pages/guides/ai/examples/nextjs-vector-search.mdx b/apps/docs/pages/guides/ai/examples/nextjs-vector-search.mdx index b340a6607b6..017eb68ec95 100644 --- a/apps/docs/pages/guides/ai/examples/nextjs-vector-search.mdx +++ b/apps/docs/pages/guides/ai/examples/nextjs-vector-search.mdx @@ -22,13 +22,13 @@ We assume that you have a Next.js project with a collection of `.mdx` files nest ## Create a project -1. [Create a new project](https://app.supabase.com) in the Supabase Dashboard. +1. [Create a new project](https://supabase.com/dashboard) in the Supabase Dashboard. 1. Enter your project details. 1. Wait for the new database to launch. ## Prepare the database -Let's prepare the database schema. We can use the "OpenAI Vector Search" quickstart in the [SQL Editor](https://app.supabase.com/project/_/sql), or you can copy/paste the SQL below and run it yourself. +Let's prepare the database schema. We can use the "OpenAI Vector Search" quickstart in the [SQL Editor](https://supabase.com/dashboard/project/_/sql), or you can copy/paste the SQL below and run it yourself. -1. Go to the [SQL Editor](https://app.supabase.com/project/_/sql) page in the Dashboard. +1. Go to the [SQL Editor](https://supabase.com/dashboard/project/_/sql) page in the Dashboard. 2. Click **OpenAI Vector Search**. 3. Click **Run**. diff --git a/apps/docs/pages/guides/ai/examples/openai.mdx b/apps/docs/pages/guides/ai/examples/openai.mdx index 7094a8fc936..d2462d371c9 100644 --- a/apps/docs/pages/guides/ai/examples/openai.mdx +++ b/apps/docs/pages/guides/ai/examples/openai.mdx @@ -5,7 +5,7 @@ export const meta = { title: 'Generating OpenAI GPT3 completions', description: 'Generate GPT text completions using OpenAI and Supabase Edge Functions.', subtitle: 'Generate GPT text completions using OpenAI and Supabase Edge Functions.', - video: 'https://www.youtube.com/v/29p8kIqyU_Y', + video: 'https://www.youtube-nocookie.com/v/29p8kIqyU_Y', tocVideo: '29p8kIqyU_Y', } diff --git a/apps/docs/pages/guides/ai/going-to-prod.mdx b/apps/docs/pages/guides/ai/going-to-prod.mdx new file mode 100644 index 00000000000..443e559d19a --- /dev/null +++ b/apps/docs/pages/guides/ai/going-to-prod.mdx @@ -0,0 +1,100 @@ +import Layout from '~/layouts/DefaultGuideLayout' + +export const meta = { + id: 'ai-going-to-prod', + title: 'Going to Production', + description: 'Checklist for going to production with your AI application.', + subtitle: 'Going to production checklist for AI applications.', + sidebar_label: 'Going to Production', +} + +This guide will help you to prepare your application for production. We'll provide actionable steps to help you scale your application, ensure that it is reliable, can handle the load, and provide optimal precision for your use case. + +See our [Engineering for Scale](/docs/guides/ai/engineering-for-scale) guide for more information about engineering at scale. + +## Do you need indexes? + +Sequential scans will result in significantly higher latencies and lower throughput, guaranteeing 100% precision and not being RAM bound. + +There are a couple of cases where you might not need indexes: + +- You have a small dataset and don't need to scale it. +- You are not expecting high amounts of vector search queries per second. +- You need to guarantee 100% precision. + +You don't have to create indexes in these cases and can use sequential scans instead. This type of workload will not be RAM bound and will not require any additional resources but will result in higher latencies and lower throughput. Extra CPU cores may help to improve queries per second, but it will not help to improve latency. + +On the other hand, if you need to scale your application, you will need to create indexes. This will result in lower latencies and higher throughput, but will require additional RAM to make use of Postgres Caching. Also, using indexes will result in lower precision, since you are replacing exact (KNN) search with approximate (ANN) search. + +## 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). + +- 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. + +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. + +
+ multi database + multi database +
+ +## Performance Tips when using indexes + +First, a few generic tips which you can pick and choose from: + +1. The Supabase managed platform will automatically optimize Postgres configs for you based on your compute addon. But if you self-host, consider **adjusting your Postgres config** based on RAM & CPU cores. See [example optimizations](https://gist.github.com/egor-romanov/323e2847851bbd758081511785573c08) for more details. +2. Prefer `inner-product` to `L2` or `Cosine` distances if your vectors are normalized (like `text-embedding-ada-002`). If embeddings are not normalized, `Cosine` distance should give the best results with an index. +3. **Pre-warm your database.** Implement the warm-up technique before transitioning to production or running benchmarks. + - Execute 10,000 to 50,000 "warm-up" queries before each benchmark, matching the number of `probes` you are going to use in production. Additionally, you can execute about 1,000 queries with probes ranging from three to ten times the prod's probes. Both of these help to increase RAM utilization. +4. **Establish your workload.** Increasing the lists constant for the pgvector index can accelerate your queries (at the expense of a slower build). For instance, for benchmarks with 1,000,000 embeddings, we employed a `lists` constant of 2000 (`number of vectors / 500`) as opposed to the suggested 1000 (`number of vectors / 1000`). +5. **Benchmark your own specific workloads.** Doing this during cache warm-up helps gauge the best value for the `probes` constant, balancing precision with requests per second (RPS). + +## Going into production + +1. Decide if you are going to use indexes or not. You can skip the rest of this guide if you do not use indexes. +2. Over-provision RAM during preparation. You can scale down in step `5`, but it's better to start with a larger size to get the best results for RAM requirements. (We'd recommend at least 8XL if you're using Supabase.) +3. Upload your data to the database. If you use the [`vecs`](/docs/guides/ai/python/api) library, it will automatically generate an index with default parameters. +4. Run a benchmark using randomly generated queries and observe the results. Again, you can use the `vecs` library with the `ann-benchmarks` tool. Do it with probes set to 10 (default) and then with probes set to 100 or more, so RPS will be lower than 10. +5. Monitor the RAM usage, and save it as a note for yourself. You would likely want to use a compute add-on in the future that has the same amount of RAM that was used at the moment (both actual RAM usage and RAM used for cache and buffers). +6. Scale down your compute add-on to the one that would have the same amount of RAM used at the moment. +7. Repeat step 3 to load the data into RAM. You should see RPS increase on subsequent runs, and stop when it no longer increases. Then repeat the benchmark with probes set to a higher value if you haven't already performed it for that compute add-on size. +8. Run a benchmark using real queries and observe the results. You can use the `vecs` library for that as well with `ann-benchmarks` tool. Set probes to 10 (default) and then gradually increase/decrease probes until you see that both precision and RPS match your requirements. +9. If you want higher RPS and you don't expect to have frequent inserts and reindexing, you can increase `lists` constantly. You have to rebuild the index with a higher lists value and repeat steps 6-7 to find the best combination of `lists` and `probes` constants to achieve the best RPS and precision values. Higher `lists` mean that index will build slower, but you can achieve better RPS and precision. Higher probes mean that select queries will be slower, but you can achieve better precision. + +## Useful links + +Don't forget to check out the general [Production Checklist](/docs/guides/platform/going-into-prod) to ensure your project is secure, performant, and will remain available for your users. + +You can look at our [Choosing Compute Add-on](/docs/guides/ai/choosing-compute-addon) guide to get a basic understanding of how much compute you might need for your workload. + +Or take a look at our [pgvector 0.4.0 performance](https://supabase.com/blog/pgvector-performance) blog post to see what pgvector is capable of and how the above technique can be used to achieve the best results. + +
+ multi database + multi database +
+ +export const Page = ({ children }) => + +export default Page diff --git a/apps/docs/pages/guides/ai/google-colab.mdx b/apps/docs/pages/guides/ai/google-colab.mdx index c3e02029842..d6edee7ba26 100644 --- a/apps/docs/pages/guides/ai/google-colab.mdx +++ b/apps/docs/pages/guides/ai/google-colab.mdx @@ -17,7 +17,7 @@ export const meta = { Google Colab is a hosted Jupyter Notebook service. It provides free access to computing resources, including GPUs and TPUs, and is well-suited to machine learning, data science, and education. We can use Colab to manage collections using [Supabase Vecs](/docs/guides/ai/vecs-python-client). -In this tutorial we'll connect to a database running on the Supabase [platform](https://app.supabase.com/). If you don't already have a database, you can create one here: [database.new](https://database.new). +In this tutorial we'll connect to a database running on the Supabase [platform](https://supabase.com/dashboard/). If you don't already have a database, you can create one here: [database.new](https://database.new). ## Create a new notebook @@ -39,7 +39,7 @@ pip install vecs ## Connect to your database -Find the Postgres connection string for your Supabase project in the [database settings](https://app.supabase.com/_/settings/database) of the dashboard. Copy the "URI" format, which should look something like `postgresql:/postgres:@:5432/postgres` +Find the Postgres connection string for your Supabase project in the [database settings](https://supabase.com/dashboard/_/settings/database) of the dashboard. Copy the "URI" format, which should look something like `postgresql:/postgres:@:5432/postgres` Create a new code block below the install block (`ctrl+m b`) and add the following code using the Postgres URI you copied above: @@ -79,7 +79,7 @@ collection.upsert( ) ``` -This will create a table inside your database within the `vecs` schema, called `colab_collection`. You can view the inserted items in the [Table Editor](https://app.supabase.com/project/_/editor/), by selecting the `vecs` schema from the schema dropdown. +This will create a table inside your database within the `vecs` schema, called `colab_collection`. You can view the inserted items in the [Table Editor](https://supabase.com/dashboard/project/_/editor/), by selecting the `vecs` schema from the schema dropdown. ![Colab documents](/docs/img/ai/google-colab/colab-documents.png) diff --git a/apps/docs/pages/guides/ai/integrations/llamaindex.mdx b/apps/docs/pages/guides/ai/integrations/llamaindex.mdx index 59db32b9cd3..b1aa7f80b2a 100644 --- a/apps/docs/pages/guides/ai/integrations/llamaindex.mdx +++ b/apps/docs/pages/guides/ai/integrations/llamaindex.mdx @@ -52,7 +52,7 @@ Replace the `DB_CONNECTION` with your own connection string for your database, w Now all that's left is to step through the notebook. You can do this by clicking the "execute" button (`ctrl+enter`) at the top left of each code cell. The notebook guides you through the process of creating a collection, adding data to it, and querying it. -You can view the inserted items in the [Table Editor](https://app.supabase.com/project/_/editor/), by selecting the `vecs` schema from the schema dropdown. +You can view the inserted items in the [Table Editor](https://supabase.com/dashboard/project/_/editor/), by selecting the `vecs` schema from the schema dropdown. ![Colab documents](/docs/img/ai/google-colab/colab-documents.png) diff --git a/apps/docs/pages/guides/ai/langchain.mdx b/apps/docs/pages/guides/ai/langchain.mdx index 3f66da36b3e..fa8320819a2 100644 --- a/apps/docs/pages/guides/ai/langchain.mdx +++ b/apps/docs/pages/guides/ai/langchain.mdx @@ -1,4 +1,6 @@ import Layout from '~/layouts/DefaultGuideLayout' +import { Tabs } from 'ui' +export const TabPanel = Tabs.Panel export const meta = { id: 'ai-lang-chain', @@ -14,6 +16,21 @@ export const meta = { Prepare you database with the relevant tables: + + + +1. Go to the [SQL Editor](https://supabase.com/dashboard/project/_/sql) page in the Dashboard. +2. Click **LangChain** in the Quick start section. +3. Click **Run**. + + + + ```sql -- Enable the pgvector extension to work with embedding vectors create extension vector; @@ -55,6 +72,9 @@ end; $$; ``` + + + ## Usage You can now search your documents using any Node.js application. This is intended to be run on a secure server route. diff --git a/apps/docs/pages/guides/ai/managing-collections.mdx b/apps/docs/pages/guides/ai/managing-collections.mdx index 58e700e2927..5ed4d45ce42 100644 --- a/apps/docs/pages/guides/ai/managing-collections.mdx +++ b/apps/docs/pages/guides/ai/managing-collections.mdx @@ -9,7 +9,7 @@ export const meta = { A collection is a group of vector records managed by the `vecs` Python library. Records can be added to or updated in a collection. Collections can be queried at any time, but should be indexed for scalable query performance. -Supabase provides a [Python client](/docs/guides/ai/vecs-python-client) called `vecs` for managing unstructured vector stores in Postgres. If you come from a data science background, this unstructured data approach will feel familiar. If you are more interested in a structured data approach, see [Vector columns](/docs/guides/ai/vector-columns) or read our guide on [Structured & Unstructured Embeddings](/docs/guides/ai/structured-unstructured-embeddings). +Supabase provides a [Python client](/docs/guides/ai/vecs-python-client) called `vecs` for managing unstructured vector stores in Postgres. If you come from a data science background, this unstructured data approach will feel familiar. If you are more interested in a structured data approach, see [Vector columns](/docs/guides/ai/vector-columns) or read our guide on [Structured & Unstructured Embeddings](/docs/guides/ai/structured-unstructured). Under the hood `vecs` will manage the necessary Postgres tables and columns to store and query your collections. diff --git a/apps/docs/pages/guides/ai/quickstarts/face-similarity.mdx b/apps/docs/pages/guides/ai/quickstarts/face-similarity.mdx index 6996f4c1046..160ed479b6d 100644 --- a/apps/docs/pages/guides/ai/quickstarts/face-similarity.mdx +++ b/apps/docs/pages/guides/ai/quickstarts/face-similarity.mdx @@ -49,7 +49,7 @@ Replace the `DB_CONNECTION` with your own connection string for your database, w Now all that's left is to step through the notebook. You can do this by clicking the "execute" button (`ctrl+enter`) at the top left of each code cell. The notebook guides you through the process of creating a collection, adding data to it, and querying it. -You can view the inserted items in the [Table Editor](https://app.supabase.com/project/_/editor/), by selecting the `vecs` schema from the schema dropdown. +You can view the inserted items in the [Table Editor](https://supabase.com/dashboard/project/_/editor/), by selecting the `vecs` schema from the schema dropdown. ![Colab documents](/docs/img/ai/google-colab/colab-documents.png) diff --git a/apps/docs/pages/guides/ai/quickstarts/hello-world.mdx b/apps/docs/pages/guides/ai/quickstarts/hello-world.mdx index 7b678dc1d20..d612ed23d86 100644 --- a/apps/docs/pages/guides/ai/quickstarts/hello-world.mdx +++ b/apps/docs/pages/guides/ai/quickstarts/hello-world.mdx @@ -49,7 +49,7 @@ Replace the `DB_CONNECTION` with your own connection string for your database, w Now all that's left is to step through the notebook. You can do this by clicking the "execute" button (`ctrl+enter`) at the top left of each code cell. The notebook guides you through the process of creating a collection, adding data to it, and querying it. -You can view the inserted items in the [Table Editor](https://app.supabase.com/project/_/editor/), by selecting the `vecs` schema from the schema dropdown. +You can view the inserted items in the [Table Editor](https://supabase.com/dashboard/project/_/editor/), by selecting the `vecs` schema from the schema dropdown. ![Colab documents](/docs/img/ai/google-colab/colab-documents.png) diff --git a/apps/docs/pages/guides/ai/quickstarts/text-deduplication.mdx b/apps/docs/pages/guides/ai/quickstarts/text-deduplication.mdx index 8d20cf9cca4..d15cbbd1289 100644 --- a/apps/docs/pages/guides/ai/quickstarts/text-deduplication.mdx +++ b/apps/docs/pages/guides/ai/quickstarts/text-deduplication.mdx @@ -50,7 +50,7 @@ Replace the `DB_CONNECTION` with your own connection string for your database, w Now all that's left is to step through the notebook. You can do this by clicking the "execute" button (`ctrl+enter`) at the top left of each code cell. The notebook guides you through the process of creating a collection, adding data to it, and querying it. -You can view the inserted items in the [Table Editor](https://app.supabase.com/project/_/editor/), by selecting the `vecs` schema from the schema dropdown. +You can view the inserted items in the [Table Editor](https://supabase.com/dashboard/project/_/editor/), by selecting the `vecs` schema from the schema dropdown. ![Colab documents](/docs/img/ai/google-colab/colab-documents.png) diff --git a/apps/docs/pages/guides/ai/structured-unstructured.mdx b/apps/docs/pages/guides/ai/structured-unstructured.mdx index 0c392c20e09..04b35b5b4cb 100644 --- a/apps/docs/pages/guides/ai/structured-unstructured.mdx +++ b/apps/docs/pages/guides/ai/structured-unstructured.mdx @@ -23,7 +23,7 @@ create table docs ( ); insert into docs - (id, content, url, embedding) + (id, embedding, content, url) values ('79409372-7556-4ccc-ab8f-5786a6cfa4f7', array[0.1, 0.2, 0.3], 'Hello world', '/hello-world'); ``` @@ -89,7 +89,7 @@ create table docs ( ); insert into docs - (id, embedding, meta) + (id, embedding, content, url, meta) values ( '79409372-7556-4ccc-ab8f-5786a6cfa4f7', diff --git a/apps/docs/pages/guides/ai/vector-columns.mdx b/apps/docs/pages/guides/ai/vector-columns.mdx index d941c164302..7c768606e0b 100644 --- a/apps/docs/pages/guides/ai/vector-columns.mdx +++ b/apps/docs/pages/guides/ai/vector-columns.mdx @@ -23,7 +23,7 @@ Vectors in Supabase are enabled via [pgvector](https://github.com/pgvector/pgvec > -1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard. +1. Go to the [Database](https://supabase.com/dashboard/project/_/database/tables) page in the Dashboard. 2. Click on **Extensions** in the sidebar. 3. Search for "vector" and enable the extension. diff --git a/apps/docs/pages/guides/api.mdx b/apps/docs/pages/guides/api.mdx index 7ca9052585d..5ac4bc65ba3 100644 --- a/apps/docs/pages/guides/api.mdx +++ b/apps/docs/pages/guides/api.mdx @@ -50,7 +50,7 @@ Reference: ## GraphQL API [#graphql-api-overview] Supabase uses [pg_graphql](https://supabase.github.io/pg_graphql/) to expose a GraphQL API endpoint at `https://.supabase.co/graphql/v1/`. -You can introspect and query the GraphQL API of an existing Supabase project within Studio [here](https://app.supabase.com/project/_/api/graphiql), +You can introspect and query the GraphQL API of an existing Supabase project within Studio [here](https://supabase.com/dashboard/project/_/api/graphiql), or navigate there manually at `API Docs > GraphQL > GraphiQL`. The GraphQL interface is automatically reflected from your database's schema and supports: @@ -72,11 +72,11 @@ Reference: Supabase provides a Realtime API using [Realtime](https://github.com/supabase/realtime). You can use this to listen to database changes over websockets. Realtime leverages PostgreSQL's built-in logical replication. You can manage your Realtime API simply by managing Postgres publications. -Go to your project's [Replication section](https://app.supabase.com/project/_/database/replication) to get started. +Go to your project's [Replication section](https://supabase.com/dashboard/project/_/database/replication) to get started. ## API URL and Keys -You can find the API URL and Keys in the [Dashboard](https://app.supabase.com/project/_/settings/api). +You can find the API URL and Keys in the [Dashboard](https://supabase.com/dashboard/project/_/settings/api).
Accept the invite + +``` +When the user clicks on the link, the request will hit `https://api.example.com/v1/authenticate` and you can grab the `token_hash` and `type` query parameters from the URL. Then, you can call the [`verifyOtp`](/docs/reference/javascript/auth-verifyotp) method to get back an authenticated session before redirecting the user back to the client. Since the `verifyOtp` method makes a `POST` request to Supabase Auth to verify the user, the session will be returned in the response body, which can be read by the server. For example: + +```js +const { token_hash, type } = Object.fromEntries(new URLSearchParams(window.location.search)) +const { data: { session }, error } = supabase.auth.verifyOtp({ token_hash, type }) + +// subsequently redirect the user back to the client +// ... + +``` + export const Page = ({ children }) => export default Page diff --git a/apps/docs/pages/guides/auth/auth-email.mdx b/apps/docs/pages/guides/auth/auth-email.mdx index ce29b1e970b..2aa114b48a2 100644 --- a/apps/docs/pages/guides/auth/auth-email.mdx +++ b/apps/docs/pages/guides/auth/auth-email.mdx @@ -10,13 +10,13 @@ export const meta = { Setting up Email logins for your Supabase application. -- Add Email authenticator to your [Supabase Project](https://app.supabase.com) +- Add Email authenticator to your [Supabase Project](https://supabase.com/dashboard) - Add the login code to your application - [JavaScript](https://github.com/supabase/supabase-js) | [Flutter](https://github.com/supabase/supabase-flutter) ## Configure email settings -1. For [Site URL](https://app.supabase.com/project/_/auth/url-configuration), enter the final (hosted) URL of your app. -1. For [Auth Providers](https://app.supabase.com/project/_/auth/providers), **enable email provider**. +1. For [Site URL](https://supabase.com/dashboard/project/_/auth/url-configuration), enter the final (hosted) URL of your app. +1. For [Auth Providers](https://supabase.com/dashboard/project/_/auth/providers), **enable email provider**. diff --git a/apps/docs/pages/guides/auth/auth-helpers/nextjs-pages.mdx b/apps/docs/pages/guides/auth/auth-helpers/nextjs-pages.mdx index bfadfe16dd3..fc23df156a4 100644 --- a/apps/docs/pages/guides/auth/auth-helpers/nextjs-pages.mdx +++ b/apps/docs/pages/guides/auth/auth-helpers/nextjs-pages.mdx @@ -31,7 +31,7 @@ npm install @supabase/auth-helpers-react ## Set up environment variables -Retrieve your project URL and anon key in your project's [API settings](https://app.supabase.com/project/_/settings/api) in the Dashboard to set up the following environment variables. For local development you can set them in a `.env.local` file. See an [example](https://github.com/supabase/auth-helpers/blob/main/examples/nextjs/.env.local.example). +Retrieve your project URL and anon key in your project's [API settings](https://supabase.com/dashboard/project/_/settings/api) in the Dashboard to set up the following environment variables. For local development you can set them in a `.env.local` file. See an [example](https://github.com/supabase/auth-helpers/blob/main/examples/nextjs/.env.local.example). ```bash .env.local NEXT_PUBLIC_SUPABASE_URL=your-supabase-url diff --git a/apps/docs/pages/guides/auth/auth-helpers/nextjs.mdx b/apps/docs/pages/guides/auth/auth-helpers/nextjs.mdx index 892c0a49373..c66d2e48e4c 100644 --- a/apps/docs/pages/guides/auth/auth-helpers/nextjs.mdx +++ b/apps/docs/pages/guides/auth/auth-helpers/nextjs.mdx @@ -43,7 +43,7 @@ This creates a new Next.js app configured with: ### Declare Environment Variables -Retrieve your project's URL and anon key from your [API settings](https://app.supabase.com/project/_/settings/api), and create a `.env.local` file with the following environment variables: +Retrieve your project's URL and anon key from your [API settings](https://supabase.com/dashboard/project/_/settings/api), and create a `.env.local` file with the following environment variables: ```bash .env.local NEXT_PUBLIC_SUPABASE_URL=your-supabase-url @@ -68,7 +68,7 @@ npm install @supabase/auth-helpers-nextjs @supabase/supabase-js ### Declare Environment Variables -Retrieve your project's URL and anon key from your [API settings](https://app.supabase.com/project/_/settings/api), and create a `.env.local` file with the following environment variables: +Retrieve your project's URL and anon key from your [API settings](https://supabase.com/dashboard/project/_/settings/api), and create a `.env.local` file with the following environment variables: ```bash .env.local NEXT_PUBLIC_SUPABASE_URL=your-supabase-url @@ -79,7 +79,7 @@ NEXT_PUBLIC_SUPABASE_ANON_KEY=your-supabase-anon-key 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. -In Next.js Server Components, you can read a cookie, but you can't write back to it. Middleware on the other hand, allow you to both read a write to cookies. +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. @@ -528,7 +528,7 @@ This allows for the Supabase client to be easily instantiated in the correct con
-
- -## Prerequisites - -Make sure you have these installed on your local machine: - -- [Docker Desktop](https://docs.docker.com/desktop/) -- [Supabase CLI](/docs/guides/cli) -- [Git](https://github.com/git-guides/install-git) - ## Log in to the Supabase CLI ```bash @@ -369,7 +354,7 @@ The last step is deploying these changes to a live Supabase project. ## Deploy your project -You've been developing your project locally, making changes to your tables via migrations. It's time to deploy your project to the Supabase Platform and start scaling up to millions of users! Head over to [Supabase](https://app.supabase.com) and create a new project to deploy to. +You've been developing your project locally, making changes to your tables via migrations. It's time to deploy your project to the Supabase Platform and start scaling up to millions of users! Head over to [Supabase](https://supabase.com/dashboard) and create a new project to deploy to. ### Link your project @@ -377,7 +362,7 @@ Associate your project with your remote project using [`supabase link`](/docs/re ```bash supabase link --project-ref -# You can get from your project's dashboard URL: https://app.supabase.com/project/ +# You can get from your project's dashboard URL: https://supabase.com/dashboard/project/ supabase db remote commit # Capture any changes that you have made to your remote database before you went through the steps above @@ -401,7 +386,7 @@ Deploy any local database migrations using [`db push`](/docs/reference/cli/usage supabase db push ``` -Visiting your live project on [Supabase](https://app.supabase.com), you'll see a new `employees` table, complete with the `department` column you added in the second migration above. +Visiting your live project on [Supabase](https://supabase.com/dashboard), you'll see a new `employees` table, complete with the `department` column you added in the second migration above. ### Deploy Edge Functions @@ -413,23 +398,38 @@ supabase functions deploy ### Use Auth locally -To use Auth locally, update your project's `supabase/config.toml` file that gets created after running `supabase init`. Add any providers you want, and set enabled to `true` +To use Auth locally, update your project's `supabase/config.toml` file that gets created after running `supabase init`. Add any providers you want, and set enabled to `true`. -```bash config.toml +```bash supabase/config.toml [auth.external.github] enabled = true -client_id = "" -secret = "" +client_id = "env($SUPABASE_AUTH_GITHUB_CLIENT_ID)" +secret = "env($SUPABASE_AUTH_GITHUB_SECRET)" +redirect_uri = "http://localhost:54321/auth/v1/callback" +``` + +As a best practice, any secret values should be loaded from environment variables. You can add them to `.env` file in your project's root directory for the CLI to automatically substitute them. + +```bash .env +SUPABASE_AUTH_GITHUB_CLIENT_ID="redacted" +SUPABASE_AUTH_GITHUB_SECRET="redacted" ``` For these changes to take effect, you need to run `supabase stop` and `supabase start` again. +If you have additional triggers or RLS policies defined on your `auth` schema, you can pull them as a migration file locally. + +```bash +supabase db remote commit --schema auth +``` + ### Enabling Local Logging Local logs rely on the Supabase Analytics Server. This can be enabled via the CLI configuration, and requires a Google Cloud project and BigQuery access. - The Google Cloud project must have billing enabled. Read more about this requirement + The Google Cloud project must have billing enabled. The dataset created and managed by Analytics + will be within the US. Read more about this requirement [here](https://supabase.com/docs/reference/self-hosting-analytics/introduction#bigquery). @@ -459,10 +459,9 @@ This will switch the logging drivers and will direct logs to the Analytics serve ## Limitations and considerations -The local development environment is not as feature-complete as the Supabase Platform. We're working towards parity between the hosted platform and the local environment. Here are some of the differences: +The local development environment is not as feature-complete as the Supabase Platform. Here are some of the differences: -- The Functions interface is coming soon. -- You cannot update your project settings in the Dashboard. This must be done using the CLI. +- You cannot update your project settings in the Dashboard. This must be done using the local config file. - The CLI version determines the local version of Studio used, so make sure you keep your local [Supabase CLI up to date](https://github.com/supabase/cli#getting-started). We're constantly adding new features and bug fixes. export const Page = ({ children }) => diff --git a/apps/docs/pages/guides/cli/managing-config.mdx b/apps/docs/pages/guides/cli/managing-config.mdx new file mode 100644 index 00000000000..1f93c9e52cf --- /dev/null +++ b/apps/docs/pages/guides/cli/managing-config.mdx @@ -0,0 +1,50 @@ +import Layout from '~/layouts/DefaultGuideLayout' + +export const meta = { + id: 'managing-config', + title: 'Managing config and secrets', + description: 'Managing local configuration using config.toml.', +} + +The Supabase CLI uses a `config.toml` file to manage local configuration. This file is located in the `supabase` directory of your project. + +## Config reference + +The `config.toml` file is automatically created when you run `supabase start`. + +There are a wide variety of options available, which can be found in the [CLI Config Reference](/docs/reference/cli/config). + +For example, to enable the "Apple" OAuth provider for local development, you can append the following information to `config.toml`: + +```toml +[auth.external.apple] +enabled = false +client_id = "" +secret = "" +redirect_uri = "" # Overrides the default auth redirectUrl. +``` + +## Using secrets inside config.toml + +You can reference environment variables within the `config.toml` file using the `env()` function. This will detect any values stored in an `.env` file at the root of your project directory. This is particularly useful for storing sensitive information like API keys, and any other values that you don't want to check into version control. + +For example, if your `.env` contained the following values: + +```bash +GITHUB_CLIENT_ID="" +GITHUB_SECRET="" +``` + +Then you would reference them inside of our `config.toml` like this: + +```toml +[auth.external.github] +enabled = true +client_id = "env(GITHUB_CLIENT_ID)" +secret = "env(GITHUB_SECRET)" +redirect_uri = "" # Overrides the default auth redirectUrl. +``` + +export const Page = ({ children }) => + +export default Page diff --git a/apps/docs/pages/guides/cli/managing-environments.mdx b/apps/docs/pages/guides/cli/managing-environments.mdx index c3e41a404c4..b20a4c5c5f1 100644 --- a/apps/docs/pages/guides/cli/managing-environments.mdx +++ b/apps/docs/pages/guides/cli/managing-environments.mdx @@ -3,37 +3,15 @@ import Layout from '~/layouts/DefaultGuideLayout' export const meta = { id: 'managing-environments', title: 'Managing Environments', - description: 'How to deploy Supabase schema changes with a CI / CD pipeline.', - video: 'https://www.youtube.com/v/rOLyOsBR1Uc', + description: 'Manage multiple environments using Database Migrations and GitHub Actions.', + subtitle: 'Manage multiple environments using Database Migrations and GitHub Actions.', + video: 'https://www.youtube-nocookie.com/v/rOLyOsBR1Uc', + tocVideo: 'rOLyOsBR1Uc', } -## Overview +This guide shows you how to set up your local Supabase development environment that integrates with GitHub Actions to automatically test and release schema changes to staging and production Supabase projects. -The Supabase CLI provides the tools you need to manage multiple environments. -This guide shows you how to set up your local Supabase development environment that integrates with GitHub Actions to automatically -test and release schema changes to staging and production Supabase projects. - -## Prerequisites - -Make sure you have these installed on your local machine: - -- [Docker Desktop](https://docs.docker.com/desktop/) -- [Supabase CLI](/docs/guides/cli) -- [Git](https://github.com/git-guides/install-git) - -To get started: - -- Create a [Supabase project](https://app.supabase.com) or use an existing one -- Initialize a local Git repository - -
- -
+![Deploy migration](/docs/img/guides/cli/cicd-github.png) ## Set up a local environment @@ -53,7 +31,7 @@ supabase link --project-ref $PROJECT_ID You can get your `$PROJECT_ID` from your project's dashboard URL: ``` -https://app.supabase.com/project/ +https://supabase.com/dashboard/project/ ``` If you're using an existing Supabase project, you might have made schema changes through the Dashboard. @@ -196,6 +174,7 @@ The Supabase CLI requires a few environment variables to run in non-interactive - `SUPABASE_ACCESS_TOKEN` is your personal access token - `SUPABASE_DB_PASSWORD` is your project specific database password +- `SUPABASE_PROJECT_ID` is your project specific reference string We recommend adding these as [encrypted secrets](https://docs.github.com/en/actions/security-guides/encrypted-secrets) to your GitHub Actions runners. @@ -218,19 +197,22 @@ on: jobs: test: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest + steps: - uses: actions/checkout@v3 - uses: supabase/setup-cli@v1 + with: + version: latest - name: Start Supabase local development setup run: supabase start - - name: Verify generated types are up-to-date + - name: Verify generated types are checked in run: | - supabase gen types typescript --local > types.ts - if [ "$(git diff --ignore-space-at-eol types.ts | wc -l)" -gt "0" ]; then + supabase gen types typescript --local > types.gen.ts + if ! git diff --ignore-space-at-eol --exit-code --quiet types.gen.ts; then echo "Detected uncommitted changes after build. See status below:" git diff exit 1 @@ -251,21 +233,22 @@ on: jobs: deploy: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest env: SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }} SUPABASE_DB_PASSWORD: ${{ secrets.STAGING_DB_PASSWORD }} - STAGING_PROJECT_ID: abcdefghijklmnopqrst + SUPABASE_PROJECT_ID: ${{ secrets.STAGING_PROJECT_ID }} steps: - uses: actions/checkout@v3 - uses: supabase/setup-cli@v1 + with: + version: latest - - run: | - supabase link --project-ref $STAGING_PROJECT_ID - supabase db push + - run: supabase link --project-ref $SUPABASE_PROJECT_ID + - run: supabase db push ``` @@ -282,21 +265,22 @@ on: jobs: deploy: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest env: SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }} SUPABASE_DB_PASSWORD: ${{ secrets.PRODUCTION_DB_PASSWORD }} - PRODUCTION_PROJECT_ID: abcdefghijklmnopqrst + SUPABASE_PROJECT_ID: ${{ secrets.PRODUCTION_PROJECT_ID }} steps: - uses: actions/checkout@v3 - uses: supabase/setup-cli@v1 + with: + version: latest - - run: | - supabase link --project-ref $PRODUCTION_PROJECT_ID - supabase db push + - run: supabase link --project-ref $SUPABASE_PROJECT_ID + - run: supabase db push ``` diff --git a/apps/docs/pages/guides/cli/testing-emails.mdx b/apps/docs/pages/guides/cli/testing-emails.mdx new file mode 100644 index 00000000000..f9e3cce6706 --- /dev/null +++ b/apps/docs/pages/guides/cli/testing-emails.mdx @@ -0,0 +1,24 @@ +import Layout from '~/layouts/DefaultGuideLayout' + +export const meta = { + id: 'testing-emails-locally', + title: 'Testing emails locally', + description: 'Testing emails for Supabase Auth on your local machine.', + subtitle: 'Testing emails for Supabase Auth on your local machine.', +} + +The Supabase CLI uses [Inbucket](https://github.com/inbucket/inbucket) to capture emails sent from your local machine. This is useful for testing emails sent from Supabase Auth. + +## Accessing Inbucket + +By default, Inbucket is available at [localhost:54324](http://localhost:54324) when you run `supabase start`. Simply open this URL in your browser to view the emails. + +## Going into Production + +The "default" email provided by Supabase is only for development purposes. It is [heavily restricted](/docs/guides/platform/going-into-prod#auth-rate-limits) to ensure that it is not used for spam. + +Before you go into production, you must configure your own email provider. This is as simple as enabling a new SMTP credentials in your [project settings](https://supabase.com/dashboard/project/_/settings/auth). + +export const Page = ({ children }) => + +export default Page diff --git a/apps/docs/pages/guides/cli/using-environment-variables-in-config.mdx b/apps/docs/pages/guides/cli/using-environment-variables-in-config.mdx deleted file mode 100644 index b50724d29cb..00000000000 --- a/apps/docs/pages/guides/cli/using-environment-variables-in-config.mdx +++ /dev/null @@ -1,35 +0,0 @@ -import Layout from '~/layouts/DefaultGuideLayout' - -export const meta = { - id: 'using-environment-variables-in-config', - title: 'Using environment variables in config.toml', - description: 'How to use environment variables in config.toml with the Supabase CLI.', -} - -The Supabase CLI is capable of utilizing environment variables stored in our project's root directory's `.env` file within the `config.toml` file. - -We can reference the environment variable by using the `env()` function. - -Inside of our `.env` file we add the environment variable as we normally would - -```bash -GITHUB_CLIENT_ID="" -GITHUB_SECRET="" -``` - -And then reference them inside of our `config.toml` - -```toml -[auth.external.github] -enabled = true -client_id = "env(GITHUB_CLIENT_ID)" -secret = "env(GITHUB_SECRET)" -# Overrides the default auth redirectUrl. -redirect_uri = "" -``` - -These same environment variables will be referenced by the `supabase start` command from the Supabase CLI. - -export const Page = ({ children }) => - -export default Page diff --git a/apps/docs/pages/guides/database.mdx b/apps/docs/pages/guides/database.mdx index 5bff7cc1a89..440277b9a8b 100644 --- a/apps/docs/pages/guides/database.mdx +++ b/apps/docs/pages/guides/database.mdx @@ -98,7 +98,7 @@ Read about resetting your database password [here](/docs/guides/database/managin ## Next steps - Read more about [Postgres](https://www.postgresql.org/about/) -- Sign in: [app.supabase.com](https://app.supabase.com) +- Sign in: [supabase.com/dashboard](https://supabase.com/dashboard) export const Page = ({ children }) => diff --git a/apps/docs/pages/guides/database/arrays.mdx b/apps/docs/pages/guides/database/arrays.mdx index 5bef947cfa9..0ac61fcd68c 100644 --- a/apps/docs/pages/guides/database/arrays.mdx +++ b/apps/docs/pages/guides/database/arrays.mdx @@ -20,7 +20,7 @@ Create a test table with a text array (an array of strings): > -1. Go to the [Table editor](https://app.supabase.com/project/_/editor) page in the Dashboard. +1. Go to the [Table editor](https://supabase.com/dashboard/project/_/editor) page in the Dashboard. 1. Click **New Table** and create a table with the name `arraytest`. 1. Click **Save**. 1. Click **New Column** and create a column with the name `textarray`, type `text`, and select **Define as array**. @@ -49,7 +49,7 @@ create table arraytest ( > -1. Go to the [Table editor](https://app.supabase.com/project/_/editor) page in the Dashboard. +1. Go to the [Table editor](https://supabase.com/dashboard/project/_/editor) page in the Dashboard. 1. Select the `arraytest` table. 1. Click **Insert row** and add `["Harry", "Larry", "Moe"]`. 1. Click **Save.** @@ -85,7 +85,7 @@ const { data, error } = await supabase > -1. Go to the [Table editor](https://app.supabase.com/project/_/editor) page in the Dashboard. +1. Go to the [Table editor](https://supabase.com/dashboard/project/_/editor) page in the Dashboard. 1. Select the `arraytest` table. You should see: diff --git a/apps/docs/pages/guides/database/connecting-to-postgres.mdx b/apps/docs/pages/guides/database/connecting-to-postgres.mdx index 45db57cd344..3b6ba626626 100644 --- a/apps/docs/pages/guides/database/connecting-to-postgres.mdx +++ b/apps/docs/pages/guides/database/connecting-to-postgres.mdx @@ -11,7 +11,7 @@ Supabase provides several options for programmatically connecting to your Postgr 1. Direct connections using Postgres' standard connection system 2. Connection pooling using PgBouncer -3. Programmatic access uing the [Serverless APIs](/docs/guides/api) +3. Programmatic access using the [Serverless APIs](/docs/guides/api) ## Serverless APIs @@ -23,7 +23,7 @@ Supabase provides auto-updating [APIs](/docs/guides/database/api). This is the e ## Direct connections -Every Supabase project provides a full Postgres database. You can connect to the database using [any tool which supports Postgres](#integrations). You can find the connection string in the [Database settings](https://app.supabase.com/project/_/settings/database) inside the dashboard: +Every Supabase project provides a full Postgres database. You can connect to the database using [any tool which supports Postgres](#integrations). You can find the connection string in the [Database settings](https://supabase.com/dashboard/project/_/settings/database) inside the dashboard: 1. Go to the `Settings` section. 2. Click `Database`. @@ -38,7 +38,7 @@ Every Supabase project provides a full Postgres database. You can connect to the ## Connection Pooler -Every Supabase project comes with PgBouncer for connection pooling. A connection pooler is useful for managing a large number of _temporary_ connections. For example, if you are using [Prisma](/docs/guides/integrations/prisma), Drizzle, Kysely, or anything deployed to a Serverless environment (AWS Lambdas or Edge Functions). You can find the connection pool config in the [Database settings](https://app.supabase.com/project/_/settings/database) inside the dashboard: +Every Supabase project comes with PgBouncer for connection pooling. A connection pooler is useful for managing a large number of _temporary_ connections. For example, if you are using [Prisma](/partners/integrations/prisma), Drizzle, Kysely, or anything deployed to a Serverless environment (AWS Lambdas or Edge Functions). You can find the connection pool config in the [Database settings](https://supabase.com/dashboard/project/_/settings/database) inside the dashboard: 1. Go to the `Settings` section. 2. Click `Database`. @@ -267,7 +267,7 @@ psql "sslmode=verify-full sslrootcert=$HOME/Downloads/prod-supabase.cer host=db. - Install Drizzle and releated dependencies. + Install Postgres.js and releated dependencies. @@ -275,7 +275,6 @@ psql "sslmode=verify-full sslrootcert=$HOME/Downloads/prod-supabase.cer host=db. ```shell npm i postgres - npm i -D drizzle-kit ``` diff --git a/apps/docs/pages/guides/database/extensions.mdx b/apps/docs/pages/guides/database/extensions.mdx index 8b729b6ba73..68271fbc284 100644 --- a/apps/docs/pages/guides/database/extensions.mdx +++ b/apps/docs/pages/guides/database/extensions.mdx @@ -19,7 +19,7 @@ Supabase has pre-installed some of the most useful open source extensions. > -1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard. +1. Go to the [Database](https://supabase.com/dashboard/project/_/database/tables) page in the Dashboard. 2. Click **Extensions** in the sidebar. 3. Enable or disable an extension. diff --git a/apps/docs/pages/guides/database/extensions/http.mdx b/apps/docs/pages/guides/database/extensions/http.mdx index 7f686ed8e7b..89ad11ae91f 100644 --- a/apps/docs/pages/guides/database/extensions/http.mdx +++ b/apps/docs/pages/guides/database/extensions/http.mdx @@ -45,7 +45,7 @@ You can use the `http` extension to make these network requests from Postgres. > -1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard. +1. Go to the [Database](https://supabase.com/dashboard/project/_/database/tables) page in the Dashboard. 2. Click on **Extensions** in the sidebar. 3. Search for "http" and enable the extension. diff --git a/apps/docs/pages/guides/database/extensions/hypopg.mdx b/apps/docs/pages/guides/database/extensions/hypopg.mdx index 18688e276e4..b272e9e40e0 100644 --- a/apps/docs/pages/guides/database/extensions/hypopg.mdx +++ b/apps/docs/pages/guides/database/extensions/hypopg.mdx @@ -20,7 +20,7 @@ The motivation for HypoPG is to allow users to quickly search for an index to im > -1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard. +1. Go to the [Database](https://supabase.com/dashboard/project/_/database/tables) page in the Dashboard. 2. Click on **Extensions** in the sidebar. 3. Search for "hypopg" and enable the extension. diff --git a/apps/docs/pages/guides/database/extensions/pgcron.mdx b/apps/docs/pages/guides/database/extensions/pg_cron.mdx similarity index 96% rename from apps/docs/pages/guides/database/extensions/pgcron.mdx rename to apps/docs/pages/guides/database/extensions/pg_cron.mdx index 8b4b5016c4d..fb4214c8f1c 100644 --- a/apps/docs/pages/guides/database/extensions/pgcron.mdx +++ b/apps/docs/pages/guides/database/extensions/pg_cron.mdx @@ -1,7 +1,7 @@ import Layout from '~/layouts/DefaultGuideLayout' export const meta = { - id: 'pgcron', + id: 'pg_cron', title: 'pg_cron: Job Scheduling', description: 'pgnet: a simple cron-based job scheduler for PostgreSQL that runs inside the database.', @@ -21,7 +21,7 @@ The `pg_cron` extension is a simple cron-based job scheduler for PostgreSQL that > -1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard. +1. Go to the [Database](https://supabase.com/dashboard/project/_/database/tables) page in the Dashboard. 2. Click on **Extensions** in the sidebar. 3. Search for "pg_cron" and enable the extension. diff --git a/apps/docs/pages/guides/database/extensions/pg_graphql.mdx b/apps/docs/pages/guides/database/extensions/pg_graphql.mdx index e4a27cb523c..eee0cfb1103 100644 --- a/apps/docs/pages/guides/database/extensions/pg_graphql.mdx +++ b/apps/docs/pages/guides/database/extensions/pg_graphql.mdx @@ -24,7 +24,7 @@ For more information about how the SQL schema is reflected into a GraphQL schema > -1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard. +1. Go to the [Database](https://supabase.com/dashboard/project/_/database/tables) page in the Dashboard. 2. Click on **Extensions** in the sidebar. 3. Search for "pg_graphql" and enable the extension. diff --git a/apps/docs/pages/guides/database/extensions/pg_hashids.mdx b/apps/docs/pages/guides/database/extensions/pg_hashids.mdx index ad02c19b1ff..2b413c1533f 100644 --- a/apps/docs/pages/guides/database/extensions/pg_hashids.mdx +++ b/apps/docs/pages/guides/database/extensions/pg_hashids.mdx @@ -18,7 +18,7 @@ export const meta = { > -1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard. +1. Go to the [Database](https://supabase.com/dashboard/project/_/database/tables) page in the Dashboard. 2. Click on **Extensions** in the sidebar. 3. Search for "pg_hashids" and enable the extension. diff --git a/apps/docs/pages/guides/database/extensions/pg_jsonschema.mdx b/apps/docs/pages/guides/database/extensions/pg_jsonschema.mdx index 4cf54f21a19..1fba7fa77fc 100644 --- a/apps/docs/pages/guides/database/extensions/pg_jsonschema.mdx +++ b/apps/docs/pages/guides/database/extensions/pg_jsonschema.mdx @@ -18,7 +18,7 @@ export const meta = { > -1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard. +1. Go to the [Database](https://supabase.com/dashboard/project/_/database/tables) page in the Dashboard. 2. Click on **Extensions** in the sidebar. 3. Search for "pg_jsonschema" and enable the extension. diff --git a/apps/docs/pages/guides/database/extensions/pgnet.mdx b/apps/docs/pages/guides/database/extensions/pg_net.mdx similarity index 97% rename from apps/docs/pages/guides/database/extensions/pgnet.mdx rename to apps/docs/pages/guides/database/extensions/pg_net.mdx index 5a93b7a48b5..d7c9d8c7c62 100644 --- a/apps/docs/pages/guides/database/extensions/pgnet.mdx +++ b/apps/docs/pages/guides/database/extensions/pg_net.mdx @@ -1,7 +1,7 @@ import Layout from '~/layouts/DefaultGuideLayout' export const meta = { - id: 'pgnet', + id: 'pg_net', title: 'pg_net: Async Networking', description: 'pg_net: an async networking extension for PostgreSQL.', } @@ -26,7 +26,7 @@ It differs from the `http` extension in that it is asynchronous by default. This > -1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard. +1. Go to the [Database](https://supabase.com/dashboard/project/_/database/tables) page in the Dashboard. 2. Click on **Extensions** in the sidebar. 3. Search for "pg_net" and enable the extension. diff --git a/apps/docs/pages/guides/database/extensions/pgrepack.mdx b/apps/docs/pages/guides/database/extensions/pg_repack.mdx similarity index 96% rename from apps/docs/pages/guides/database/extensions/pgrepack.mdx rename to apps/docs/pages/guides/database/extensions/pg_repack.mdx index bea7f7474b2..aaab00c01ed 100644 --- a/apps/docs/pages/guides/database/extensions/pgrepack.mdx +++ b/apps/docs/pages/guides/database/extensions/pg_repack.mdx @@ -1,6 +1,7 @@ import Layout from '~/layouts/DefaultGuideLayout' export const meta = { + id: 'pg_repack', title: 'pg_repack: Physical storage optimization and maintenance', description: 'A tool to remove bloat from tables and indexes and optimize physical data order and physical storage', @@ -35,7 +36,7 @@ Get started with pg_repack by enabling the extension in the Supabase Dashboard. > -1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard. +1. Go to the [Database](https://supabase.com/dashboard/project/_/database/tables) page in the Dashboard. 2. Click on **Extensions** in the sidebar. 3. Search for "pg_repack" and enable the extension. diff --git a/apps/docs/pages/guides/database/extensions/pg_stat_monitor.mdx b/apps/docs/pages/guides/database/extensions/pg_stat_monitor.mdx index 232bbb116e0..8e050767f7d 100644 --- a/apps/docs/pages/guides/database/extensions/pg_stat_monitor.mdx +++ b/apps/docs/pages/guides/database/extensions/pg_stat_monitor.mdx @@ -24,7 +24,7 @@ For more information on query optimization, check out the [query performance gui > -1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard. +1. Go to the [Database](https://supabase.com/dashboard/project/_/database/tables) page in the Dashboard. 2. Click on **Extensions** in the sidebar. 3. Search for "pg_stat_monitor" and enable the extension. diff --git a/apps/docs/pages/guides/database/extensions/pg_stat_statements.mdx b/apps/docs/pages/guides/database/extensions/pg_stat_statements.mdx index cb64a901b0c..34cbb3af35b 100644 --- a/apps/docs/pages/guides/database/extensions/pg_stat_statements.mdx +++ b/apps/docs/pages/guides/database/extensions/pg_stat_statements.mdx @@ -34,7 +34,7 @@ For more information on query optimization, check out the [query performance gui > -1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard. +1. Go to the [Database](https://supabase.com/dashboard/project/_/database/tables) page in the Dashboard. 2. Click on **Extensions** in the sidebar. 3. Search for "pg_stat_statements" and enable the extension. diff --git a/apps/docs/pages/guides/database/extensions/pgaudit.mdx b/apps/docs/pages/guides/database/extensions/pgaudit.mdx index 0352fb6d131..3b780c1e4e8 100644 --- a/apps/docs/pages/guides/database/extensions/pgaudit.mdx +++ b/apps/docs/pages/guides/database/extensions/pgaudit.mdx @@ -20,7 +20,7 @@ PGAudit grants fine grain control over which statements and objects are emitted > -1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard. +1. Go to the [Database](https://supabase.com/dashboard/project/_/database/tables) page in the Dashboard. 2. Click on **Extensions** in the sidebar. 3. Search for "pgaudit" and enable the extension. diff --git a/apps/docs/pages/guides/database/extensions/pgjwt.mdx b/apps/docs/pages/guides/database/extensions/pgjwt.mdx index 1643f7e8ca4..fcc0bc9d427 100644 --- a/apps/docs/pages/guides/database/extensions/pgjwt.mdx +++ b/apps/docs/pages/guides/database/extensions/pgjwt.mdx @@ -18,7 +18,7 @@ The [pgjwt](https://github.com/michelp/pgjwt) (PostgreSQL JSON Web Token) extens > -1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard. +1. Go to the [Database](https://supabase.com/dashboard/project/_/database/tables) page in the Dashboard. 2. Click on **Extensions** in the sidebar. 3. Search for "pgjwt" and enable the extension. diff --git a/apps/docs/pages/guides/database/extensions/pgroonga.mdx b/apps/docs/pages/guides/database/extensions/pgroonga.mdx index 669c5d7829b..8130dd0ef71 100644 --- a/apps/docs/pages/guides/database/extensions/pgroonga.mdx +++ b/apps/docs/pages/guides/database/extensions/pgroonga.mdx @@ -18,7 +18,7 @@ export const meta = { > -1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard. +1. Go to the [Database](https://supabase.com/dashboard/project/_/database/tables) page in the Dashboard. 2. Click on **Extensions** in the sidebar. 3. Search for "pgroonga" and enable the extension. @@ -100,6 +100,83 @@ The pgroonga index is used to retrive the result set: | 2 | 'Groonga is a fast full text search engine that supports all languages.' | ``` +## Full Text Search + +The `&@~` operator performs full text search. It returns any matching results. Unlike `LIKE` operator, pgroonga can search any text that contains the keyword case insensitive. + +Take the following example: + +{/* prettier-ignore */} +```sql +select * from memos where content &@~ 'groonga'; +``` + +And the result: + +```markdown +id | content +----+------------------------------------------------------------------------ +2 | Groonga is a fast full text search engine that supports all languages. +3 | PGroonga is a PostgreSQL extension that uses Groonga as index. +4 | There is groonga command. +(3 rows) +``` + +### Match all search words + +To find all memos where content contains BOTH of the words `postgres` and `pgroonga`, we can just use space to separate each words: + +{/* prettier-ignore */} +```sql +select * from memos where content &@~ 'postgres pgroonga'; +``` + +And the result: + +```markdown +id | content +----+---------------------------------------------------------------- +3 | PGroonga is a PostgreSQL extension that uses Groonga as index. +(1 row) +``` + +### Match any search words + +To find all memos where content contain ANY of the words `postgres` or `pgroonga`, use the upper case `OR`: + +{/* prettier-ignore */} +```sql +select * from memos where content &@~ 'postgres OR pgroonga'; +``` + +And the result: + +```markdown +id | content +----+---------------------------------------------------------------- +1 | PostgreSQL is a relational database management system. +3 | PGroonga is a PostgreSQL extension that uses Groonga as index. +(2 rows) +``` + +### Search that matches words with negation + +To find all memos where content contain the word `postgres` but not `pgroonga`, use `-` symbol: + +{/* prettier-ignore */} +```sql +select * from memos where content &@~ 'postgres -pgroonga'; +``` + +And the result: + +```markdown +id | content +----+-------------------------------------------------------- +1 | PostgreSQL is a relational database management system. +(1 row) +``` + ## Resources - Official [PGroonga documentation](https://pgroonga.github.io/tutorial/) diff --git a/apps/docs/pages/guides/database/extensions/pgrouting.mdx b/apps/docs/pages/guides/database/extensions/pgrouting.mdx index 73a047fb362..34b9612bb39 100644 --- a/apps/docs/pages/guides/database/extensions/pgrouting.mdx +++ b/apps/docs/pages/guides/database/extensions/pgrouting.mdx @@ -32,7 +32,7 @@ The core functionality of `pgRouting` is a set of path finding algorithms includ > -1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard. +1. Go to the [Database](https://supabase.com/dashboard/project/_/database/tables) page in the Dashboard. 2. Click on **Extensions** in the sidebar. 3. Search for "pgrouting" and enable the extension. diff --git a/apps/docs/pages/guides/database/extensions/pgsodium.mdx b/apps/docs/pages/guides/database/extensions/pgsodium.mdx index 8e5f92ff5fe..15028024222 100644 --- a/apps/docs/pages/guides/database/extensions/pgsodium.mdx +++ b/apps/docs/pages/guides/database/extensions/pgsodium.mdx @@ -63,7 +63,7 @@ It also enables some Postgres specific features including: > -1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard. +1. Go to the [Database](https://supabase.com/dashboard/project/_/database/tables) page in the Dashboard. 2. Click on **Extensions** in the sidebar. 3. Search for "pgsodium" and enable the extension. diff --git a/apps/docs/pages/guides/database/extensions/pgtap.mdx b/apps/docs/pages/guides/database/extensions/pgtap.mdx index 219eedd11ee..42d4521687e 100644 --- a/apps/docs/pages/guides/database/extensions/pgtap.mdx +++ b/apps/docs/pages/guides/database/extensions/pgtap.mdx @@ -25,7 +25,7 @@ Let's cover some basic concepts: > -1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard. +1. Go to the [Database](https://supabase.com/dashboard/project/_/database/tables) page in the Dashboard. 2. Click on **Extensions** in the sidebar. 3. Search for "pgtap" and enable the extension. diff --git a/apps/docs/pages/guides/database/extensions/pgvector.mdx b/apps/docs/pages/guides/database/extensions/pgvector.mdx index e9ce3274f8d..b142920f358 100644 --- a/apps/docs/pages/guides/database/extensions/pgvector.mdx +++ b/apps/docs/pages/guides/database/extensions/pgvector.mdx @@ -33,7 +33,7 @@ This is particularly useful if you're building on top of OpenAI's [GPT-3](https: > -1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard. +1. Go to the [Database](https://supabase.com/dashboard/project/_/database/tables) page in the Dashboard. 2. Click on **Extensions** in the sidebar. 3. Search for "vector" and enable the extension. diff --git a/apps/docs/pages/guides/database/extensions/plpgsql_check.mdx b/apps/docs/pages/guides/database/extensions/plpgsql_check.mdx index a064f463df6..647ca16fc03 100644 --- a/apps/docs/pages/guides/database/extensions/plpgsql_check.mdx +++ b/apps/docs/pages/guides/database/extensions/plpgsql_check.mdx @@ -18,7 +18,7 @@ export const meta = { > -1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard. +1. Go to the [Database](https://supabase.com/dashboard/project/_/database/tables) page in the Dashboard. 2. Click on **Extensions** in the sidebar. 3. Search for "plpgsql_check" and enable the extension. diff --git a/apps/docs/pages/guides/database/extensions/plv8.mdx b/apps/docs/pages/guides/database/extensions/plv8.mdx index bac280e8d78..77c55e87d7d 100644 --- a/apps/docs/pages/guides/database/extensions/plv8.mdx +++ b/apps/docs/pages/guides/database/extensions/plv8.mdx @@ -25,7 +25,7 @@ It can be used for database functions, triggers, queries and more. > -1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard. +1. Go to the [Database](https://supabase.com/dashboard/project/_/database/tables) page in the Dashboard. 2. Click on **Extensions** in the sidebar. 3. Search for "plv8" and enable the extension. diff --git a/apps/docs/pages/guides/database/extensions/postgis.mdx b/apps/docs/pages/guides/database/extensions/postgis.mdx index 9ff6e55db30..da0562db7af 100644 --- a/apps/docs/pages/guides/database/extensions/postgis.mdx +++ b/apps/docs/pages/guides/database/extensions/postgis.mdx @@ -26,7 +26,7 @@ You can get started with PostGIS by enabling the PostGIS extension in your Supab > -1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard. +1. Go to the [Database](https://supabase.com/dashboard/project/_/database/tables) page in the Dashboard. 2. Click on **Extensions** in the sidebar. 3. Search for "postgis" and enable the extension. diff --git a/apps/docs/pages/guides/database/extensions/rum.mdx b/apps/docs/pages/guides/database/extensions/rum.mdx index bfe5ede32ee..bf83786732c 100644 --- a/apps/docs/pages/guides/database/extensions/rum.mdx +++ b/apps/docs/pages/guides/database/extensions/rum.mdx @@ -39,7 +39,7 @@ You can get started with rum by enabling the extension in your Supabase dashboar > -1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard. +1. Go to the [Database](https://supabase.com/dashboard/project/_/database/tables) page in the Dashboard. 2. Click on **Extensions** in the sidebar. 3. Search for "rum" and enable the extension. diff --git a/apps/docs/pages/guides/database/extensions/timescaledb.mdx b/apps/docs/pages/guides/database/extensions/timescaledb.mdx index 265e8662ad4..b6daaed4814 100644 --- a/apps/docs/pages/guides/database/extensions/timescaledb.mdx +++ b/apps/docs/pages/guides/database/extensions/timescaledb.mdx @@ -20,7 +20,7 @@ export const meta = { > -1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard. +1. Go to the [Database](https://supabase.com/dashboard/project/_/database/tables) page in the Dashboard. 2. Click on **Extensions** in the sidebar. 3. Search for "timescaledb" and enable the extension. diff --git a/apps/docs/pages/guides/database/extensions/uuid-ossp.mdx b/apps/docs/pages/guides/database/extensions/uuid-ossp.mdx index b270fdd581f..f07cde1ede4 100644 --- a/apps/docs/pages/guides/database/extensions/uuid-ossp.mdx +++ b/apps/docs/pages/guides/database/extensions/uuid-ossp.mdx @@ -23,7 +23,7 @@ This makes them particularly well suited as Primary Keys. It is occasionally ref > -1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard. +1. Go to the [Database](https://supabase.com/dashboard/project/_/database/tables) page in the Dashboard. 2. Click on **Extensions** in the sidebar. 3. Search for "uuid-ossp" and enable the extension. diff --git a/apps/docs/pages/guides/database/extensions/wrappers.mdx b/apps/docs/pages/guides/database/extensions/wrappers.mdx index 34305abd1cb..5d99f88ea08 100644 --- a/apps/docs/pages/guides/database/extensions/wrappers.mdx +++ b/apps/docs/pages/guides/database/extensions/wrappers.mdx @@ -36,7 +36,7 @@ from > -1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard. +1. Go to the [Database](https://supabase.com/dashboard/project/_/database/tables) page in the Dashboard. 2. Click on **Extensions** in the sidebar. 3. Search for "wrappers" and enable the extension. diff --git a/apps/docs/pages/guides/database/json.mdx b/apps/docs/pages/guides/database/json.mdx index 46e60985db4..b7f35e96e58 100644 --- a/apps/docs/pages/guides/database/json.mdx +++ b/apps/docs/pages/guides/database/json.mdx @@ -46,7 +46,7 @@ create table books ( -1. Go to the [Table Editor](https://app.supabase.com/project/_/editor) page in the Dashboard. +1. Go to the [Table Editor](https://supabase.com/dashboard/project/_/editor) page in the Dashboard. 2. Click **New Table** and create a table called `books`. 3. Include a primary key with the following properties and click save: @@ -112,7 +112,7 @@ values -1. Go to the [Table Editor](https://app.supabase.com/project/_/editor) page in the Dashboard. +1. Go to the [Table Editor](https://supabase.com/dashboard/project/_/editor) page in the Dashboard. 2. Select the `books` table in the sidebar. 3. Click **+ Insert row** and add 5 rows with the following properties: @@ -244,7 +244,7 @@ create table customers ( ); alter table customers -add constriant check_metadata check ( +add constraint check_metadata check ( json_matches_schema( '{ "type": "object", diff --git a/apps/docs/pages/guides/database/managing-passwords.mdx b/apps/docs/pages/guides/database/managing-passwords.mdx index 79e51176195..f96c00437ac 100644 --- a/apps/docs/pages/guides/database/managing-passwords.mdx +++ b/apps/docs/pages/guides/database/managing-passwords.mdx @@ -13,7 +13,7 @@ If you use special symbols in your postgres password, you must remember to [perc ## Changing your project password When you created your project you were also asked to enter a password. This is actually the password for your database, specifically for the `postgres` user. -You can update this from the Dashboard under the [database settings](https://app.supabase.com/project/_/settings/database) page. +You can update this from the Dashboard under the [database settings](https://supabase.com/dashboard/project/_/settings/database) page. ## Creating a secure password diff --git a/apps/docs/pages/guides/database/overview.mdx b/apps/docs/pages/guides/database/overview.mdx index c4f59398be9..80ddf753488 100644 --- a/apps/docs/pages/guides/database/overview.mdx +++ b/apps/docs/pages/guides/database/overview.mdx @@ -100,7 +100,7 @@ Read about resetting your database password [here](/docs/guides/database/managin ## Next steps - Read more about [Postgres](https://www.postgresql.org/about/) -- Sign in: [app.supabase.com](https://app.supabase.com) +- Sign in: [supabase.com/dashboard](https://supabase.com/dashboard) export const Page = ({ children }) => diff --git a/apps/docs/pages/guides/database/postgres/dropping-all-tables-in-schema.mdx b/apps/docs/pages/guides/database/postgres/dropping-all-tables-in-schema.mdx index 602db8f9822..734c7b132e0 100644 --- a/apps/docs/pages/guides/database/postgres/dropping-all-tables-in-schema.mdx +++ b/apps/docs/pages/guides/database/postgres/dropping-all-tables-in-schema.mdx @@ -27,7 +27,7 @@ end $$; This query works by listing out all the tables in the given schema and then executing a `drop table` for each (hence the `for... loop`). -You can run this query using the [SQL Editor](https://app.supabase.com/project/_/sql) in the Supabase Dashboard, or via `psql` if you're [connecting directly to the database](/docs/guides/database/connecting-to-postgres#direct-connections). +You can run this query using the [SQL Editor](https://supabase.com/dashboard/project/_/sql) in the Supabase Dashboard, or via `psql` if you're [connecting directly to the database](/docs/guides/database/connecting-to-postgres#direct-connections). export const Page = ({ children }) => diff --git a/apps/docs/pages/guides/database/postgres/first-row-in-group.mdx b/apps/docs/pages/guides/database/postgres/first-row-in-group.mdx index ba0297f903c..362f987d34c 100644 --- a/apps/docs/pages/guides/database/postgres/first-row-in-group.mdx +++ b/apps/docs/pages/guides/database/postgres/first-row-in-group.mdx @@ -25,7 +25,7 @@ The expected output we want is: | 3 | Brighton | 34 | | 2 | Liverpool | 84 | -From the [SQL Editor](https://app.supabase.com/project/_/sql), you can run a query like: +From the [SQL Editor](https://supabase.com/dashboard/project/_/sql), you can run a query like: ```sql select distinct diff --git a/apps/docs/pages/guides/database/postgres/indexes.mdx b/apps/docs/pages/guides/database/postgres/indexes.mdx index 60e7a3e2eeb..33087ac1a8a 100644 --- a/apps/docs/pages/guides/database/postgres/indexes.mdx +++ b/apps/docs/pages/guides/database/postgres/indexes.mdx @@ -29,7 +29,7 @@ create table persons ( -All the queries in this guide can be run using the [SQL Editor](https://app.supabase.com/project/_/sql) in the Supabase Dashboard, or via `psql` if you're [connecting directly to the database](/docs/guides/database/connecting-to-postgres#direct-connections). +All the queries in this guide can be run using the [SQL Editor](https://supabase.com/dashboard/project/_/sql) in the Supabase Dashboard, or via `psql` if you're [connecting directly to the database](/docs/guides/database/connecting-to-postgres#direct-connections). diff --git a/apps/docs/pages/guides/database/postgres/which-version-of-postgres.mdx b/apps/docs/pages/guides/database/postgres/which-version-of-postgres.mdx index 3a04c7ce06a..28273954fbe 100644 --- a/apps/docs/pages/guides/database/postgres/which-version-of-postgres.mdx +++ b/apps/docs/pages/guides/database/postgres/which-version-of-postgres.mdx @@ -8,7 +8,7 @@ export const meta = { It's important to know which version of PostgreSQL you are running as each major version has different features and may cause breaking changes. You may also need to update your schema when [upgrading](https://www.postgresql.org/docs/current/pgupgrade.html) or downgrading to a major Postgres version. -Run the following query using the [SQL Editor](https://app.supabase.com/project/_/sql) in the Supabase Dashboard: +Run the following query using the [SQL Editor](https://supabase.com/dashboard/project/_/sql) in the Supabase Dashboard: ```sql select diff --git a/apps/docs/pages/guides/database/replication.mdx b/apps/docs/pages/guides/database/replication.mdx index ba9939d6793..6d32aebf95b 100644 --- a/apps/docs/pages/guides/database/replication.mdx +++ b/apps/docs/pages/guides/database/replication.mdx @@ -11,11 +11,11 @@ Replication is a technique for copying the data from one database to another. Su - Spreading out the "load." For example, if your database has a lot of reads, you might want to split it between two databases. - Reducing latency. For example, you may want one database in London to serve your European customers, and one in New York to serve the US. -Replication is done through _publications_, a method of choosing which changes to send to other systems (usually another Postgres database). Publications can be managed in the [Dashboard](https://app.supabase.com) or with SQL. +Replication is done through _publications_, a method of choosing which changes to send to other systems (usually another Postgres database). Publications can be managed in the [Dashboard](https://supabase.com/dashboard) or with SQL. ## Manage publications in the Dashboard -1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard. +1. Go to the [Database](https://supabase.com/dashboard/project/_/database/tables) page in the Dashboard. 2. Click on **Replication** in the sidebar. 3. Control which database events are sent by toggling **Insert**, **Update**, and **Delete**. 4. Control which tables broadcast changes by selecting **Source** and toggling each table. diff --git a/apps/docs/pages/guides/database/tables.mdx b/apps/docs/pages/guides/database/tables.mdx index d8f7b5ca3bf..223874fbd4f 100644 --- a/apps/docs/pages/guides/database/tables.mdx +++ b/apps/docs/pages/guides/database/tables.mdx @@ -48,7 +48,7 @@ and run the SQL queries yourself. /> -1. Go to the [Table Editor](https://app.supabase.com/project/_/editor) page in the Dashboard. +1. Go to the [Table Editor](https://supabase.com/dashboard/project/_/editor) page in the Dashboard. 2. Click **New Table** and create a table with the name `todos`. 3. Click **Save**. 4. Click **New Column** and create a column with the name `task` and type `text`. @@ -425,7 +425,7 @@ Views provide the several benefits: #### Simplicity -As a query becomes complex it becomes a hassle to call it. Especially when we run it at regularly. In the example above, instead of repeatedly running: +As a query becomes more complex, it can be a hassle to call it over and over - especially when we run it regularly. In the example above, instead of repeatedly running: ```sql select diff --git a/apps/docs/pages/guides/database/webhooks.mdx b/apps/docs/pages/guides/database/webhooks.mdx index 9cada9989cb..f08bcb3b289 100644 --- a/apps/docs/pages/guides/database/webhooks.mdx +++ b/apps/docs/pages/guides/database/webhooks.mdx @@ -33,7 +33,7 @@ Database Webhooks were previously known as Function Hooks. ## Creating a webhook -1. Create a new [Database Webhook](https://app.supabase.com/project/_/database/hooks) in the Dashboard. +1. Create a new [Database Webhook](https://supabase.com/dashboard/project/_/database/hooks) in the Dashboard. 1. Give your Webhook a name. 1. Select the table you want to hook into. 1. Select one or more events (table inserts, updates, or deletes) you want to hook into. diff --git a/apps/docs/pages/guides/functions/debugging.mdx b/apps/docs/pages/guides/functions/debugging.mdx index 9b18d06b1fd..6b1d46d1eda 100644 --- a/apps/docs/pages/guides/functions/debugging.mdx +++ b/apps/docs/pages/guides/functions/debugging.mdx @@ -6,7 +6,7 @@ export const meta = { description: 'Debug Edge Functions in production.', } -You can debug your deployed Edge Functions using the ["Functions" section](https://app.supabase.com/project/_/functions) of the Dashboard. There are two debugging tools available: +You can debug your deployed Edge Functions using the ["Functions" section](https://supabase.com/dashboard/project/_/functions) of the Dashboard. There are two debugging tools available: - Invocations: shows the Request and Response for each execution. - Logs: shows any platform events, including deployments and errors. diff --git a/apps/docs/pages/guides/functions/kysely-postgres.mdx b/apps/docs/pages/guides/functions/kysely-postgres.mdx index 765946a81ef..3987a77e45d 100644 --- a/apps/docs/pages/guides/functions/kysely-postgres.mdx +++ b/apps/docs/pages/guides/functions/kysely-postgres.mdx @@ -24,7 +24,7 @@ Combining Kysely with Deno Postgres gives you a convenient developer experience Find the example on [GitHub](https://github.com/supabase/supabase/tree/master/examples/edge-functions/supabase/functions/kysely-postgres) -Get your database connection credentials from your [Supabase Dashboard](https://app.supabase.com/project/_/settings/database) and store them in an `.env` file: +Get your database connection credentials from your [Supabase Dashboard](https://supabase.com/dashboard/project/_/settings/database) and store them in an `.env` file: ```bash .env DB_HOSTNAME= diff --git a/apps/docs/pages/guides/functions/quickstart.mdx b/apps/docs/pages/guides/functions/quickstart.mdx index 235556c439e..14de4f79620 100644 --- a/apps/docs/pages/guides/functions/quickstart.mdx +++ b/apps/docs/pages/guides/functions/quickstart.mdx @@ -199,7 +199,8 @@ For use-cases which require low-latency we recommend [Edge Functions](/docs/guid ## Organizing your Edge Functions -We recommend developing “fat functions”. This means that you should develop few large functions, rather than many small functions. One common pattern when developing Functions is that you need to share code between two or more Functions. To do this, you can store any shared code in a folder prefixed with an underscore (`_`). We recommend this folder structure: +We recommend developing “fat functions”. This means that you should develop few large functions, rather than many small functions. One common pattern when developing Functions is that you need to share code between two or more Functions. To do this, you can store any shared code in a folder prefixed with an underscore (`_`). We also recommend a separate folder for [Unit Tests](/docs/guides/functions/unit-test) including the name of the function followed by a `-test` suffix. +We recommend this folder structure: ```bash └── supabase @@ -212,7 +213,10 @@ We recommend developing “fat functions”. This means that you should develop │ ├── function-one # Use hyphens to name functions. │ │ └── index.ts │ └── function-two - │ └── index.ts + │ │ └── index.ts + │ └── tests + │ └── function-one-test.ts + │ └── function-two-test.ts ├── migrations └── config.toml ``` diff --git a/apps/docs/pages/guides/functions/secrets.mdx b/apps/docs/pages/guides/functions/secrets.mdx index ffbe7920042..9f0b8be8506 100644 --- a/apps/docs/pages/guides/functions/secrets.mdx +++ b/apps/docs/pages/guides/functions/secrets.mdx @@ -12,6 +12,15 @@ It's common that you will need to use sensitive information or environment-speci Deno.env.get(MY_SECRET_NAME) ``` +### Local Development + +When developing functions locally, you be able to load environment variables two ways: + +1. Through a default `.env` file placed at `supabase/functions/.env`, which will get loaded on `supabase start` +2. Through the `--env-file` option for `supabase functions serve`, for example: `supabase functions serve --env-file ./path/to/.env-file` + +To perform a one-time setup of your local development secrets, use the first option to create the `.env` file that will apply to all functions. + ### Default secrets By default, Edge Functions have access to these secrets: diff --git a/apps/docs/pages/guides/functions/troubleshooting.mdx b/apps/docs/pages/guides/functions/troubleshooting.mdx index d083f360f18..8e8449b1db3 100644 --- a/apps/docs/pages/guides/functions/troubleshooting.mdx +++ b/apps/docs/pages/guides/functions/troubleshooting.mdx @@ -8,7 +8,7 @@ export const meta = { ## Unable to call Edge Function -If you're unable to call your Edge Function or are experiencing any CORS issues, even though you followed the [CORS guide](/docs/guides/functions/cors), please head over to [app.supabase.com/project/\_/functions](https://app.supabase.com/project/_/functions), select your function from the list & click Logs. Do you see any errors listed there? +If you're unable to call your Edge Function or are experiencing any CORS issues, even though you followed the [CORS guide](/docs/guides/functions/cors), please head over to [supabase.com/dashboard/project/\_/functions](https://supabase.com/dashboard/project/_/functions), select your function from the list & click Logs. Do you see any errors listed there? There are two debugging tools available: Invocations and Logs. Invocations shows the Request and Response for each execution, while Logs shows any platform events, including deployments and errors. @@ -21,7 +21,7 @@ There are two debugging tools available: Invocations and Logs. Invocations shows ## Edge Function takes too long to respond -- Head over to [app.supabase.com/project/\_/functions](https://app.supabase.com/project/_/functions), select your function from the list & click Logs. +- Head over to [supabase.com/dashboard/project/\_/functions](https://supabase.com/dashboard/project/_/functions), select your function from the list & click Logs. - In the looks, look for the `booted` event and check if they have consistent boot times. - If the boot times are similar, it's likely an issue with your function's code, like a large dependency etc. - If only some of the `booted` events are slow, please find the affected `region` in the metadata and submit a support request via the "Help" button at the top. @@ -34,6 +34,31 @@ There are two debugging tools available: Invocations and Logs. Invocations shows - Search the [Edge Runtime](https://github.com/supabase/edge-runtime) and [CLI](https://github.com/supabase/cli) repos for the error message, to see if it has been reported before. - If the output from the commands above does not help you to resolve the issue, please open a support ticket via the Supabase Dashboard (by clicking the "Help" button at the top right) and include all output and details about your commands. +# Advanced Techniques + +## Checking Function Boot Time + +Check the logs for the function. In the logs, look for a "Booted" event and note the reported boot time. If available, click on the event to access more details, including the regions from where the function was served. Investigate if the boot time is excessively high (`higher than 1 second`) and note any patterns or regions where it occurs. + +## Finding Bundle Size + +To find the bundle size of a function, run the following command locally: + +```bash +deno info /path/to/function/index.ts +``` + +Look for the "size" field in the output which represents an approximated the bundle size of the function.You can find the accurate bundle size when you deploy your function via Supabase CLI. If the function is part of a larger application, consider examining the bundle size of the specific function independently. + +## Analyze Dependencies + +Review the dependencies listed in the output of the `deno info` command. Pay attention to any significantly large dependencies, as they can contribute to increased bundle size and potential boot time issues. +Examine if there are any unnecessary or redundant dependencies that can be removed. Check for outdated dependencies and recommend updating to the latest versions if applicable. When running deno info make sure to provide the correct path of the import map if you use one. + +```bash +deno info --import-map=/path/to/import_map.json /path/to/function/index.ts +``` + export const Page = ({ children }) => export default Page diff --git a/apps/docs/pages/guides/functions/unit-test.mdx b/apps/docs/pages/guides/functions/unit-test.mdx new file mode 100644 index 00000000000..9532e435858 --- /dev/null +++ b/apps/docs/pages/guides/functions/unit-test.mdx @@ -0,0 +1,140 @@ +import Layout from '~/layouts/DefaultGuideLayout' + +export const meta = { + id: 'unit-test', + title: 'Testing your Edge Functions', + description: 'Writing Unit Tests for Edge Functions using Deno Test', +} + +Testing is an essential step in the development process to ensure the correctness and performance of your Edge Functions. + +When creating test files for Edge Functions, it is recommended to place them in the `supabase/tests` directory and name the file by the function name followed by `-test.ts`. For example, `hello-world-test.ts`. + +```typescript +// deno-test.ts + +// Import required libraries and modules +import { + assert, + assertExists, + assertEquals, +} from 'https://deno.land/std@0.192.0/testing/asserts.ts' +import { createClient, SupabaseClient } from 'https://esm.sh/@supabase/supabase-js@2.23.0' +import { delay } from 'https://deno.land/x/delay@v0.2.0/mod.ts' + +// Set up the configuration for the Supabase client +const supabaseUrl = Deno.env.get('SUPABASE_URL') ?? '' +const supabaseKey = Deno.env.get('SUPABASE_ANON_KEY') ?? '' +const options = { + auth: { + autoRefreshToken: false, + persistSession: false, + detectSessionInUrl: false, + }, +} + +// Test the creation and functionality of the Supabase client +const testClientCreation = async () => { + var client: SupabaseClient = createClient(supabaseUrl, supabaseKey, options) + + // Verify if the Supabase URL and key are provided + if (!supabaseUrl) throw new Error('supabaseUrl is required.') + if (!supabaseKey) throw new Error('supabaseKey is required.') + + // Test a simple query to the database + const { data: table_data, error: table_error } = await client + .from('my_table') + .select('*') + .limit(1) + if (table_error) { + throw new Error('Invalid Supabase client: ' + table_error.message) + } + assert(table_data, 'Data should be returned from the query.') +} + +// Test the 'hello-world' function +const testHelloWorld = async () => { + var client: SupabaseClient = createClient(supabaseUrl, supabaseKey, options) + + // Invoke the 'hello-world' function with a parameter + const { data: func_data, error: func_error } = await client.functions.invoke('hello-world', { + body: { name: 'bar' }, + }) + + // Check for errors from the function invocation + if (func_error) { + throw new Error('Invalid response: ' + func_error.message) + } + + // Log the response from the function + console.log(JSON.stringify(func_data, null, 2)) + + // Assert that the function returned the expected result + assertEquals(func_data.message, 'Hello bar!') +} + +// Register and run the tests +Deno.test('Client Creation Test', testClientCreation) +Deno.test('Hello-world Function Test', testHelloWorld) +``` + +This test case consists of two parts. The first part tests the client library and verifies that the database can be connected to and returns values from a table (`my_table`). The second part tests the edge function and checks if the received value matches the expected value. Here's a brief overview of the code: + +- We import various testing functions from the Deno standard library, including `assert`, `assertExists`, and `assertEquals`. +- We import the `createClient` and `SupabaseClient` classes from the `@supabase/supabase-js` library to interact with the Supabase client. +- We define the necessary configuration for the Supabase client, including the Supabase URL, API key, and authentication options. +- The `testClientCreation` function tests the creation of a Supabase client instance and queries the database for data from a table. It verifies that data is returned from the query. +- The `testHelloWorld` function tests the "Hello-world" Edge Function by invoking it using the Supabase client's `functions.invoke` method. It checks if the response message matches the expected greeting. +- We run the tests using the `Deno.test` function, providing a descriptive name for each test case and the corresponding test function. + + + +Please make sure to replace the placeholders (`supabaseUrl`, `supabaseKey`, `my_table`) with the +actual values relevant to your Supabase setup. + + + +### Running Edge Functions Locally + +To locally test and debug Edge Functions, you can utilize the Supabase CLI. Let's explore how to run Edge Functions locally using the Supabase CLI: + +1. Ensure that the Supabase server is running by executing the following command: + + ```bash + supabase start + ``` + +2. In your terminal, use the following command to serve the Edge Functions locally: + + ```bash + supabase functions serve + ``` + + This command starts a local server that runs your Edge Functions, enabling you to test and debug them in a development environment. + +3. Create the environment variables file: + + ```bash + # creates the file + touch .env.local + # adds the SUPABASE_URL secret + echo "SUPABASE_URL=http://localhost:54321" >> .env.local + # adds the SUPABASE_ANON_KEY secret + echo "SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0" >> .env.local + # Alternatively, you can open it in your editor: + open .env.local + ``` + +4. To run the tests, use the following command in your terminal: + + ```bash + deno test --allow-all deno-test.ts --env-file .env.local + ``` + +## Resources + +- Full guide on Testing Supabase Edge Functions on [Mansueli's tips](https://blog.mansueli.com/testing-supabase-edge-functions-with-deno-test) + +export const Page = ({ children }) => + +export default Page diff --git a/apps/docs/pages/guides/getting-started.mdx b/apps/docs/pages/guides/getting-started.mdx index eeda3fbb035..0b5f90df3af 100644 --- a/apps/docs/pages/guides/getting-started.mdx +++ b/apps/docs/pages/guides/getting-started.mdx @@ -185,6 +185,13 @@ export const quickstarts = [ 'Learn how to create a Supabase project, add some sample data to your database, and query the data from a Vue app.', icon: '/docs/img/icons/vuejs-icon', }, + { + title: 'refine', + href: '/guides/getting-started/quickstarts/refine', + description: + 'Learn how to create a Supabase project, add some sample data to your database, and query the data from a refine app.', + icon: '/docs/img/icons/refine-icon', + }, ] export const webapps = [ @@ -244,6 +251,13 @@ export const webapps = [ 'Learn how to build a user management app with SvelteKit and Supabase Database, Auth, and Storage functionality.', icon: '/docs/img/icons/svelte-icon', }, + { + title: 'refine', + href: '/guides/getting-started/tutorials/with-refine', + description: + 'Learn how to build a user management app with refine and Supabase Database, Auth, and Storage functionality.', + icon: '/docs/img/icons/refine-icon', + }, ] export const mobile = [ diff --git a/apps/docs/pages/guides/getting-started/architecture.mdx b/apps/docs/pages/guides/getting-started/architecture.mdx index 9d2f24b5efe..4e7ebdf2504 100644 --- a/apps/docs/pages/guides/getting-started/architecture.mdx +++ b/apps/docs/pages/guides/getting-started/architecture.mdx @@ -140,7 +140,7 @@ Less, but better. ### Everything is portable To avoid lock-in, we make it easy to migrate in and out. Our cloud offering is compatible with our self-hosted product. -We use existing standards to increase portability (like pg_dump an CSV files). If a new standard emerges which competes with a "Supabase" approach, we will deprecate the approach in favor of the standard. +We use existing standards to increase portability (like pg_dump and CSV files). If a new standard emerges which competes with a "Supabase" approach, we will deprecate the approach in favor of the standard. This forces us compete on experience. We aim to be the best Postgres hosting service. ### Play the long game diff --git a/apps/docs/pages/guides/getting-started/quickstarts/flutter.mdx b/apps/docs/pages/guides/getting-started/quickstarts/flutter.mdx index ef3ad59dc21..56fa3a0e891 100644 --- a/apps/docs/pages/guides/getting-started/quickstarts/flutter.mdx +++ b/apps/docs/pages/guides/getting-started/quickstarts/flutter.mdx @@ -13,9 +13,9 @@ export const meta = { - [Create a new project](https://app.supabase.com) in the Supabase Dashboard. + [Create a new project](https://supabase.com/dashboard) in the Supabase Dashboard. - After your project is ready, create a table in your Supabase database using the [SQL Editor](https://app.supabase.com/project/_/sql) in the Dashboard. Use the following SQL statement to create a `countries` table with some sample data. + After your project is ready, create a table in your Supabase database using the [SQL Editor](https://supabase.com/dashboard/project/_/sql) in the Dashboard. Use the following SQL statement to create a `countries` table with some sample data. @@ -79,7 +79,7 @@ export const meta = { - Open `lib/main.dart` and edit the main function to initialize Supabase using your [project URL and public API (anon) key](https://app.supabase.com/project/_/settings/api). + Open `lib/main.dart` and edit the main function to initialize Supabase using your [project URL and public API (anon) key](https://supabase.com/dashboard/project/_/settings/api). diff --git a/apps/docs/pages/guides/getting-started/quickstarts/nextjs.mdx b/apps/docs/pages/guides/getting-started/quickstarts/nextjs.mdx index b8f12ac7d8a..b586d9ff914 100644 --- a/apps/docs/pages/guides/getting-started/quickstarts/nextjs.mdx +++ b/apps/docs/pages/guides/getting-started/quickstarts/nextjs.mdx @@ -13,9 +13,9 @@ export const meta = { - [Create a new project](https://app.supabase.com) in the Supabase Dashboard. + [Create a new project](https://supabase.com/dashboard) in the Supabase Dashboard. - After your project is ready, create a table in your Supabase database using the [SQL Editor](https://app.supabase.com/project/_/sql) in the Dashboard. + After your project is ready, create a table in your Supabase database using the [SQL Editor](https://supabase.com/dashboard/project/_/sql) in the Dashboard. Use the following SQL statement to create a `countries` table with some sample data. @@ -64,7 +64,7 @@ export const meta = { - Rename `.env.local.example` to `.env.local` and populate with [your project's URL and Anon Key](https://app.supabase.com/project/_/settings/api). + Rename `.env.local.example` to `.env.local` and populate with [your project's URL and Anon Key](https://supabase.com/dashboard/project/_/settings/api). diff --git a/apps/docs/pages/guides/getting-started/quickstarts/nuxtjs.mdx b/apps/docs/pages/guides/getting-started/quickstarts/nuxtjs.mdx index a3246924345..f30b5184fdb 100644 --- a/apps/docs/pages/guides/getting-started/quickstarts/nuxtjs.mdx +++ b/apps/docs/pages/guides/getting-started/quickstarts/nuxtjs.mdx @@ -13,9 +13,9 @@ export const meta = { - [Create a new project](https://app.supabase.com) in the Supabase Dashboard. + [Create a new project](https://supabase.com/dashboard) in the Supabase Dashboard. - After your project is ready, create a table in your Supabase database using the [SQL Editor](https://app.supabase.com/project/_/sql) in the Dashboard. Use the following SQL statement to create a `countries` table with some sample data. + After your project is ready, create a table in your Supabase database using the [SQL Editor](https://supabase.com/dashboard/project/_/sql) in the Dashboard. Use the following SQL statement to create a `countries` table with some sample data. @@ -78,7 +78,7 @@ export const meta = { - In `src/App.vue`, create a Supabase client using your [Project URL and public API (anon) key](https://app.supabase.com/project/_/settings/api). + In `src/App.vue`, create a Supabase client using your [Project URL and public API (anon) key](https://supabase.com/dashboard/project/_/settings/api). Replace the existing content in your `src/App.vue` file with the following code. diff --git a/apps/docs/pages/guides/getting-started/quickstarts/reactjs.mdx b/apps/docs/pages/guides/getting-started/quickstarts/reactjs.mdx index 02422ea1927..e56d031baa0 100644 --- a/apps/docs/pages/guides/getting-started/quickstarts/reactjs.mdx +++ b/apps/docs/pages/guides/getting-started/quickstarts/reactjs.mdx @@ -13,9 +13,9 @@ export const meta = { - [Create a new project](https://app.supabase.com) in the Supabase Dashboard. + [Create a new project](https://supabase.com/dashboard) in the Supabase Dashboard. - After your project is ready, create a table in your Supabase database using the [SQL Editor](https://app.supabase.com/project/_/sql) in the Dashboard. Use the following SQL statement to create a `countries` table with some sample data. + After your project is ready, create a table in your Supabase database using the [SQL Editor](https://supabase.com/dashboard/project/_/sql) in the Dashboard. Use the following SQL statement to create a `countries` table with some sample data. @@ -77,7 +77,7 @@ export const meta = { - In `App.jsx`, create a Supabase client using your [Project URL and public API (anon) key](https://app.supabase.com/project/_/settings/api). + In `App.jsx`, create a Supabase client using your [Project URL and public API (anon) key](https://supabase.com/dashboard/project/_/settings/api). Add a `getCountries` function to fetch the data and display the query result to the page. diff --git a/apps/docs/pages/guides/getting-started/quickstarts/redwoodjs.mdx b/apps/docs/pages/guides/getting-started/quickstarts/redwoodjs.mdx index 9398ef662c5..3c0a0f5f9f3 100644 --- a/apps/docs/pages/guides/getting-started/quickstarts/redwoodjs.mdx +++ b/apps/docs/pages/guides/getting-started/quickstarts/redwoodjs.mdx @@ -12,7 +12,7 @@ export const meta = { - [Create a new project](https://app.supabase.com) in the Supabase Dashboard. + [Create a new project](https://supabase.com/dashboard) in the Supabase Dashboard. Be sure to make note of the Database Password you used as you will need this later to connect to your database. @@ -27,7 +27,7 @@ export const meta = { - After your project is ready, gather the following information about your [database connections](https://app.supabase.com/project/_/settings/database): + After your project is ready, gather the following information about your [database connections](https://supabase.com/dashboard/project/_/settings/database): * Connection String (port 5432) * Connection Pooling / Connection String (port 6543) diff --git a/apps/docs/pages/guides/getting-started/quickstarts/refine.mdx b/apps/docs/pages/guides/getting-started/quickstarts/refine.mdx new file mode 100644 index 00000000000..4bc37f973df --- /dev/null +++ b/apps/docs/pages/guides/getting-started/quickstarts/refine.mdx @@ -0,0 +1,248 @@ +import Layout from '~/layouts/DefaultGuideLayout' +import StepHikeCompact from '~/components/StepHikeCompact' + +export const meta = { + title: 'Use Supabase with refine', + subtitle: + 'Learn how to create a Supabase project, add some sample data to your database, and query the data from a refine app.', + breadcrumb: 'Framework Quickstarts', +} + + + + + + + [Create a new project](https://app.supabase.com) in the Supabase Dashboard. + + After your project is ready, create a table in your Supabase database using the [SQL Editor](https://app.supabase.com/project/_/sql) in the Dashboard. Use the following SQL statement to create a `countries` table with some sample data. + + + + + + ```sql SQL_EDITOR + -- Create the table + CREATE TABLE countries ( + id SERIAL PRIMARY KEY, + name VARCHAR(255) NOT NULL + ); + -- Insert some sample data into the table + INSERT INTO countries (name) VALUES ('United States'); + INSERT INTO countries (name) VALUES ('Canada'); + INSERT INTO countries (name) VALUES ('Mexico'); + ```` + + + + + + + + + + Create a [refine](https://github.com/refinedev/refine) app using the [create refine-app](https://refine.dev/docs/getting-started/quickstart/). + + The `refine-supabase` preset adds `@refinedev/supabase` supplementary package that supports Supabase in a refine app. `@refinedev/supabase` out-of-the-box includes the Supabase dependency: [supabase-js](https://github.com/supabase/supabase-js). + + + + + + ```bash Terminal + npm create refine-app@latest -- --preset refine-supabase my-app + ``` + + + + + + + + + You will develop your app, connect to the Supabase backend and run the refine app in VS Code. + + + + + + ```bash Terminal + cd my-app + code . + ``` + + + + + + + + + Start the app, go to http://localhost:5173 in a browser, and you should be greeted with the refine Welcome page. + + + + + + ```bash Terminal + npm run dev + ``` + + + ![refine welcome page](/docs/img/refine-qs-welcome-page.png) + + + + + + + + + + You now have to update the `supabaseClient` with the `SUPABASE_URL` and `SUPABASE_KEY` of your Supabase API. The `supabaseClient` is used in auth provider and data provider methods that allow the refine app to connect to your Supabase backend. + + + + + + ```ts src/utility/supabaseClient.ts + import { createClient } from "@refinedev/supabase"; + + const SUPABASE_URL = YOUR_SUPABASE_URL; + const SUPABASE_KEY = YOUR_SUPABASE_KEY + + export const supabaseClient = createClient(SUPABASE_URL, SUPABASE_KEY, { + db: { + schema: "public", + }, + auth: { + persistSession: true, + }, + }); + ``` + + + + + + + + + You have to then configure resources and define pages for `countries` resource. + + Use the following command to automatically add resources and generate code for pages for `countries` using refine Inferencer. + + This defines pages for `list`, `create`, `show` and `edit` actions inside the `src/pages/countries/` directory with `` component. + + The `` component depends on `@refinedev/react-table` and `@refinedev/react-hook-form` packages. In order to avoid errors, you should install them as dependencies with `npm install @refinedev/react-table @refinedev/react-hook-form`. + + + The `` is a refine Inferencer component that automatically generates necessary code for the `list`, `create`, `show` and `edit` pages. + + More on [how the Inferencer works is available in the docs here](https://refine.dev/docs/packages/documentation/inferencer/). + + + + + + + + ```bash Terminal + npm run refine create-resource countries + ``` + + + + + + + + Add routes for the `list`, `create`, `show`, and `edit` pages. + + + You should remove the `index` route for the Welcome page presented with the `` component. + + + + + + ```tsx src/App.tsx + import { Refine, WelcomePage } from "@refinedev/core"; + import { RefineKbar, RefineKbarProvider } from "@refinedev/kbar"; + import routerBindings, { + DocumentTitleHandler, + NavigateToResource, + UnsavedChangesNotifier, + } from "@refinedev/react-router-v6"; + import { dataProvider, liveProvider } from "@refinedev/supabase"; + import { BrowserRouter, Route, Routes } from "react-router-dom"; + + import "./App.css"; + import authProvider from "./authProvider"; + import { supabaseClient } from "./utility"; + import { CountriesCreate, CountriesEdit, CountriesList, CountriesShow } from "./pages/countries"; + + function App() { + return ( + + + + + } + /> + + } /> + } /> + } /> + } /> + + + + + + + + + ); + } + + export default App; + ``` + + + + + + + + Now you should be able to see the countries pages along the `/countries` routes. You may now edit and add new countries using the Inferencer generated UI. + + The Inferencer auto-generated code gives you a good starting point on which to keep building your `list`, `create`, `show` and `edit` pages. They can be obtained by clicking the `Show the auto-generated code` buttons in their respective pages. + + + + ![refine List Page](/docs/img/refine-qs-countries-ui.png) + + + + + +export const Page = ({ children }) => + +export default Page diff --git a/apps/docs/pages/guides/getting-started/quickstarts/solidjs.mdx b/apps/docs/pages/guides/getting-started/quickstarts/solidjs.mdx index 29890f4e269..76a7819cc8d 100644 --- a/apps/docs/pages/guides/getting-started/quickstarts/solidjs.mdx +++ b/apps/docs/pages/guides/getting-started/quickstarts/solidjs.mdx @@ -13,9 +13,9 @@ export const meta = { - [Create a new project](https://app.supabase.com) in the Supabase Dashboard. + [Create a new project](https://supabase.com/dashboard) in the Supabase Dashboard. - After your project is ready, create a table in your Supabase database using the [SQL Editor](https://app.supabase.com/project/_/sql) in the Dashboard. Use the following SQL statement to create a `countries` table with some sample data. + After your project is ready, create a table in your Supabase database using the [SQL Editor](https://supabase.com/dashboard/project/_/sql) in the Dashboard. Use the following SQL statement to create a `countries` table with some sample data. @@ -77,7 +77,7 @@ export const meta = { - In `App.jsx`, create a Supabase client using your [Project URL and public API (anon) key](https://app.supabase.com/project/_/settings/api). + In `App.jsx`, create a Supabase client using your [Project URL and public API (anon) key](https://supabase.com/dashboard/project/_/settings/api). Add a `getCountries` function to fetch the data and display the query result to the page. diff --git a/apps/docs/pages/guides/getting-started/quickstarts/sveltekit.mdx b/apps/docs/pages/guides/getting-started/quickstarts/sveltekit.mdx index f4989365455..80f68142466 100644 --- a/apps/docs/pages/guides/getting-started/quickstarts/sveltekit.mdx +++ b/apps/docs/pages/guides/getting-started/quickstarts/sveltekit.mdx @@ -13,9 +13,9 @@ export const meta = { - [Create a new project](https://app.supabase.com) in the Supabase Dashboard. + [Create a new project](https://supabase.com/dashboard) in the Supabase Dashboard. - After your project is ready, create a table in your Supabase database using the [SQL Editor](https://app.supabase.com/project/_/sql) in the Dashboard. Use the following SQL statement to create a `countries` table with some sample data. + After your project is ready, create a table in your Supabase database using the [SQL Editor](https://supabase.com/dashboard/project/_/sql) in the Dashboard. Use the following SQL statement to create a `countries` table with some sample data. @@ -77,7 +77,7 @@ export const meta = { - Create a `/src/lib` directory in your SvelteKit app, create a file called `supabaseClient.js` and add the following code to initialize the Supabase client with your [project URL and public API (anon) key](https://app.supabase.com/project/_/settings/api). + Create a `/src/lib` directory in your SvelteKit app, create a file called `supabaseClient.js` and add the following code to initialize the Supabase client with your [project URL and public API (anon) key](https://supabase.com/dashboard/project/_/settings/api). @@ -128,12 +128,10 @@ export const meta = { ```svelte src/routes/+page.svelte
    - {#each countries as country} + {#each data.countries as country}
  • {country.name}
  • {/each}
diff --git a/apps/docs/pages/guides/getting-started/quickstarts/vue.mdx b/apps/docs/pages/guides/getting-started/quickstarts/vue.mdx index b353d6ef35d..adb2715d03c 100644 --- a/apps/docs/pages/guides/getting-started/quickstarts/vue.mdx +++ b/apps/docs/pages/guides/getting-started/quickstarts/vue.mdx @@ -13,9 +13,9 @@ export const meta = { - [Create a new project](https://app.supabase.com) in the Supabase Dashboard. + [Create a new project](https://supabase.com/dashboard) in the Supabase Dashboard. - After your project is ready, create a table in your Supabase database using the [SQL Editor](https://app.supabase.com/project/_/sql) in the Dashboard. Use the following SQL statement to create a `countries` table with some sample data. + After your project is ready, create a table in your Supabase database using the [SQL Editor](https://supabase.com/dashboard/project/_/sql) in the Dashboard. Use the following SQL statement to create a `countries` table with some sample data. @@ -77,7 +77,7 @@ export const meta = { - Create a `/src/lib` directory in your Vue app, create a file called `supabaseClient.js` and add the following code to initialize the Supabase client with your [project URL and public API (anon) key](https://app.supabase.com/project/_/settings/api). + Create a `/src/lib` directory in your Vue app, create a file called `supabaseClient.js` and add the following code to initialize the Supabase client with your [project URL and public API (anon) key](https://supabase.com/dashboard/project/_/settings/api). diff --git a/apps/docs/pages/guides/getting-started/tutorials/with-flutter.mdx b/apps/docs/pages/guides/getting-started/tutorials/with-flutter.mdx index 70e6ef4c153..36cd58b94e3 100644 --- a/apps/docs/pages/guides/getting-started/tutorials/with-flutter.mdx +++ b/apps/docs/pages/guides/getting-started/tutorials/with-flutter.mdx @@ -48,7 +48,7 @@ We can setup deep links with just a minor tweak on our Flutter application. We will use `io.supabase.flutterquickstart` as the scheme, and `login-callback` as the host for our deep link in this example, but you can change it to whatever you would like. -First, add `io.supabase.flutterquickstart://login-callback/` as a new [redirect URL](https://app.supabase.com/project/_/auth/url-configuration) in the Dashboard. +First, add `io.supabase.flutterquickstart://login-callback/` as a new [redirect URL](https://supabase.com/dashboard/project/_/auth/url-configuration) in the Dashboard. ![Supabase console deep link setting](/docs/img/deeplink-setting.png) diff --git a/apps/docs/pages/guides/getting-started/tutorials/with-nextjs.mdx b/apps/docs/pages/guides/getting-started/tutorials/with-nextjs.mdx index 108325ba6d7..29c255f5a4c 100644 --- a/apps/docs/pages/guides/getting-started/tutorials/with-nextjs.mdx +++ b/apps/docs/pages/guides/getting-started/tutorials/with-nextjs.mdx @@ -978,7 +978,7 @@ At this stage you have a fully functional application! ## See also -- See the complete [example on GitHub](https://github.com/supabase/supabase/tree/master/examples/user-management/nextjs-ts-user-management) and deploy it to Vercel +- See the complete [example on GitHub](https://github.com/supabase/supabase/tree/master/examples/user-management/nextjs-user-management) and deploy it to Vercel - Explore the [pre-built Auth UI for React](/docs/guides/auth/auth-helpers/auth-ui) - Explore the [Auth Helpers for Next.js](/docs/guides/auth/auth-helpers/nextjs) - Explore the [Supabase Cache Helpers](https://github.com/psteinroe/supabase-cache-helpers) diff --git a/apps/docs/pages/guides/getting-started/tutorials/with-react.mdx b/apps/docs/pages/guides/getting-started/tutorials/with-react.mdx index 303ff7f583f..18030dc0b6e 100644 --- a/apps/docs/pages/guides/getting-started/tutorials/with-react.mdx +++ b/apps/docs/pages/guides/getting-started/tutorials/with-react.mdx @@ -158,7 +158,7 @@ export default function Account({ session }) { getProfile() }, [session]) - async function updateProfile(event) { + async function updateProfile(event, avatarUrl) { event.preventDefault() setLoading(true) @@ -168,7 +168,7 @@ export default function Account({ session }) { id: user.id, username, website, - avatar_url, + avatarUrl, updated_at: new Date(), } @@ -176,6 +176,8 @@ export default function Account({ session }) { if (error) { alert(error.message) + } else { + setAvatarUrl(avatarUrl) } setLoading(false) } @@ -378,8 +380,7 @@ return ( url={avatar_url} size={150} onUpload={(event, url) => { - setAvatarUrl(url) - updateProfile(event) + updateProfile(event, url) }} /> {/* ... */} diff --git a/apps/docs/pages/guides/getting-started/tutorials/with-redwoodjs.mdx b/apps/docs/pages/guides/getting-started/tutorials/with-redwoodjs.mdx index c3428ee814e..375198da2f4 100644 --- a/apps/docs/pages/guides/getting-started/tutorials/with-redwoodjs.mdx +++ b/apps/docs/pages/guides/getting-started/tutorials/with-redwoodjs.mdx @@ -14,27 +14,6 @@ export const meta = { GitHub](https://github.com/redwoodjs/redwoodjs-supabase-quickstart).
-{/* ## Video demo */} - -{/* Coming soon. */} - -{/* Use the annotated timeline to step through this tutorial. */} - -{/* ## Instant deploy */} - -{/* Want to deploy a completed version of this example without following the guide? */} - -{/* [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/git/external?repository-url=https%3A%2F%2Fgithub.com%2Fsupabase%2Fsupabase%2Ftree%2Fmaster%2Fexamples%2Freact-user-management&project-name=supabase-user-management&repository-name=supabase-user-management&demo-title=Supabase%20User%20Management&demo-description=An%20example%20web%20app%20using%20Supabase%20and%20Next.js&demo-url=https%3A%2F%2Fsupabase-react-user-management.vercel.app&demo-image=https%3A%2F%2Fi.imgur.com%2FZ3HkQqe.png&integration-ids=oac_jUduyjQgOyzev1fjrW83NYOv&external-id=nextjs-user-management) */} - -{/* Clicking this button the application will: */} - -{/* - Launch and prepare the Postgres database in Supabase. */} -{/* - Launch the app in Vercel. */} -{/* - Fork the example into your own GitHub account. */} -{/* - Prepare the deployed application with all the necessary environment variables. */} - -{/* If you want to do it yourself, let's get started:` */} - ## About RedwoodJS A Redwood application is split into two parts: a frontend and a backend. This is represented as two node projects within a single monorepo. diff --git a/apps/docs/pages/guides/getting-started/tutorials/with-refine.mdx b/apps/docs/pages/guides/getting-started/tutorials/with-refine.mdx new file mode 100644 index 00000000000..5c1487153f2 --- /dev/null +++ b/apps/docs/pages/guides/getting-started/tutorials/with-refine.mdx @@ -0,0 +1,638 @@ +import Layout from '~/layouts/DefaultGuideLayout' + +export const meta = { + title: 'Build a User Management App with refine', + description: 'Learn how to use Supabase in your refine App.', +} + + + +![Supabase User Management example](/docs/img/user-management-demo.png) + + + If you get stuck while working through this guide, refer to the [full example on + GitHub](https://github.com/supabase/supabase/tree/master/examples/user-management/refine-user-management). + + +## About refine + +[refine](https://github.com/refinedev/refine) is a React-based framework used to rapidly build data-heavy applications like admin panels, dashboards, storefronts and any type of CRUD apps. It separates app concerns into individual layers, each backed by a React context and respective provider object. For example, the auth layer represents a context served by a specific set of [`authProvider`](https://refine.dev/docs/tutorial/understanding-authprovider/index/) methods that carry out authentication and authorization actions such as logging in, logging out, getting roles data, etc. Similarly, the data layer offers another level of abstraction that is equipped with [`dataProvider`](https://refine.dev/docs/tutorial/understanding-dataprovider/index/) methods to handle CRUD operations at appropriate backend API endpoints. + +refine provides hassle-free integration with Supabase backend with its supplementary [`@refinedev/supabase`](https://github.com/refinedev/refine/tree/master/packages/supabase) package. It generates `authProvider` and `dataProvider` methods at project initialization, so we don't need to expend much effort to define them ourselves. We just need to choose Supabase as our backend service while creating the app with `create refine-app`. + + + It is possible to customize the `authProvider` for Supabase and as we'll see below, it can be + tweaked from `src/authProvider.ts` file. In contrast, the Supabase `dataProvider` is part of + `node_modules` and therefore is not subject to modification. + + + + +## Building the App + +Let's start building the refine app from scratch. + +### Initialize a refine app + +We can use [create refine-app](https://refine.dev/docs/tutorial/getting-started/headless/create-project/#launch-the-refine-cli-setup) command to initialize +an app. Run the following in the terminal: + +```bash +npm create refine-app@latest -- --preset refine-supabase +``` + +In the above command, we are using the `refine-supabase` preset which chooses the Supabase supplementary package for our app. We are not using any UI framework, so we'll have a headless UI with plain React and CSS styling. + +The `refine-supabase` preset installs the `@refinedev/supabase` package which out-of-the-box includes the Supabase dependency: [supabase-js](https://github.com/supabase/supabase-js). + +We also need to install `@refinedev/react-hook-form` and `react-hook-form` packages that allow us to use [React Hook Form](https://react-hook-form.com) inside refine apps. Run: + +```bash +npm install @refinedev/react-hook-form react-hook-form +``` + +With the app initialized and packages installed, at this point before we begin discussing refine concepts, let's try running the app: + +```bash +cd app-name +npm run dev +``` + +We should have a running instance of the app with a Welcome page at `http://localhost:5173`. + +Let's move ahead to understand the generated code now. + +### refine `supabaseClient` + +The `create refine-app` generated a Supabase client for us in the `src/utility/supabaseClient.ts` file. It has two constants: `SUPABASE_URL` and `SUPABASE_KEY`. We want to replace them as `supabaseUrl` and `supabaseAnonKey` respectively and assign them our own Supabase server's values. + +We'll update it with environment variables managed by Vite: + +```ts src/utility/supabaseClient.ts +import { createClient } from '@refinedev/supabase' + +const supabaseUrl = import.meta.env.VITE_SUPABASE_URL +const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY + +export const supabaseClient = createClient(supabaseUrl, supabaseAnonKey, { + db: { + schema: 'public', + }, + auth: { + persistSession: true, + }, +}) +``` + +And then, we want to save the environment variables in a `.env.local` file. All you need are the API URL and the `anon` key that you copied [earlier](#get-the-api-keys). + +```bash .env.local +VITE_SUPABASE_URL=YOUR_SUPABASE_URL +VITE_SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY +``` + +The `supabaseClient` will be used in fetch calls to Supabase endpoints from our app. As we'll see below, the client is instrumental in implementing authentication using refine's auth provider methods and CRUD actions with appropriate data provider methods. + +One optional step is to update the CSS file `src/App.css` to make the app look nice. +You can find the full contents of this file [here](https://raw.githubusercontent.com/supabase/supabase/master/examples/user-management/refine-user-management/src/App.css). + +In order for us to add login and user profile pages in this App, we have to tweak the `` component inside `App.tsx`. + +### The `` Component + +The `App.tsx` file initially looks like this: + +```tsx src/App.tsx +import { Refine, WelcomePage } from '@refinedev/core' +import { RefineKbar, RefineKbarProvider } from '@refinedev/kbar' +import routerBindings, { + DocumentTitleHandler, + UnsavedChangesNotifier, +} from '@refinedev/react-router-v6' +import { dataProvider, liveProvider } from '@refinedev/supabase' +import { BrowserRouter, Route, Routes } from 'react-router-dom' + +import './App.css' +import authProvider from './authProvider' +import { supabaseClient } from './utility' + +function App() { + return ( + + + + + } /> + + + + + + + + ) +} + +export default App +``` + +We'd like to focus on the [``](https://refine.dev/docs/api-reference/core/components/refine-config/) component, which comes with several props passed to it. Notice the `dataProvider` prop. It uses a `dataProvider()` function with `supabaseClient` passed as argument to generate the data provider object. The `authProvider` object also uses `supabaseClient` in implementing its methods. You can look it up in `src/authProvider.ts` file. + +## Customize `authProvider` + +If you examine the `authProvider` object you can notice that it has a `login` method that implements a OAuth and Email / Password strategy for authentication. We'll, however, remove them and use Magic Links to allow users sign in with their email without using passwords. + +We want to use `supabaseClient` auth's `signInWithOtp` method inside `authProvider.login` method: + +```ts +login: async ({ email }) => { + try { + const { error } = await supabaseClient.auth.signInWithOtp({ email }); + + if (!error) { + alert("Check your email for the login link!"); + return { + success: true, + }; + }; + + throw error; + } catch (e: any) { + alert(e.message); + return { + success: false, + e, + }; + } +}, +``` + +We also want to remove `register`, `updatePassword`, `forgotPassword` and `getPermissions` properties, which are optional type members and also not necessary for our app. The final `authProvider` object looks like this: + +```ts src/authProvider.ts +import { AuthBindings } from '@refinedev/core' + +import { supabaseClient } from './utility' + +const authProvider: AuthBindings = { + login: async ({ email }) => { + try { + const { error } = await supabaseClient.auth.signInWithOtp({ email }) + + if (!error) { + alert('Check your email for the login link!') + return { + success: true, + } + } + + throw error + } catch (e: any) { + alert(e.message) + return { + success: false, + e, + } + } + }, + logout: async () => { + const { error } = await supabaseClient.auth.signOut() + + if (error) { + return { + success: false, + error, + } + } + + return { + success: true, + redirectTo: '/', + } + }, + onError: async (error) => { + console.error(error) + return { error } + }, + check: async () => { + try { + const { data } = await supabaseClient.auth.getSession() + const { session } = data + + if (!session) { + return { + authenticated: false, + error: { + message: 'Check failed', + name: 'Session not found', + }, + logout: true, + redirectTo: '/login', + } + } + } catch (error: any) { + return { + authenticated: false, + error: error || { + message: 'Check failed', + name: 'Not authenticated', + }, + logout: true, + redirectTo: '/login', + } + } + + return { + authenticated: true, + } + }, + getIdentity: async () => { + const { data } = await supabaseClient.auth.getUser() + + if (data?.user) { + return { + ...data.user, + name: data.user.email, + } + } + + return null + }, +} + +export default authProvider +``` + +### Set up a Login component + +We have chosen to use the headless refine core package that comes with no supported UI framework. So, let's set up a plain React component to manage logins and sign ups. + +Create and edit `src/components/auth.tsx`: + +```ts src/components/auth.tsx +import { useState } from 'react' +import { useLogin } from '@refinedev/core' + +export default function Auth() { + const [email, setEmail] = useState('') + const { isLoading, mutate: login } = useLogin() + + const handleLogin = async (event: { preventDefault: () => void }) => { + event.preventDefault() + login({ email }) + } + + return ( +
+
+

Supabase + refine

+

Sign in via magic link with your email below

+
+
+ setEmail(e.target.value)} + /> +
+
+ +
+
+
+
+ ) +} +``` + +Notice we are using the [`useLogin()`](https://refine.dev/docs/api-reference/core/hooks/authentication/useLogin/) refine auth hook to grab the `mutate: login` method to use inside `handleLogin()` function and `isLoading` state for our form submission. The `useLogin()` hook conveniently offers us access to `authProvider.login` method for authenticating the user with OTP. + +### Account page + +After a user is signed in we can allow them to edit their profile details and manage their account. + +Let's create a new component for that in `src/components/account.tsx`. + +```tsx src/components/account.tsx +import { BaseKey, useGetIdentity, useLogout } from '@refinedev/core' +import { useForm } from '@refinedev/react-hook-form' + +interface IUserIdentity { + id?: BaseKey + username: string + name: string +} + +export interface IProfile { + id?: string + username?: string + website?: string + avatar_url?: string +} + +export default function Account() { + const { data: userIdentity } = useGetIdentity() + + const { mutate: logOut } = useLogout() + + const { + refineCore: { formLoading, queryResult, onFinish }, + register, + control, + handleSubmit, + } = useForm({ + refineCoreProps: { + resource: 'profiles', + action: 'edit', + id: userIdentity?.id, + redirect: false, + onMutationError: (data) => alert(data?.message), + }, + }) + + return ( +
+
+
+ + +
+
+ + +
+
+ + +
+ +
+ +
+ +
+ +
+
+
+ ) +} +``` + +Notice above that, we are using three refine hooks, namely the [`useGetIdentity()`](https://refine.dev/docs/api-reference/core/hooks/authentication/useGetIdentity/), [`useLogOut()`](https://refine.dev/docs/api-reference/core/hooks/authentication/useLogout/) and [`useForm()`](https://refine.dev/docs/packages/documentation/react-hook-form/useForm/) hooks. + +`useGetIdentity()` is a auth hook that gets the identity of the authenticated user. It grabs the current user by invoking the `authProvider.getIdentity` method under the hood. + +`useLogOut()` is also an auth hook. It calls the `authProvider.logout` method to end the session. + +`useForm()`, in contrast, is a data hook that exposes a series of useful objects that serve the edit form. For example, we are grabbing the `onFinish` function to submit the form with the `handleSubmit` event handler. We are aslo using `formLoading` property to present state changes of the submitted form. + +The `useForm()` hook is a higher-level hook built on top of refine's `useForm()` core hook. It fully supports form state management, field validation and submission using React Hook Form. Behind the scenes, it invokes the `dataProvider.getOne` method to get the user profile data from our Supabase `/profiles` endpoint and also invokes `dataProvider.update` method when `onFinish()` is called. + +### Launch! + +Now that we have all the components in place, let's define the routes for the pages in which they should be rendered. + +Add the routes for `/login` with the `` component and the routes for `index` path with the `` component. So, the final `App.tsx`: + +```tsx src/App.tsx +import { Authenticated, Refine } from '@refinedev/core' +import { RefineKbar, RefineKbarProvider } from '@refinedev/kbar' +import routerBindings, { + CatchAllNavigate, + DocumentTitleHandler, + UnsavedChangesNotifier, +} from '@refinedev/react-router-v6' +import { dataProvider, liveProvider } from '@refinedev/supabase' +import { BrowserRouter, Outlet, Route, Routes } from 'react-router-dom' + +import './App.css' +import authProvider from './authProvider' +import { supabaseClient } from './utility' +import Account from './components/account' +import Auth from './components/auth' + +function App() { + return ( + + + + + }> + + + } + > + } /> + + } />}> + } /> + + + + + + + + + ) +} + +export default App +``` + +Let's test the App by running the server again: + +```bash +npm run dev +``` + +And then open the browser to [localhost:5173](http://localhost:5173) and you should see the completed app. + +![Supabase refine](/docs/img/supabase-refine-demo.png) + +## Bonus: Profile photos + +Every Supabase project is configured with [Storage](/docs/guides/storage) for managing large files like photos and videos. + +### Create an upload widget + +Let's create an avatar for the user so that they can upload a profile photo. We can start by creating a new component: + +Create and edit `src/components/avatar.tsx`: + +```tsx src/components/avatar.tsx +import { useEffect, useState } from 'react' +import { supabaseClient } from '../utility/supabaseClient' + +type TAvatarProps = { + url?: string + size: number + onUpload: (filePath: string) => void +} + +export default function Avatar({ url, size, onUpload }: TAvatarProps) { + const [avatarUrl, setAvatarUrl] = useState('') + const [uploading, setUploading] = useState(false) + + useEffect(() => { + if (url) downloadImage(url) + }, [url]) + + async function downloadImage(path: string) { + try { + const { data, error } = await supabaseClient.storage.from('avatars').download(path) + if (error) { + throw error + } + const url = URL.createObjectURL(data) + setAvatarUrl(url) + } catch (error: any) { + console.log('Error downloading image: ', error?.message) + } + } + + async function uploadAvatar(event: React.ChangeEvent) { + try { + setUploading(true) + + if (!event.target.files || event.target.files.length === 0) { + throw new Error('You must select an image to upload.') + } + + const file = event.target.files[0] + const fileExt = file.name.split('.').pop() + const fileName = `${Math.random()}.${fileExt}` + const filePath = `${fileName}` + + const { error: uploadError } = await supabaseClient.storage + .from('avatars') + .upload(filePath, file) + + if (uploadError) { + throw uploadError + } + onUpload(filePath) + } catch (error: any) { + alert(error.message) + } finally { + setUploading(false) + } + } + + return ( +
+ {avatarUrl ? ( + Avatar + ) : ( +
+ )} +
+ + +
+
+ ) +} +``` + +### Add the new widget + +And then we can add the widget to the Account page at `src/components/account.tsx`: + +```tsx src/components/account.tsx +// Import the new components +import { Controller } from 'react-hook-form' +import Avatar from './avatar' + +// ... + +return ( +
+
+ { + return ( + { + onFinish({ + ...queryResult?.data?.data, + avatar_url: filePath, + onMutationError: (data: { message: string }) => alert(data?.message), + }) + field.onChange({ + target: { + value: filePath, + }, + }) + }} + /> + ) + }} + /> + {/* ... */} + +
+) +``` + +### Storage management + + + +At this stage, you have a fully functional application! + +export const Page = ({ children }) => + +export default Page diff --git a/apps/docs/pages/guides/integrations.mdx b/apps/docs/pages/guides/integrations.mdx deleted file mode 100644 index e23bc20609b..00000000000 --- a/apps/docs/pages/guides/integrations.mdx +++ /dev/null @@ -1,41 +0,0 @@ -import Layout from '~/layouts/DefaultLayout' -import Link from 'next/link' -import Image from 'next/image' -import { GlassPanel } from 'ui' -import { integrations } from '~/components/Navigation/NavigationMenu/NavigationMenu.constants' - -export const meta = { - title: 'Integrations', -} - -Explore a variety of integrations from Supabase partners. Need a different integration? Find a [Supabase expert](https://supabase.com/partners/experts) to help build your next idea. - -
- {integrations.items.map((item) => ( -
-

{item.name}

-
- {item.items?.map((integration) => ( - - - } - > - {integration.description} - - - - - ))} - -
- -
- ))} -
- -export const Page = ({ children }) => - -export default Page diff --git a/apps/docs/pages/guides/integrations/appsmith.mdx b/apps/docs/pages/guides/integrations/appsmith.mdx deleted file mode 100644 index f65602ef9fc..00000000000 --- a/apps/docs/pages/guides/integrations/appsmith.mdx +++ /dev/null @@ -1,173 +0,0 @@ -import Layout from '~/layouts/DefaultGuideLayout' - -export const meta = { - id: 'appsmith', - title: 'Appsmith', - description: - 'Get started with Supabase and Appsmith, an open-source framework for building internal tools.', -} - -This guide explains how to quickly build a Support Dashboard by connecting a Supabase back-end to an Appsmith front-end. - -[Appsmith](https://www.appsmith.com/) is an open-source framework for building internal tools. It lets you drag-and-drop UI components to build pages, connect to any API, database or GraphQL source and write logic with JavaScript objects. - -If you don’t have an Appsmith account, create one [here](https://app.appsmith.com/user/signup). - -Let’s get started! - -## Step 1: Set up your Backend on Supabase - -- On the [Supabase dashboard](https://app.supabase.com), click `New project` and set the name to **Support Dashboard** - -![create-project-supabase-01](/docs/img/guides/integrations/appsmith/create-project-supabase-01.png) - -- Create a new table by clicking on the Create Table option on the side navigation. -- Supabase provides many ways to add data to the tables, from writing queries to creating schemas using UI to simply uploading CSV files. For our support dashboard, we will be creating the **tickets** table by uploading the [CSV file](https://raw.githubusercontent.com/vihar/datasets/master/tickets.csv) on Supabase. - -![create-table-supabase-02](/docs/img/guides/integrations/appsmith/create-table-supabase-02.png) - -The database is now set up. - -## Step 2: Connect the database to Appsmith - -- Note down the database connection information under Project Settings in Supabase. - -![project-settings-supabase-03](/docs/img/guides/integrations/appsmith/project-settings-supabase-03.png) - -- On Appsmith, create a new application under the dashboard under your preferred organization. -- Click on the `+` icon next to Datasources on the left navigation bar under Page1 -- Next, click on Create New tab and choose PostgreSQL datasource, you’ll see the following screenshot: - -![create-datasource-appsmith-04](/docs/img/guides/integrations/appsmith/create-datasource-appsmith-04.png) - -- Fill out the form to connect to your Supabase instance. Click **Test** to test connection and then **Save** to save the datasource - -![connect-supabase-datasource-05](/docs/img/guides/integrations/appsmith/connect-supabase-datasource-05.png) - -## Step 3: Build UI on Appsmith - -- Click on the + icon next to widgets and drag and drop a Tab widget. We can configure using the property pane by clicking on the cog icon on the top-right corner. -- As seen in the below screenshot, we have added four tabs to support the dashboard. - -![property-pane-appsmith-06](/docs/img/guides/integrations/appsmith/property-pane-appsmith-06.png) - -- Add widgets to the **Home** tab to create the dashboard as shown in the screenshot below. For eg: **Critical Open Issues** is a **Text** widget and below it is an **Input** widget which we will bind later to display the number of open tickets. - -- Set up the **New** button to open a modal which will have a form to raise a new ticket. - -![bind-query-appsmith-07](/docs/img/guides/integrations/appsmith/bind-query-appsmith-07.png) - -- In the modal widget, add a few widgets to accept input when creating a new ticket. Please refer to the screenshot below. - -![modal-appsmith-08](/docs/img/guides/integrations/appsmith/modal-appsmith-08.png) - -## Step 4: Writing Queries in Appsmith and binding data to widgets - -- Click on the + icon next to Datasources on the navigation bar and click New Query next to the Supabase connection here to create a new query. - -![create-query-appsmith-09](/docs/img/guides/integrations/appsmith/create-query-appsmith-09.png) - -- Rename the query to create_new_ticket under the query pane; here we can write SQL that can collect the data from the widgets using mustache templates. - -```jsx -INSERT INTO PUBLIC."tickets"("id","createdAt","user","updatedAt","description", -"status","priority","category","assignedTo") -VALUES('{{appsmith.store.ticket.id}}','{{moment().format('yyyy-mm-ddHH:MM:ss')}}','{{c_user.text}}', -'{{moment().format('yyyy-mm-ddHH:MM:ss')}}','{{c_description.text}}','{{c_status.selectedOptionValue}}', -'{{c_property.selectedOptionValue}}', -'{{c_category.selectedOptionValue}}','{{c_assignee.selectedOptionValue}}'); -``` - -- Click the **Confirm** button on the modal and under **Events**, set the **onClick** property to execute the create_new_ticket query. -- Create a second query named **get_tickets** that will list all the tickets. - - *** - -```jsx -SELECT * FROM public."tickets"; -``` - -- Drag and drop a table widget under the **Assigned To Me** tab. Open the property pane and add the following snippet under **Table Data** to bind the query results. - -```jsx -{ - { - get_tickets.data.filter( - (t) => t.assignedTo === 'confidence@appsmith.com' && t.status !== 'closed' - ) - } -} -``` - -- Drag and drop a table widget under the **Resolved** tab. Open the property pane and add the following snippet under **Table Data** to bind the query results. - -```jsx -{ - { - get_tickets.data.filter((t) => t.status === 'open') - } -} -``` - -- Drag and drop a table widget under the **Closed** tab. Open the property pane and add the following snippet under **Table Data** to bind the query results. - -```jsx -{ - { - get_tickets.data.filter((t) => t.status === 'closed') - } -} -``` - -## Step 5: Creating Charts in Appsmith - -- On the **Home** tab, click on the first Chart widget. Add Title **Open Issues By Category**. Change the **Chart Type** property to **Column Chart**. -- Update the x-axis and y-axis Labels under **Axis** on the property pane. -- Add the following code snippet under the **Series Data** property to bind the data to be displayed on the x and y axes. - -```jsx -[ - { - "x": "Hardware", - "y": {{get_tickets.data.filter(t => t.status==='open' && t.category==='hardware').length}} - }, - { - "x": "Software", - "y": {{get_tickets.data.filter(t => t.status==='open' && t.category==='software').length}} - }, - { - "x": "Other", - "y": {{get_tickets.data.filter(t => t.status==='open' && t.category==='other').length}} - } -] -``` - -- The second chart will be a pie chart. Add the title, axes labels as mentioned above, -- Add the following code snippet under the **Series Data** property of the pie chart. - -```jsx -[ - { - "x": "High", - "y": {{get_tickets.data.filter(t => t.status==='open' && t.priority==='high').length}} - }, - { - "x": "Medium", - "y": {{get_tickets.data.filter(t => t.status==='open' && t.priority==='medium').length}} - }, - { - "x": "Low", - "y": {{get_tickets.data.filter(t => t.status==='open' && t.priority==='low').length}} - } -] -``` - -## Resources - -- [Appsmith](https://www.appsmith.com/) official website. -- [Appsmith GitHub](https://github.com/appsmithorg). -- [Appsmith](https://docs.appsmith.com/) documentation. - -export const Page = ({ children }) => - -export default Page diff --git a/apps/docs/pages/guides/integrations/auth0.mdx b/apps/docs/pages/guides/integrations/auth0.mdx deleted file mode 100644 index f22ce35ba82..00000000000 --- a/apps/docs/pages/guides/integrations/auth0.mdx +++ /dev/null @@ -1,470 +0,0 @@ -import Layout from '~/layouts/DefaultGuideLayout' - -export const meta = { - id: 'auth0', - title: 'Auth0', - description: - 'Swap out Supabase authentication with Auth0. Let Auth0 handle tokens and signing users in and out, while Supabase enforces authorization policies with Row Level Security (RLS).', -} - -This guide steps through building a Next.js application with Auth0 and Supabase. We configure Auth0 to handle authenticating users and managing tokens, while writing our authorization logic in Supabase - using Row Level Security policies. - -> Note: This guide is heavily inspired by the [Using Next.js and Auth0 with Supabase](https://auth0.com/blog/using-nextjs-and-auth0-with-supabase/) article on [Auth0's blog](https://auth0.com/blog/). Check it out for a practical step-by-step guide on integrating Auth0 and Supabase. - -The full code example for this guide can be found [here](https://github.com/dijonmusters/supabase-auth0-example). - -[Auth0](https://auth0.com/) is an authentication and authorization platform, offering numerous strategies to authenticate and manage users. It provides fine-grain control over how users sign in to your application, the token that is generated, and what data is stored about your users. - -[Next.js](https://nextjs.org/) is a web application framework built on top of React. We will be using it for this example, as it allows us to write server-side logic within our application. Auth0 have also written a [very well integrated authentication library](https://www.npmjs.com/package/@auth0/nextjs-auth0) specifically for Next.js. - -> Note: API routes (serverless functions) in Next.js closely resemble the structure of Node server frameworks - such as Express, Koa and Fastify. The server-side logic in this guide could easily be refactored in one of these frameworks and managed as a separate application to the front-end. - -If you don’t have an Auth0 account, create one [here](https://auth0.com/signup). - -You will also need a Supabase account, which can be created by signing in [here](https://app.supabase.com/). - -## Step 1: Creating an Auth0 tenant - -From the Auth0 dashboard, click the menu to the right of the Auth0 logo, and select `Create tenant`. - -![Create tenant from Auth0 dashboard](/docs/img/guides/integrations/auth0/IYzHxeW.png) - -Enter a `Domain` for your tenant - this will need to be unique. - -Select a `Region` - this should be geographically close to the majority of your users. - -Select `Development` for `Environment Tag` - this should be production when you're ready to go live. - -![Auth0 tenant settings](/docs/img/guides/integrations/auth0/iSA3E0J.png) - -## Step 2: Setting up an Auth0 application - -From the sidebar menu, select `Applications` > `Applications` and click `Create Application`. - -Give your application a name, select the `Regular Web Applications` option and click `Create`. - -![Auth0 application settings](/docs/img/guides/integrations/auth0/ANU4Wez.png) - -Select `Settings` and navigate to the `Application URIs` section, and update the following: - -`Allowed Callback URLs`: `http://localhost:3000/api/auth/callback` - -`Allowed Logout URLs`: `http://localhost:3000` - -Scroll to the bottom of the `Settings` section and reveal the `Advanced Settings`. - -Select `OAuth` and set `JSON Web Token Signature` to `RS256`. - -Confirm `OIDC Conformant` is `Enabled`. - -Click `Save` to update the settings. - -## Step 3: Creating a Supabase project - -From your [Supabase dashboard](https://app.supabase.com/), click `New project`. - -Enter a `Name` for your Supabase project. - -Enter a secure `Database Password`. - -Select the same `Region` you selected for your Auth0 tenant. - -Click `Create new project`. - -![New Supabase project settings](/docs/img/guides/integrations/auth0/qnmJEU7.png) - -## Step 4: Creating data in Supabase - -From the sidebar menu in the [Supabase dashboard](https://app.supabase.com/), click `Table editor`, then `New table`. - -Enter `todo` as the `Name` field. - -Select `Enable Row Level Security (RLS)`. - -Create two new columns: - -- `title` as `text` -- `user_id` as `text` -- `is_complete` as `bool` with the default value `false` - -Click `Save` to create the new table. - -![Todo table](/docs/img/guides/integrations/auth0/33kqP4K.png) - -From the `Table editor` view, select the `todo` table and click `Insert row`. - -Fill out the `title` field and click `Save`. - -![New row settings](/docs/img/guides/integrations/auth0/mEhHAWC.png) - -Click `Insert row` and add a couple of extra todos. - -![List of todos](/docs/img/guides/integrations/auth0/dLOvhdq.png) - -## Step 5: Building a Next.js app - -Create a new Next.js project: - -```bash -npx create-next-app -``` - -Create a `.env.local` file and enter the following values: - -``` -AUTH0_SECRET=any-secure-value -AUTH0_BASE_URL=http://localhost:3000 -AUTH0_ISSUER_BASE_URL=https://..auth0.com -AUTH0_CLIENT_ID=get-from-auth0-dashboard -AUTH0_CLIENT_SECRET=get-from-auth0-dashboard -NEXT_PUBLIC_SUPABASE_URL=get-from-supabase-dashboard -NEXT_PUBLIC_SUPABASE_ANON_KEY=get-from-supabase-dashboard -SUPABASE_JWT_SECRET=get-from-supabase-dashboard -``` - -> Note: Auth0 values can be found under `Settings > Basic Information` for your application. - -![Auth0 settings](/docs/img/guides/integrations/auth0/o07FaoV.png) - -> Note: Supabase values can be found under `Settings > API` for your project. - -![Supabase settings](/docs/img/guides/integrations/auth0/r1GAfLo.png) - -Restart your Next.js development server to read in the new values from `.env.local`. - -```bash -npm run dev -``` - -## Step 6: Install Auth0 Next.js library - -Install the `@auth0/nextjs-auth0` library. - -```bash -npm i @auth0/nextjs-auth0 -``` - -Create a new file `pages/api/auth/[...auth0].js` and add: - -```jsx -// pages/api/auth/[...auth0].js - -import { handleAuth } from '@auth0/nextjs-auth0' - -export default handleAuth() -``` - -> Note: This will create a few API routes for us. The main ones we will use are `/api/auth/login` and `/api/auth/logout` to handle signing users in and out. - -Open `pages/_app.js` and wrap our `Component` with the `UserProvider` from Auth0: - -```jsx -// pages/_app.js - -import React from 'react' -import { UserProvider } from '@auth0/nextjs-auth0/client' - -const App = ({ Component, pageProps }) => { - return ( - - - - ) -} - -export default App -``` - -Update `pages/index.js` to ensure the user is logged in to view the landing page. - -```jsx -// pages/index.js - -import styles from '../styles/Home.module.css' -import { withPageAuthRequired } from '@auth0/nextjs-auth0' -import Link from 'next/link' - -const Index = ({ user }) => { - return ( -
-

- Welcome {user.name}!{' '} - - Logout - -

-
- ) -} - -export const getServerSideProps = withPageAuthRequired() - -export default Index -``` - -> Note: `withPageAuthRequired` will automatically redirect the user to `/api/auth/login` if they are not currently logged in. - -Test this is working by navigating to `http://localhost:3000` which should redirect you to an Auth0 sign in screen. - -![Auth0 sign in screen](/docs/img/guides/integrations/auth0/xLRL7S7.png) - -Either `Sign up` for a new account, or click `Continue with Google` to sign in. - -You should now be able to view the landing page. - -![Landing page](/docs/img/guides/integrations/auth0/YdBKRy6.png) - -## Step 7: Sign Auth0 token for Supabase - -Currently, neither Supabase or Auth0 allow for a custom signing secret to be set for their JWT. They also use different [signing algorithms](https://auth0.com/docs/configure/applications/signing-algorithms). - -Therefore, we need to extract the bits we need from Auth0's JWT, and sign our own to send to Supabase. - -We can do that using Auth0's `afterCallback` function, which gets called anytime the user authenticates. - -Install the `jsonwebtoken` library. - -```bash -npm i jsonwebtoken -``` - -Update `pages/api/auth/[...auth0].js` with the following: - -```jsx -// pages/api/auth/[...auth0].js - -import { handleAuth, handleCallback } from '@auth0/nextjs-auth0' -import jwt from 'jsonwebtoken' - -const afterCallback = async (req, res, session) => { - const payload = { - userId: session.user.sub, - exp: Math.floor(Date.now() / 1000) + 60 * 60, - } - - session.user.accessToken = jwt.sign(payload, process.env.SUPABASE_JWT_SECRET) - - return session -} - -export default handleAuth({ - async callback(req, res) { - try { - await handleCallback(req, res, { afterCallback }) - } catch (error) { - res.status(error.status || 500).end(error.message) - } - }, -}) -``` - -Our `payload` for the JWT will contain our user's unique identifier from Auth0 - `session.user.sub` and an expiry of 1 hour. - -We are signing this JWT using Supabase's signing secret, so Supabase will be able to validate it is authentic and hasn't been tampered with in transit. - -> Note: We need to sign the user out and back in again to run the `afterCallback` function, and create our new token. - -Now we just need to send the token along with the request to Supabase. - -## Step 8: Requesting data from Supabase - -Create a new file called `utils/supabase.js` and add the following: - -```jsx -// utils/supabase.js - -import { createClient } from '@supabase/supabase-js' - -const getSupabase = (access_token) => { - const options = {} - - if (access_token) { - options.global = { - headers: { - Authorization: `Bearer ${access_token}`, - }, - } - } - - const supabase = createClient( - process.env.NEXT_PUBLIC_SUPABASE_URL, - process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY, - options - ) - - return supabase -} - -export { getSupabase } -``` - -This will be our client for talking to Supabase. We can pass it an `access_token` and it will be attached to our request. - -Let's load our `todos` from Supabase in our landing page! - -```jsx -// pages/index.js - -import styles from '../styles/Home.module.css' -import { withPageAuthRequired } from '@auth0/nextjs-auth0' -import { getSupabase } from '../utils/supabase' -import Link from 'next/link' -import { useEffect } from 'react' - -const Index = ({ user }) => { - const [todos, setTodos] = useState([]) - const supabase = getSupabase(user.accessToken) - - useEffect(() => { - const fetchTodos = async () => { - const { data } = await supabase.from('todo').select('*') - setTodos(data) - } - - fetchTodos() - }, []) - - return ( -
-

- Welcome {user.name}!{' '} - - Logout - -

- {todos?.length > 0 ? ( - todos.map((todo) =>

{todo.content}

) - ) : ( -

You have completed all todos!

- )} -
- ) -} - -export const getServerSideProps = withPageAuthRequired() - -export default Index -``` - -Alternatively, we could fetch todos on the server using the `getServerSideProps` function. - -```jsx -// pages/index.js - -import styles from '../styles/Home.module.css' -import { withPageAuthRequired, getSession } from '@auth0/nextjs-auth0' -import { getSupabase } from '../utils/supabase' -import Link from 'next/link' - -const Index = ({ user, todos }) => { - return ( -
-

- Welcome {user.name}!{' '} - - Logout - -

- {todos?.length > 0 ? ( - todos.map((todo) =>

{todo.content}

) - ) : ( -

You have completed all todos!

- )} -
- ) -} - -export const getServerSideProps = withPageAuthRequired({ - async getServerSideProps({ req, res }) { - const { - user: { accessToken }, - } = await getSession(req, res) - - const supabase = getSupabase(accessToken) - - const { data: todos } = await supabase.from('todo').select('*') - - return { - props: { todos }, - } - }, -}) - -export default Index -``` - -Either way, when we reload our application, we are still getting the empty state for todos. - -![Empty todo list](/docs/img/guides/integrations/auth0/XgEMwnN.png) - -This is because we enabled Row Level Security, which blocks all requests by default. To enable our user to select their `todos` we need to write a policy. - -## Step 9: Write a policy to allow select - -Our policy will need to know who our currently logged in user is to determine whether or not they should have access. Let's create a PostgreSQL function to extract the current user from our new JWT. - -Navigate back to the Supabase dashboard, select `SQL` from the sidebar menu, and click `New query`. This will create a new query called `new sql snippet`, which will allow us to run any SQL against our Postgres database. - -Write the following and click `Run`. - -```sql -create or replace function auth.user_id() returns text as $$ - select nullif(current_setting('request.jwt.claims', true)::json->>'userId', '')::text; -$$ language sql stable; -``` - -This will create a function called `auth.user_id()`, which will inspect the `userId` field of our JWT payload. - -> Note: To learn more about PostgreSQL functions, check out [our deep dive video](https://www.youtube.com/watch?v=MJZCCpCYEqk). - -Let's create a policy that checks whether this user is the owner of the todo. - -Select `Authentication` from the Supabase sidebar menu, click `Policies`, and then `New Policy` on the `todo` table. - -![Create new policy](/docs/img/guides/integrations/auth0/M7XyhHe.png) - -From the modal, select `Create a policy from scratch` and add the following. - -![Policy settings for SELECT](/docs/img/guides/integrations/auth0/wuWz3am.png) - -This policy is calling the function we just created to get the currently logged in user's ID `auth.user_id()` and checking whether this matches the `user_id` column for the current `todo`. If it does, then it will allow the user to select it, otherwise it will continue to deny. - -Click `Review` and then `Save policy`. - -> Note: To learn more about RLS and policies, check out [our deep dive video](https://www.youtube.com/watch?v=Ow_Uzedfohk). - -The last thing we need to do is update the `user_id` columns for our existing `todos`. - -Head back to the Supabase dashboard, and select `Table editor` from the sidebar. - -![User ID null in Supabase Table Editor](/docs/img/guides/integrations/auth0/dLOvhdq.png) - -Each of our `user_id` columns are set to `NULL`! - -To get the ID for our Auth0 user, head over to the Auth0 dashboard, select `User Management` from the sidebar, click `Users` and select your test user. - -![List of users in Auth0 dashboard](/docs/img/guides/integrations/auth0/GdXS013.png) - -Copy their `user_id`. - -![User ID in Auth0 dashboard](/docs/img/guides/integrations/auth0/tbvd0Uj.png) - -Update each row in Supabase. - -![User ID set to Auth0 user](/docs/img/guides/integrations/auth0/tPu4Tt8.png) - -Now when we refresh our application, we should finally see our list of `todos`! - -> Note: Check out [the repo](https://github.com/dijonmusters/supabase-auth0-example/blob/main/pages/index.js) for an example of writing new `todos` to Supabase. - -## Resources - -- [Auth0](https://auth0.com/) official website. -- [Auth0 blog](https://auth0.com/blog/). -- [Using Next.js and Auth0 with Supabase article](https://auth0.com/blog/using-nextjs-and-auth0-with-supabase/). -- [Auth0 community](https://community.auth0.com/). -- [Auth0 documentation](https://auth0.com/docs/). - -export const Page = ({ children }) => - -export default Page diff --git a/apps/docs/pages/guides/integrations/authsignal.mdx b/apps/docs/pages/guides/integrations/authsignal.mdx deleted file mode 100644 index 65d83b22d01..00000000000 --- a/apps/docs/pages/guides/integrations/authsignal.mdx +++ /dev/null @@ -1,557 +0,0 @@ -import Layout from '~/layouts/DefaultGuideLayout' - -export const meta = { - id: 'authsignal', - title: 'Authsignal', - description: - 'Add an MFA step after sign-in. Use Supabase Auth to sign in with email and password and Authsignal to initiate an Authenticator App challenge.', -} - -This guide shows how to integrate [Authsignal](https://www.authsignal.com/) with [Next.js](https://nextjs.org/) and [Supabase](https://supabase.com/) in order to add an MFA step after sign-in. - -The user flow is as follows: - -1. The user enters their email and password to sign in -2. If the user has set up MFA, they're prompted to complete an MFA challenge (via Authenticator App) in order to complete sign-in -3. If the user has not set up MFA, they're signed in immediately and will see a button to set up MFA - -The approach uses a temporary encrypted cookie to ensure that the Supabase auth cookies (`access_token` and `refresh_token`) are only set if the MFA challenge was successful. Session data is encrypted using [@hapi/iron](https://hapi.dev/family/iron). - -The full code version of this example can be found [here](https://github.com/authsignal/supabase-example). - -A live demo can be found [here](https://authsignal-supabase-example.vercel.app). - -## How it works - -1. A sign-in form posts email and password to the Next.js API route `/api/sign-in` -2. The `signIn` API route calls the Supabase client's `signInWithEmail` method and gets back a session object -3. The `signIn` API route then calls the Authsignal client's `track` method to determine if an MFA challenge is required -4. If a challenge is required, the `signIn` API route saves the session object in a temporary encrypted cookie and redirects to Authsignal -5. Once the challenge is completed, Authsignal redirects back to `/api/callback` which retrieves the session and sets the Supabase auth cookies -6. The `callback` API route then redirects to the index page which is protected with Supabase's `withPageAuth` wrapper around `getServerSideProps` - -## Step 1: Configuring an Authsignal tenant - -Go to the [Authsignal Portal](https://portal.authsignal.com) and create a new project and tenant. - -You will also need to [enable at least one authenticator for your tenant](https://portal.authsignal.com/organisations/tenants/authenticators) - for example Authenticator Apps. - -Finally, to configure the sign-in action to always challenge, go [here](https://portal.authsignal.com/actions/signIn/rules) and set the default action outcome to `CHALLENGE` and click save. - -![Authsignal settings](https://raw.githubusercontent.com/authsignal/supabase-example/main/authsignal-settings.png) - -## Step 2: Creating a Supabase project - -From your [Supabase dashboard](https://app.supabase.com/), click `New project`. - -Enter a `Name` for your Supabase project and enter or generate a secure `Database Password`, then click `Create new project`. - -Once your project is created go to `Authentication -> Settings -> Auth Providers` and ensure `Enable Email provider` is checked and that `Confirm Email` is unchecked. - -![Supabase settings](https://raw.githubusercontent.com/authsignal/supabase-example/main/supabase-settings.png) - -## Step 3: Building a Next.js app - -Create a new Next.js project: - -```bash -npx create-next-app --typescript supabase-authsignal-example -cd supabase-authsignal-example -``` - -Create a `.env.local` file and enter the following values: - -```sh -NEXT_PUBLIC_SUPABASE_URL=get-from-supabase-dashboard -NEXT_PUBLIC_SUPABASE_ANON_KEY=get-from-supabase-dashboard -AUTHSIGNAL_SECRET=get-from-authsignal-dashboard -TEMP_TOKEN_SECRET=this-is-a-secret-value-with-at-least-32-characters -``` - -Supabase values can be found under `Settings > API` for your project. - -Authsignal values can be found under `Settings > API Keys` for your tenant. - -`TEMP_TOKEN_SECRET` is used to encrypt the temporary cookie. Set it to a random 32 character length string. - -Restart your Next.js development server to read in the new values from `.env.local`. - -```bash -npm run dev -``` - -## Step 4: Installing dependencies - -Install the Supabase client and Auth helpers for Next.js: - -```bash -npm install @supabase/supabase-js @supabase/auth-helpers-nextjs -``` - -Install the Authsignal Node.js client: - -```bash -npm install @authsignal/node -``` - -Finally install 2 packages to help encrypt and serialize session data in cookies: - -```bash -npm install @hapi/iron cookie -npm install --save-dev @types/cookie -``` - -## Step 5: Initializing the Authsignal client - -Add the following code to `/lib/authsignal.ts`: - -```ts -import { Authsignal } from '@authsignal/node' - -const secret = process.env.AUTHSIGNAL_SECRET - -if (!secret) { - throw new Error('AUTHSIGNAL_SECRET is undefined') -} - -const redirectUrl = 'http://localhost:3000/api/callback' - -export const authsignal = new Authsignal({ secret, redirectUrl }) -``` - -The `redirectUrl` here is a Next.js API route which Authsignal will redirect back to after an MFA challenge. We'll implement this below. - -## Step 6: Managing session data in cookies - -Next we will add some helper functions for managing cookies: - -- `setTempCookie` encrypts and serializes the Supabase session data and sets it in a temporary cookie -- `getSessionFromTempCookie` decrypts and parses this session data back from the cookie -- `setAuthCookie` sets the Supabase auth cookies (`access_token` and `refresh_token`) and clears the temporary cookie - -Add the following code to `/lib/cookies.ts`: - -```ts -import Iron from '@hapi/iron' -import { Session } from '@supabase/supabase-js' -import { parse, serialize } from 'cookie' -import { NextApiRequest, NextApiResponse } from 'next' - -export async function setTempCookie(session: Session, res: NextApiResponse) { - const token = await Iron.seal(session, TEMP_TOKEN_SECRET, Iron.defaults) - - const cookie = serialize(TEMP_COOKIE, token, { - maxAge: session.expires_in, - httpOnly: true, - secure: process.env.NODE_ENV === 'production', - path: '/', - sameSite: 'lax', - }) - - res.setHeader('Set-Cookie', cookie) -} - -export async function getSessionFromTempCookie(req: NextApiRequest): Promise { - const cookie = req.headers.cookie as string - - const cookies = parse(cookie ?? '') - - const tempCookie = cookies[TEMP_COOKIE] - - if (!tempCookie) { - return undefined - } - - const session = await Iron.unseal(tempCookie, TEMP_TOKEN_SECRET, Iron.defaults) - - return session -} - -export function setAuthCookie(session: Session, res: NextApiResponse) { - const { access_token, refresh_token, expires_in } = session - - const authCookies = [ - { name: ACCESS_TOKEN_COOKIE, value: access_token }, - refresh_token ? { name: REFRESH_TOKEN_COOKIE, value: refresh_token } : undefined, - ] - .filter(isDefined) - .map(({ name, value }) => - serialize(name, value, { - maxAge: expires_in, - httpOnly: true, - secure: process.env.NODE_ENV === 'production', - path: '/', - sameSite: 'lax', - }) - ) - - // Also clear the temp cookie - const updatedCookies = [...authCookies, serialize(TEMP_COOKIE, '', { maxAge: -1, path: '/' })] - - res.setHeader('Set-Cookie', updatedCookies) -} - -const isDefined = (value: T | undefined): value is T => !!value - -const TEMP_TOKEN_SECRET = process.env.TEMP_TOKEN_SECRET! -const TEMP_COOKIE = 'as-mfa-cookie' -const ACCESS_TOKEN_COOKIE = 'sb-access-token' -const REFRESH_TOKEN_COOKIE = 'sb-refresh-token' -``` - -## Step 7: Building the UI - -We will add some form components for signing in and signing up as well as a basic home page. - -Add the following code to `/pages/sign-up.tsx`: - -```ts -import Link from 'next/link' -import { useRouter } from 'next/router' - -export default function SignUpPage() { - const router = useRouter() - - return ( -
-
{ - e.preventDefault() - - const target = e.target as typeof e.target & { - email: { value: string } - password: { value: string } - } - - const email = target.email.value - const password = target.password.value - - await fetch('/api/sign-up', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ email, password }), - }).then((res) => res.json()) - - router.push('/') - }} - > - - - - - -
-
- {'Already have an account? '} - - Sign in - -
-
- ) -} -``` - -Then add the following code to `/pages/sign-in.tsx`: - -```ts -import Link from 'next/link' -import { useRouter } from 'next/router' - -export default function SignInPage() { - const router = useRouter() - - return ( -
-
{ - e.preventDefault() - - const target = e.target as typeof e.target & { - email: { value: string } - password: { value: string } - } - - const email = target.email.value - const password = target.password.value - - const { state, mfaUrl } = await fetch('/api/sign-in', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ email, password }), - }).then((res) => res.json()) - - if (state === 'CHALLENGE_REQUIRED') { - window.location.href = mfaUrl - } else { - router.push('/') - } - }} - > - - - - - -
-
- {"Don't have an account? "} - - Sign up - -
-
- ) -} -``` - -Now we will use Supabase's `withPageAuth` wrapper around `getServerSideProps` to make the home page require authentication via SSR. Replace the existing code in `/pages/index.tsx` with the following: - -```ts -import { getUser, User, withPageAuth } from '@supabase/auth-helpers-nextjs' -import { GetServerSideProps } from 'next' -import { useRouter } from 'next/router' -import { authsignal } from '../lib/authsignal' - -interface Props { - user: User - isEnrolled: boolean -} - -export const getServerSideProps: GetServerSideProps = withPageAuth({ - redirectTo: '/sign-in', - async getServerSideProps(ctx) { - const { user } = await getUser(ctx) - - const { isEnrolled } = await authsignal.getUser({ userId: user.id }) - - return { - props: { user, isEnrolled }, - } - }, -}) - -export default function HomePage({ user, isEnrolled }: Props) { - const router = useRouter() - - return ( -
-
-
Signed in as: {user?.email}
- - -
-
- ) -} -``` - -Optional: To make things look a bit nicer, you can add the following to `/styles/globals.css`: - -```css -main { - min-height: 100vh; - display: flex; - flex: 1; - flex-direction: column; - justify-content: center; - align-items: center; -} - -section, -form { - display: flex; - flex-direction: column; - min-width: 300px; -} - -button { - cursor: pointer; - font-weight: 500; - line-height: 1; - border-radius: 6px; - border: none; - background-color: #24b47e; - color: #fff; - padding: 0 15px; - height: 40px; - margin: 10px 0; - transition: background-color 0.15s, color 0.15s; -} - -input { - outline: none; - font-family: inherit; - font-weight: 400; - background-color: #fff; - border-radius: 6px; - color: #1d1d1d; - border: 1px solid #e8e8e8; - padding: 0 15px; - margin: 5px 0; - height: 40px; -} - -a { - color: #24b47e; - cursor: pointer; -} -``` - -## Step 8: Adding the API routes - -Now we'll replace the existing api routes in `/pages/api/` with 5 new routes: - -- `/sign-in.ts`: handles signing in with Supabase and initiating the MFA challenge with Authsignal -- `/sign-up.ts`: handles signing up with Supabase -- `/sign-out.ts`: clears the Supabase auth cookies and signs the user out -- `/mfa.ts`: handles the user's attempt to set up MFA or to manage their existing MFA settings -- `/callback.ts`: handles completing the MFA challenge with Authsignal - -Add the following code to `/pages/api/sign-in.ts`: - -```ts -import { supabaseClient } from '@supabase/auth-helpers-nextjs' -import { NextApiRequest, NextApiResponse } from 'next' -import { authsignal } from '../../lib/authsignal' -import { setAuthCookie, setTempCookie } from '../../lib/cookies' - -export default async function signIn(req: NextApiRequest, res: NextApiResponse) { - const { email, password } = req.body - - const { data, error } = await supabaseClient.auth.api.signInWithEmail(email, password) - - if (error || !data?.user) { - return res.send({ error }) - } - - const { state, url: mfaUrl } = await authsignal.track({ - action: 'signIn', - userId: data.user.id, - }) - - if (state === 'CHALLENGE_REQUIRED') { - await setTempCookie(data, res) - } else { - setAuthCookie(data, res) - } - - res.send({ state, mfaUrl }) -} -``` - -Then to handle new sign-ups add the following to `/pages/api/sign-up.ts`: - -```ts -import { supabaseClient } from '@supabase/auth-helpers-nextjs' -import { Session } from '@supabase/supabase-js' -import { NextApiRequest, NextApiResponse } from 'next' -import { setAuthCookie } from '../../lib/cookies' - -export default async function signUp(req: NextApiRequest, res: NextApiResponse) { - const { email, password } = req.body - - const { data, error } = await supabaseClient.auth.api.signUpWithEmail(email, password) - - if (error || !isSession(data)) { - res.send({ error }) - } else { - setAuthCookie(data, res) - res.send({ data }) - } -} - -const isSession = (data: any): data is Session => !!data?.access_token -``` - -To clear the auth cookies on sign-out add the following to `/pages/api/sign-out.ts`: - -```ts -import { supabaseClient } from '@supabase/auth-helpers-nextjs' -import { NextApiRequest, NextApiResponse } from 'next' - -export default async function signOut(req: NextApiRequest, res: NextApiResponse) { - supabaseClient.auth.api.deleteAuthCookie(req, res, { redirectTo: '/sign-in' }) -} -``` - -To handle the user's actions to set up MFA or manage their existing MFA settings, add the following to `/pages/api/mfa.ts`: - -```ts -import { getUser, withApiAuth } from '@supabase/auth-helpers-nextjs' -import { NextApiRequest, NextApiResponse } from 'next' -import { authsignal } from '../../lib/authsignal' - -export default withApiAuth(async function mfa(req: NextApiRequest, res: NextApiResponse) { - if (req.method !== 'POST') { - return res.status(405).send({ message: 'Only POST requests allowed' }) - } - - const { user } = await getUser({ req, res }) - - const { isEnrolled } = req.body - - const { url: mfaUrl } = await authsignal.track({ - action: isEnrolled ? 'manageSettings' : 'enroll', - userId: user.id, - redirectToSettings: isEnrolled, - }) - - res.send({ mfaUrl }) -}) -``` - -Because the user should be authenticated with Supabase to set up or manage MFA, we can use Supabase's `withApiAuth` wrapper to protect this route. - -The `redirectToSettings` param specifies whether the user should be redirected to the MFA page settings panel after a challenge, rather than redirecting them immediately back to the application. - -Finally we need a route to handle the redirect back from Authsignal after an MFA challenge. Add the following to `/pages/api/callback.ts`: - -```ts -import { NextApiRequest, NextApiResponse } from 'next' -import { authsignal } from '../../lib/authsignal' -import { getSessionFromTempCookie, setAuthCookie } from '../../lib/cookies' - -export default async function callback(req: NextApiRequest, res: NextApiResponse) { - const token = req.query.token as string - - const { success } = await authsignal.validateChallenge({ token }) - - if (success) { - const session = await getSessionFromTempCookie(req) - - if (session) { - setAuthCookie(session, res) - } - } - - res.redirect('/') -} -``` - -That's it! You should now be able to sign up a new user and set up MFA. - -Then if you sign out, you'll be prompted to complete an MFA challenge when signing back in again. - -## Resources - -- To learn more about Authsignal take a look at the [API Documentation](https://docs.authsignal.com/). -- You can customize the look and feel of the Authsignal Prebuilt MFA page [here](https://portal.authsignal.com/organisations/tenants/customizations). - -export const Page = ({ children }) => - -export default Page diff --git a/apps/docs/pages/guides/integrations/bracket.mdx b/apps/docs/pages/guides/integrations/bracket.mdx deleted file mode 100644 index 0f5e9c513b5..00000000000 --- a/apps/docs/pages/guides/integrations/bracket.mdx +++ /dev/null @@ -1,86 +0,0 @@ -import Layout from '~/layouts/DefaultGuideLayout' - -export const meta = { - id: 'bracket', - title: 'Bracket', - description: - 'Set up two-way syncs between Supabase and familiar tools like Airtable, Google Sheets, and Notion.', -} - -This guide explains how to set up a sync between your Supabase database and tools like Google Sheets, Airtable, or Notion using [Bracket](https://usebracket.com) — a two-way data syncing tool. - -With customizable two-way and one-way syncs, Bracket enables business teams to read from & write to their Supabase database without ever leaving a spreadsheet. If you don’t have a Bracket account, you can [create one here](https://app.usebracket.com). - -This guide assumes you have a Supabase account and database. You do not need existing tables in Supabase if you are replicating data from a SaaS tool into Supabase. Otherwise, an existing table in Supabase is required. - -We’ll go through an example using Supabase and Airtable. - -## Step 1: Connect Bracket to Supabase - -To connect Supabase to Bracket, you'll first need to pull the credentials for your Supabase database: - -1. In the Supabase dashboard, go to the settings page using the gear icon on the bottom left, and open up your **Database** settings. In the **Connection info** section, you'll find the credentials required to connect to Bracket. - - ![Supabase dashboard](/docs/img/guides/integrations/bracket/001_supabase_dashboard.png) - -2. On the Bracket web app, decide whether you want Supabase as your primary or secondary source. You should choose Supabase as your primary source if 1) you’re syncing one-way from Supabase, or 2) you’re syncing two ways, but want edits made in Supabase to win any merge conflicts with the secondary source. For the rest of this example, we’ll use the Supabase database as the primary source. - - ![Choose new source](/docs/img/guides/integrations/bracket/002_bracket_choose_new_source.png) - -3. Choose Postgres as your source in the dropdown - - ![Connect Postgres](/docs/img/guides/integrations/bracket/003_bracket_connect_postgres.png) - -4. Use the credentials you found in the Supabase dashboard under Database settings to fill out this section in the Bracket web app - - ![Select Postgres data](/docs/img/guides/integrations/bracket/004_select_postgres_data.png) - -5. Either select a full table or write a SQL query. Note that regardless of which option you choose, you must have a primary key with unique constraints. - -## Step 2: Connect Bracket to your Secondary Source - -1. Select the Secondary Source and use the OAuth flow to grant limited and secure syncing access to your Airtable base, Notion database, or Google Sheet. For this example, we’ll assume an Airtable base. -2. Select an existing Airtable table to sync, or generate a table from scratch using an existing Postgres table. - -## Step 3: Map fields and configure the sync - -1. Choose the direction you want data to be syncing (one-way vs. two-way) -2. Map each Supabase field to each Airtable field. - 1. Each field can only be mapped to one field in the other source - 2. You don’t need to sync all fields over for a sync to work - - ![Map fields](/docs/img/guides/integrations/bracket/005_map_fields.png) - -3. Name your sync and set the sync frequency. After that, you’re ready to start syncing! - -## Step 4: Get syncing - -1. Click _Test Run_ to ensure that Supabase and Airtable are connected to each other via Bracket. Note: this doesn’t sync data, it only tests that they can connect to each other properly -2. Click _Run Once_ to get data synced over for the first time. If there are errors, you can view them in the *Run History* section by clicking the errors link -3. If there are no errors, go ahead and turn on the sync by clicking the *Active/Inactive* toggle. Once the toggle is on, the _Run history_, _Field mapping_, and _Advanced_ sections cannot be clicked or edited. In order to interact or make changes to these sections, first turn the sync toggle off - - ![Sync overview](/docs/img/guides/integrations/bracket/006_sync_overview.png) - -## Build-a-table - -1. If you start with data in Supabase, you can auto-generate an Airtable table with the same fields and easily sync data between the two. You can also do the reverse if Airtable is your primary source! -2. If you generate a Supabase table from an existing Airtable, you can find it in the Table Editor on the Supabase dashboard. - -## Roles - -If you want to limit Bracket’s permissions to a specific role within Supabase, the roles must meet the following minimum RLS permissions: -1. If syncing one way from Supabase: `SELECT` permissions on all tables synced with Bracket -2. If syncing one way to Supabase or syncing two ways _with deletes disabled_: `SELECT`, `INSERT`, and `UPDATE` permissions on all tables synced with Bracket -3. If syncing one way to Supabase or syncing two ways _with deletes enabled_: `ALL` permissions on all tables synced with Bracket - -## Resources - -- [Bracket official website](https://www.usebracket.com/) -- [Bracket console](https://app.usebracket.com/) -- [Bracket docs](https://docs.usebracket.com/introduction) -- [Bracket: Postgres + Airtable sync tutorial](https://www.youtube.com/watch?v=Fv4QD7JMYqY&lc=Ugy-IbAKaPMjRnISLZJ4AaABAg) - - -export const Page = ({ children }) => - -export default Page \ No newline at end of file diff --git a/apps/docs/pages/guides/integrations/clerk.mdx b/apps/docs/pages/guides/integrations/clerk.mdx deleted file mode 100644 index df89a84dbd3..00000000000 --- a/apps/docs/pages/guides/integrations/clerk.mdx +++ /dev/null @@ -1,191 +0,0 @@ -import Layout from '~/layouts/DefaultGuideLayout' - -export const meta = { - id: 'clerk', - title: 'Clerk', - description: - 'This guide explains how to connect your Supabase database with Clerk, a powerful authentication provider built for the modern web.', -} - -This guide explains how to connect your Supabase database with [Clerk](https://clerk.com), an authentication provider built for the modern web. - -Clerk authenticates users, manages session tokens, and provides user management functionality that can be used in combination with the authorization logic available in Supabase through PostgreSQL Row Level Security (RLS) policies. - -This guide assumes you have a Supabase account and database project already set up. - -If you don't have a Clerk account, you can [create one now](https://dashboard.clerk.com/sign-up). - -## Step 1: Create JWT template - -The first step is to create a new Clerk application from your Clerk Dashboard if you haven't done so already. You can choose whichever authentication strategy and social login providers you prefer. For more information, check out Clerk's [guide](https://clerk.com/docs/authentication/set-up-your-application). - -After your Clerk application has been created, use the lefthand menu to navigate to the **JWT Templates** page. - -Click on the button to create a new template based on Supabase. - -![Create Supabase JWT template from Clerk dashboard](/docs/img/guides/integrations/clerk/01_supabase-template.png) - -This will pre-populate the default claims required by Supabase. You can include additional claims or modify them as necessary. [Shortcodes](https://clerk.com/docs/request-authentication/jwt-templates#shortcodes) are also available for adding dynamic values. - -ℹ️ Note the name of the JWT template (which you can change) because this will be needed later. - -![JWT template claims](/docs/img/guides/integrations/clerk/02_jwt-claims.png) - -## Step 2: Sign JWT with Supabase secret - -Supabase requires JWTs be signed with the HS256 signing algorithm and use their signing key. Find the JWT secret key in your Supabase project under **Settings** > **API** in the **Config** section. - -![Sign with Supabase secret](/docs/img/guides/integrations/clerk/03_jwt-secret.png) - -Click to reveal the JWT secret, copy it, and then paste it in the Signing key field in the Clerk JWT template. - -![Paste signing key](/docs/img/guides/integrations/clerk/04_signing-key.png) - -After the key is added, click the **Apply Changes** button to save your template. - -## Step 3: Configure client - -The next step is to configure your client. Supabase provides an official [JavaScript/TypeScript client library](https://github.com/supabase/supabase-js) and there are [libraries in other languages](/docs/reference/javascript/installing) built by the community. - -This guide will use a Next.js project with the JS client as an example, but the mechanism of setting the authentication token should be similar with other libraries and frameworks. - -Assuming a Next.js application, set the following environment variables in an `.env.local` file: - -```bash -NEXT_PUBLIC_CLERK_FRONTEND_API=your-frontend-api -NEXT_PUBLIC_SUPABASE_URL=your-supabase-url -NEXT_PUBLIC_SUPABASE_KEY=your-supabase-anon-key -``` - -**Note**: If you're using Create React App, replace the `NEXT_PUBLIC` prefix with `REACT_APP` - -Your Clerk Frontend API can be found on the [API Keys](https://dashboard.clerk.com/last-active?path=api-keys) screen. - -![Clerk Frontend API](/docs/img/guides/integrations/clerk/05_clerk-frontend-api.png) - -To get the ones needed for Supabase, navigate to the same Settings > API page as before and locate the anon public key and URL. - -![Supabase keys](/docs/img/guides/integrations/clerk/06_supabase-keys.png) - -**Note**: It is recommended that you enable [Row Level Security](/docs/guides/auth/row-level-security) (RLS) for your database tables and configure access policies as needed. - -After setting those three environment variables, you should be able to start up your application development server. - -Install the JavaScript client for Supabase with: - -```bash -npm install @supabase/supabase-js -``` - -Initialize the Supabase client by passing it the environment variables. - -This can be saved to a common file, for example as `lib/supabaseClient.js` - -```jsx -import { createClient } from '@supabase/supabase-js' - -const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL -const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_KEY - -export const supabase = createClient(supabaseUrl, supabaseKey) -export default supabase -``` - -## Step 4: Set up Clerk Provider - -Install the latest Clerk Next.js SDK by running the following: - -```bash -npm install @clerk/nextjs@next -``` - -**Note**: There is also a Clerk library for [React](https://github.com/clerkinc/javascript/tree/main/packages/react) and [React Native with Expo](https://github.com/clerkinc/javascript/tree/main/packages/expo). - -After the package is installed, wrap your application with the `` component. - -In a Next.js application, this is typically done in `pages/_app.js`: - -```jsx -import { ClerkProvider } from '@clerk/nextjs' - -function MyApp({ Component, pageProps }) { - return ( - - - - ) -} - -export default MyApp -``` - -## Step 5: Set auth token with Supabase - -In order to access the custom JWT, you can use the `getToken` function returned by the Clerk `useAuth` hook and pass it the name of your template (hopefully you remembered from earlier). - -**Note**: The `getToken({ template: })` call is asynchronous and returns a Promise that needs to be resolved before accessing the token value. This token is short-lived for better security and should be called before every request to your Supabase backend. The caching and refreshing of the token is handled automatically by Clerk. - -Call `supabase.auth.setAuth(token)` to override the JWT on the current client. The JWT will then be sent to Supabase with all subsequent network requests. - -```jsx -import { useAuth } from '@clerk/nextjs' -import supabase from '../lib/supabaseClient' - -export default function Home() { - const { getToken } = useAuth() - - const fetchData = async () => { - // TODO #1: Replace with your JWT template name - const token = await getToken({ template: 'supabase' }) - - supabase.auth.setAuth(token) - - // TODO #2: Replace with your database table name - const { data, error } = await supabase.from('your_table').select() - - // TODO #3: Handle the response - } - - return ( - - ) -} -``` - -## Access user ID in RLS policies - -It is common practice to need access to the user identifier on the database level, especially when working with RLS policies in Postgres. Although Supabase provides a special function `auth.uid()` to extract the user ID from the JWT, this does not work with Clerk. The workaround is to write a custom SQL function to read the `sub` property from the JWT claims. - -In the **SQL Editor** section of the Supabase dashboard, click New Query and enter the following: - -```sql -create or replace function requesting_user_id() -returns text -language sql stable -as $$ - select nullif(current_setting('request.jwt.claims', true)::json->>'sub', '')::text; -$$; -``` - -This will create a `requesting_user_id()` function that can be used within an RLS policy. - -For example, this policy would check that the user making the request is authenticated and matches the `user_id` column of a todos table. - -## Access user ID in table column - -If you would like the requesting user ID from the JWT to automatically populate a text type column in your database table, you can set the **Default Value** field to the previously defined `requesting_user_id()` function. - -![Set requesting_user_id() as default value](/docs/img/guides/integrations/clerk/07_requesting-user-id.png) - -## Resources - -- [Clerk + Supabase starter repo](https://github.com/clerkinc/clerk-supabase-starter) -- [Next.js + Supabase + Clerk tutorial](https://clerk.com/blog/nextjs-supabase-todos-with-multifactor-authentication) -- [Clerk guide for Next.js Authentication](https://clerk.com/docs/nextjs/get-started-with-nextjs) -- [Clerk Community Discord channel](https://discord.com/invite/b5rXHjAg7A) - -export const Page = ({ children }) => - -export default Page diff --git a/apps/docs/pages/guides/integrations/cloudflare-workers.mdx b/apps/docs/pages/guides/integrations/cloudflare-workers.mdx deleted file mode 100644 index 352e8d9590f..00000000000 --- a/apps/docs/pages/guides/integrations/cloudflare-workers.mdx +++ /dev/null @@ -1,108 +0,0 @@ -import Layout from '~/layouts/DefaultGuideLayout' - -export const meta = { - id: 'cloudflare-workers', - title: 'Cloudflare Workers', - description: - "Using Supabase from your Cloudflare Workers just got even easier.", -} - -Using Supabase in Cloudflare Workers has always been a great way to interact with your data from the edge. Supabase-js communicates with your Supabase Postgres instance via HTTP using PostgREST, so you never need to worry about running out of database connections. - -In this guide we'll walk you through a new addition to the Cloudflare Workers dashboard - the ability to authenticate directly with your Supabase account, and automatically inject your Supabase environment variables into your Worker code. - -## How To Enable Supabase Integration in Cloudflare Workers - -Start by heading to the [Cloudflare Dashboard](https://dash.cloudflare.com), go to the Workers & Pages tab and hit 'Create Application' followed by 'Create Worker'. - -![Cloudflare Dashboard 2](/docs/img/guides/integrations/cloudflare-integration/2.png) - -Deploy the Hello World example Worker. Once it's deployed hit 'Configure Worker'. - -![Cloudflare Dashboard 3](/docs/img/guides/integrations/cloudflare-integration/3.png) - -On the configuration page select the Settings tab, followed by the Integrations option. - -You should now see the database integration options. On the Supabase card, click 'Add Integration' - -![Cloudflare Dashboard 4](/docs/img/guides/integrations/cloudflare-integration/4.png) - -After reviewing and accepting the terms, you will be shown the option to connect and a Supabase popup should appear. - -Follow the flow by selecting your Supabase Org and the Project you wish to connect to. If you don't have any projects yet, head over to the [Supabase Dashboard](https://app.supabase.com) to create one. - -![Cloudflare Dashboard 5](/docs/img/guides/integrations/cloudflare-integration/5.png) - -Once it's connected you will be given the option to select which Supabase Key you want to pull into the Worker context. - -The `Anon` key here is one that always adhires to the Database's RLS policies (read more on [Row Level Security](https://supabase.com/docs/guides/auth/row-level-security)). - -The `Service Role` is typically ok to use in backend contexts, such as Cloudflare Workers, but note that this key **bypasses your Row Level Security policies**, and has the ablity to read, write, and delete any data in your database. - -![Cloudflare Dashboard 6](/docs/img/guides/integrations/cloudflare-integration/6.png) - -Once this is done the `SUPABASE_KEY` and `SUPABASE_URL` environment variables will now be available from your Cloudflare Worker code. - -![Cloudflare Dashboard 7](/docs/img/guides/integrations/cloudflare-integration/7.png) - -You can now install the supabase-js client in your Worker: - -`npm install @supabase/supabase-js` - -Then you can initiate the Supabase client, and start querying your data: - -```javascript -import { createClient } from '@supabase/supabase-js' - -export default { - async fetch(request, env) { - const supabase = createClient(env.SUPABASE_URL, env.SUPABASE_KEY) - const { data, error } = await supabase.from('countries').select('*') - if (error) throw error - return new Response(JSON.stringify(data), { - headers: { - 'Content-Type': 'application/json', - }, - }) - }, -} -``` - -The snippet above assumes you already have a `countries` table. Run the following in the [SQL Editor in the Supabase Dashboard](https://app.supabase.com/project/_/sql) if you wish to install this demo schema: - -```sql -create table countries ( - id serial primary key, - name varchar(255) not null -); - -insert into countries - (name) -values - ('Oceania'); - -insert into countries - (name) -values - ('Genovia'); - -insert into countries - (name) -values - ('Wakanda'); - -insert into countries - (name) -values - ('Lilliput'); -``` - -Remember that you don't need to use supabase-js to connect to your Supabase database, you can connect "directly" to the underlying Postgres database using the connection string (every Supabase database comes pre-installed with a [connection pooler](https://supabase.com/docs/guides/database/connecting-to-postgres#connection-pool)), or you can try Cloudflare's new [TCP socket method of connecting to Postgres](https://blog.cloudflare.com/workers-tcp-socket-api-connect-databases/) directly from Cloudflare Workers. - -- [Cloudflare Integration Docs](https://developers.cloudflare.com/workers/learning/integrations/databases/#supabase). -- [Cloudflare Dashboard](dash.cloudflare.com/). -- [Cloudflare Integration Announcement](https://blog.cloudflare.com/announcing-database-integrations/). - -export const Page = ({ children }) => - -export default Page diff --git a/apps/docs/pages/guides/integrations/dhiwise.mdx b/apps/docs/pages/guides/integrations/dhiwise.mdx deleted file mode 100644 index 68e57e41ceb..00000000000 --- a/apps/docs/pages/guides/integrations/dhiwise.mdx +++ /dev/null @@ -1,203 +0,0 @@ -import Layout from '~/layouts/DefaultGuideLayout' - -export const meta = { - id: 'dhiwise', - title: 'DhiWise', - description: - 'Get started with Supabase and DhiWise. Convert your Figma designs into Flutter apps, store data, and authenticate your users', -} - -This guide explains how to connect Supabase backend to DhiWise Flutter application quickly. - -[DhiWise](https://www.dhiwise.com/) is a Developer tool to convert Figma designs into React and Flutter applications. It lets you quickly integrate Databases and APIs into your React and Flutter Apps. - -If you don't have a DhiWise account, create one [here](https://app.dhiwise.com). - -DhiWise supports easy Supabase Integration in just five steps. - -Let's get started! - -## Step 1: SignIn to Supabase - -Go to [Supabase](https://supabase.com/), Click `Sign In`, and create a new account by authenticating with **GitHub**. If you already have an account, you will be logged in. - -## Step 2: Create a new project in Supabase - -Click on `New project` from the Dashboard and select an organization. If you don't have an organization, create one using `+ New organization.` - -- Give your Supabase project a `name.` -- Enter a secure `Database Password.` -- Choose the `region` where your app's backend is hosted. -- Click `Create new project.` - -![New Project](/docs/img/guides/integrations/dhiwise/newProj.png) - -## Step 3: Find the API key and URL - -Once your project is created, you can access the API Key and URL string, Or if you already have an account go to your `organization-> app-> settings-> API`. - -![auth keys](/docs/img/guides/integrations/dhiwise/authKeys.png) - -## Step 4: Integrations - -There are two ways you can integrate Supabase into your DhiWise Flutter applications. - -### Authentication - -You can integrate `Supabase Email/Password SignUp` or `Supabase Email/Password SignIn` on your components. - -- Open the screen of your flutter application -- Go to the component on which you want to add authentication -- on the `onClick` method - select `authentication` -- From the list, If you want SignUp - select `SignUp with Email/Password`; otherwise, select `SignIn with Email/Password` from Supabase Auth section - -![Auth](/docs/img/guides/integrations/dhiwise/auth.gif) - -And that's it. Supabase authentication will be added to the selected component. - -After downloading the application source code, - -1. Add Supabase URL and Supabase public key inside **_lib/core/utils/initial_bindings_** file. -2. For additional details, refer ***https://supabase.com/docs/guides/with-flutter*** - -### Working with Data - -When you first integrate Supabase in your DhiWise Flutter application, You will be asked to add [Supabase auth key and URL](##step-3-find-the-api-key-and-url). When you add them, all the tables available in your Supabase project will be synced in DhiWise. You can integrate Select and Create queries on your Flutter screen for a particular table in DhiWise. - -### Select records - -![Create](/docs/img/guides/integrations/dhiwise/select.png) - -#### **Step 1:** - -Select the screen from the screen list where you want to integrate Supabase. - -#### **Step 2:** - -Next, go to the view where you want to add Integration, and from the suggestion box for the `onClick` property, choose `Supabase integration,` which will take you to the Integration screen. Where you will be asked to `Enter function name.` Enter the name of your function and click `Submit.` - -#### **Step 3:** - -After submitting the function name, you will be asked to select a type of Supabase integration. To retrieve data from Supabase, choose `select.` - -#### **Step 4:** - -Next, select the table from which you want to fetch records from the listed Tables. - -#### **Step 5:** - -Select the type of integration - -| Type | Description | -| ------------ | ------------------------------------------------- | -| **Single** | Used to fetch a single record from the database. | -| **Multiple** | Used to fetch multiple records from the database. | - - - -For Multiple types, you need to set `data limit,` `order by, and `order.` - - - -#### **Step 6:** - -You will be redirected to the API Integration screen, where you can set request and response. - -For request binding, the below types are supported. Also, Select the operator for comparison before moving forward. - -| Type | Description | -| ----------------------- | ---------------------------------------------------------- | -| **View** | Select any component from your screen. | -| **Constant** | Select a constant you've created in your app. | -| **Get from preference** | Select the key you want to fetch from preference. | -| **Navigation argument** | Select data that's been passed from one screen to another. | - -For response binding, the below types are supported. - -| Type | Description | -| ---------------------- | ------------------------------------- | -| **View** | Select any component from the screen. | -| **Save to preference** | Storing the data to preference. | - -#### **Select 7:** - -`Handle action` - Select the action you wish to take once the Supabase call has either been accepted successfully or refused due to an error. - -Available action for On success and On error are, - -1. [Show Alert](https://docs.dhiwise.com/docs/flutter/show-alert) -2. [Navigation](https://docs.dhiwise.com/docs/flutter/navigation) - -#### **Step 8:** - -Finally, you have added Supabase to your application to fetch records on your screen! - - - -Suppose you want to fetch records from Supabase and populate the item list on your screen. You can integrate Supabase as discussed above and bind the response with your list view. - - - -

Create records

- -#### **Step 1:** - -Choose the screen you wish to integrate Supabase for from the list of screens. - -#### **Step 2:** - -Next, switch to the component you want to add Integration, and on the `onClick` property, choose `Supabase integration,` which will take you to its integration screen, where you will be asked to **Enter function name**, which will be used in generated code. Enter the name for it and click `Submit` - -#### **Step 3:** - -After submitting the function name, you will be asked to select a type of Supabase integration. For example, to create a record in Supabase, choose `Create.` - -#### **Step 4:** - -Next, select the table where you want to create a record from the listed Tables. - -#### **Step 5:** - -If you want to create a Single record, Select **Select**. Otherwise, **Multiple**. - -#### **Step 6:** - -Now, you will be redirected to the API Integration screen, where you can set request and response. - -For request binding, the below types are supported. - -| Type | Description | -| ----------------------- | ---------------------------------------------------------- | -| **View** | Select any component from the screen | -| **Constant** | Select a constant you've created in your app. | -| **Get from preference** | Select the key you want to fetch from preference. | -| **Navigation argument** | Select data that's been passed from one screen to another. | - -For response binding, the below types are supported. - -| Type | Description | -| ---------------------- | ------------------------------------ | -| **View** | Select any component from the screen | -| **Save to preference** | Storing the data to preference. | - -#### **Select 7:** - -`Handle action` - Select the action you wish to take once the Supabase call has either been accepted successfully or refused due to an error. - -Available action for On success and On error are, - -1. [Show Alert](https://docs.dhiwise.com/docs/flutter/show-alert) -2. [Navigation](https://docs.dhiwise.com/docs/flutter/navigation) - -#### **Step 9:** - -Finally, you have added Supabase to your application to create records from your screen data! - -## Resources - -- [DhiWise Official Website](https://dhiwise.com) -- [DhiWise Documentation](https://docs.dhiwise.com) - -export const Page = ({ children }) => - -export default Page diff --git a/apps/docs/pages/guides/integrations/directus.mdx b/apps/docs/pages/guides/integrations/directus.mdx deleted file mode 100644 index 2d62ee6dd28..00000000000 --- a/apps/docs/pages/guides/integrations/directus.mdx +++ /dev/null @@ -1,137 +0,0 @@ -import Layout from '~/layouts/DefaultGuideLayout' - -export const meta = { - id: 'directus', - title: 'Directus', - description: - 'In this guide, we will show you how to create a Supabase project, install the Directus platform locally and configure the two to connect.', -} - -In this guide, we will demonstrate how to create a new Supabase project, install a fresh instance of the Directus platform, then configure the two to work together seamlessly. If you're unfamiliar with either of these systems, don't worry! We'll start off with an overview of each platform and explain how they complement each other, noting any overlap in capabilities. - -## Introduction - -![Supabase App](/docs/img/guides/integrations/directus/supabase-20220608A.webp) - -[Supabase](https://supabase.com/) is an open-source Firebase alternative that provides a PostgreSQL database, storage, authentication, and a dynamic REST API based on your schema. While it is possible to self-host Supabase on your own infrastructure, this article will focus on Supabase Cloud's Free plan, which is the fastest and easiest way to get started. - -![Directus App](/docs/img/guides/integrations/directus/directus-20220608A.webp) - -[Directus](https://directus.io/) is an open-source data platform that layers on top of any SQL database, providing a powerful suite of tools. The Directus Engine provides dynamic REST and GraphQL APIs based on your schema, hooks and automation, authentication and access control, and file transformations. Directus Studio enables engineers and non-technical users alike to browse, manage, and visualize database content through a no-code app. - -} -Supabase is a suite of open-source tools making Postgres databases, file storage, authentication, and edge functions more accessible to developers of all skill levels. Directus is also developer tooling and additionally provides a Data Studio that is safe and intuitive enough for anyone, including non-technical users, to use. This is the crucial bit that gives the two platforms such a strong “network effect.” - -When these two systems are brought together, you get a scalable datastore, limitless connectivity options, and a no-code app that allows your technical and business teams to collaborate together efficiently. - -The two platforms share an overlap of capabilities that deepens their integration and offers developers the freedom of choice across a broader spectrum of connectivity. Key areas of intersection include: - -The ability to generate _powerful_ APIs dynamically to connect data -User management and fine-grained access control -Digital asset storage and management. - -More importantly, Directus and Supabase share a common vision for your data, making them quite symbiotic. Both solutions are completely open-source, with self-hosted and cloud deployment options available. They are unopinionated in their approach, with vendor-agnostic data storage, and they both focus on providing a polished developer experience along with comprehensive documentation. - -By linking the Supabase database with your Directus Project, _you get a superset of data tools._ You'll benefit from Supabase's Postgres database and its _dev-centric_ admin app with the raw power to run SQL queries, **_as well as_** the Directus no-code app, which enables intuitive permissions-based data access for the whole team. - -Let's dive into how we actually set up and link these two platforms to create a modern data stack powerhouse. - -## Create a Supabase Project - -As mentioned, while you can [deploy Supabase locally](/docs/guides/getting-started/local-development). For the purpose of this guide, we'll use Supabase Cloud: - -1. Create a **Supabase** account by signing in with GitHub. -2. Give your organization a name (this can be changed later). -3. Click **New Project** and select your organization. -4. Follow the prompts, setting a project Name, Database Password, Region, and Pricing Plan, then click **Create New Project**. -5. After your project has been provisioned, navigate to **Settings > Database** in the sidebar. -6. Scroll down to **Connection Info** and take note of your database's **Host**, **Database Name**, **Port**, **User**, and **Password**. You will need to enter this during your Directus project setup. - -## Optional: Add PostGIS to Support Geometry and Mapping - -To take full advantage of the built-in geometry and mapping features Directus offers, we recommend enabling Geometric Data Support. To add PostGIS, follow these steps: - -![Enable PostGis](/docs/img/guides/integrations/directus/enable-PostGIS-20220608A.webp) - -1. From the sidebar, navigate to **Database > Extensions**. -2. Use the search bar to look up `PostGIS`. -3. Toggle the PostGIS option to enable it. - -## Set up Directus - -At the time of writing this article, [Directus Cloud](https://directus.cloud/) does not yet support hybrid deployments for connecting an external database. So, we'll be deploying a self-hosted instance to connect with Supabase. To install a self-hosted instance of Directus that's connected to our Supabase project, follow these steps: - -1. Run the following command in your terminal: - -```bash -npm init directus-project example-project -``` - -2. Using the up/down arrow keys, select `Postgres` from the list: - -```bash -? Choose your database client Postgres -``` - -3. Next, you will be prompted to input database credentials. Add in the Supabase Database Connection Info noted above as follows: - -- **Database Host** – The IP address for your database. -- **Port** – Port number your database is running on. -- **Database Name** – Name of your existing database. -- **Database User** – Name of existing user in database. -- **Database Password** – Password to enter database. -- **Enable SSL** – Select Y for yes or N for no. -- **Root** – The root name. - -4. Now, simply set an email and password for your first Directus admin account. To be clear, this is Directus-specific, and is unrelated to your database user: - -```bash -Create your first admin user: -? Email: admin@example.com -? Password: ******** -``` - -Once this is complete, you should see details about your new project: - -```bash -Your project has been created at /example-project. -The configuration can be found in /example-project/.env -``` - -5. Lastly, navigate to your new project folder (in this case `example-project`) and start the platform: - -```bash -cd example-project -npx directus start -``` - -**Please note:** To prevent public accessibility when using the supabase-js library,turn on row level security (RLS) on all these tables inside of the Supabase Dashboard. By default when RLS is turned on these tables cannot be read from or written to with the supabase-js library. - -That's it! Your project is now up and running locally. You can access the Directus Studio in the browser via the URL displayed, and log in with the Directus admin credentials you entered above: - -```bash -✨ Server started at http://localhost:8055 -``` - -In a matter of minutes, we've created a flexible data backend, with access to an intuitive no-code app for managing and visualizing data along with a robust connectivity toolkit. This modern data stack is flexible and scalable enough to power any data-driven project… all you need to do is build the frontend! - -## Next Steps - -From here, the sky's the limit on what you can build. You'll probably want to invite some new collaborators to your project and start architecting your data model. - -Below are some additional resources to dive in and start exploring these two platforms: - -**Directus** - -- See the [Directus guides](https://directus.io/guides/). -- Join the Directus community on [Discord](https://directus.chat/). -- Check out the source code on the official [Directus GitHub Repo](https://github.com/directus/directus). - -**Supabase** - -- Explore the [Supabase documentation](https://supabase.com/docs) -- Join the Supabase community on [Discord](https://discord.supabase.com/) - -export const Page = ({ children }) => - -export default Page diff --git a/apps/docs/pages/guides/integrations/draftbit.mdx b/apps/docs/pages/guides/integrations/draftbit.mdx deleted file mode 100644 index 357a2ef3eea..00000000000 --- a/apps/docs/pages/guides/integrations/draftbit.mdx +++ /dev/null @@ -1,237 +0,0 @@ -import Layout from '~/layouts/DefaultGuideLayout' - -export const meta = { - id: 'draftbit', - title: 'Draftbit', - description: 'Connect your Supabase postgres database to your Low-code mobile app.', -} - -This guide explains how to connect a Supabase back-end to a Draftbit front-end and then configure all CRUD operations necessary to build a simple mobile app. - -[Draftbit](https://draftb.it/3Fkbask) is a "pro-code" low-code mobile app building platform. Draftbit exports React Native source code that is 100% run on open-source languages and libraries. - -Draftbit is back-end agnostic and connects to Supabase via REST API. - -> Note: For the demonstration purpose of this guide, we are using a pre-populated database in Supabase. We are calling `Groceries`. To follow along, rename it any way you prefer. - -![Prepopulated Database](/docs/img/guides/integrations/draftbit/prepopulated-database.png) - -If you don’t have a Draftbit account, create one [here](https://draftb.it/3Fkbask). Once you’ve got your account set up, Create a New App. You can select `Start From a Blank App` for this demo and proceed to the Builder interface. - -## Step 1: Get the RESTful endpoint and Project API key - -To connect the REST API in the Draftbit app, the following fields are required: - -- Base URL of the REST API, which is in the format: `https://.supabase.co/rest/v1` where the `` (the space between `Bearer` and `` is required). Click **Add** after adding the value. -- Enter another key name to access the API Key such as `Api_Key_Header`. When making the service connection in the next section, it will be passed as the header `apiKey` value. -- The value of this key requires you to enter an authorization token that starts with syntax is ``. Click **Add** after adding the value. -- Click **Save** to save these keys and close the modal. - -![Add header values in Draftbit](/docs/img/guides/integrations/draftbit/authheader.png) - -## Step 3: Add Supabase RESTful endpoint in Draftbit - -In your Draftbit builder interface: - -- Open the **API & Cloud Services** modal from the top menu bar. -- From the **Connect a service** menu, click on **Rest API**. - -- In Step 1: Enter a name for your REST API. Then, paste your `Base URL` (from the first section) into the Base URL field. -- In Step 2: Under **Key** add `Authorization` and `apikey`. Then, under **Value**, select the global variables (from the previous section) to add the actual values for both keys. -- Click Save. - -![Create a API service in Draftbit](/docs/img/guides/integrations/draftbit/service.png) - -## Making API requests with Supabase & Draftbit - -### GET request to Fetch all records - -In this section, let's populate a Fetch component with all the data from a simple Supabase and then display the data fetched from the Supabase data table in a List component. - -For reference, here is a how the Components tree looks like for this screen: - -![Components tree](/docs/img/guides/integrations/draftbit/ctree.png) - -The next step is to create an endpoint. Let's try fetching all the data using a `GET` HTTP request. Select the Supabase service in the **API & Cloud Services** modal, and then: - -- Click **Add endpoint**. -- In Step 1: enter the name for the endpoint. Make sure the **Method** select is `GET`. -- In Step 2: add the base name path: `/groceries/select=*`, where `groceries` is the table name in Supabase. -- In Step 4: click the **Test** button next to the Endpoint input to verify the response coming from the Supabase. -- Click Save. - -![Creating a GET request endpoint](/docs/img/guides/integrations/draftbit/get-request.gif) - -In the Builder, on the app screen: - -- Select the Fetch component in the Components tree and go to the [Data tab from Properties Panel](doc:introduction-to-the-builder#properties-panel). -- For **Service**, select the name of the Supabase Service. -- For **Endpoint**, select the endpoint you want to fetch the data from. -- Select the List component in the Components and go to the [Data tab from Properties Panel](doc:introduction-to-the-builder#properties-panel). In Data, select `Top-Level Response` from the dropdown menu. -- Then, select the Text component in the Components and then go to the Data tab from the Properties Panel. -- Add a `{{varName}}` value (inside the curly braces) to represent a column field from the Supabase. For example, add `{{title}}` to represent the column name from the Supabase Base. -- Under **Variables**, you will see the variable name defined in the previous step. From the dropdown menu, select the appropriate field that represents the data field. - -![Fetching data on app screen](/docs/img/guides/integrations/draftbit/fetchall.gif) - -### GET request to fetch single row - -Open the **API & Cloud services** modal from the top menu, select the Supabase service, and then: - -- Click **Add endpoint**. -- In Step 1: enter a name for the endpoint. -- In Step 2: add the `/groceries/column-name=eq.{{column-name}}` variable. Then, add a Test value for the `{{column-name}}`. For example, it can be the `title` or the `id`. -- In Step 4: click the **Test** button next to the Endpoint input to verify the response coming from the Supabase. -- Click Save. - -![Creating endpoint to fetch a single row](/docs/img/guides/integrations/draftbit/getsingle.gif) - -On app screen: - -- Select the Fetch component in the Components tree and go to the Data tab from Properties Panel -- For **Service**, select the name of the Supabase Service. -- For **endpoint**, select the endpoint you want to fetch the data from. -- Set the value for the `id` in the Configuration > URL Structure section to Navigation > id. -- Select the List component in the Components and go to the Data tab from Properties Panel. In Data, select `Top-Level Response` from the dropdown menu. -- Then, select the Text component in the Components and then go to the Data tab from the Properties Panel. -- Add a `{{varName}}` value (inside the curly braces) to represent the column field from the Supabase. For example, add `{{title}}` to represent the field and value from the Supabase data table. -- Under **Variables**, you will see the variable name defined in the previous step. From the dropdown menu, select the appropriate field that represents the data field. - -![Displaying data from single row](/docs/img/guides/integrations/draftbit/fetchsingle.png) - -### POST request to submit a new row - -Submitting new Data from the Draftbit app to Supabase's REST API requires the request to be sent using the HTTP `POST` method. - -For this section, you need to use at least one component that accepts user input and has a Field Name prop to POST data using Supabase REST API. - -You can use one of the following components in Draftbit: - -- Text Input -- Text Area/Text Field -- Checkbox -- Slider -- Radio Button Group -- Radio Button - -In addition, you need a Touchable component like a Button to attach the POST action. After you have created these components, we will create the `POST` endpoint: - -- Click **Add endpoint**. -- In Step 1: enter a name for the endpoint and select the Method to `POST`. -- In Step 2: enter the base name as path: `/groceries`. -- In Step 3: add a valid Body structure to submit a POST request. Add one or many `{{variable}}` for test values. Click Body Preview to validate the structure of the Body in the request. For the example, let's create a variable called `{{inputValue}}`. -- In Step 4: to see the new row added to the Supabase data table as JSON response inside the Builder, you have to pass a new header called `Prefer` with its value as `return=representation`. -- In Step 5: click the **Test** button next to the Endpoint input to verify the response coming from the Supabase and click Save. - -![Make a POST request to add new data to Supabase database](/docs/img/guides/integrations/draftbit/postrequest.gif) - -Once you follow the above steps, you should get a 200 OK response with exactly the new record as a JSON you have entered for your schema. - -An example of how Body in a request will look like: - -```json -{ - "title": {{inputValue}} -} -``` - -Where `title` is the column name in your Supabase database table. - -In Draftbit, using a Touchable or a Button component, you can trigger the action **API Request** to submit the data to the endpoint. - -Now, there is a working `POST` request in Draftbit. Map its response to the components on your screen in Draftbit. - -First, for each input component, make sure you have set the Field Names (found in the Configs tab, second from the left) to unique values. For example, in the screen below, there is one TextInput field component with the value of the `Field Name` prop of `textInputValue`. - -![Field Name prop on a TextInput component](/docs/img/guides/integrations/draftbit/textinput.png) - -Next, on your Button component, go to the Interactions tab in the Properties panel located on the far-right-hand side. Select an Action called `API request`. - -In the API request action: - -- In **Service**, select the name to Supabase API Service. -- In **Endpoint**, select the name of the Endpoint. -- Then add the configuration for the body request to be sent by selecting the values for `{{inputValue}}`. - -![Setting up the API Request to send a POST request](/docs/img/guides/integrations/draftbit/postapirequest.png) - -After completing the above steps, you can trigger the API request to submit new data to the Supabase database. - -### PATCH request to Update a new record - -Updating an existing record from the Draftbit app to Supabase's REST API requires the request to be sent using the HTTP `PATCH` method. - -After you have created your screen components in the Draftbit builder, open the Supabase service and make the `PATCH` endpoint: - -- Click **Add endpoint**. -- In Step 1: enter a name for the endpoint and select the Method to `PATCH`. -- In Step 2: enter the base name as path: `/groceries?id=eq.{{id}}`, where `id` is the value of an existing record in the database. -- In Step 3: add a valid Body structure to submit a PATCH request. Add one or many `{{variable}}` for test values depending on the structure of your app. Click Body Preview to validate the structure of the Body in the request. For the example, let's create a variable called `{{inputValue}}`. -- In Step 5: click the **Test** button next to the Endpoint input to verify the response coming from the Supabase and click Save. - -![Creating an endpoint for PATCH request](/docs/img/guides/integrations/draftbit/patch.gif) - -Next, on your Button component, go to the Interactions tab in the Properties panel located on the far-right-hand side. Select an Action called `API request`. - -In the API request action: - -- In **Service**, select the name to Supabase API Service. -- In **Endpoint**, select the name of the Endpoint. -- Then add the configuration for the query param, and the body request to be sent by selecting the values for `{{inputValue}}`. - -![Setting up the API Request to send a PATCH request](/docs/img/guides/integrations/draftbit/patchapirequest.png) - -After completing the above steps, you can trigger the API request to update existing data in the Supabase database. - -### DELETE request to remove an existing record - -The `DELETE` request is to the Supabase with an item's `column-name` to remove that particular record from the table. You can use a [filter from Supabase](https://supabase.io/docs/reference/javascript/using-filters) to filter the value of a specific `column-name`. - -After you have created your screen components in the Draftbit builder, open the Supabase service and create the `DELETE` endpoint: - -- Click **Add endpoint**. -- In Step 1: enter a name for the endpoint and select the Method to `DELETE`. -- In Step 2: add `/groceries/columnName=eq.{{columnName}}`. Then, add a Test value for the `{{columnName}}`. For example, the `{{columnName}}` here can be `id` of the record. -- In Step 4: click the **Test** button next to the Endpoint input to verify the response from the Supabase. -- Click Save. - -![Creating an endpoint for DELETE request](/docs/img/guides/integrations/draftbit/delete.gif) - -Next, on your Button component, go to the Interactions tab in the Properties panel located on the far-right-hand side. Select an Action called `API request`. - -In the API Request action: - -- In **Service**, select the name to Supabase API Service. -- In **Endpoint**, select the name of the Endpoint. -- Then, add the configuration for the query request to be sent by selecting a value. For example, in this case it will be the `id` of the record coming from the Navigation parameter. - -![Setting up the API Request to send a DELETE request](/docs/img/guides/integrations/draftbit/deleteapirequest.gif) - -## Resources - -- [Draftbit](https://draftb.it/3Fkbask) official website. -- [Draftbit Community](https://community.draftbit.com/home). -- [Draftbit](https://docs.draftbit.com/) documentation. - -export const Page = ({ children }) => - -export default Page diff --git a/apps/docs/pages/guides/integrations/estuary.mdx b/apps/docs/pages/guides/integrations/estuary.mdx deleted file mode 100644 index 77e52581a5a..00000000000 --- a/apps/docs/pages/guides/integrations/estuary.mdx +++ /dev/null @@ -1,110 +0,0 @@ -import Layout from '~/layouts/DefaultGuideLayout' - -export const meta = { - id: 'estuary', - title: 'Estuary', - description: - 'Create a real-time data pipeline connecting Firestore and Supabase to make migration simple.', -} - -Estuary Flow is a platform for creating [real-time data pipelines at scale](https://www.estuary.dev/). -It combines the intuitive interface of an ELT service with an event-driven runtime and a variety of open-source connectors. - -You can use Flow to migrate your data from Firestore to the Postgres database in your Supabase project. -You do this by building a real-time pipeline that captures data from Firestore and materializes (loads) that data to Postgres. -Once created, the pipeline backfills all your historical data from Firestore and continues to process new data events in real time. - -## Prerequisites - -Before you begin, you'll need: - -- An Estuary account. [Head to the web app to start for free](https://dashboard.estuary.dev). - -- For your Firestore database: - - - A Google service account with read access to your Firestore database, via [roles/datastore.viewer](https://cloud.google.com/datastore/docs/access/iam). You can assign this role when you [create the service account](https://cloud.google.com/iam/docs/creating-managing-service-accounts#creating), or [add it to an existing service account](https://cloud.google.com/iam/docs/granting-changing-revoking-access#single-role). - - A generated [JSON service account key](https://cloud.google.com/iam/docs/creating-managing-service-account-keys#creating) for the service account. - -- A Supabase project - -## Step 1: Capture your Firestore data - -You'll start by creating a **capture**, a task in Flow that connects to your data source system: in this case, Firestore. This process will create one or more data **collections**, backed by a real-time data lake. - -1. Go to the [**Captures** tab](https://dashboard.estuary.dev/captures) of the Flow web app and choose **New Capture**. - -2. Locate and select the **Google Firestore** card. - - A form appears with the properties required for a Firestore capture. - -3. Set a name for your capture. - - Click inside the **Name** field to generate a drop-down menu of available **prefixes** and select one (likely, this will be the name of your organization). Append a unique capture name after the `/` to create the full name, for example, `acmeCo/myFirestoreCapture`. - -4. Fill out the required properties for Firestore. - - **Database**: Flow can autodetect the database name, but you may optionally specify it here. This is helpful if the service account used has access to multiple Firebase projects. Your database name usually follows the format `projects/$PROJECTID/databases/(default)`. - - **Credentials**: The JSON service account key created per the prerequisites. - -5. Click **Next**. - - Flow uses the provided configuration to initiate a connection with Firestore. It maps each collection in the Firestore database to a Flow collection. - -6. Optionally, use the **Collection Selector** to remove any collections you don't need to migrate to Supabase. - -7. Click **Save and Publish**. - - You'll see a notification when the capture publishes successfully. - - The data currently in your Firestore database has been captured to Flow, and future updates to it will be captured continuously. - - Click **Materialize Collections** to continue. - -## Step 2: Materialize your collections to Postgres - -Next, you'll add a Postgres materialization to connect the captured collections to tables in your Supabase Postgres database. - -1. On the **Create Materialization** page, search for and select the **PostgreSQL** tile. - -A form appears with the properties required for a Postgres materialization. - -2. Choose a unique name for your materialization like you did when naming your capture; for example, `acmeCo/mySupabaseMaterialization`. - -3. Fill out the required properties for Postgres. You can find most of these in Supabase by going to the **Settings** section and clicking **Database**. - - **Address**: Format at `:`. - - **User**: Usually, this is `postgres`. - - **Password**: The password you set when you created your Supabase project. - -4. Click **Next**. - - Flow initiates a connection with the database and the **Collection Selector** expands. - It's populated with your collections from Firestore, each mapped to a Postgres table. - -5. For each collection, apply a stricter JSON schema. - This ensure that the less-structured Firestore data will be written to a Postgres table in the correct shape. - - In the Collection Selector, choose a collection and click its **Specification** tab. - - Click **Schema Inference**. Flow scans the data in your collection and infers a new schema to use for materialization. - - Review the new schema and click **Apply Inferred Schema**. - -6. Click **Save and Publish**. You'll see a notification when the materialization publishes successfully. - - Your Firestore collections are copied to tables in Supabase. As long as you leave the capture and materialation running, any changes to the Firestore data will be reflected in Supabase in milliseconds. - -## Resources - -For more information, visit the [Flow docs](https://docs.estuary.dev/). In particular: - -- [Guide to create a Data Flow](https://docs.estuary.dev/guides/create-dataflow/) -- [Firestore capture connector](https://docs.estuary.dev/reference/Connectors/capture-connectors/google-firestore/) -- [Postgres materializaiton connector](https://docs.estuary.dev/reference/Connectors/materialization-connectors/PostgreSQL/) - -export const Page = ({ children }) => - -export default Page diff --git a/apps/docs/pages/guides/integrations/fezto.mdx b/apps/docs/pages/guides/integrations/fezto.mdx deleted file mode 100644 index 60007bc3ed3..00000000000 --- a/apps/docs/pages/guides/integrations/fezto.mdx +++ /dev/null @@ -1,34 +0,0 @@ -import Layout from '~/layouts/DefaultGuideLayout' - -export const meta = { - id: 'fezto', - title: 'Frontend Zero to One', - description: 'Create an app automatically from your Supabase Postgres using OpenAPI', - video: 'https://www.youtube.com/v/GOC6a0_AlgI', -} - -[Frontend Zero to One is](https://www.fezto.xyz) is a service which creates an app for your Supabase Postgres database on-the-fly without any drag and drop, using the OpenAPI spec provided by PostgREST. - -
- -
- -# Setup - -In the [Supabase control panel](https://app.supabase.com/), open your project and click the Settings cog icon, and then "API". -You will need: - -1. From the Project URL copy the project ID from https://your-project-id.supabase.co -2. From the "Project API keys" section copy the "anon" "public" API Key into the - -Paste both into the [FEZTO Supabase setup page](https://www.fezto.xyz/#/supabase) and click Launch. -You can now share and bookmark the browser URL which includes the projectID and anon key with others to launch the same app. - -export const Page = ({ children }) => - -export default Page diff --git a/apps/docs/pages/guides/integrations/flutterflow.mdx b/apps/docs/pages/guides/integrations/flutterflow.mdx deleted file mode 100644 index ebbd3b7ed87..00000000000 --- a/apps/docs/pages/guides/integrations/flutterflow.mdx +++ /dev/null @@ -1,144 +0,0 @@ -import Layout from '~/layouts/DefaultGuideLayout' - -export const meta = { - id: 'flutterflow', - title: 'FlutterFlow', - description: - 'FlutterFlow is a low-code tool that allows you to build Flutter apps incredibly fast.', - canonical: 'https://docs.flutterflow.io/actions/actions/backend-database/supabase', -} - - - FlutterFlow and Supabase integration is currently in alpha, and supported features may be limited. - - -[FlutterFlow](https://flutterflow.io/) is a low-code builder for developing native mobile applications using Flutter. You can use the simple drag-and-drop interface to build your app faster than traditional development. - -This guide gives you a quick overview of implementing basic CRUD operations using FlutterFlow and Supabase. You can find the full docs on FlutterFlow and Supabase [here](https://docs.flutterflow.io/actions/actions/backend-database/supabase). - -
- -
- - -## Step 1: Connect FlutterFlow to Supabase - -Before we dive into the code, this guide assumes that you have the following ready: - -- [Supabase](https://database.new/) project created -- Have setup tables in your Supabase project -- [FlutterFlow](https://app.flutterflow.io/) project created - -You can then connect your Supabase project to your FlutterFlow project with the following steps: - -1. In your Supabase project, navigate to Project Settings > API. Copy the Project URL. -2. Return to FlutterFlow, navigate to Settings and Integrations > Integrations > Supabase. Turn on the toggle (i.e., enable Supabase) and paste the API URL. -3. Similarly, from the Supabase API section, copy the anon key (under Project API keys) and paste it inside the FlutterFlow > Settings and Integrations > Integrations > Supabase > Anon Key. -4. Click on the Get Schema button. This will show the list of all tables with their schema (structure) created in Supabase. -5. (Optional) If you have defined an Array for any Column Data Type in Supabase, you must set its type here. To do so, tap the "Click to set Array type" and choose the right one. - - - -## Step 2: Inserting rows - -Go to your project page on FlutterFlow and follow the steps below to define the Action to any widget. - -1. Select the Widget (e.g., Button) on which you want to define the action. -2. Select Actions from the Properties panel (the right menu), and click Open. This will open an Action flow Editor in a new popup window. - 1. Click on + Add Action. - 2. On the right side, search and select the Supabase > Insert Row action. - 3. Set the Table to your table name (e.g., assignments). - 4. Under the Set Fields section, click on the + Add Field button. - 5. Click on the Field name and scroll down to find the Value Source dropdown and change it to From Variable. - 6. Click on UNSET and select Widget State > Name of the TextField. - 7. Similarly, add the field for the other UI elements. - - - -## Step 3: Selecting and displaying rows - -To query a Supabase table on a ListView: - -1. Select the ListView widget. Make sure you choose the ListView widget, not the ListTile. -2. Select Backend Query from the properties panel (the right menu), and click Add Backend Query. -3. Set the Query Type to Supabase Query. -4. Select your Table from the dropdown list -5. Set the Query Type to List of Rows. -6. Optional: If you want to display the limited result, say, for example, you have thousands of entries, but you want to display only 100, you can specify the limit. -7. Click Confirm. - - - -## Step 4: Updating rows - -Go to your project page on FlutterFlow and follow the steps below to define the Action to any widget. - -1. Select the Widget (e.g., Button) on which you want to define the action. -2. Select Actions from the Properties panel (the right menu), and click Open. This will open an Action flow Editor in a new popup window. - 1. Click on + Add Action. - 2. On the right side, search and select the Supabase > Update Row action. - 3. Set the Table to your table name (e.g., assignments). - 4. Optional: If you want to get the rows after the update is finished, enable the Return Matching Rows option. - 5. Now, you must set the row you want to update. Usually, this is done by finding a row in a table that matches the current row ID. To do so, click + Add Filter button inside the Matching Rows section. - 1. Set the Field Name to the field that contains the IDs. Typically, this is the id column. - 2. Set the Relation to Equal To because you want to find a row with the exact id. - 3. Into the Value Source, you can select the From Variable and provide the id of the row for which you just updated values in the UI. - 6. Under the Set Fields section, click on the + Add Field button. - 7. Click on the field name. - 8. Scroll down to find the Value Source dropdown and change it to From Variable. - 9. Click on UNSET and select Widget State > Name of the TextField. - 10. Similarly, add the field for the other UI elements. - -## Step 5: Deleting rows - -Go to your project page on FlutterFlow and follow the steps below to define the Action to any widget. - -1. Select the Widget (e.g., Button) on which you want to define the action. -2. Select Actions from the Properties panel (the right menu), and click Open. This will open an Action flow Editor in a new popup window. - 1. Click on + Add Action. - 2. On the right side, search and select the Supabase -> Delete Row action. - 3. Set the Table to your table name (e.g., assignments). - 4. Optional: Later, if you want to know which rows were deleted from a table, enable the Return Matching Rows option. - 5. Now, you must set the row you want to delete. Usually, this is done by finding a row in a table that matches the current row ID. To do so, click + Add Filter button inside the Matching Rows section. - 1. Set the Field Name to the field that contains the IDs. Typically, this is the id column. - 2. Set the Relation to Equal To because you want to find a row with the exact id. - 3. Into the Value Source, you can select the From Variable and provide the id of the row you want to delete. - - - -## Resources - -You can find more detailed guides on FlutterFlow’s docs. - -- [FlutterFlow Supabase available actions](https://docs.flutterflow.io/actions/actions/backend-database/supabase) -- [Retrieving Data from Supabase on FlutterFlow](https://docs.flutterflow.io/data-and-backend/supabase/supabase-database/retrieving-data) -- [Adding data to Supabase DB from FlutterFlow](https://docs.flutterflow.io/data-and-backend/supabase/supabase-database/adding-data) - -export const Page = ({ children }) => - -export default Page diff --git a/apps/docs/pages/guides/integrations/forestadmin.mdx b/apps/docs/pages/guides/integrations/forestadmin.mdx deleted file mode 100644 index 910a42f3473..00000000000 --- a/apps/docs/pages/guides/integrations/forestadmin.mdx +++ /dev/null @@ -1,53 +0,0 @@ -import Layout from '~/layouts/DefaultGuideLayout' - -export const meta = { - id: 'forestadmin', - title: 'ForestAdmin', - description: - 'Get started with Supabase and Forest Admin, a tool for automatically generating an Admin Panel without having to build it.', -} - -This guide outlines how to instantly generate an Admin Panel on top of your Supabase backend. - -[Forest Admin](https://www.forestadmin.com/) offers an off-the-shelf Admin Panel system that can reduce the amount of time and effort needed to create, maintain, and manage internal tools. -It automatically builds a backend API and provides a user-friendly interface to Create, Read, Update and Delete, Search, Segment your data, trigger custom actions, control permissions, and set up workflows on top of your app's data. - -![forest-admin-collections](/docs/img/guides/integrations/forestadmin/forest-admin-collections.png) - -If you don’t have a Forest Admin account, you can create one in a few minutes [here](https://app.forestadmin.com/signup). - -Let’s get started! - -## Step 1: Configure your Supabase Backend -If you already have a Supabase project set up, simply go to the Project Settings / Database tab to access the Database Settings and retrieve the connection string (URI tab). This is the only information you will need to connect your Supabase account to Forest Admin. - -![supabase-db-credentials](/docs/img/guides/integrations/forestadmin/supabase-db-credentials.png) - -If you don't have anything set up on Supabase yet, you can [create a Project](https://app.supabase.com/new/_) in just a few seconds. Once done, just go to the Database tab and create your first table. - -![supabase-db-create-table](/docs/img/guides/integrations/forestadmin/supabase-db-create-table.png) - -## Step 2: Connect the database to Forest Admin - -First, you have to create a new project on Forest Admin: -![forestadmin-create-project](/docs/img/guides/integrations/forestadmin/forestadmin-create-project.png) - -Then, you can use Forest Admin's Instant Setup for the Cloud mode to quickly get started. Alternatively, you can host the generated backend admin API on your own (Advanced setup), giving you full control of the backend code. In this guide, we will use the Cloud mode and the Instant Setup. -![forestadmin-hosting](/docs/img/guides/integrations/forestadmin/forestadmin-hosting.png) - -Finally, you can enter the database credentials you obtained in Step 1 and set them in Forest Admin by using the Connection URI mode. Don't forget to replace the password in the connection string with the database password you set in Supabase. Note that if you forget it, you can always go to your Supabase Database settings and reset your database password. -![forestadmin-db-credentials](/docs/img/guides/integrations/forestadmin/forestadmin-db-credentials.png) - -## You're all done! - -There it is, the configuration of Forest Admin is now complete and your admin panel is now ready-to-use with all the features of an admin panel provided out of the box. You can now browse or manipulate all your data in a structured way, use search with support for complex filters, build dashboards, invite your team mates and start collaborating around your business operations and much more. - -## Resources - -- [Forest Admin](https://www.forestadmin.com/) official website. -- [Forest Admin GitHub](https://github.com/ForestAdmin). -- [Forest Admin](https://docs.forestadmin.com/documentation-portal/) documentation. - -export const Page = ({ children }) => - -export default Page diff --git a/apps/docs/pages/guides/integrations/illa.mdx b/apps/docs/pages/guides/integrations/illa.mdx deleted file mode 100644 index 3dbe3b53616..00000000000 --- a/apps/docs/pages/guides/integrations/illa.mdx +++ /dev/null @@ -1,107 +0,0 @@ -import Layout from '~/layouts/DefaultGuideLayout' - -export const meta = { - id: 'illa', - title: 'ILLA', - description: - 'Get started with Supabase and ILLA, a low-code platform for developers that enables the rapid development and deployment of internal tools.', -} - -This tutorial outlines the process of creating an Admin Panel using ILLA Builder and Supabase in a few simple steps. ILLA is a low-code platform for developers that enables the rapid development and deployment of internal tools. It allows for creating pages by dragging and dropping UI components, connecting to any database or API, and writing JavaScript. To learn more about ILLA and give it a try, visit their website at [https://www.illacloud.com/](https://www.illacloud.com/). Let's begin! - -### Step 1: Set up your Back end on Supabase - -On the [Supabase dashboard](https://app.supabase.com/projects), click `New project` and set the name to adminPanel. - -![Create Supabase Project for ILLA Admin Panel](/docs/img/guides/integrations/illa/supabase-illa-project.png) - -Create a new table by clicking on the `Create a new table` . - -Supabase offers a variety of options for populating tables with data, including writing queries, creating schemas through a user interface, and uploading CSV files. - -![Create Supabase Table for ILLA Admin Panel](/docs/img/guides/integrations/illa/supabase-table-1.png) - -![Config Supabase Table for ILLA Admin Panel](/docs/img/guides/integrations/illa/supabase-table-2.png) - -Fill out the info in the table. The database is now set up. - -### Step 2: Build UI on ILLA Cloud - -On [ILLA Cloud](https://cloud.illacloud.com/), click Create New to create a new application. - -![Create new project on ILLA Builder](/docs/img/guides/integrations/illa/supabase-illa-create-project.png) - -Drag components from the `Insert` panel to the canvas. - -Select the components on the canvas and configure the property on the `Inspect` panel. - -As seen in the below screenshot, we have built a simple admin panel. - -![Build UI with ILLA Builder](/docs/img/guides/integrations/illa/supabase-illa-UI.png) - -### Step 3: Connect to Supabase and config CRUD - -Note down the database connection information under [Project Settings](https://app.supabase.com/project/hdcfnsxpwwgboqomdrhp/settings/database) in Supabase. - -![Note information in supabase](/docs/img/guides/integrations/illa/supabase-information.png) - -In the Action List, click `+ New` and select Supabase DB. - -![Connect ILLA to Supabase](/docs/img/guides/integrations/illa/supabase-illa-connect.png) - -Fill out the form to connect to your Supabase instance. Test connection and save resource. - -![Config Supabase in ILLA](/docs/img/guides/integrations/illa/supabase-illa-connect-2.png) - -Click `Create Action` to create an action with the Supabase resource and config your CRUD. - -![Select Supabase resource in ILLA](/docs/img/guides/integrations/illa/supabase-illa-select.png) - -Use `{{` to get the front-end input data. The following is an example of the User Management page in the Admin Panel. - -Search for a user by the name inputted in input1 - -``` -SELECT * -FROM user -WHERE name = "{{input1.value}}" -; -``` - -Update user data. Update user information when id matches - -``` -UPDATE user -SET name = "{{input3.value}}" -, email = "{{input4.value}}" -WHERE id="{{input2.value}}" -; -``` - -Insert user data - -``` -INSERT INTO user VALUES("{{input5.value}}","{{input6.value}}","{{input7.value}}"); -``` - -Delete a user by id - -``` -DELETE FROM user WHERE id = "{{input2.value}}"; -``` - -### Step 4: Show data on components - -Configure the properties of components with `{{` . For example: - -![Show Supabase data on ILLA components](/docs/img/guides/integrations/illa/supabase-illa-show-data.png) - -## Resources - -- [ILLA Cloud official website](https://www.illacloud.com/) -- [ILLA Cloud GitHub](https://github.com/illacloud/illa-builder) -- [ILLA Cloud documentation](https://www.illacloud.com/docs/about-illa) - -export const Page = ({ children }) => - -export default Page diff --git a/apps/docs/pages/guides/integrations/keyri.mdx b/apps/docs/pages/guides/integrations/keyri.mdx deleted file mode 100644 index 6e607205669..00000000000 --- a/apps/docs/pages/guides/integrations/keyri.mdx +++ /dev/null @@ -1,163 +0,0 @@ -import Layout from '~/layouts/DefaultGuideLayout' - -export const meta = { - id: 'keyri', - title: 'Keyri', - description: - 'QR authentication for an easy and flexible biometric solution across all platforms.', - video: 'https://www.youtube.com/v/jrjrcpc2PFQ', -} - -Keyri can be used to incorporate sign-in-with-QR functionality into your Supabase app, allowing users to scan a QR code on your web app with your mobile app and be instantly logged into the web app without having to input any credentials. - -Configuration is split into Web and Mobile components. On web, the Keyri QR Widget needs to be installed along with an event listener, and in your mobile app, install the Keyri SDK and pass into it the user's refresh token when sign-in-with-QR is initiated. When the refresh token lands in your web app, it's passed into Supabase's `setSession()` method. - -# Sign up for Keyri - -First make a free account on the Keyri dashboard ([https://app.keyri.com](https://app.keyri.com)). On Add Your Application, set a name and input the domain on which your app will eventually be deployed. You can create multiple application in Keyri to account for your development, staging, and production environments - -![](https://archbee-image-uploads.s3.amazonaws.com/FQ4YmCkDokMJylbTAsoOR/HvTIja3KfgKUIMiNVKAqP_screen-shot-2022-10-13-at-21524-pm.png) - -Note your application key from the Keys and Credentials section - this will be used in the Mobile portion of the implementation - -![](https://archbee-image-uploads.s3.amazonaws.com/FQ4YmCkDokMJylbTAsoOR/KnD6LkWs-PUDtTS1sT9Rz_screen-shot-2022-10-13-at-21746-pm.png) - -# Web - -
- -
- -For your web app, first download KeyriQR.html (available [here](https://raw.githubusercontent.com/Keyri-Co/library-keyri-connect/main/KeyriQR.html)) and save it to a public directory. - -Next, embed KeyriQR.html in your login page as an iFrame within the desired div. This serves as the widget that displays the dynamic QR code and connects with the Keyri API. - -```html -
- -
-``` - -Next, for the same login view, set up an event listener to pick up the session token that the iFrame emits when the QR code is scanned by your app. - -```javascript -useEffect(() => { - window.addEventListener('message', async (evt) => { - if (evt.data.keyri && evt.data.data && document.location.origin == evt.origin) { - const { data } = evt; - if (!data.error) { - let refresh_token = JSON.parse(data.data).refreshToken; - await handleQrLogin(refresh_token); - } else if (data.error) { - console.log(`Keyri error: ${data.message}`); - } - } -}); -``` - -That's it! - -# Mobile - -
- -
- - -### Install Flutter - -First, install the Flutter SDK, found at flutter.dev - -Make sure to add Flutter to your PATH, for example: - -```shell -export PATH="$PATH:`pwd`/flutter/bin" -``` - -### Apple - initial setup - -Download the latest version of Xcode from the Mac App Store. Make sure the Xcode provided simulator is using a 64-bit device (iPhone 5s or later). You can check the device by viewing the settings in the simulator’s **Hardware > Device** or **File > Open Simulator** menus. - -### Android - initial setup - -Download the latest version of [Android Studio](https://developer.android.com/studio). Install Android SDK and needed emulator(s). - -### Create Project - -Run this command in your terminal/shell at the desired location for your new project - -```shell -$ flutter create my_app -``` - -You can then CD into the new directory, and run the test app with - -```shell -flutter run -``` - -This is a good test - if things are configured correctly so far you should see the default Flutter test app deployed. - -### Add dependencies (Keyri and Supabase) - -Open your Pubspec.yaml file, which should be at the top level directory in your new project - -Add Keyri and Supabase under **dependencies** - -![](https://archbee-image-uploads.s3.amazonaws.com/FQ4YmCkDokMJylbTAsoOR/jlAfOTEchuZpBq8TeXhJZ_screen-shot-2022-09-29-at-060908.png) - -One can now access Supabase and Keyri sdks in their Flutter code - -### Utilize the two together - -1. Make a request to Supabase to authenticate the user -2. Parse the response to extract the token -3. Authenticate using Keyri - 1. Below, we show how to utilize the EasyKeyriAuth function, which takes the user through scanning the code, creating the session, displaying the confirmation screen, and finalizing the payload transmission - - Note - you can find your App Key in the Keyri Developer Portal​ - 2. Alternatively, intermediate functions in the Keyri SDK, discussed in the mobile docs, can provide control over displaying a custom QR Scanner and/or Confirmation screen - -```kotlin -// Sign in user with email and password -// Alternatively one can utilize the Supabase API to accomplish the same thing -final response = await client.auth.signIn(email: 'email', password: 'password'); -if (response.error != null) { - // Error - print('Error: ${response.error?.message}'); -} else { - // Success - final session = response.data; - // This is the payload that needs to be send through Keyri - final refreshToken = session.refreshToken - // EasyKeyriAuth guides the user through scanning and parsing the QR, confirming the session, and configuring the payload - // One can also use the initiateQRSession method to use the Keyri Scanner with a custom Confirmation screen - // Or the ProcessLink method if you have your own scanner or are using deep linking - await keyri - .easyKeyriAuth([App Key], - '{"refreshToken":"$refreshToken"}', [email]) - .then((authResult) => _onAuthResult(authResult)) - .catchError((error, stackTrace) => _onError(error)); -} -``` - -export const Page = ({ children }) => - -export default Page diff --git a/apps/docs/pages/guides/integrations/onesignal.mdx b/apps/docs/pages/guides/integrations/onesignal.mdx deleted file mode 100644 index 08128b3155f..00000000000 --- a/apps/docs/pages/guides/integrations/onesignal.mdx +++ /dev/null @@ -1,337 +0,0 @@ -import Layout from '~/layouts/DefaultGuideLayout' - -export const meta = { - id: 'onesignal', - title: 'OneSignal', - description: - 'OneSignal allows you to send cloud messages to your users. Combine OneSignal with your Supabase apps and you can reach out to your users whenever there is a change in your database.', - video: 'https://www.youtube.com/v/mw0DLwItue4', -} - -[OneSignal](https://onesignal.com/) is a tool that allows you to send messages across different channels such as the following to keep your users engaged. - -- Push notifications -- SMS -- Emails -- In-app notifications - -Here is William giving us the overview of how OneSignal can work with Supabase to send notifications to your users. - -
- -
- -In this guide, we will build a similar app and steps you through how you can integrate OneSignal with Supabase to create a seamless cloud messaging experience for your users using Database webhooks and edge functions through a simple Next.js application. - -![Entity Diagram](/docs/img/guides/integrations/onesignal/diagram.png) - -We will create a simple ordering app and use Supabase Database Webhooks in conjunction with Edge Function to provide a real-time push notification experience. - -You can find the complete example app along with the edge functions code to send the notifications [here](https://github.com/supabase-community/onesignal). - -![Ordering app UI](/docs/img/guides/integrations/onesignal/app-ui.png) - -## Step 1: Getting started - -Before we dive into the code, this guide assumes that you have the following ready - -- [Supabase](https://supabase.com/) project created -- [OneSignal](https://onesignal.com/) app created -- [Supabase CLI](https://supabase.com/docs/guides/cli) installed on your machine - -Let’s create a Next.js app with tailwind CSS pre-installed - -```bash -npx create-next-app -e with-tailwindcss --ts -``` - -We will then install the Supabase and OneSignal SDK. - -```bash -npm i @supabase/supabase-js -npm i react-onesignal -``` - -After that, follow the instructions [here](https://documentation.onesignal.com/docs/web-push-custom-code-setup) to set up OneSignal for the web. You can set the URL of the app as a local host if you want to run the app locally, or add a remote URL if you want to deploy your app to a public hosting. You should add the file you obtain in step 4 of the instruction under the `public` directory of your Next.js app like [this](https://github.com/supabase-community/onesignal/tree/main/app/public). - -## Step 2: Build Next.js app - -The Next.js app will have a login form for the user to sign in, and a button that they can press to make an order once they are signed in. Update the `index.tsx` file to the following. - -```tsx pages/index.tsx -import { createClient, User } from '@supabase/supabase-js' -import type { NextPage } from 'next' -import Head from 'next/head' -import React, { useEffect, useState } from 'react' -import OneSignal from 'react-onesignal' - -const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL! -const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY! -const oneSignalAppId = process.env.NEXT_PUBLIC_ONESIGNAL_APP_ID! - -const supabase = createClient(supabaseUrl, supabaseAnonKey) - -const Home: NextPage = () => { - const [user, setUser] = useState(null) - - const [oneSignalInitialized, setOneSignalInitialized] = useState(false) - - /** - * Initializes OneSignal SDK for a given Supabase User ID - * @param uid Supabase User ID - */ - const initializeOneSignal = async (uid: string) => { - if (oneSignalInitialized) { - return - } - setOneSignalInitialized(true) - await OneSignal.init({ - appId: oneSignalAppId, - notifyButton: { - enable: true, - }, - - allowLocalhostAsSecureOrigin: true, - }) - - await OneSignal.setExternalUserId(uid) - } - - const sendMagicLink = async (event: React.FormEvent) => { - event.preventDefault() - const { email } = Object.fromEntries(new FormData(event.currentTarget)) - if (typeof email !== 'string') return - - const { error } = await supabase.auth.signInWithOtp({ email }) - if (error) { - alert(error.message) - } else { - alert('Check your email inbox') - } - } - - // Place a order with the selected price - const submitOrder = async (event: React.FormEvent) => { - event.preventDefault() - const { price } = Object.fromEntries(new FormData(event.currentTarget)) - if (typeof price !== 'string') return - - const { error } = await supabase.from('orders').insert({ price: Number(price) }) - if (error) { - alert(error.message) - } - } - - useEffect(() => { - const initialize = async () => { - const initialUser = (await supabase.auth.getUser())?.data.user - setUser(initialUser ?? null) - if (initialUser) { - initializeOneSignal(initialUser.id) - } - } - - initialize() - - const authListener = supabase.auth.onAuthStateChange(async (event, session) => { - const user = session?.user ?? null - setUser(user) - if (user) { - initializeOneSignal(user.id) - } - }) - - return () => { - authListener.data.subscription.unsubscribe() - } - }, []) - - return ( - <> - - OneSignal Order Notification App - - - -
- {user ? ( -
- - -
- ) : ( -
- - -
- )} -
- - ) -} - -export default Home -``` - -There is quite a bit of stuff going on here, but basically, it’s creating a simple UI for the user to sign in using the [magic link](https://supabase.com/docs/guides/auth/auth-magic-link), and once the user is signed in, will initialize OneSignal to ask the user to receive notifications on the website. - -Notice that inside the `initializeOneSignal()` function, we are setting the Supabase user ID as an [external user ID of OneSignal](https://documentation.onesignal.com/docs/external-user-ids). This allows us to later send push notifications to the user using their Supabase user ID from the backend, which is very handy. - -```tsx -await OneSignal.setExternalUserId(uid) -``` - -The front-end side of things is done here. Let’s get into the backend. - -We also need to set our environment variables. Create a `.env.local` file and use the following template to set the environment variables. You can find your Supabase configuration in your dashboard under `settings > API`, and you can find the OneSignal app ID from `Settings > Keys & IDs` - -```bash -NEXT_PUBLIC_SUPABASE_URL=YOUR_SUPABASE_URL -NEXT_PUBLIC_SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY -NEXT_PUBLIC_ONESIGNAL_APP_ID=YOUR_ONESIGNAL_APP_ID -``` - -![Where to find OneSignal App ID](/docs/img/guides/integrations/onesignal/onesignal-app-id.png) - -## Step 3: Create the Edge Function - -Let’s create an edge function that will receive [database webhooks](https://supabase.com/docs/guides/database/webhooks) from the database and calls the OneSignal API to send the push notification. - -```bash -supabase functions new notify -``` - -Replace the contents of `supabase/functions/notify/index.ts` with the following - -```tsx -import { serve } from 'https://deno.land/std@0.177.0/http/server.ts' -import * as OneSignal from 'https://esm.sh/@onesignal/node-onesignal@1.0.0-beta7' - -const _OnesignalAppId_ = Deno.env.get('ONESIGNAL_APP_ID')! -const _OnesignalUserAuthKey_ = Deno.env.get('USER_AUTH_KEY')! -const _OnesignalRestApiKey_ = Deno.env.get('ONESIGNAL_REST_API_KEY')! -const configuration = OneSignal.createConfiguration({ - userKey: _OnesignalUserAuthKey_, - appKey: _OnesignalRestApiKey_, -}) - -const onesignal = new OneSignal.DefaultApi(configuration) - -serve(async (req) => { - try { - const { record } = await req.json() - - // Build OneSignal notification object - const notification = new OneSignal.Notification() - notification.app_id = _OnesignalAppId_ - notification.include_external_user_ids = [record.user_id] - notification.contents = { - en: `You just spent $${record.price}!`, - } - const onesignalApiRes = await onesignal.createNotification(notification) - - return new Response(JSON.stringify({ onesignalResponse: onesignalApiRes }), { - headers: { 'Content-Type': 'application/json' }, - }) - } catch (err) { - console.error('Failed to create OneSignal notification', err) - return new Response('Server error.', { - headers: { 'Content-Type': 'application/json' }, - status: 400, - }) - } -}) -``` - -If you see bunch of errors in your editor, it's because your editor is not configured to use Deno. Follow the official setup guide [here](https://deno.land/manual@v1.28.3/getting_started/setup_your_environment) to setup your IDE to use Deno. - -The function receives a `record` object, which is the row inserted in your `orders` table, and constructs a notification object to then send to OneSignal to deliver the push notification. - -We also need to set the environment variable for the function. Create a `.env` file under your `supabase` directory and paste the following. - -```bash -ONESIGNAL_APP_ID=YOUR_ONESIGNAL_APP_ID -USER_AUTH_KEY=YOUR_USER_AUTH_KEY -ONESIGNAL_REST_API_KEY=YOUR_ONESIGNAL_REST_API_KEY -``` - -`ONESIGNAL_APP_ID` and `ONESIGNAL_REST_API_KEY` can be found under `Settings > Keys & IDs` of your OneSignal app, and `USER_AUTH_KEY` can be found by going to `Account & API Keys` page by clicking your icon in the top right corner and scrolling to the `User Auth Key` section. - -![Where to find OneSignal User Auth Key](/docs/img/guides/integrations/onesignal/onesignal-api-key.png) - -Once your environment variables are filled in, you can run the following command to set the environment variable. - -```bash -supabase secrets set --env-file ./supabase/.env -``` - -At this point, the function should be ready to be deployed! Run the following command to deploy your functions to the edge! The `no-verify-jwt` flag is required if you plan to call the function from a webhook. - -```bash -supabase functions deploy notify --no-verify-jwt -``` - -## Step 4: Setting up the Supabase database - -Finally, we get to set up the database! Run the following SQL to set up the `orders` table. - -```sql -create table - if not exists public.orders ( - id uuid not null primary key default uuid_generate_v4 (), - created_at timestamptz not null default now (), - user_id uuid not null default auth.uid (), - price int8 not null - ); -``` - -As you can see, the `orders` table has 4 columns and 3 of them have default values. That means all we need to send from the front-end app is the price. That is why our insert statement looked very simple. - -```tsx -const { error } = await supabase.from('orders').insert({ - price: 100, -}) -``` - -Let’s also set up the webhook so that whenever a new row is inserted in the `orders` table, it calls the edge function. Go to `Database > Webhooks` and create a new Database Webhook. The table should be set to `orders` and Events should be inserted. The type should be HTTP Request, the HTTP method should be POST, and the URL should be the URL of your edge function. Hit confirm to save the webhook configuration. - -![Supabase Webhooks configuration](/docs/img/guides/integrations/onesignal/webhook.png) - -At this point, the app should be complete! Run your app locally with `npm run dev`, or deploy your app to a hosting service and see how you receive a push notification when you place an order! -Remember that if you decide to deploy your app to a hosting service, you would need to create another OneSignal app configured for your local address. - -![Ordering app UI](/docs/img/guides/integrations/onesignal/app-ui.png) - -## Resources - -This particular example was using Next.js, but you can apply the same principles to implement send push notification, SMS, Emails, and in-app-notifications on other platforms as well. - -- [OneSignal + Flutter + Supabase example](https://github.com/OneSignalDevelopers/onesignal-supabase-sample-integration-supabase) -- [OneSignal Mobile Quickstart](https://documentation.onesignal.com/docs/mobile-sdk-setup) -- [OneSignal Documentation](https://documentation.onesignal.com/docs/onesignal-platform) -- [OneSignal Onboarding guide](https://documentation.onesignal.com/docs/onboarding-with-onesignal) - -export const Page = ({ children }) => - -export default Page diff --git a/apps/docs/pages/guides/integrations/passage.mdx b/apps/docs/pages/guides/integrations/passage.mdx deleted file mode 100644 index 46ebc61ba1e..00000000000 --- a/apps/docs/pages/guides/integrations/passage.mdx +++ /dev/null @@ -1,608 +0,0 @@ -import Layout from '~/layouts/DefaultGuideLayout' - -export const meta = { - id: 'passage', - title: 'Passage', - description: - 'Build a Next.js application that uses passkey authentication with Passage and Row Level Security (RLS) with Supabase.', -} - -This guide steps through building a Next.js application with Passage and Supabase. We will use Passage to authenticate users and manage tokens, while using Supabase for storing data and enforcing authorization logic using Row Level Security policies. - -The full code example for this guide can be found [here](https://github.com/passageidentity/supabase-passage-example). - -[Passage](https://passage.id) is a passwordless authentication platform that makes it simple for developers to add passkey authentication to their apps and websites, providing better security and simpler sign in for your users. They provide simple frontend components that handle all of the complexity of passwordless login for developers in just two lines of codes. Passage also provides session management, user management, and in-depth customization capabilities. - -Next.js is a web application framework built on top of React. We will be using it for this example, as it allows us to write server-side logic within our application. Passage’s [frontend elements](https://docs.passage.id/frontend/passage-element) and [Node.js SDK](https://docs.passage.id/backend/overview/node) are designed to work for Next.js. - -For this guide, you will need a Passage account which can be created [here](https://console.passage.id/register), and a Supabase account which can be created [here](https://app.supabase.com/). - -## 1. Create a Passage application -In the [Passage Console](https://console.passage.id), create a new application with the following configuration: - -* Application name: Todo Application -* Authentication origin: http://localhost:3000 -* Redirect URL: /dashboard - -![Passage Console dashboard](/docs/img/guides/integrations/passage/01.png) - -![Fill out the required fields to create a new application](/docs/img/guides/integrations/passage/02.png) - -## 2. Create and configure a Supabase project - -#### Create a new project -In the [Supabase dashboard](https://app.supabase.com/), click `New Project`. -Enter a name for your project and create a secure database password. - -#### Create a table schema -We are building Todo list application, similar to the [Supabase demo application](https://github.com/supabase/supabase/tree/master/examples/todo-list/nextjs-todo-list) so we will need a table for the todo list items. - -Create a new table in the Table Editor view. - -Set the Name field to `todo`. - -Select Enable Row Level Security (RLS). - -Create the following new columns. - * `title` as `text` - * `user_id` as `text` with a default value of `auth.user_id()` - * `is_complete` as `bool` with a default value of `false` - -Click `Save` to create the table. - -![Table schema.](/docs/img/guides/integrations/passage/03.png) - -#### Add initial data to the table -From the Table editor, select the `todo` table and click `Insert row`. Fill out the required fields with an example todo item, leaving the `user_id` as NULL and click `Save`. - -![Example todo item.](/docs/img/guides/integrations/passage/04.png) - -After adding a few todo items, the table editor view will look like this: - -![Table with multiple todo items.](/docs/img/guides/integrations/passage/05.png) - -## 3. Build a Next.js app - -#### Create Next.js app - -Create a new Next.js project on the command line. You can choose your settings through the setup wizard - for this guide we will use JavaScript instead of TypeScript. This example uses the create script in Next v13.2. - -```sh -npx create-next-app -cd / -``` - -You should be able to use the default settings for the project, but here are the settings used for the example app. - -![Settings for Next.js application.](/docs/img/guides/integrations/passage/06.png) - -#### Configure your ENV - -Create a `.env` file and enter the following values. - -```sh -NEXT_PUBLIC_PASSAGE_APP_ID=get-from-passage-settings -PASSAGE_API_KEY=get-from-passage-settings -NEXT_PUBLIC_SUPABASE_URL=get-from-supabase-dashboard -NEXT_PUBLIC_SUPABASE_ANON_KEY=get-from-supabase-dashboard -SUPABASE_JWT_SECRET=get-from-supabase-dashboard -``` - -The Supabase values can be found under `Project->Settings->API Settings`. - -![Supabase environment variables.](/docs/img/guides/integrations/passage/07.png) - -The Passage values can be found under `General->Settings` and `General->API Keys`. - -![Passage environment variables.](/docs/img/guides/integrations/passage/08.png) - -![Passage environment variables.](/docs/img/guides/integrations/passage/09.png) - -> The `PASSAGE_API_KEY` and `SUPABASE_JWT_SECRET` are secret values and should never be shared publicly. They will only be used in the server-side code of the Next.js application. - -Restart your Next.js development server to read in the environment variables. - -```bash -npm run dev -``` - -## 4. Add Passage login to your app - -#### Add Passage Element - -Install the `@passageidentity/passage-elements` package. - -```bash -npm install @passageidentity/passage-elements -``` - -Create a new folder called components with a new login file `components/login.js` and add the following content. - -```js -// components/login.js -import { useEffect } from 'react'; - -const PassageLogin = () => { - - useEffect(()=>{ - require('@passageidentity/passage-elements/passage-auth'); - }, []); - - return ( - <> - - - ) - } - - export default PassageLogin -``` - -Then update `pages/index.js` to include the login component. - -```js -// pages/index.js -import styles from '@/styles/Home.module.css' -import PassageLogin from '@/components/login' - - -export default function Home(props) { - return( -
- -
- ) -} -``` - -When we have a successful registration the Passage element will request a redirect to `/dashboard` per the redirect URL we set during app creation. - -Create a new file `pages/dashboard.js` for this new route with the following content: - -```js -// pages/dashboard.js -import styles from '@/styles/Home.module.css' - -export default function Dashboard({isAuthorized, userID, todos}) { - return( -
-
-

- You've logged in! -

-
-
- ) -} -``` -Now when you visit `http://localhost:3000` in a browser you will have a fully functioning and passwordless login page! - -![Simple app with Passage login page.](/docs/img/guides/integrations/passage/10.png) - -Go ahead and go through the registration process. You will be able to register an account with either a passkey or a magic link. Once you've logged in, you will notice that you just get redirected to `/dashboard` page. -The login was successful, but we need to build in the functionality to know when a user is authenticated and show them the appropriate view. - -#### Use Passage to verify the JWT - -Now we will need to use a Passage SDK to verify the JWT from Passage. - -Install the Passage Node.js library. - -```bash -npm install @passageidentity/passage-node -``` - -Create a utils folder and a file called `utils/passage.js` with the following content. - -```js -// utils/passage.js -import Passage from '@passageidentity/passage-node'; - -const passage = new Passage({ - appID: process.env.NEXT_PUBLIC_PASSAGE_APP_ID, - apiKey: process.env.PASSAGE_API_KEY, -}); - -export const getAuthenticatedUserFromSession = async (req, res) => { - try { - const userID = await passage.authenticateRequest(req); - if (userID) { - return {isAuthorized: true, userID: userID}; - } - } catch (error) { - // authentication failed - return {isAuthorized: false, userID: ''}; - } -} -``` - -This will be used in the `getServerSideProps()` function to check authentication status for a user. Add this function to `index.js` then update the `Home` function to use the props. - -```js -// pages/index.js -import styles from '@/styles/Home.module.css' -import PassageLogin from '@/components/login' -import { getAuthenticatedUserFromSession } from '@/utils/passage' -import { useEffect } from 'react' -import Router from 'next/router'; - -export default function Home({isAuthorized}) { - useEffect(()=> { - if(isAuthorized){ - Router.push('/dashboard') - } - }) - - return( -
- -
- ) -} - -export const getServerSideProps = async (context) => { - const loginProps = await getAuthenticatedUserFromSession(context.req, context.res) - return { - props: { - isAuthorized: loginProps.isAuthorized?? false, - userID: loginProps.userID?? '' - }, - } -} -``` - -We will also use this logic on the dashboard page to check if a user is authenticated. If not we should redirect them to the login page. We will also add a quick sign out button using Passage while we are at it. - -```js -// pages/dashboard.js -import styles from '@/styles/Home.module.css' -import { useEffect } from 'react'; -import Router from 'next/router'; -import { getAuthenticatedUserFromSession } from '@/utils/passage' -import { PassageUser } from '@passageidentity/passage-elements/passage-user' - -export default function Dashboard({isAuthorized, userID}) { - useEffect(() => { - if(!isAuthorized){ - Router.push('/'); - } - }) - - const signOut = async ()=>{ - new PassageUser().signOut() - Router.push('/') - } - - return ( -
-

- Welcome {userID}!{' '} -

-

- -
- ) - } - -export const getServerSideProps = async (context) => { - const loginProps = await getAuthenticatedUserFromSession(context.req, context.res) - - return { - props: { - isAuthorized: loginProps.isAuthorized?? false, - userID: loginProps.userID?? '', - }, - } -} -``` - -The app can now tell the difference between an authenticated and unauthenticated user. When you log into the application, you will be redirected to the dashboard and see this message. - -![Authenticated users can see their user ID.](/docs/img/guides/integrations/passage/11.png) - -## 5. Integrate Supabase into Next.js app - -Passage and Supabase do not currently allow for custom signing secrets. Therefore, we will need to extract the necessary claims from the Passage JWT and sign a new JWT to send to Supabase. - -Because of the sensitive nature of this functionality, we will handle the authentication and JWT exchange in Next.js’s server-side rendering function `getServerSideProps()`. Imports used in this function will not be bundled client-side. Additionally, the JWT provided by Passage is stored in a cookie which is automatically passed to `getServerSideProps()`. - -#### Sign Passage token for Supabase - -Install the Supabase client SDK and the popular Node package `jsonwebtoken`, which allows us to easily work with JWTs. - -```bash -npm install @supabase/supabase-js jsonwebtoken -``` - -Create a new file called `utils/supabase.js` and add the following content. This function accepts a Passage user ID and then creates and signs a Supabase JWT for that user. This allows Supabase to verify the token and authenticate the user when making Supabase calls. - -```js -// utils/supabase.js -import { createClient } from '@supabase/supabase-js' -import jwt from 'jsonwebtoken' - -const getSupabase = (userId) => { - const options = {} - - if (userId) { - const payload = { - userId, - exp: Math.floor(Date.now() / 1000) + 60 * 60, - } - const token = jwt.sign(payload, process.env.SUPABASE_JWT_SECRET) - - options.global = { - headers: { - Authorization: `Bearer ${token}`, - }, - } - } - - const supabase = createClient( - process.env.NEXT_PUBLIC_SUPABASE_URL, - process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY, - options - ) - return supabase -} - -export { getSupabase } -``` - -#### Enable Row Level Security (RLS) in Supabase - -To enable users to view and create their own todo items we need to write a RLS policy. Our policy will check the currently logged in user is to determine whether or not they should have access. Let's create a PostgreSQL function to extract the current user from our new JWT. - -Navigate back to the Supabase dashboard, select `SQL Editor` from the sidebar menu, and click `New query`. This will create a new query called `new sql snippet`, which will allow us to run any SQL against our Postgres database. - -Write the following and click `Run`. - -```sql -create or replace function auth.user_id() returns text as $$ - select nullif(current_setting('request.jwt.claims', true)::json->>'userId', '')::text; -$$ language sql stable; -``` - -You should see the output `Success, no rows returned`. This created a function called `auth.user_id()` which will return the `userId` field of our JWT payload. - -> To learn more about PostgresSQL functions, check out this [deep dive video](https://www.youtube.com/watch?v=MJZCCpCYEqk). - -Now we can create a policy that checks whether the current user is the owner of a todo item. From the `Authentication` sidebar menu in Supabase, click `Policies` then create a new policy. - -![RLS policies for a table.](/docs/img/guides/integrations/passage/12.png) - -Choose `For full customization create a policy from scratch` and add the following. - -![Policy to restrict access to todo items.](/docs/img/guides/integrations/passage/13.png) - -This policy is calling the function we just created to get the currently logged in user's ID `auth.user_id() `and checking whether this matches the `user_id` column for the current todo. If it does, then it will allow the user to select it, otherwise it will deny access. - -Click `Review` and then `Save policy`. - -> Note: To learn more about RLS and policies, check out this [deep dive video](https://www.youtube.com/watch?v=Ow_Uzedfohk). - -#### Fetch data from Supabase - -Now we can fetch data from Supabase specific to that user. We will update `pages/dashboard.js` to do the following: -1. authenticate the user using Passage -2. create and sign a JWT for the user with the Supabase secret -3. query Supabase to fetch a user’s todo list items - -```js -// pages/dashboard.js -import styles from '@/styles/Home.module.css' -import { useEffect } from 'react'; -import Router from 'next/router'; -import { getAuthenticatedUserFromSession } from '@/utils/passage' -import { getSupabase } from '../utils/supabase' - - -export default function Dashboard({isAuthorized, userID, todos}) { - useEffect(() => { - if(!isAuthorized){ - Router.push('/'); - } - }) - - return( -
-
-

- Welcome {userID}!{' '} -

-

- -

-
- {todos?.length > 0 ? ( - todos.map((todo) =>
  • {todo.title}
  • ) - ) : ( -

    You have completed all todos!

    - )} -
    -
    -
    - ) - } - -export const getServerSideProps = async (context) => { - const loginProps = await getAuthenticatedUserFromSession(context.req, context.res) - - if(loginProps.isAuthorized){ - const supabase = getSupabase(loginProps.userID) - const {data} = await supabase.from('todo').select() - - - return { - props: { - isAuthorized: loginProps.isAuthorized?? false, - userID: loginProps.userID?? '', - todos: data?? [], - }, - } - } else { - return { - props: { - isAuthorized: loginProps.isAuthorized?? false, - userID: loginProps.userID?? '' - }, - } - } -} -``` - -When we reload our application, we are still getting the empty state for todos. - -This is because we enabled Row Level Security, which blocks all requests by default and lets you granularly control access to the data in your database. - -#### Update the UserID data - -The last thing we need to do is update the `user_id` columns for our existing todos. Head back to the Supabase dashboard, and select `Table editor` from the sidebar. You will see that the `user_id` field is NULL for all of our todo items. - -![User ID is NULL for all todo items.](/docs/img/guides/integrations/passage/14.png) - -To get the user ID for our Passage user, go back to the Passage Console and check the `Users` tab. - -![Get User ID from Passage.](/docs/img/guides/integrations/passage/15.png) - -Copy this user ID and update two of the three rows in the Supabase database to match this user ID. When you are done, the database table will look like this. - -![Updated User ID in Supabase.](/docs/img/guides/integrations/passage/16.png) - -Now when we refresh the application, we will see the todo items for our user! - -![Authenticated users can see their todo items.](/docs/img/guides/integrations/passage/17.png) - -## Bonus: Add todo items - -To build out a bit more functionality in our application, we can now let users add items to their to do list. Create a file `pages/api/addTodo.js` with the following content. - -```js -// pages/api/addTodo.js -import { getSupabase } from "../../utils/supabase"; - -export default async function handler(req, res) { - const { userID, todo } = req.body; - const supabase = getSupabase(userID); - const { data, error } = await supabase - .from("todo") - .insert({ title: todo }) - .select() - .single(); - if (error) return res.status(400).json(error); - res.status(200).json(data); -} -``` -Then update `pages/dashboard.js` to include a form for submitting new to do items. The complete file will look like this. - -```js -//pages/dashboard.js -import styles from "@/styles/Home.module.css"; -import { useEffect, useState } from "react"; -import Router from "next/router"; -import { getAuthenticatedUserFromSession } from "@/utils/passage"; -import { getSupabase } from "../utils/supabase"; -import { PassageUser } from "@passageidentity/passage-elements/passage-user"; - -export default function Dashboard({ isAuthorized, userID, initialTodos }) { - const [todos, setTodos] = useState(initialTodos); - useEffect(() => { - if (!isAuthorized) { - Router.push("/"); - } - }); - - const handleSubmit = async (e) => { - e.preventDefault(); - const data = new FormData(e.target); - const todo = data.get("todo"); - const res = await fetch("/api/addTodo", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ todo, userID }), - }).then((res) => res.json()); - setTodos([...todos, res]); - }; - - const signOut = async () => { - new PassageUser().signOut(); - Router.push("/"); - }; - - return ( -
    -
    -

    Welcome {userID}!

    -

    - -

    -
    - {todos?.length > 0 ? ( - todos.map((todo) =>
  • {todo.title}
  • ) - ) : ( -

    You have completed all todos!

    - )} -
    -
    - - -
    -
    -
    - ); -} - -export const getServerSideProps = async (context) => { - const loginProps = await getAuthenticatedUserFromSession( - context.req, - context.res - ); - - if (loginProps.isAuthorized) { - const supabase = getSupabase(loginProps.userID); - const { data } = await supabase - .from("todo") - .select() - .is("is_complete", false); - - return { - props: { - isAuthorized: loginProps.isAuthorized ?? false, - userID: loginProps.userID ?? "", - initialTodos: data ?? [], - }, - }; - } else { - return { - props: { - isAuthorized: loginProps.isAuthorized ?? false, - userID: loginProps.userID ?? "", - }, - }; - } -}; -``` - -Finally, we need to add a new RLS policy in Supabase to allow users to insert their own todo items. - -![RLS policy for inserting todo items.](/docs/img/guides/integrations/passage/18.png) - -That's it! Now the website has form for submitting new items for the to do list. - -![Authenticated users can create todo items](/docs/img/guides/integrations/passage/19.png) - -## Resources - -* [Passage website](https://passage.id) -* [Complete developer documentation](https://docs.passage.id) -* [Passage Github](https://github.com/passageidentity), including SDKs and example apps for Next.js -* [Developer community](https://discord.com/invite/445QpyEDXh) on Discord - - -export const Page = ({ children }) => - -export default Page \ No newline at end of file diff --git a/apps/docs/pages/guides/integrations/pgmustard.mdx b/apps/docs/pages/guides/integrations/pgmustard.mdx deleted file mode 100644 index 078181c6f2c..00000000000 --- a/apps/docs/pages/guides/integrations/pgmustard.mdx +++ /dev/null @@ -1,73 +0,0 @@ -import Layout from '~/layouts/DefaultGuideLayout' - -export const meta = { - id: 'pgmustard', - title: 'pgMustard', - description: - 'Troubleshoot slow queries on Supabase with pgMustard, a visualization tool that also gives advice.', -} - -This guide explains how to troubleshoot slow queries on Supabase using `explain` and pgMustard. - -[pgMustard](https://pgmustard.com/) is a visualization tool for [`explain analyze`](https://www.postgresql.org/docs/current/using-explain.html#USING-EXPLAIN-ANALYZE) that also gives performance tips. - -## Step 1: Get the query plan from Supabase - -Use `explain analyze` to get a query plan from Postgres. This will run the query behind the scenes, so be careful with data modification queries. - -pgMustard requires plans to be in json format, and the buffers, verbose, and settings parameters allow it to give better tips. - -So a good prefix for your query would be: - -```jsx -explain (analyze, format json, buffers, verbose, settings) -``` - -Run the query, and copy the output. - -![01-supabase-run-query](/docs/img/guides/integrations/pgmustard/01-supabase-run-query.png) - -If you’re using the Supabase SQL Editor, this is easily copied from the cell titled `QUERY PLAN`, as seen above. - -If you have any trouble, check out pgMustard’s guide for [getting a query plan](https://www.pgmustard.com/getting-a-query-plan). - -## Step 2: Paste the query plan into pgMustard - -Paste the json output into pgMustard and press Submit. - -![02-paste-plan-pgmustard](/docs/img/guides/integrations/pgmustard/02-paste-plan-pgmustard.png) - -## Step 3: Look through the top tips and slowest operations - -Review the top tips in pgMustard. These are scored on a scale of 0 to 5 stars, based on how much time-saving potential they have (5 stars meaning lots of potential). - -![03-review-tips-pgmustard](/docs/img/guides/integrations/pgmustard/03-review-tips-pgmustard.png) - -Click one of the tips, or one of the operations, to see more information. - -![04-click-tip-pgmustard](/docs/img/guides/integrations/pgmustard/04-click-tip-pgmustard.png) - -## Step 4: Consider your options - -If you get some promising suggestions, you may wish to explore them. - -If you don’t get any tips, your query might be quite fast for the amount of work it’s doing. - -For the example we saw in Step 3, let's try adding an index on the `customer_name` field in Supabase. - -![05-create-index-supabase](/docs/img/guides/integrations/pgmustard/05-create-index-supabase.png) - -Going through Steps 1-3 again, we now get an efficient index scan, that will scale nicely as our data grows. - -![06-check-pgmustard](/docs/img/guides/integrations/pgmustard/06-check-pgmustard.png) - -We could look into why Postgres isn’t choosing to do an index-only scan here, but pgMustard is letting us know that it doesn’t think we’ll gain much by doing so, by scoring the tip 0.3 out of 5. - -## Resources - -- [pgMustard](https://www.pgmustard.com) official website. -- [pgMustard explain glossary](https://www.pgmustard.com/docs/explain). - -export const Page = ({ children }) => - -export default Page diff --git a/apps/docs/pages/guides/integrations/picket.mdx b/apps/docs/pages/guides/integrations/picket.mdx deleted file mode 100644 index 55f7c755e57..00000000000 --- a/apps/docs/pages/guides/integrations/picket.mdx +++ /dev/null @@ -1,572 +0,0 @@ -import Layout from '~/layouts/DefaultGuideLayout' - -export const meta = { - id: 'picket', - title: 'Picket', - description: - "Get the best of web2 and web3. Picket allows your users to log in with their wallet without sacrificing Supabase's awesome data management and security features.", -} - -[Picket](https://picketapi.com) is a developer-first, multi-chain web3 auth platform. With Picket, you can easily authenticate users via their wallets and token gate anything. - -This guide steps through building a simple todo list Next.js application with Picket and Supabase. We use Picket to allow users to login into our app with their wallets and leverage Supabase's Row Level Security (RLS) to securely store user information off-chain. - -> Checkout a [live demo](https://picket-supabase-auth-example-v36z.vercel.app/) of a Picket + Supabase integration - -The code for this guide is based of [this example repo](https://github.com/picketapi/picket-supabase-auth-example). - -## Requirements - -- You have [Supabase](https://supabase.com) account. If you don't, sign up at https://supabase.com/ -- You have a [Picket](https://picketapi.com) account. If you don't, sign up at https://picketapi.com/ -- You've read the [Picket Setup Guide](https://docs.picketapi.com/picket-docs/quick-start-guides/quick-start-guides/start-here-setup) -- Familiarity with [React](https://reactjs.org/) and [Next.js](https://nextjs.org/) - -## Step 1: Create a Picket Project - -First, we'll create a new project in our [Picket dashboard](https://picketapi.com/dashboard). - -Click the `Create New Project` button at the top of the Projects section on your [Picket dashboard](https://picketapi.com/dashboard). Edit the project to give it a memorable name. - -![Picket project settings](/docs/img/guides/integrations/picket/picket_project.png) - -We're done for now! We'll revisit this project when we are setting up environment variables in our app. - -## Step 2: Create a Supabase Project - -From your [Supabase dashboard](https://app.supabase.com/), click `New project`. - -Enter a `Name` for your Supabase project. - -Enter a secure `Database Password`. - -Select the any `Region`. - -Click `Create new project`. - -![Supabase project settings](/docs/img/guides/integrations/picket/supabase_project.png) - -## Step 3: Create new New Table with RLS in Supabase - -### Create a `todos` Table - -From the sidebar menu in the [Supabase dashboard](https://app.supabase.com/), click `Table editor`, then `New table`. - -Enter `todos` as the `Name` field. - -Select `Enable Row Level Security (RLS)`. - -Create four columns: - -- `name` as `text` -- `wallet_address` as `text` -- `completed` as `bool` with the default value `false` -- `created_at` as timestamptz with a default value of `now()` - -Click `Save` to create the new table. - -![todos table](/docs/img/guides/integrations/picket/new_table.png) - -### Setup Row Level Security (RLS) - -Now we want to make sure that only the `todos` owner, the user's `wallet_address`, can access their todos. The key component of the this RLS policy is the expression - -```sql -((auth.jwt() ->> 'walletAddress'::text) = wallet_address) -``` - -This expression checks that the wallet address in the requesting JWT access token is the same as the `wallet_address` in the `todos` table. - -![RLS policy](/docs/img/guides/integrations/picket/rls_policy.png) - -## Step 4: Create a Next.js app - -Now, let's start building! - -Create a [new Typescript Next.js app](https://nextjs.org/docs/getting-started) - -```bash -npx create-next-app@latest --typescript -``` - -Create a `.env.local` file and enter the following values - -- `NEXT_PUBLIC_PICKET_PUBLISHABLE_KEY` => Copy the publishable key from the Picket project you created in step 1 -- `PICKET_PROJECT_SECRET_KEY` => Copy the secret key from the Picket project you created in the step 1 -- `NEXT_PUBLIC_SUPABASE_URL` => You can find this URL under "Settings > API" in your Supabase project -- `NEXT_PUBLIC_SUPABASE_ANON_KEY` => You can find this project API key under "Settings > API" in your Supabase project -- `SUAPBASE_JWT_SECRET`=> You can find this secret under "Settings > API" in your Supabase project - -```bash -NEXT_PUBLIC_PICKET_PUBLISHABLE_KEY="YOUR_PICKET_PUBLISHABLE_KEY" -PICKET_PROJECT_SECRET_KEY="YOUR_PICKET_PROJECT_SECRET_KEY" -NEXT_PUBLIC_SUPABASE_URL="YOUR_SUPABASE_URL" -NEXT_PUBLIC_SUPABASE_ANON_KEY="YOUR_SUPABASE_ANON_KEY" -SUPABASE_JWT_SECRET="YOUR_SUPABASE_JWT_SECRET" -``` - -## Step 5: Setup Picket for Wallet Login - -> For more information on how to setup [Picket](https://picketapi.com) in your Next.js app, checkout the [Picket getting started guide](https://docs.picketapi.com/picket-docs/quick-start-guides/quick-start-guides/wallet-login) -> After initializing our app, we can setup Picket. - -Install the Picket [React](/picket-docs/reference/libraries-and-sdks/react-sdk-picket-react) and [Node](/picket-docs/reference/libraries-and-sdks/node.js-library-picket-node) libraries - -```bash -npm i @picketapi/picket-react @picketapi/picket-node -``` - -Update `pages/_app.tsx` to setup the `PicketProvider` - -```tsx -import '../styles/globals.css' -import type { AppProps } from 'next/app' - -import { PicketProvider } from '@picketapi/picket-react' - -export default function App({ Component, pageProps }: AppProps) { - return ( - - - - ) -} -``` - -Update `pages/index.tsx` to let users log in and out with their wallet - -```tsx -import { GetServerSideProps } from 'next' -import { useRouter } from 'next/router' -import { useCallback } from 'react' - -import styles from '../styles/Home.module.css' - -import { usePicket } from '@picketapi/picket-react' -import { cookieName } from '../utils/supabase' - -type Props = { - loggedIn: boolean -} - -export default function Home(props: Props) { - const { loggedIn } = props - const { login, logout, authState } = usePicket() - const router = useRouter() - - const handleLogin = useCallback(async () => { - let auth = authState - // no need to re-login if they've already connected with Picket - if (!auth) { - // login with Picket - auth = await login() - } - - // login failed - if (!auth) return - - // create a corresponding supabase access token - await fetch('/api/login', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - accessToken: auth.accessToken, - }), - }) - // redirect to their todos page - router.push('/todos') - }, [authState, login, router]) - - const handleLogout = useCallback(async () => { - // clear both picket and supabase session - await logout() - await fetch('/api/logout', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - }) - // refresh the page - router.push('/') - }, [logout, router]) - - return ( -
    -
    - {loggedIn ? ( - - ) : ( - - )} -
    -
    - ) -} - -export const getServerSideProps: GetServerSideProps = async ({ req }) => { - // get supabase token server-side - const accessToken = req.cookies[cookieName] - - if (!accessToken) { - return { - props: { - loggedIn: false, - }, - } - } - - return { - props: { - loggedIn: true, - }, - } -} -``` - -## Step 6: Issue a Supabase JWT on Wallet Login - -Great, now we have setup a typical Picket Next.js app. Next, we need to implement the log in/out API routes to allow users to securely query our Supabase project. - -First, install dependencies - -```bash -npm install @supabase/supabase-js jsonwebtoken cookie js-cookie -``` - -Create a utility function to create a Supabase client with a custom access token in `utils/supabase.ts` - -```ts -import { createClient, SupabaseClientOptions } from '@supabase/supabase-js' - -export const cookieName = 'sb-access-token' - -const getSupabase = (accessToken: string) => { - const options: SupabaseClientOptions<'public'> = {} - - if (accessToken) { - options.global = { - headers: { - // This gives Supabase information about the user (wallet) making the request - Authorization: `Bearer ${accessToken}`, - }, - } - } - - const supabase = createClient( - process.env.NEXT_PUBLIC_SUPABASE_URL!, - process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, - options - ) - - return supabase -} - -export { getSupabase } -``` - -Create new api route `pages/api/login.ts`. This route validates the Picket access token then issues another equivalent Supabase access token for us to use with the Supabase client. - -```ts -import type { NextApiRequest, NextApiResponse } from 'next' -import jwt from 'jsonwebtoken' -import cookie from 'cookie' -import Picket from '@picketapi/picket-node' - -import { cookieName } from '../../utils/supabase' - -// create picket node client with your picket secret api key -const picket = new Picket(process.env.PICKET_PROJECT_SECRET_KEY!) - -const expToExpiresIn = (exp: number) => exp - Math.floor(Date.now() / 1000) - -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - const { accessToken } = req.body - // omit expiration time,.it will conflict with jwt.sign - const { exp, ...payload } = await picket.validate(accessToken) - const expiresIn = expToExpiresIn(exp) - - const supabaseJWT = jwt.sign( - { - ...payload, - }, - process.env.SUPABASE_JWT_SECRET!, - { - expiresIn, - } - ) - - // Set a new cookie with the name - res.setHeader( - 'Set-Cookie', - cookie.serialize(cookieName, supabaseJWT, { - path: '/', - secure: process.env.NODE_ENV !== 'development', - // allow the cookie to be accessed client-side - httpOnly: false, - sameSite: 'strict', - maxAge: expiresIn, - }) - ) - res.status(200).json({}) -} -``` - -And now create an equivalent logout api route `/pages/api/logout.ts` to delete the Supabase access token cookie. - -```ts -import type { NextApiRequest, NextApiResponse } from 'next' -import cookie from 'cookie' - -import { cookieName } from '../../utils/supabase' - -export default async function handler(_req: NextApiRequest, res: NextApiResponse) { - // Clear the supabase cookie - res.setHeader( - 'Set-Cookie', - cookie.serialize(cookieName, '', { - path: '/', - maxAge: -1, - }) - ) - - res.status(200).json({}) -} -``` - -We can now login and logout to the app with our wallet! - -## Step 7: Interacting with Data in Supabase - -Now that we can login to the app, it's time to start interacting with Supabase. Let's make a todo list page for authenticated users. - -Create a new file `pages/todos.tsx` - -```tsx -import { GetServerSideProps } from 'next' -import Head from 'next/head' -import Link from 'next/link' -import { useState, useMemo } from 'react' -import jwt from 'jsonwebtoken' -import Cookies from 'js-cookie' - -import styles from '../styles/Home.module.css' - -import { getSupabase, cookieName } from '../utils/supabase' - -type Todo = { - name: string - completed: boolean -} - -type Props = { - walletAddress: string - todos: Todo[] -} - -const displayWalletAddress = (walletAddress: string) => - `${walletAddress.slice(0, 6)}...${walletAddress.slice(-4)}` - -export default function Todos(props: Props) { - const { walletAddress } = props - const [todos, setTodos] = useState(props.todos) - - // avoid re-creating supabase client every render - const supabase = useMemo(() => { - const accessToken = Cookies.get(cookieName) - return getSupabase(accessToken || '') - }, []) - - return ( -
    - - Picket 💜 Supabase - - -
    -

    Your Personal Todo List

    -
    -

    Welcome {displayWalletAddress(walletAddress)},

    -

    - Your todo list is stored in Supabase and are only accessible to you and your wallet - address. Picket + Supabase makes it easy to build scalable, hybrid web2 and web3 apps. - Use Supabase to store non-critical or private data off-chain like user app preferences - or todo lists. -

    -
    -
    -

    Todo List

    - {todos.map((todo) => ( -
    - { - await supabase.from('todos').upsert({ - wallet_address: walletAddress, - name: todo.name, - completed: !todo.completed, - }) - setTodos((todos) => - todos.map((t) => (t.name === todo.name ? { ...t, completed: !t.completed } : t)) - ) - }} - /> - - {todo.name} - -
    - ))} -
    - - Go back home → - -
    -
    -
    -
    - ) -} - -export const getServerSideProps: GetServerSideProps = async ({ req }) => { - // example of fetching data server-side - const accessToken = req.cookies[cookieName] - - // require authentication - if (!accessToken) { - return { - redirect: { - destination: '/', - }, - props: { - walletAddress: '', - todos: [], - }, - } - } - - // check if logged in user has completed the tutorial - const supabase = getSupabase(accessToken) - const { walletAddress } = jwt.decode(accessToken) as { - walletAddress: string - } - - // get todos for the users - // if none exist, create the default todos - let { data } = await supabase.from('todos').select('*') - - if (!data || data.length === 0) { - let error = null - ;({ data, error } = await supabase - .from('todos') - .insert([ - { - wallet_address: walletAddress, - name: 'Complete the Picket + Supabase Tutorial', - completed: true, - }, - { - wallet_address: walletAddress, - name: 'Create a Picket Account (https://picketapi.com/)', - completed: false, - }, - { - wallet_address: walletAddress, - name: 'Read the Picket Docs (https://docs.picketapi.com/)', - completed: false, - }, - { - wallet_address: walletAddress, - name: 'Build an Awesome Web3 Experience', - completed: false, - }, - ]) - .select('*')) - - if (error) { - // log error and redirect home - console.error(error) - return { - redirect: { - destination: '/', - }, - props: { - walletAddress: '', - todos: [], - }, - } - } - } - - return { - props: { - walletAddress, - todos: data as Todo[], - }, - } -} -``` - -This is a long file, but don't be intimidated. The page is actually straightforward. It - -1. Verifies server-side that the user is authenticated and if they are not redirects them to the homepage -2. Checks to see if they already have `todos` . If so, it returns them. If not, it initializes them for the users -3. We render the `todos` and when the user selects or deselects a todo, we update the data in the database - -## Step 8: Try it Out! - -And that's it. If you haven't already, run your app to test it out yourself - -```bash -# start the app -npm run dev -# open http://localhost:3000 -``` - -### What's Next? - -- Explore the [Picket documentation](https://docs.picketapi.com/picket-docs/) -- Play with the live [Picket + Supabase demo](https://picket-supabase-auth-example-v36z.vercel.app/) -- Checkout Picket's [example Github repositories](https://github.com/picketapi) - -### Common Use-Case for Picket + Supabase - -- Account linking. Allow users to associate their wallet address(es) with their existing web2 account in your app -- Leverage Supabase's awesome libraries and ecosystem while still enabling wallet login -- Store app-specific data, like user preferences, about your user's wallet adress off-chain -- Cache on-chain data to improve your DApp's performance - -export const Page = ({ children }) => - -export default Page diff --git a/apps/docs/pages/guides/integrations/plasmic.mdx b/apps/docs/pages/guides/integrations/plasmic.mdx deleted file mode 100644 index 38440d5f194..00000000000 --- a/apps/docs/pages/guides/integrations/plasmic.mdx +++ /dev/null @@ -1,313 +0,0 @@ -import Layout from '~/layouts/DefaultGuideLayout' - -export const meta = { - id: 'plasmic', - title: 'Plasmic', - description: - 'Get started with Supabase and Plasmic, an open-source framework for building internal tools.', -} - -In this guide, we will show you how to build a crowd-sourced Pokemon Pokedex, by connecting **Supabase**, an open source Firebase backend alternative, with **Plasmic**, a visual builder for the web. While many users leverage Plasmic to quickly launch and iterate on landing pages, in this tutorial we’ll show just how powerful Plasmic can be as a general-purpose visual builder for React, which can be used to design and implement fully featured read-write applications. - -You can play with the live demo here: -[https://plasmic-supabase-demo.vercel.app/](https://plasmic-supabase-demo.vercel.app/) - -You can also take a look at the Plasmic project here: -[https://studio.plasmic.app/projects/66RKaSPCwKxYjCfXWHCxn6](https://studio.plasmic.app/projects/66RKaSPCwKxYjCfXWHCxn6) - -> You’ll need to enable 3rd-party cookies in your browser for the project to properly load. - -![pokedex-screenshot](/docs/img/guides/integrations/plasmic/application-screenshot-00.png) - -At a high level, - -- **Supabase** is used to store the database of Pokemon (backed by Postgres) and provides an authentication backend. Our code base includes React components for querying the database, displaying this data, and supporting user sessions. -- **Plasmic** is used to create the pages and visual design of the application. We import our Supabase components into the Studio, which can be visually assembled and configured there (e.g. for displaying data). -- Plasmic designed pages are rendered back into the Next.js application. - -## Step 1: Set up your Backend on Supabase - -- On the [Supabase dashboard](https://app.supabase.com/), click `New project` and set the name of the project. - By default, Supabase will already be set up for user signups with email, with users being stored in a `users` table. - -![create-project-supabase](/docs/img/guides/integrations/plasmic/create-project-supabase-01.png) - -- Navigate to the `Table Editor` on the left side navigation bar. Here we can create a `New table` to store our Pokemon entries. Make sure you are in the `schema public` view. Create a new table called `entries`, with 6 columns: - - `id`: is a unique ID for the entry. This column should be generated automatically as the primary column. - - `user_id`: Create a relation to the `user` table by clicking on the link icon next to the column name. Here, you can select the `id` column of the `user` table. - - `name`, `description`, `imageUrl`: This will store the name, description, and imageUrl for each Pokemon. - - `inserted_at` : This will be an automatically populated column, set to when the row was first inserted. - -> Note: In this tutorial we’ve turned off “[Row Level Security (RLS)](/docs/guides/auth/row-level-security)”. In practice, you will want to create policies that restrict who gets to create, edit, and delete posts. By turning this off, any user can modify the database without restrictions. - -![create-table-supabase](/docs/img/guides/integrations/plasmic/create-table-supabase-02.png) - -For your convenience, feel free to import the following CSV into Supabase to pre-populate your database. In order to import, you must select `Import data via spreadsheet`, in the new table dialog box. (It does not work on existing tables.) - -[pokedex-export.csv](/docs/img/guides/integrations/plasmic/pokedex-export.csv) - -## Step 2: Set up your codebase - -We have a working code example for you [here](https://github.com/plasmicapp/plasmic/tree/master/examples/supabase-demo). This starter comes with all of the code components you need to get started querying Supabase through Plasmic Studio. - -> Code components are React components defined in your code base that we import into Plasmic Studio for use. Your project will be configured to look for these at `http://localhost:3000/plasmic-host` You can use these components in your design, as well as style them. Check out `supabase-demo/plasmic-init.ts` to see how they are registered with Plasmic. - -First, clone the repo to your development machine and install the dependencies. - -```bash -git clone git@github.com:plasmicapp/plasmic.git -cd plasmic/examples/supabase-demo/ -yarn install -``` - -Copy `.env.example` to `.env.local`, which will store the environment variables when running a local development server. Add your Supabase project’s URL and public key, which you can find in the `API` tab on the left pane of your Supabase dashboard. - -Now run the dev server, which listens at `http://localhost:3000` - -```bash -yarn dev -``` - -## Step 3: Explore the existing application - -Navigate to [http://localhost:3000](http://localhost:3000) in your web browser. The project will already be set up for user signups, logins, and an admin interface for adding and editing Pokemon to the database. Feel free to sign up with your email address for an account and add Pokemon to the database. Supabase will require that you verify your email address before you can log in. - -If you pre-populated the database in Step 1. you should see the following homepage after logging in. Otherwise, feel free to add Pokemon manually via the UI. - -![application-screenshot](/docs/img/guides/integrations/plasmic/application-screenshot-03.png) - -## Step 4: Clone the Plasmic project - -Now let’s try to make some additions! The code base is currently configured to a read-only copy of the Plasmic project. Let’s make an editable copy first. - -Open the default starter Plasmic project here: -[https://studio.plasmic.app/projects/66RKaSPCwKxYjCfXWHCxn6](https://studio.plasmic.app/projects/66RKaSPCwKxYjCfXWHCxn6) - -![clone-project-plasmic](/docs/img/guides/integrations/plasmic/clone-project-plasmic-04.png) - -To make an editable copy, click on the `Copy Project` button in the blue bar. This will clone the project and redirect you to your copy. - -### Step 4a: Configure your code base to use the new Plasmic project - -Take note of the `project ID` and `API token`. You can find the project ID in the URL: -`https://studio.plasmic.app/projects/PROJECTID`. - -The API token can be found by clicking the `Code` button in the top bar. - -![api-token-plasmic](/docs/img/guides/integrations/plasmic/api-token-plasmic-05.png) - -Now go back to `.env.local` and update the corresponding project ID and token fields. - -### Step 4b: Configure your Plasmic project app host - -To tell Plasmic to look for your code components on your dev server, you’ll need to update your project’s app host to `http://localhost:3000/plasmic-host`. - -> Note: At this point, you’ll need to keep your dev server running at `http://localhost:3000`for the project to load. - - - -After restarting the dev server and Plasmic Studio, you should now be able to make edits across Plasmic Studio and your codebase. - -## Step 5: Create a new page for our Pokedex gallery - -Let’s make a visual gallery for our Pokemon by using the code components from the code base. - -Create a new page called `Gallery`, and set a path for this page (`/gallery`). - - - -Insert a `SupabaseGrid` by searching the AddDrawer (by clicking the blue + button) - -> For source see `components/CodeComponents/DatabaseComponents.tsx` - -![add-supabasegrid-plasmic](/docs/img/guides/integrations/plasmic/add-supabasegrid-plasmic-07.png) - -Then in the right-hand panel, configure the props on `SupabaseGrid`. - -- `tableName` should match the table you created in Supabase -- `tableColumns` are a comma-delimited list of columns you want to select from the table -- We also set the number of columns and spacing shown in the grid - -![set-props-plasmic](/docs/img/guides/integrations/plasmic/set-props-plasmic-08.png) - -The `SupabaseGrid` will loop over the rows from the query. - -Now customize the repeated content by inserting instances of `SupabaseField`. Select the type of content and a selector string to fetch a single value. In the example below, we use `{{row.imageUrl}}` to retrieve the `imageUrl` column of the row. Apply any styling and layout you want on these elements. - -![add-supabasefield-plasmic](/docs/img/guides/integrations/plasmic/add-supabasefield-plasmic-09.png) - -### Putting it all together (video) - -For your convenience, the following video shows you how to create the page end-to-end. - - - -## Step 6: Check your dev server - -If you have been running your development server this whole time, you’ll see that we have been automatically fetching and rebuilding your site as you make changes in Plasmic Studio. If you need to restart your dev server, just run: - -```bash -yarn dev -``` - -See the results at `http://localhost:3000/gallery`. - -## How does this all work under the hood? - -### SupabaseGrid - -`SupabaseGrid` is a code component that was registered in `plasmic-init.ts`. The `props` field is used to tell the Plasmic Studio the component prop interface, which allows us to expose these props in the right pane as shown in the screenshots earlier. See the docs for details on [component registration](https://docs.plasmic.app/learn/registering-code-components/). - -```tsx -// plasmic-init.ts -... - -PLASMIC.registerComponent(SupabaseGrid, { - name: "SupabaseGrid", - props: { - tableName: "string", - tableColumns: "string", - queryFilters: "object", - children: { - type: "slot", - defaultValue: { - type: "text", - value: "Placeholder", - }, - }, - numColumns: { - type: "number", - defaultValue: 4, - }, - columnGap: { - type: "number", - defaultValue: 16, - }, - rowGap: { - type: "number", - defaultValue: 16, - }, - count: "number", - loading: { - type: "slot", - defaultValue: { - type: "text", - value: "Loading...", - }, - }, - }, - importPath: "./components/CodeComponents/DisplayCollections", -}); -``` - -### SupabaseQuery - -`SupabaseGrid` wraps a `SupabaseQuery` component, where we perform the query based on the provided props and store the result in a `SupabaseQueryContext`. This will be used in downstream components to display the data. - -```tsx -// supabase-demo/components/CodeComponents/DatabaseComponents.tsx -export function SupabaseQuery(props: SupabaseQueryProps) { - // These props are set in the Plasmic Studio - const { children, tableName, columns, className, filters, single } = props; - const [result, setResult] = React.useState(undefined); - -... - - // Performs the Supabase query - let query = supabase.from(tableName!).select(columns + ",id"); - query = applyFilter(query, validFilters, contexts); - const { data, error, status } = await (single ? query.single() : query.order('id', { ascending: false })); - - if (error && status !== 406) { - throw error; - } else if (data) { - setResult(data); - } - -... - - // Save the result in a `SupabaseQueryContext for use with downstream components - return ( -
    - - {children} - -
    - ); -} -``` - -Note that this code component is defined in your codebase. Feel free to augment it to expose more powerful querying capabilities to the Plasmic Studio. - -### SupabaseGridCollection - -`SupabaseGrid` also nests a `SupabaseGridCollection` under the `SupabaseQuery`. This code component is a simple CSS grid, where we retrieve the Supabase query results from `SupabaseQueryContext`, and iterate over the results. For each row, we populate a `RowContext`, which will be used by the children to read the results of a single row. Note the use of `repeatedElement`, a special convenience function that enables the component’s children to be repeated. In this case, this represents a single card to be shown in the gallery. - -```tsx -// supabase-demo/components/CodeComponents/DisplayCollections.tsx - -export function SupabaseGridCollection(props: SupabaseGridCollectionProps) { - const supabaseQuery = React.useContext(SupabaseQueryContext) - const { children, columns, columnGap, rowGap, count, className, loading, testLoading } = props - - const result = supabaseQuery - if (!result || testLoading) { - return loading - } - - return ( -
    - {result.slice(0, count).map((row: any, i: any) => ( - -
    {repeatedElement(i === 0, children)}
    -
    - ))} -
    - ) -} -``` - -### SupabaseField - -`SupabaseField` will either render a `SupabaseTextField` or `SupabaseImgField` depending on the type. These code components simply read a single value from the contexts and display the data. - -```tsx -// supabase-demo/components/CodeComponents/DisplayCollections.tsx - -export function SupabaseTextField({ name, className }: { name?: string; className?: string }) { - const contexts = useAllContexts() - if (!name) { - return

    You need to set the name prop

    - } - return
    {getPropValue(name, contexts)}
    -} -``` - -In summary, by populating state into React contexts, we can store and retrieve data for use in other code components, which can be used for arbitrarily powerful interactions in Plasmic Studio. - -export const Page = ({ children }) => - -export default Page diff --git a/apps/docs/pages/guides/integrations/polyscale.mdx b/apps/docs/pages/guides/integrations/polyscale.mdx deleted file mode 100644 index 6db1d4d2c61..00000000000 --- a/apps/docs/pages/guides/integrations/polyscale.mdx +++ /dev/null @@ -1,74 +0,0 @@ -import Layout from '~/layouts/DefaultGuideLayout' - -export const meta = { - id: 'polyscale', - title: 'PolyScale', - description: - 'The easiest way to get low-latency reads from your Supabase database for multi-region applications is by using PolyScale, a code-free global caching service.', -} - -[PolyScale](https://polyscale.ai) is an intelligent, serverless database caching engine which allows low-latency reads from Supabase globally, no coding required. Supabase can be connected to PolyScale in minutes, providing you fast access to your Supabase data around the globe. - -This guide explains how to connect Supabase to a PolyScale cache. - -The video below illustrates how to get connected. Or you can read the steps below. - -
    - -
    - - -PolyScale provides caching for TCP connections and GraphQL. Support for caching with the Supabase client is coming soon. - - -## Step 0: Create a PolyScale account - -If you do not already have a PolyScale account, you can create an account [here](https://app.polyscale.ai/signup). PolyScale offers a free plan and no credit card is required. - -## Step 1: Create your PolyScale Cache - -### 1.1 Retrieve your Supabase Host - -In your Supabase project, click on `Settings > Database` and scroll down to the `Connection info` section to copy your database `Host`. - -![supabase-host](/docs/img/guides/integrations/polyscale/supabase-host.png) - -### 1.2 Configure your PolyScale Cache - -- In your PolyScale account, click on the **New Cache** button -- Give the cache a **Name** -- Select **PostgreSQL** for the **Type** -- Enter the **Host** from Step 1.1 above -- Enter `5432` for the **Port** -- Click **Create** - -![create-cache-supabase](/docs/img/guides/integrations/polyscale/create-cache-supabase-400.png) - -Your cache is now created. PolyScale automatically checks to see that your database is accessible from all our global PoPs. - -## Step 2: Connect to your PolyScale Cache - -Using your PolyScale cache is simple -- instead of connecting to your Supabase database directly, you'll replace your original connection string with the PolyScale connection string in your application. - -For example, if your original connection string was: `postgres://postgres:zqSPGHFAbPLvVCKw@db.rogpiubvixysbakciwqz.supabase.co:5432` - -Your PolyScale connection string would be: `postgres://postgres:zqSPGHFAbPLvVCKw@psedge.global:5432?application_name=a645cb93-fa53-46b2-9d6c-227e357e5bfb` - -You can read more about connecting to PolyScale [here](https://docs.polyscale.ai/connecting-to-polyscale#postgresql) - -That's it. - -## All done! - -You can read more about PolyScale [here](https://www.polyscale.ai/) or check out our [documentation](https://docs.polyscale.ai/). - -export const Page = ({ children }) => - -export default Page diff --git a/apps/docs/pages/guides/integrations/prisma.mdx b/apps/docs/pages/guides/integrations/prisma.mdx deleted file mode 100644 index 80aa12bbb26..00000000000 --- a/apps/docs/pages/guides/integrations/prisma.mdx +++ /dev/null @@ -1,291 +0,0 @@ -import Layout from '~/layouts/DefaultGuideLayout' - -export const meta = { - id: 'prisma', - title: 'Prisma', - description: 'Connect your Supabase postgres database to your Prisma project.', -} - -This guide explains how to quickly connect the Postgres database provided by Supabase to a Prisma project. - -[Prisma](https://prisma.io) is an [open source](https://github.com/prisma/prisma) next-generation ORM. It consists of the following parts: - -- **Prisma Client**: Auto-generated and type-safe query builder for Node.js & TypeScript. -- **Prisma Migrate**: Migration system. -- **Prisma Studio**: GUI to view and edit data in your database. - -## Step 1: Get the connection string from Supabase project settings - -Go to the settings page from the sidebar and navigate to the **Database** tab. You’ll find the database’s connection string with a placeholder for the password you provided when you created the project. - -![Getting the connection string](/docs/img/guides/integrations/prisma/zntcsh3ic91gf1gy8j73.png) - -## Step 2: Testing the connection - -To make sure that everything works correctly, let’s try the connection string in a Prisma project. - -If you already have one, all you need to do is set the `DATABASE_URL` to the connection string (including the password) in your `.env` file, and you’re good to go. - -In case you don’t have a Prisma project or this is your first time working with Prisma, you’re going to use the repo from the [quickstart](https://www.prisma.io/docs/getting-started/quickstart) guide. - -### Cloning the starter project - -Navigate into a directory of your choice and run the following command in your terminal: - -```bash -curl https://codeload.github.com/prisma/prisma-examples/tar.gz/latest | tar -xz --strip=2 prisma-examples-latest/databases/postgresql-supabase -``` - -You can now navigate into the directory and install the project’s dependencies: - -```bash -cd postgresql-supabase -npm install -``` - -### A look at the project’s structure - -This project comes with TypeScript configured and has the following structure. - -- A `prisma` directory which contains: - - A `seed.ts` file: This is the data used to seed your database. - - A `schema.prisma` file: Where you define the different database models and relations between them. -- A `script.ts` file: where you will run some queries using Prisma Client. - -This starter also comes with the following packages installed: -- [`@prisma/client`](https://www.npmjs.com/package/@prisma/client): An auto-generated and type-safe query builder that’s _tailored_ to your data. -- [`prisma`](https://www.npmjs.com/package/prisma): Prisma’s command-line interface (CLI). It allows you to initialize new project assets, generate Prisma Client, and analyze existing database structures through introspection to automatically create your application models. - -> Note: Prisma works with both JavaScript and TypeScript. However, to get the best possible development experience, using TypeScript is highly recommended. - -### Configuring the project - -Create a `.env` file at the root of your project: - -```bash -touch .env -``` - -In the `.env` file, add a `DATABASE_URL` variable and add the connection string from **step 1**. The `.env` file should look like: - -```bash .env -DATABASE_URL="postgres://postgres:[YOUR-PASSWORD]@db.[YOUR-PROJECT-REF].supabase.co:5432/postgres" -``` - -This is what your `schema.prisma` file should look like: - -```go -datasource db { - provider = "postgresql" - url = env("DATABASE_URL") -} - -generator client { - provider = "prisma-client-js" -} - -model Post { - id Int @id @default(autoincrement()) - title String - content String? - published Boolean @default(false) - author User? @relation(fields: [authorId], references: [id]) - authorId Int? -} - -model User { - id Int @id @default(autoincrement()) - email String @unique - name String? - posts Post[] -} -``` - - -To test that everything works correctly, run the following command to create a migration: - -```bash -npx prisma migrate dev --name init -``` - -You can optionally give your migration a name, depending on the changes you made. Since this is the project’s first migration, you’re setting the `--name` flag to “init”. If everything works correctly, you should get the following message in your terminal: - -```text -Your database is now in sync with your schema. -:heavy_check_mark: Generated Prisma Client (4.x.x) to ./node_modules/@prisma/client in 111ms -``` - -This will create a `prisma/migrations` folder inside your `prisma` directory and synchronize your Prisma schema with your database schema. - -> **Note**: If you want to skip the process of creating a migration history, you can use the [`prisma db push`](https://www.prisma.io/docs/concepts/components/prisma-migrate/db-push) command instead of `prisma migrate dev`. However, we recommend using `prisma migrate dev` to evolve your database schema in development. -> If you would like to get a conceptual overview of how Prisma Migrate works and which commands to use in what environment, refer to [this page in the Prisma documentation](https://www.prisma.io/docs/concepts/components/prisma-migrate/mental-model). - -If you go to your Supabase project, in the table editor, you should see that two tables have been created, a `Post`, `User`, and `_prisma_migrations` tables. The `_prisma_migrations` table is used to keep track of the migration history and ensure that the database schema stays in sync with your Prisma schema. - -![tables created in the UI](/docs/img/guides/integrations/prisma/7y4qq4wwvfrheti6r09u.png) - -That’s it! You have now successfully connected a Prisma project to a PostgreSQL database hosted on Supabase and ran your first migration. - -## Connection pooling with Supabase - -If you’re working in a serverless environment (for example Node.js functions hosted on AWS Lambda, Vercel or Netlify Functions), you need to set up [connection pooling](https://www.prisma.io/docs/guides/performance-and-optimization/connection-management#serverless-environments-faas) using a tool like [PgBouncer](https://www.pgbouncer.org/). That’s because every function invocation may result in a [new connection to the database](https://www.prisma.io/docs/guides/performance-and-optimization/connection-management#the-serverless-challenge). - -Supabase [supports connection management using PgBouncer](/docs/guides/database/connecting-to-postgres#connection-pool) which prevents a traffic spike from overwhelming your database. - -Go to the **Database** page from the sidebar in the Supabase dashboard and navigate to **Connection pool** settings: - -![Connection pool settings](/docs/img/guides/integrations/prisma/w0oowg8vq435ob5c3gf0.png) - -When updating your database schema, you need to use the non-pooled connection URL (like the one used in **step 1**). You can configure the non-pooled connection string by using the `directUrl` property in the datasource block. - -Update your `.env` file with the following changes: -1. Rename the `DATABASE_URL` environment variable to `DIRECT_URL` -1. Create a `DATABASE_URL` environment variable and paste in the new connection string from the dashboard as its value - -Append the `?pgbouncer=true` flag to the `DATABASE_URL` variable. - -Your `.env` file should resemble the following: - -```bash .env -# PostgreSQL connection string used for migrations -DIRECT_URL="postgres://postgres:[YOUR-PASSWORD]@db.[YOUR-PROJECT-REF].supabase.co:5432/postgres" - -# PostgreSQL connection string with pgBouncer config — used by Prisma Client -DATABASE_URL="postgres://postgres:[YOUR-PASSWORD]@db.[YOUR-PROJECT-REF].supabase.co:6543/postgres?pgbouncer=true" -``` - -Update your Prisma schema by setting the `directUrl` in the datasource block: - -```go -datasource db { - provider = "postgresql" - url = env("DATABASE_URL") - directUrl = env("DIRECT_URL") -} -``` - -> **Note**: This feature is available from Prisma version [4.10.0](https://github.com/prisma/prisma/releases/tag/4.10.0) and higher. - -If you want to learn more about Prisma, check out the [docs](https://www.prisma.io/docs/reference/api-reference/prisma-schema-reference#fields). Also in case you have any questions or run into any issue, feel free to start a discussion in the repo’s [discussions section](https://github.com/prisma/prisma/discussions). - -## Troubleshooting - -### Missing grants - -If your database schema is out of sync from your migration history, `prisma migrate dev` will detect a migration history conflict or a [schema drift](https://www.prisma.io/docs/guides/database/developing-with-prisma-migrate/troubleshooting-development#schema-drift). When `prisma migrate dev` detects the drift, it might ask to to reset your database schema. If you choose yes, it will delete the `public` schema along with the default grants defined in your database. - -If you run into this problem, create a draft migration using `prisma migrate dev --create-only`, and add the following helper SQL: - -```sql -grant usage on schema public to postgres, anon, authenticated, service_role; - -grant all privileges on all tables in schema public to postgres, anon, authenticated, service_role; -grant all privileges on all functions in schema public to postgres, anon, authenticated, service_role; -grant all privileges on all sequences in schema public to postgres, anon, authenticated, service_role; - -alter default privileges in schema public grant all on tables to postgres, anon, authenticated, service_role; -alter default privileges in schema public grant all on functions to postgres, anon, authenticated, service_role; -alter default privileges in schema public grant all on sequences to postgres, anon, authenticated, service_role; -``` - -Run `prisma migrate dev` to apply the draft migration to the database. - -### Using Supabase Auth with Prisma - -If you would like to use Supabase Auth and Prisma in your application, you will have to enable the `multiSchema` Preview feature flag in the `generator` block of your Prisma schema: - -```go -datasource db { - provider = "postgresql" - url = env("DATABASE_URL") - directUrl = env("DIRECT_URL") -} - -generator client { - provider = "prisma-client-js" - previewFeatures = ["multiSchema"] -} -``` - -Next, specify the database schemas you would like to include in your Prisma schema: - -```go -datasource db { - provider = "postgresql" - url = env("DATABASE_URL") - directUrl = env("DIRECT_URL") - schemas = ["public", "auth"] -} - -generator client { - provider = "prisma-client-js" - previewFeatures = ["multiSchema"] -} -``` - -You can then specify what schema a model or enum belongs to using the `@@schema` attribute: - -```go -model User { - id Int @id - // ... - - @@schema("auth") // or @@schema("public") -} -``` - -To learn more about using Prisma with multiple database schemas, refer to [this page in the Prisma docs](https://www.prisma.io/docs/guides/database/multi-schema#learn-more-about-the-multischema-preview-feature). - -### Using PostgreSQL Row Level Security with Prisma - - -If you would like to use Row Level Security (RLS) with Prisma, check out the [Prisma Client Extension - Row Level Security example](https://github.com/prisma/prisma-client-extensions/tree/main/row-level-security) that provides the primitives you could use to build and extend Prisma Client in PostgreSQL. - -Also check out [useSupabaseRowLevelSecurity](https://github.com/dthyresson/prisma-extension-supabase-rls) Prisma Client extension that supports [Supabase RLS](/docs/guides/auth/row-level-security#authrole) and policies written to use [Supabase auth](/docs/guides/auth/overview). - -The example and extension use [Prisma Client extensions](https://www.prisma.io/docs/concepts/components/prisma-client/client-extensions) Preview feature. - -### Enabling PosgreSQL extensions - -If you would like to use a PostgreSQL extension with Prisma, enable the `postgresqlExtensions` Preview feature flag in the `generator` block of your Prisma schema: - -```go -datasource db { - provider = "postgresql" - url = env("DATABASE_URL") - directUrl = env("DIRECT_URL") -} - -generator client { - provider = "prisma-client-js" - previewFeatures = ["postgresqlExtensions"] -} -``` - -Next, specify the extensions you need in the `datasource` block: - -```go -datasource db { - provider = "postgresql" - url = env("DATABASE_URL") - directUrl = env("DIRECT_URL") - extensions = [hstore(schema: "myHstoreSchema"), pg_trgm, postgis(version: "2.1")] -} - -generator client { - provider = "prisma-client-js" - previewFeatures = ["postgresqlExtensions"] -} -``` - -To learn more about using Prisma with PostgreSQL extensions, refer to [this page in the Prisma docs](https://www.prisma.io/docs/concepts/components/prisma-schema/postgresql-extensions). - -## Resources - -- [Prisma](https://prisma.io) official website. -- [Prisma GitHub](https://github.com/prisma/prisma). -- [Prisma](https://www.prisma.io/docs/) documentation. - -export const Page = ({ children }) => - -export default Page diff --git a/apps/docs/pages/guides/integrations/sequin.mdx b/apps/docs/pages/guides/integrations/sequin.mdx deleted file mode 100644 index 200ca829ea0..00000000000 --- a/apps/docs/pages/guides/integrations/sequin.mdx +++ /dev/null @@ -1,91 +0,0 @@ -import Layout from '~/layouts/DefaultGuideLayout' - -export const meta = { - id: 'sequin', - title: 'Sequin', - description: - 'Sync platforms like Stripe and Salesforce with your Supabase database in real-time using Sequin.', -} - -This guide explains how to quickly setup a sync between Sequin and a Supabase Postgres database. - -[Sequin](https://sequin.io) allows you to sync platforms like Stripe and Salesforce with Supabase in real-time. You'll be able to read and write to your [Stripe](https://stripe.com/) customers or [Salesforce](https://www.salesforce.com/) accounts right from the Supabase client using SQL. Here's how to get setup. - -## Step 1: Connect Sequin to Supabase - -To connect Supabase to Sequin, you'll first need to retrieve the credentials for your Supabase Postgres database: - -1. In the Supabase dashboard, go to the settings page and open up your **Database** settings. In the **Connection info** section, you'll find the credentials you need - like `host` and `user`: - - ![TBD](/docs/img/guides/integrations/sequin/001_supabase_dash.png) - -2. In the [Sequin console](https://app.sequin.io), go to your sync's configuration and open the **Destination** section. Select **Launch or Connect** and then click **Connect** to configure the connection to your Supabase Postgres: - - ![TBD](/docs/img/guides/integrations/sequin/002_connect.png) - -3. In the connection modal that appears, enter the `Host` and `Port` for your Supabase database and click **Continue**. - - ![TBD](/docs/img/guides/integrations/sequin/003_step_1.png) - -4. Now, enter the `Database name` and set the `schema` name for your sync. For instance, if your syncing Stripe, you'll likely want to name your synced schema something like `stripe`. Finally, enter the `user` and `password` for your Supabase database and then click **Continue**. Sequin will verify it can properly connect to your database with the correct permissions. - - ![TBD](/docs/img/guides/integrations/sequin/004_step_2.png) - -5. Sequin is now connected to your Supabase Postgres database and will ask you to confirm which database users should be able to access your synced schema. Select all of the users and click **Continue**: - - ![TBD](/docs/img/guides/integrations/sequin/005_step_3.png) - -6. That's it. Sequin will now create a new schema and permissions group in your Supabase database. Name the database connection in Sequin something like `Supabase` and your done! - -In the Supabase dashboard, you can go to the **Table Editor** and you'll see a new schema full of your synced platform data. - -![TBD](/docs/img/guides/integrations/sequin/006_see_data.png) - -## Step 2: Grant Permissions - -To ensure the right users can access the synced schema Sequin manages, you'll need to run a couple permission grants. - -1. In the Sequin console, click the **Connect** button next to your sync and copy down your `Schema` and unique `Read Group`. - - ![TBD](/docs/img/guides/integrations/sequin/007_get_read.png) - -2. Now, in the Supabase dashboard, go to the **SQL Editor** and run the following permission grants: - - ```sql - GRANT sequin_read_▒▒▒▒ TO postgres, anon, authenticated, service_role; - - GRANT USAGE ON SCHEMA {{your_schema_name}} TO anon, authenticated, service_role; - - GRANT ALL ON ALL TABLES IN SCHEMA {{your_schema_name}} TO anon, authenticated, service_role; - - ALTER DEFAULT PRIVILEGES FOR ROLE postgres, supabase_admin IN SCHEMA {{your_schema_name}} GRANT ALL ON TABLES TO anon, authenticated, service_role; - ``` - - These permission grants ensure that the various Supabase database users can access and read all the tables in your synced schema. - -## Step 3: Configure the Supabase Client - -Finally, you'll need to define a new [Supabase client](/docs/reference/javascript/initializing#api-schemas) in your application to access your synced schema. In the file where you initialized your Supabase client, define a new client with a `schema` parameter: - -```javascript -export const supabase_schema = createClient( - 'https://xyzcompany.supabase.co', - 'public-anon-key', - { - schema: {{your_schema_name}}, - } -); -``` - -You'll use this client to query for data in your synced schema. - -## Resources - -- [Sequin](https://sequin.io) official website. -- [Sequin Console](https://app.sequin.io). -- [Sequin](https://docs.sequin.io/welcome) documentation. -- [Sequin + Supabase + Stripe Tutorial](https://github.com/sequin-io/build-a-saas-with-next-js-supabase-stripe-and-sequin) - -export const Page = ({ children }) => - -export default Page diff --git a/apps/docs/pages/guides/integrations/snaplet.mdx b/apps/docs/pages/guides/integrations/snaplet.mdx deleted file mode 100644 index 74297c2670b..00000000000 --- a/apps/docs/pages/guides/integrations/snaplet.mdx +++ /dev/null @@ -1,111 +0,0 @@ -import Layout from '~/layouts/DefaultGuideLayout' - -export const meta = { - id: 'snaplet', - title: 'Snaplet', - description: - 'Get started with Supabase and Snaplet, a developer tool for working with safe, versioned, up-to-date production-like data', -} - -This step-by-step guide explains how to use Snaplet to clone your production Supabase project into another development database. - -[Snaplet](https://snaplet.dev/) is a developer tool that copies a Postgres database, transforming personal information, so that you can **safely code against actual data.** This functionality makes it possible to easily achieve environment parity in Supabase. - -Let's get started! - -Follow along in the video below as the founder of Snaplet, Peter Pistorius, takes you through the entire process. Otherwise, you can skip the video and dive into the step-by-step guide. - -
    - -
    - -## Step 1: Prerequisites - -1. **A production Supabase project's connection string:** These can be found in Supabase via `Organization > Project > Database > Connection Pooling > Connection string` -2. **A development Supabase project's connection string:** Same steps as above, but a different project/environment -3. **A read-only role** in Production (recommended): This can be done by running the following statements on Supabase via `Organization > SQL Editor > + New Query` - -> To create a read-only role across all schemas you can checkout the Snaplet [docs](https://docs.snaplet.dev/guides/postgresql/#create-a-read-only-role) - -## Step 2: Copying your production database - -### 2.1. Connect your data source - -Navigate to [https://www.snaplet.dev/](https://www.snaplet.dev/) and sign up for a new account (it’s free). Once you have successfully signed up for a new account, create a team, and start by connecting to your Supabase project. - -![connect-your-database](/docs/img/guides/integrations/snaplet/connect-your-database.png) - -Enter the credentials of your production Supabase project. Find the "Connection string" in Supabase via `Organization > Project > Settings > Database > Connection string` (at the bottom of the page). - -The password is the same password you used when creating the Supabase project. - -![supabase-connection-db-info](/docs/img/guides/integrations/snaplet/supabase-connection-db-info.png) - -You’ll have to confirm providing Snaplet access to your database. Snaplet will prompt you to only provide `read-only` access to your database. Snaplet has a guide in their documentation on how to do so [here](https://docs.snaplet.dev/guides/postgresql/#create-read-only-role). - -> Note that whatever connection string you provide here will be that of your Data Source – essentially the production database in a real-life scenario - -![checking-database-credentials](/docs/img/guides/integrations/snaplet/checking-database-credentials.png) - -### 2.2. Transform your data - -![transform-your-data](/docs/img/guides/integrations/snaplet/transform-your-data.png) - -The next step is to exclude any schemas that you do not require. You are able to exclude an entire schema by clicking on the drop-down at the top, selecting the schema you would like to exclude and clicking ‘Exclude schema’. Alternatively, you can select a given schema and exclude only specific tables from that particular schema. Exclude any non-required table data (such as logs) and extensions and view your columns. - -At this point, Snaplet will automatically detect any columns that have Personally Identifiable Information (PII) and mark them in purple. If there are any additional columns that hold data you would like to anonymise, you can click on the respective column name and provide a replacement value for the data in that column. To complete the onboarding, click on `Review and Save` and proceed to the dashboard. - -![create-your-first-snapshot](/docs/img/guides/integrations/snaplet/create-your-first-snapshot.png) - -### 2.3. Create a Snapshot - -Create a snapshot of your production database. This is what you’re going to restore later into your data target (more on that later in the guide). - -## Step 3: Pasting into your development database - -### 3.1. Create a data target on Supabase - -Your data target is where you want Snaplet to restore the captured snapshot of your production project. This would most likely be either your staging or developer Supabase project. If you don’t already have a developer database setup on Supabase, create a new data target by setting up a new project on Supabase. To create a new project, follow the steps below: - -1. Go to [app.supabase.com](https://app.supabase.com/) -2. Click on “new project” -3. Enter your project details -4. Wait for the new database to launch - -> Remember the password you use when creating the project. You’ll need this password to connect your database to Snaplet later. - -### 3.2. Install the Snaplet CLI - -1. Open your terminal and run `curl -sL https://app.snaplet.dev/get-cli/ | bash` -2. Run `snaplet auth` -3. Navigate to [https://app.snaplet.dev/access-token/cli](https://app.snaplet.dev/access-token/cli) to get your access token -4. Paste your access token in the terminal - -### 3.3. Restore to the data target - -You're now ready to restore your production snapshot into your Supabase development project. - -1. Navigate to your project directory -2. Run `snaplet setup` – you will be prompted to enter your database credentials. These are the database credentials of your **data target.** This could be your staging or development database - -Once completed, you will be presented with a list of databases that are connected to your Snaplet account. - -1. Select a data source from the list -2. Run `snaplet snapshot restore` - -## All done! - -As a Supabase user, you can see how this solves an issue developers all typically experience when attempting to create multiple development environments and populating each of those environments with data that can be worked with. Snaplet simplifies this process down to creating the respective Supabase projects, connecting the data source (The production database) to Snaplet and telling Snaplet where to restore that data (staging and development databases). - -If you want to learn more about Snaplet, you can explore the Snaplet [docs](https://docs.snaplet.dev/). If you have any questions, feel free to [reach out on Discord](https://discord.com/invite/6HUuajc866). - -export const Page = ({ children }) => - -export default Page diff --git a/apps/docs/pages/guides/integrations/stytch.mdx b/apps/docs/pages/guides/integrations/stytch.mdx deleted file mode 100644 index a76eacfeaa3..00000000000 --- a/apps/docs/pages/guides/integrations/stytch.mdx +++ /dev/null @@ -1,663 +0,0 @@ -import Layout from '~/layouts/DefaultGuideLayout' - -export const meta = { - id: 'stytch', - title: 'Stytch', - description: - "Build a Next.js application powered by password-less authentication from Stytch, and Supabase's Row Level Security (RLS).", -} - -In this guide we will build a simple expense tracker web application using Stytch, Supabase, and Next.js. - -[Stytch](https://stytch.com?utm_source=supabase&utm_medium=guide) provides an all-in-one platform for passwordless auth. Stytch makes it easy for you to embed passwordless solutions into your websites and apps for better security, better conversion rates, and a better end user experience. Their easy-to-use SDKs and direct API access allows for maximum control and customization. In this example we will use [Email magic links](https://stytch.com/products/email-magic-links?utm_source=supabase&utm_medium=guide) to create and log in our users, and Session management. There is an additional, optional step to enable [Google One Tap](https://stytch.com/blog/improving-conversion-with-google-one-tap?utm_source=supabase&utm_medium=guide) which is an especially high-converting Google OAuth sign-up and login flow. - -We will leverage Supabase to store and authorize access to user data. Supabase makes it simple to set up Row Level Security (RLS) policies which ensure users can only read and write data that they are authorized to do so. If you do not already have a Supabase account, you will need to create one. - -This guide will use [Next.js](https://nextjs.org/) which is a web application framework built on top of React. Stytch provides a [Node.js library](https://github.com/stytchauth/stytch-node) and a [React library](https://stytch.com/docs/sdks/javascript-sdk) which makes building Next.js apps super easy. - -> Note: You can find a completed version of this project on [Github](https://github.com/stytchauth/stytch-nextjs-supabase). - -## Step 0: Create a Stytch Account - -If you already have a Stytch account you may skip this step. - -Go to [Stytch](https://stytch.com?utm_source=supabase&utm_medium=guide), and create an account. Note that Stytch provides two ways to create an account, either via Google OAuth, or through Email magic links.  This is the same user experience we will be building in this guide! - -![Stytch redirect URL settings](/docs/img/guides/integrations/stytch/01.png) - -## Step 1: Set up Stytch redirect URLs - -First we need to add the redirect URLs that will be used during the Email magic link flow. This step helps ensure bad actors cannot spoof your magic links and hijack redirects. - -Navigate to your [redirect URL settings](https://stytch.com/dashboard/redirect-urls?utm_source=supabase&utm_medium=guide) in the Stytch dashboard, and under **Test environment** create an entry where the **URL** is `http://localhost:3000/api/authenticate` and the **Type** is `All`. - -![Edit Stytch redirect URL settings](/docs/img/guides/integrations/stytch/02.png) - -After pressing **Confirm**, the redirect URLs dashboard will update to show your new entry. We will use this URL later on. - -![Stytch redirect URL settings](/docs/img/guides/integrations/stytch/03.png) - -## Step 2: Create a Supabase project - -From your [Supabase dashboard](https://app.supabase.com/), click **New project**. - -Enter a `Name` for your Supabase project. - -Enter a secure `Database Password`. - -Click **Create new project**. It may take a couple minutes for your project to be provisioned. - -![New Supabase project settings](/docs/img/guides/integrations/stytch/04.png) - -## Step 3: Creating data in Supabase - -Once your Supabase project is provisioned, click Table editor, then New table. This tool is available from the sidebar menu in the [Supabase dashboard](https://app.supabase.com/). - -Enter `expenses` as the **Name** field. - -Select `Enable Row Level Security (RLS)`. - -Add three new columns: - -- `user_id` as `text` - -- `title` as `text` - -- `value` as `float8` - -Click **Save** to create the new table. - -![Creating a new table](/docs/img/guides/integrations/stytch/05.png) - -From the Table editor view, select the expenses table and click **Insert row**. - -Fill out the title and value fields (leave user_id blank for now) and click **Save**. - -![Creating a new row](/docs/img/guides/integrations/stytch/06.png) - -Use **Insert Row** to further populate the table with expenses. - -![Multiple rows](/docs/img/guides/integrations/stytch/07.png) - -## Step 4: Building a Next.js app - -Using a terminal, create a new Next.js project: - -```bash -npx create-next-app stytch-supabase-example -``` - -Next, within `stytch-supabase-example` create a `.env.local` file and enter the following values: - -``` -STYTCH_PROJECT_ENV=test -STYTCH_PROJECT_ID=GET_FROM_STYTCH_DASHBOARD -STYTCH_PUBLIC_TOKEN=GET_FROM_STYTCH_DASHBOARD -STYTCH_SECRET=GET_FROM_STYTCH_DASHBOARD -NEXT_PUBLIC_SUPABASE_URL=GET_FROM_SUPABASE_DASHBOARD -NEXT_PUBLIC_SUPABASE_KEY=GET_FROM_SUPABASE_DASHBOARD -SUPABASE_SIGNING_SECRET=GET_FROM_SUPABASE_DASHBOARD -``` - -> Note: Stytch values can be found in the project [dashboard](https://stytch.com/dashboard/api-keys?utm_source=supabase&utm_medium=guide) under **API Keys**. - -![Stytch API keys](/docs/img/guides/integrations/stytch/08.png) - -> Note: Supabase values can be found under **Settings** > **API** for your project. - -![Supabase API keys](/docs/img/guides/integrations/stytch/09.png) - -Start your Next.js development server to read in the new values from `.env.local`. - -```bash -npm run dev -``` - -You should have a running Next.js application on `localhost:3000`. - -## Step 5: Build the Login Form - -Now we will replace the default Next.js home page with a login UI. We will use the Stytch React library. - -> Note: Stytch provides direct API access for those that want to build login UI themselves - -Install the `@stytch/stytch-react` library. - -```bash -npm install @stytch/stytch-react -``` - -In the root directory, create a new folder named `components` and file in that folder named `/StytchLogin.js`. Within this file, paste the snippet below. This will configure, and style the Stytch React component to use Email magic links. - -```jsx -// components/StytchLogin.js -import React from 'react' -import { Stytch } from '@stytch/stytch-react' - -const stytchConfig = { - loginOrSignupView: { - products: ['emailMagicLinks'], - emailMagicLinksOptions: { - loginRedirectURL: 'http://localhost:3000/api/authenticate', - loginExpirationMinutes: 30, - signupRedirectURL: 'http://localhost:3000/api/authenticate', - signupExpirationMinutes: 30, - createUserAsPending: true, - }, - }, - style: { - fontFamily: '"Helvetica New", Helvetica, sans-serif', - width: '321px', - primaryColor: '#0577CA', - }, -} - -const StytchLogin = ({ publicToken }) => { - return ( - - ) -} - -export default StytchLogin -``` - -Additionally, create a profile component by creating a file called `Profile.js` in `/components`. We will use this component to render our expenses stored in Supabase later on. - -```jsx -// components/Profile.js -import React from 'react' -import Link from 'next/link' - -export default function Profile({ user }) { - return ( -
    -

    Welcome {user.userId}

    -

    Your expenses

    - {user.expenses?.length > 0 ? ( - user.expenses.map((expense) => ( -

    - {expense.title}: ${expense.value} -

    - )) - ) : ( -

    You have no expenses!

    - )} - - - - -
    - ) -} -``` - -Finally, replace the contents of the file `/pages/index.js` to render our new `StytchLogin` and `Profile` components. - -```jsx -// pages/index.js -import styles from '../styles/Home.module.css' -import Profile from '../components/Profile' -import StytchLogin from '../components/StytchLogin' - -const Index = ({ user, publicToken }) => { - let content - if (user) { - content = - } else { - content = - } - - return
    {content}
    -} - -export async function getServerSideProps({ req, res }) { - const user = null // Will update later - return { - props: { user, publicToken: process.env.STYTCH_PUBLIC_TOKEN }, - } -} - -export default Index -``` - -On `localhost:3000` there is now a login form prompting for your email address. - -![Email login step one](/docs/img/guides/integrations/stytch/10.png) - -Enter your email address and press **Continue with email**. - -![Email login step two](/docs/img/guides/integrations/stytch/11.png) - -In your inbox you will find a login request from your app. - -![Email login step three](/docs/img/guides/integrations/stytch/12.png) - -However, if you click the link in the email you will get a 404. We need to build an API route to handle the email magic link authentication. - -## Step 6: Authenticate and start a session - -To make authentication easier we will use the Stytch Node.js library. Run - -```bash -npm install stytch -``` - -Additionally, we will need to store the authenticated session in a cookie. Run - -```bash -npm install cookies-next -``` - -Create a new folder named `utils` and inside a file named`stytchLogic.js` with the following contents - -```jsx -// utils/stytchLogic.js -import * as stytch from 'stytch' -import { getCookie, setCookies, removeCookies } from 'cookies-next' - -export const SESSION_COOKIE = 'stytch_cookie' - -let client -const loadStytch = () => { - if (!client) { - client = new stytch.Client({ - project_id: process.env.STYTCH_PROJECT_ID, - secret: process.env.STYTCH_SECRET, - env: process.env.STYTCH_PROJECT_ENV === 'live' ? stytch.envs.live : stytch.envs.test, - }) - } - - return client -} - -export const getAuthenticatedUserFromSession = async (req, res) => { - const sessionToken = getCookie(SESSION_COOKIE, { req, res }) - if (!sessionToken) { - return null - } - - try { - const stytchClient = loadStytch() - const resp = await stytchClient.sessions.authenticate({ - session_token: sessionToken, - }) - return resp.session.user_id - } catch (error) { - console.log(error) - return null - } -} - -export const revokeAndClearSession = async (req, res) => { - const sessionToken = getCookie(SESSION_COOKIE, { req, res }) - - if (sessionToken) { - try { - const stytchClient = loadStytch() - await stytchClient.sessions.revoke({ - session_token: sessionToken, - }) - } catch (error) { - console.log(error) - } - removeCookies(SESSION_COOKIE, { req, res }) - } - - return res.redirect('/') -} - -export const authenticateTokenStartSession = async (req, res) => { - const { token, type } = req.query - let sessionToken - try { - const stytchClient = loadStytch() - const resp = await stytchClient.magicLinks.authenticate(token, { - session_duration_minutes: 30, - }) - sessionToken = resp.session_token - } catch (error) { - console.log(error) - const errorString = JSON.stringify(error) - return res.status(400).json({ errorString }) - } - - setCookies(SESSION_COOKIE, sessionToken, { - req, - res, - maxAge: 60 * 60 * 24, - secure: true, - }) - - return res.redirect('/') -} -``` - -This logic is responsible for setting up the Stytch client we will use to call the API. It provides functions we will use to login, logout, and validate user sessions. - -In order to complete the email login flow, create a new file `pages/api/authenticate.js` with the contents: - -```jsx -// pages/api/authenticate.js -import { authenticateTokenStartSession } from '../../utils/stytchLogic' - -export default async function handler(req, res) { - return authenticateTokenStartSession(req, res) -} -``` - -We will also create a logout API endpoint with similar contents. In `pages/api/logout.js` include the following: - -```jsx -// pages/api/logout.js -import { revokeAndClearSession } from '../../utils/stytchLogic' - -export default async function handler(req, res) { - return revokeAndClearSession(req, res) -} -``` - -Finally, update `pages/index.js` by importing `getAuthenticatedUserFromSession`, and calling it to set the user variable in `getServerSideProps`. - -```jsx -// pages/index.js -import styles from '../styles/Home.module.css' - -import StytchLogin from '../components/StytchLogin' -import Profile from '../components/Profile' -import { getAuthenticatedUserFromSession } from '../utils/stytchLogic' - -const Index = ({ user, publicToken }) => { - let content - if (user) { - content = - } else { - content = - } - - return
    {content}
    -} - -export async function getServerSideProps({ req, res }) { - const userId = await getAuthenticatedUserFromSession(req, res) - if (userId) { - return { - props: { user: { userId }, publicToken: process.env.STYTCH_PUBLIC_TOKEN }, - } - } - return { - props: { publicToken: process.env.STYTCH_PUBLIC_TOKEN }, - } -} - -export default Index -``` - -Return to `localhost:3000`, and login again by sending yourself a new email. Upon clicking through in the email you should be presented with “Welcome $USER_ID”. If you refresh the page, you should remain in an authenticated state. If you press **Logout** then you should return to the login screen. - -![Profile page](/docs/img/guides/integrations/stytch/13.png) - -Now that we have a working login flow with persistent authentication it is time to pull in our expense data from Supabase. - -## Step 7: Requesting user data from Supabase - -First, install the Supabase client: - -```bash -npm install @supabase/supabase-js -``` - -In order to pass an authenticated `user_id` to Supabase we will package it within a JWT. Install jsonwebtoken: - -```bash -npm install jsonwebtoken -``` - -Create a new file `utils/supabase.js` and add the following: - -```jsx -// utils/supabase.js -import { createClient } from '@supabase/supabase-js' -import jwt from 'jsonwebtoken' - -const getSupabase = (userId) => { - const supabase = createClient( - process.env.NEXT_PUBLIC_SUPABASE_URL, - process.env.NEXT_PUBLIC_SUPABASE_KEY - ) - - if (userId) { - const payload = { - userId, - exp: Math.floor(Date.now() / 1000) + 60 * 60, - } - - supabase.auth.session = () => ({ - access_token: jwt.sign(payload, process.env.SUPABASE_SIGNING_SECRET), - }) - } - - return supabase -} - -export { getSupabase } -``` - -Our payload for the JWT will contain our user's unique identifier from Stytch, their `user_id`. We are signing this JWT using Supabase's signing secret, so Supabase will be able to validate it is authentic and hasn't been tampered with in transit. - -Let's load our expenses from Supabase on the home page! Update `pages/index.js` a final time to make a request for expense data from Supabase. - -```jsx -import styles from '../styles/Home.module.css' - -import StytchLogin from '../components/StytchLogin' -import Profile from '../components/Profile' -import { getAuthenticatedUserFromSession } from '../utils/stytchLogic' -import { getSupabase } from '../utils/supabase' - -const Index = ({ user, publicToken }) => { - let content - if (user) { - content = - } else { - content = - } - - return
    {content}
    -} - -export async function getServerSideProps({ req, res }) { - const userId = await getAuthenticatedUserFromSession(req, res) - - if (userId) { - const supabase = getSupabase(userId) - const { data: expenses } = await supabase.from('expenses').select('*') - - return { - props: { - user: { userId, expenses }, - publicToken: process.env.STYTCH_PUBLIC_TOKEN, - }, - } - } else { - return { - props: { publicToken: process.env.STYTCH_PUBLIC_TOKEN }, - } - } -} - -export default Index -``` - -When we reload our application, we are still getting the empty state for expenses. - -This is because we enabled Row Level Security, which blocks all requests by default and lets you granularly control access to the data in your database. To enable our user to select their expenses we need to write a RLS policy. - -## Step 8: Write a policy to allow select - -Our policy will need to know who our currently logged in user is to determine whether or not they should have access. Let's create a PostgreSQL function to extract the current user from our new JWT. - -Navigate back to the Supabase dashboard, select SQL from the sidebar menu, and click **New query**. This will create a new query,, which will allow us to run any SQL against our Postgres database. - -Write the following and click **Run**. - -```sql -create or replace function auth.user_id() returns text as $$ - select nullif(current_setting('request.jwt.claims', true)::json->>'userId', '')::text; -$$ language sql stable; -``` - -You should see the output `Success, no rows returned`. This created a function called `auth.user_id()`, which will inspect the `userId` field of our JWT payload. - -> Note: To learn more about PostgreSQL functions, check out this [deep dive video](https://www.youtube.com/watch?v=MJZCCpCYEqk). - -Let's create a policy that checks whether this user is the owner of an expense. - -Select **Authentication** from the Supabase sidebar menu, click **Policies**, then **New Policy**. - -![Supabase authentication page](/docs/img/guides/integrations/stytch/14.png) - -From the modal, select **For full customization create a policy from scratch** and add the following. - -![Supabase create policy page](/docs/img/guides/integrations/stytch/15.png) - -This policy is calling the function we just created to get the currently logged in user's `user_id` `auth.user_id()` and checking whether this matches the `user_id` column for the current expense. If it does, then it will allow the user to select it, otherwise it will continue to deny. - -Click Review and then Save policy. After you've saved, click Enable RLS on the table to enable the policy we just created. - -> Note: To learn more about RLS and policies, check out this [video](https://www.youtube.com/watch?v=Ow_Uzedfohk). - -The last thing we need to do is update the `user_id` columns for our existing expenses. - -Head back to the Supabase dashboard, and select Table editor from the sidebar. You will notice each entry has `user_id` set to `NULL`. We need to update this value to the proper `user_id`. - -![Supabase null users in table](/docs/img/guides/integrations/stytch/16.png) - -To get the `user_id` for our Stytch user, you can pull it from the welcome page in our example app (eg `user-test-61497d40-f957-45cd-a6c8-5408d22e93bc`). - -![Get user_id](/docs/img/guides/integrations/stytch/17.png) - -Update each row in Supabase to this `user_id`. - -![Populate user_id](/docs/img/guides/integrations/stytch/18.png) - -Return to `localhost:3000`, and you will see your expenses listed. - -![Listed expenses](/docs/img/guides/integrations/stytch/19.png) - -We now have a basic expense tracker application powered by Stytch, Supabase, and Next.js. From here you could add additional features like adding, editing, and organizing your expenses further. - -> Note: You can find a completed version of this project on [Github](https://github.com/stytchauth/stytch-nextjs-supabase). - -## Optional: Add Google One Tap - -In this optional step, we will extend our application to allow users to login with Google One Tap in addition to Email magic links. - -You will need to follow the first four steps of [this guide](https://stytch.com/docs/oauth?utm_source=supabase&utm_medium=guide#guides_google-sdk) to create a Google project, set up Google OAuth consent, and configure credentials and redirect URLs. - -First, we will make some adjustments to the `StytchLogin` component. We will update the configuration, so that it uses both Google OAuth, and Email magic links. - -```jsx -// components/StytchLogin.js -import React from 'react' -import { Stytch } from '@stytch/stytch-react' - -const stytchConfig = { - loginOrSignupView: { - products: ['oauth', 'emailMagicLinks'], - oauthOptions: { - providers: [ - { - type: 'google', - one_tap: true, - position: 'embedded', - }, - ], - loginRedirectURL: 'http://localhost:3000/api/authenticate?type=oauth', - signupRedirectURL: 'http://localhost:3000/api/authenticate?type=oauth', - }, - emailMagicLinksOptions: { - loginRedirectURL: 'http://localhost:3000/api/authenticate', - loginExpirationMinutes: 30, - signupRedirectURL: 'http://localhost:3000/api/authenticate', - signupExpirationMinutes: 30, - createUserAsPending: true, - }, - }, - style: { - fontFamily: '"Helvetica New", Helvetica, sans-serif', - width: '321px', - primaryColor: '#0577CA', - }, -} - -const StytchLogin = ({ publicToken }) => { - return ( - - ) -} - -export default StytchLogin -``` - -We also need to make an adjustment to the function `authenticateTokenStartSession` in `stytchLogic.js`. Stytch has separate authentication endpoints for Email magic links and OAuth, so we need to route our token correctly. - -```jsx -// utils/stytchLogic.js - -// leave the rest of the file contents as is -export const authenticateTokenStartSession = async (req, res) => { - const { token, type } = req.query - let sessionToken - try { - const stytchClient = loadStytch() - if (type == 'oauth') { - const resp = await stytchClient.oauth.authenticate(token, { - session_duration_minutes: 30, - session_management_type: 'stytch', - }) - sessionToken = resp.session.stytch_session.session_token - } else { - const resp = await stytchClient.magicLinks.authenticate(token, { - session_duration_minutes: 30, - }) - sessionToken = resp.session_token - } - } catch (error) { - console.log(error) - const errorString = JSON.stringify(error) - return res.status(400).json({ errorString }) - } - - setCookies(SESSION_COOKIE, sessionToken, { - req, - res, - maxAge: 60 * 60 * 24, - secure: true, - }) - - return res.redirect('/') -} -``` - -With these two changes you will now have a working Google One Tap authentication method along with email magic links. - -![Google One Tap](/docs/img/guides/integrations/stytch/20.png) - -## Resources - -- [Stytch blog](https://stytch.com/blog?utm_source=supabase&utm_medium=guide) -- [Stytch documentation](https://stytch.com/docs?utm_source=supabase&utm_medium=guide) - -export const Page = ({ children }) => - -export default Page diff --git a/apps/docs/pages/guides/integrations/supertokens.mdx b/apps/docs/pages/guides/integrations/supertokens.mdx deleted file mode 100644 index 1d23791f1d9..00000000000 --- a/apps/docs/pages/guides/integrations/supertokens.mdx +++ /dev/null @@ -1,406 +0,0 @@ -import Layout from '~/layouts/DefaultGuideLayout' - -export const meta = { - id: 'supertokens', - title: 'SuperTokens', - description: - 'Create a Next.js application secured by SuperTokens and PostgreSQL Row Level Security.', -} - -[SuperTokens](https://www.supertokens.com) is an open source authentication solution which provides many stratergies for authenticating and managing users. You can use the managed service for easy setup or you can self host the solution to have complete control over your data. - -In this guide we will build a simple web application using SuperTokens, Supabase, and Next.js. You will be able to sign up using SuperTokens and your email and user ID will be stored in Supabase. Once authenticated the frontend will be able to query Supabase and retrieve the user's email. Our example app will be using the [Email-Password and Social Login](https://supertokens.com/docs/thirdpartyemailpassword/introduction) recipe for authentication and session management. - -We will use Supabase to store and authorize access to user data. Supabase makes it simple to setup Row Level Security(RLS) policies which ensure users can only read and write data that belongs to them. - -### Demo App - -You can find a demo app using SuperTokens, Supabase and Nexts.js on [Github](https://github.com/supertokens/supertokens-auth-react/tree/master/examples/with-supabase) - -## Step 1: Create a new Supabase project - -From your [Supabase dashboard](https://app.supabase.com/), click `New project`. - -Enter a `Name` for your Supabase project. - -Enter a secure `Database Password`. - -Select the same `Region` you host your app's backend in. - -Click `Create new project`. - -![New Supabase project settings](/docs/img/guides/integrations/supertokens/supabase_dashboard_create.png) - -## Step 2: Creating tables in Supabase - -From the sidebar menu in the [Supabase dashboard](https://app.supabase.com/), click `Table editor`, then `New table`. - -Enter `users` as the `Name` field. - -Select `Enable Row Level Security (RLS)`. - -Remove the default columns - -Create two new columns: - -- `user_id` as `text` as primary key -- `email` as `text` - -Click `Save` to create the new table. - -![Users table](/docs/img/guides/integrations/supertokens/supabase_table_create.png) - -## Step 3: Setup your Next.js App with SuperTokens. - -Since the scope of this guide is limited to the integration between SuperTokens and Supabase, you can refer to the SuperTokens website to see [how to setup your Next.js app with SuperTokens](https://supertokens.com/docs/thirdpartyemailpassword/nextjs/about). - -Once you finish setting up your app, you will be greeted with the following screen - -![SuperTokens Auth Screen](/docs/img/guides/integrations/supertokens/supertokens_thirdpartyemailpassword_auth_screen.png) - -## Step 4: Creating a Supabase JWT to access Supabase - -In our Nextjs app when a user signs up, we want to store the user's email in Supabase. We would then retrieve this email from Supabase and display it on our frontend. - -To use the Supabase client to query the database we will need to create a JWT signed with your Supabase app's signing secret. This JWT will also need to contain the user's userId so Supabase knows an authenticated user is making the request. - -To create this flow we will need to modify SuperTokens so that, when a user signs up or signs in, a JWT signed with Supabase's signing secret is created and attached to the user's session. Attaching the JWT to the user's session will allow us to retrieve the Supabase JWT on the frontend and backend (post session verification), using which we can query Supabase. - -We want to create a Supabase JWT when we are creating a SuperTokens' session. This can be done by overriding the `createNewSession` function in your backend config. - -```ts -// config/backendConfig.ts - -import ThirdPartyEmailPasswordNode from "supertokens-node/recipe/thirdpartyemailpassword"; -import SessionNode from "supertokens-node/recipe/session"; -import { TypeInput } from "supertokens-node/lib/build/types"; -import { appInfo } from "./appInfo"; -import jwt from "jsonwebtoken"; - -let backendConfig = (): TypeInput => { - return { - framework: "express", - supertokens: { - connectionURI: "https://try.supertokens.com", - }, - appInfo, - recipeList: [ - ThirdPartyEmailPasswordNode.init({...}), - SessionNode.init({ - override: { - functions: (originalImplementation) => { - return { - ...originalImplementation, - // We want to create a JWT which contains the users userId signed with Supabase's secret so - // it can be used by Supabase to validate the user when retrieving user data from their service. - // We store this token in the accessTokenPayload so it can be accessed on the frontend and on the backend. - createNewSession: async function (input) { - const payload = { - userId: input.userId, - exp: Math.floor(Date.now() / 1000) + 60 * 60, - }; - - const supabase_jwt_token = jwt.sign(payload, process.env.SUPABASE_SIGNING_SECRET); - - input.accessTokenPayload = { - ...input.accessTokenPayload, - supabase_token: supabase_jwt_token, - }; - - return await originalImplementation.createNewSession(input); - }, - }; - }, - }, - }), - ], - isInServerlessEnv: true, - }; -}; - -``` - -As seen above, we will be using the `jsonwebtoken` library to create a JWT signed with Supabase's signing secret whose payload contains the user's userId. - -We will be storing this token in the `accessTokenPayload` which will essentially allow us to access the `supabase_token` on the frontend and backend whilst the user is logged in. - -## Step 5: Creating a Supabase client - -Create a new file called `utils/supabase.ts` and add the following: - -```ts -// utils/supabase.ts - -import { createClient } from '@supabase/supabase-js' - -const getSupabase = (access_token) => { - const supabase = createClient( - process.env.NEXT_PUBLIC_SUPABASE_URL, - process.env.NEXT_PUBLIC_SUPABASE_KEY - ) - - supabase.auth.session = () => ({ - access_token, - }) - - return supabase -} - -export { getSupabase } -``` - -This will be our client for talking to Supabase. We can pass it an `access_token` and it will be attached to our request. This `access_token` is the same as the `supabase_token` we had created earlier. - -## Step 6: Inserting users into Supabase when they sign up: - -In our example app there are two ways for signing up a user. Email-Password and Social Login based authentication. We will need to override both these APIs such that when a user signs up, their email mapped to their userId is stored in Supabase. - -```ts -// config/backendConfig.ts - -import ThirdPartyEmailPasswordNode from "supertokens-node/recipe/thirdpartyemailpassword"; -import SessionNode from "supertokens-node/recipe/session"; -import { TypeInput } from "supertokens-node/lib/build/types"; -import { appInfo } from "./appInfo"; -import jwt from "jsonwebtoken"; -import { getSupabase } from "../utils/supabase"; - -let backendConfig = (): TypeInput => { - return { - framework: "express", - supertokens: { - connectionURI: "https://try.supertokens.com", - }, - appInfo, - recipeList: [ - ThirdPartyEmailPasswordNode.init({ - providers: [...], - override: { - apis: (originalImplementation) => { - return { - ...originalImplementation, - // the thirdPartySignInUpPost function handles sign up/in via Social login - thirdPartySignInUpPOST: async function (input) { - if (originalImplementation.thirdPartySignInUpPOST === undefined) { - throw Error("Should never come here"); - } - - // call the sign up/in api for social login - let response = await originalImplementation.thirdPartySignInUpPOST(input); - - // check that there is no issue with sign up and that a new user is created - if (response.status === "OK" && response.createdNewUser) { - - // retrieve the accessTokenPayload from the user's session - const accessTokenPayload = response.session.getAccessTokenPayload(); - - // create a supabase client with the supabase_token from the accessTokenPayload - const supabase = getSupabase(accessTokenPayload.supabase_token); - - // store the user's email mapped to their userId in Supabase - const { error } = await supabase - .from("users") - .insert({ email: response.user.email, user_id: response.user.id }); - - if (error !== null) { - - throw error; - } - } - - return response; - }, - // the emailPasswordSignUpPOST function handles sign up via Email-Password - emailPasswordSignUpPOST: async function (input) { - if (originalImplementation.emailPasswordSignUpPOST === undefined) { - throw Error("Should never come here"); - } - - let response = await originalImplementation.emailPasswordSignUpPOST(input); - - if (response.status === "OK") { - - // retrieve the accessTokenPayload from the user's session - const accessTokenPayload = response.session.getAccessTokenPayload(); - - // create a supabase client with the supabase_token from the accessTokenPayload - const supabase = getSupabase(accessTokenPayload.supabase_token); - - // store the user's email mapped to their userId in Supabase - const { error } = await supabase - .from("users") - .insert({ email: response.user.email, user_id: response.user.id }); - - if (error !== null) { - - throw error; - } - } - - return response; - }, - }; - }, - }, - }), - SessionNode.init({...}), - ], - isInServerlessEnv: true, - }; -}; - -``` - -As seen above, we will be overriding the `emailPasswordSignUpPOST` and `thirdPartySignInUpPOST` APIs such that if a user signs up, we retrieve the Supabase JWT (which we created in the `createNewSession` function) from the user's accessTokenPayload and send a request to Supabase to insert the email-userid mapping. - -## Step 7: Retrieving the user's email on the frontend - -Now that our backend is setup we can modify our frontend to retrieve the user's email from Supabase. - -```tsx -// pages/index.tsx - -import React, { useState, useEffect } from 'react' -import Head from 'next/head' -import styles from '../styles/Home.module.css' -import ThirdPartyEmailPassword, { - ThirdPartyEmailPasswordAuth, -} from 'supertokens-auth-react/recipe/thirdpartyemailpassword' -import dynamic from 'next/dynamic' -import { useSessionContext } from 'supertokens-auth-react/recipe/session' -import { getSupabase } from '../utils/supabase' - -export default function Home() { - return ( - // We will wrap the ProtectedPage component with ThirdPartyEmailPasswordAuth so only an - // authenticated user can access it. This will also allow us to access the users session information - // within the component. - - - - ) -} - -function ProtectedPage() { - // retrieve the authenticated user's accessTokenPayload and userId from the sessionContext - const { accessTokenPayload, userId } = useSessionContext() - - if (sessionContext.loading === true) { - return null - } - - const [userEmail, setEmail] = useState('') - useEffect(() => { - async function getUserEmail() { - // retrieve the supabase client who's JWT contains users userId, this will be - // used by supabase to check that the user can only access table entries which contain their own userId - const supabase = getSupabase(accessTokenPayload.supabase_token) - - // retrieve the user's name from the users table whose email matches the email in the JWT - const { data } = await supabase.from('users').select('email').eq('user_id', userId) - - if (data.length > 0) { - setEmail(data[0].email) - } - } - getUserEmail() - }, []) - - return ( -
    - - SuperTokens 💫 - - - -
    -

    - You are authenticated with SuperTokens! (UserId: {userId}) -
    - Your email retrieved from Supabase: {userEmail} -

    -
    -
    - ) -} -``` - -As seen above we will be using SuperTokens `useSessionContext` hook to retrieve the authenticated user's `userId` and `accessTokenPayload`. Using React's `useEffect` hook we can use the Supabase client to retrieve the user's email from Supabase using the JWT retrieved from the user's `accessTokenPayload` and their `userId`. - -## Step 8: Create Policies to enforce Row Level Security for Select and Insert requests - -To enforce Row Level Security for the `Users` table we will need to create policies for Select and Insert requests. - -These polices will retrieve the userId from the JWT and check if it matches the userId in the Supabase table - -To do this we will need a PostgreSQL function to extract the userId from the JWT. - -The payload in the JWT will have the following structure: - -``` -// JWT payload -{ - userId, - exp -} -``` - -To create the PostgreSQL function, lets navigate back to the Supabase dashboard, select `SQL` from the sidebar menu, and click `New query`. This will create a new query called `new sql snippet`, which will allow us to run any SQL against our Postgres database. - -Write the following and click `Run`. - -```sql -create or replace function auth.user_id() returns text as $$ - select nullif(current_setting('request.jwt.claims', true)::json->>'userId', '')::text; -$$ language sql stable; -``` - -This will create a function called `auth.user_id()`, which will inspect the `userId` field of our JWT payload. - -### SELECT query policy - -Our first policy will check whether the user is the owner of the email. - -Select `Authentication` from the Supabase sidebar menu, click `Policies`, and then `New Policy` on the `Users` table. - -![Create new policy](/docs/img/guides/integrations/supertokens/create_policy.png) - -From the modal, select `Create a policy from scratch` and add the following. - -![Policy settings for SELECT](/docs/img/guides/integrations/supertokens/policy_config_select.png) - -This policy is calling the PostgreSQL function we just created to get the currently logged in user's ID `auth.user_id()` and checking whether this matches the `user_id` column for the current `email`. If it does, then it will allow the user to select it, otherwise it will continue to deny. - -Click `Review` and then `Save policy`. - -### INSERT query policy - -Our second policy will check whether the `user_id` being inserted is the same as the `userId` in the JWT. - -Create another policy and add the following: - -![Policy settings for INSERT](/docs/img/guides/integrations/supertokens/policy_config_insert.png) - -Similar to the previous policy we are calling the PostgreSQL function we created to get the currently logged in user's ID `auth.user_id()` and check whether this matches the `user_id` column for the row we are trying to insert. If it does, then it will allow the user to insert the row, otherwise it will continue to deny. - -Click `Review` and then `Save policy`. - -## Step 9: Test your changes - -You can now sign up and you should see the following screen: - -![SuperTokens App Authenticated](/docs/img/guides/integrations/supertokens/supabase_app_authenticated_screen.png) - -If you navigate to your table you should see a new row with the user's `user_id` and `email`. - -![Supabase Users table](/docs/img/guides/integrations/supertokens/table_with_user.png) - -## Resources - -- [SuperTokens](https://supertokens.com/) official website. -- [SuperTokens community](https://supertokens.com/discord). -- [SuperTokens documentation](https://supertokens.com/docs/guides). - -export const Page = ({ children }) => - -export default Page diff --git a/apps/docs/pages/guides/integrations/vercel.mdx b/apps/docs/pages/guides/integrations/vercel.mdx deleted file mode 100644 index dc147aa119e..00000000000 --- a/apps/docs/pages/guides/integrations/vercel.mdx +++ /dev/null @@ -1,248 +0,0 @@ -import Layout from '~/layouts/DefaultGuideLayout' - -export const meta = { - id: 'vercel', - title: 'Vercel', - description: - "The fastest way to get up and running with an application that uses Supabase is with Vercel's Next.js starter and Supabase integration.", -} - -This guide steps through using Vercel's dashboard to create a Next.js project integrated with Supabase. To further streamline the process, we will be using the Next.js starter template, which can be automatically forked to a new GitHub repo, without leaving the dashboard! - -If you don’t have a Vercel account, create one [here](https://vercel.com/signup). - -## Step 1: Create a Supabase project - -This guide could use an existing Supabase project, but to create the `todo` demo from scratch, navigate to [Supabase](https://app.supabase.com/), click `Sign In` and authenticate with GitHub to login or register a new account. - -From the Supabase dashboard, click `New project` and select an organization. - -> Note: You may need to create an organization first. - -Give your project a `name`, `password`, select a `region` close to your potential users and click `Create new project`. - -![Create a Supabase project](/docs/img/guides/integrations/vercel/create-supabase-project.png) - -Supabase will take a couple of minutes to configure the infrastructure. - -Once this is finished, navigate to `SQL Editor` from the sidebar menu and click `New query`. - -This will create a new SQL snippet called "New Query". Copy and paste the following and click `Run`. - -```sql -create table todos ( - id bigint generated by default as identity primary key, - title text, - is_complete boolean default false, - created_at timestamp with time zone default timezone('utc'::text, now()) not null -); - -alter table todos enable row level security; - -create policy "Anyone can view todos" on todos for - select using (true); - -create policy "Anyone can add new todos" on todos for - insert with check (true); - -insert into todos(title) -values - ('Create Supabase project'), - ('Create Vercel project'), - ('Install Supabase integration'); -``` - -This will create a new todos table, enable row level security, add policies for selecting and inserting data, and add some example rows. - -> Note: To simplify this example, we are allowing anyone to `select` and `insert` rows on the `todos` table. Usually, these actions would be locked down to only allow logged in users to perform them. Check out [this video](https://www.youtube.com/watch?v=Ow_Uzedfohk) to learn more about Row Level Security and policies. - -## Step 2: Create Vercel project - -From your [Vercel dashboard](https://vercel.com/dashboard), click `New Project`. - -![Create new Vercel project](/docs/img/guides/integrations/vercel/create-vercel-project.png) - -Under the `Clone Template` menu, click `Next.js`. - -![Clone Next.js template](/docs/img/guides/integrations/vercel/clone-next-js-template.png) - -In the `Create Git Repository` section, click `GitHub`, select your username under `GIT SCOPE`, enter a name for your project, choose whether you want your repo `private` or `public`, and click `Create`. - -![New GitHub repo settings](/docs/img/guides/integrations/vercel/repo-settings.png) - -This will create a new GitHub repository, clone and commit the Next.js starter project, then build and deploy your new project to Vercel. - -Once you have been redirected to the `Congratulations` screen, click `Go to Dashboard`. - -Navigate to `Settings`, `Integrations`, then click `Browse Marketplace`. - -Search for `Supabase` and click the Supabase integration. - -![Supabase integration](/docs/img/guides/integrations/vercel/supabase-integration.png) - -Click `Add Integration`. Select your account from the `Vercel Scope` dropdown, and click `CONTINUE`. - -![Choose scope](/docs/img/guides/integrations/vercel/choose-scope.png) - -Choose `Specific Projects` and select your new Vercel project from the dropdown, and click `Add Integration`. - -![Choose project](/docs/img/guides/integrations/vercel/choose-project.png) - -From the Supabase popup, select your new Vercel Project and Supabase project from the dropdowns. - -![Supabase integration](/docs/img/guides/integrations/vercel/link-vercel-to-supabase.png) - -## Step 3: Clone GitHub repo - -The fastest way to get this project running locally is to clone the repo that Vercel created for us. - -Navigate back to the Vercel project `Overview` page, and click `View Git Repository`. - -![Vercel Project Dashboard](/docs/img/guides/integrations/vercel/vercel-project-dashboard.png) - -This will open the GitHub repo. From here, click the arrow next to `Code` and copy the url from the dropdown. - -![GitHub repo url](/docs/img/guides/integrations/vercel/github-project-url.png) - -Open a terminal window or CLI and run the following command to clone the GitHub repo. - -```bash -git clone your-repo-url.git -``` - -Open the project in your code editor of choice, and update the contents of `pages/index.js` to the following: - -```jsx -import styles from '../styles/Home.module.css' - -export default function Home() { - return
    working
    -} -``` - -Run a local development server. - -```bash -npm run dev -``` - -Navigate to `http://localhost:3000` to confirm the project is "working". - -## Step 4: Pull environment variables from Vercel - -First, we need to login to Vercel using their CLI tool. - -```bash -npx vercel login -``` - -This will ask if we are happy to install `vercel`. Type `y` and hit `Enter`. - -We will then need to authenticate Vercel by selecting `Continue with GitHub`. - -This will open a browser window where you need to authenticate with your GitHub account. - -Next, we need to link our Vercel project. - -```bash -npx vercel link -``` - -Step through the prompts to link the Vercel project. - -![Link project from Vercel](/docs/img/guides/integrations/vercel/vercel-link.png) - -Copy the environment variables from our Vercel project. - -```bash -npx vercel env pull -``` - -This will create a `.env` file containing our Supabase environment variables. Rename this file to `.env.local` to automatically ignore it from git. - -## Step 5: Install Supabase.js - -Install the `supabase-js` library. - -```bash -npm i @supabase/supabase-js -``` - -Create a new file called `/utils/supabase.js` and add the following. - -```jsx -import { createClient } from '@supabase/supabase-js' - -export default createClient( - process.env.NEXT_PUBLIC_SUPABASE_URL, - process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY -) -``` - -Create a new file called `/components/NewTodo.js` and add the following. - -```jsx -import { useState } from 'react' -import supabase from '../utils/supabase' - -export default ({ reload }) => { - const [title, setTitle] = useState('') - - const addTodo = async (e) => { - e.preventDefault() - await supabase.from('todos').insert({ title }) - reload() - setTitle('') - } - - return ( -
    - setTitle(e.target.value)} /> -
    - ) -} -``` - -This component will be responsible for writing a new `todo` to Supabase. - -Let's import our new component in `pages/index.js` and display a list of todos. - -```jsx -import { useState, useEffect } from 'react' -import styles from '../styles/Home.module.css' -import supabase from '../utils/supabase' -import NewTodo from '../components/NewTodo' - -export default function Home() { - const [todos, setTodos] = useState([]) - - const fetchTodos = async () => { - const { data } = await supabase.from('todos').select('*') - setTodos(data) - } - - useEffect(() => { - fetchTodos() - }, []) - - return ( -
    - - {todos.map((todo) => ( -

    {todo.title}

    - ))} -
    - ) -} -``` - -## Resources - -- [Vercel official website](https://vercel.com). -- [Vercel blog](https://vercel.com/blog). -- [Vercel docs](https://vercel.com/docs). -- [Vercel Integration docs](https://vercel.com/docs/integrations) - -export const Page = ({ children }) => - -export default Page diff --git a/apps/docs/pages/guides/integrations/weweb.mdx b/apps/docs/pages/guides/integrations/weweb.mdx deleted file mode 100644 index 85809a457a9..00000000000 --- a/apps/docs/pages/guides/integrations/weweb.mdx +++ /dev/null @@ -1,337 +0,0 @@ -import Layout from '~/layouts/DefaultGuideLayout' - -export const meta = { - id: 'weweb', - title: 'WeWeb', - description: 'Build user interfaces on top of existing databases.', -} - -This guide explains how to connect a Supabase back-end to a WeWeb front-end and then configure all the CRUD operations necessary to build an Admin Portal with user authentication, roles, and permissions. - -[WeWeb](https://dashboard.weweb.io/sign-up) is a low-code front-end builder that allies the short learning curve of no-code with the freedom of code. - -It connects to Supabase via two native integrations: - -- one for data manipulation, and -- another for user authentication. - -If you don't have an WeWeb account, you can create one [here](https://dashboard.weweb.io/sign-up). - -Let's get started! - -## Step 1: Add the Supabase Data Source Plugin in WeWeb - -In order to read Supabase data in WeWeb, you'll first need to add the Supabase Data Source Plugin: - -![](https://weweb-changelog.ghost.io/content/images/2022/08/CleanShot-2022-08-09-at-11.38.44@2x.png) - -Once you've added it, you will be invited to share your Supabase project URL and public API key: - -![](https://weweb-changelog.ghost.io/content/images/2022/08/CleanShot-2022-08-09-at-11.39.33@2x.png) - -In Supabase, you can find both your project URL and public key in the `Settings` > `API` menu: - -![](https://weweb-changelog.ghost.io/content/images/2022/08/CleanShot-2022-08-09-at-11.45.46@2x.png) - -Once you have added both to WeWeb, you will have the option to enable realtime tables if you wish to do so: - -![](https://weweb-changelog.ghost.io/content/images/2022/08/CleanShot-2022-08-18-at-12.22.43@2x.png) - -**🚨 Warning 🚨** - -> Realtime is disabled by default in Supabase for better database performance and security. Learn more about [realtime functionalities](/docs/guides/realtime). - -## Step 2: GET Data from Supabase - -Once you click on `Add a Collection`, you will be invited to give your Collection a name and choose Supabase as a Data source: - -![](https://weweb-changelog.ghost.io/content/images/2022/08/CleanShot-2022-08-09-at-11.55.15@2x.png) - -You will then be able to select the Table from which you want to pull data: - -![](https://weweb-changelog.ghost.io/content/images/2022/08/CleanShot-2022-08-09-at-11.57.09.gif) - -Notice that this gives you access to 2 separate modes to access the fields in the table: - -1. a "Guided" mode, and -2. an "Advanced" mode. - -### Guided mode - -By default, the "Guided" mode returns the data from all the fields. - -In the example below, we decide to exclude the data from the `created_at` field in our `vehicles` table: - -_![](https://weweb-changelog.ghost.io/content/images/2022/08/CleanShot-2022-08-09-at-12.04.37@2x.png)_ - -As a result, WeWeb does not fetch the `created_at` field. - -This is helpful because we can exclude data that we don't want to load in the frontend, either because we don't need it or because it's confidential. - -### Advanced mode - -In our database, we created 2 separate tables for vehicles and locations. - -In the `vehicles` table, we made a reference to the `locations` table in our `location_id` field so we know where each car is: - -_![](https://weweb-changelog.ghost.io/content/images/2022/08/CleanShot-2022-08-09-at-12.30.20@2x.png)_ - -The problem is, the link only gives us the id of the location in the `locations` table. - -If you choose the "Advanced" mode, you will be able to get the `name` field of the location instead of the `id`. - -How? - -By [making custom queries to Supabase](https://supabase.com/docs/reference/javascript/select): - -_![](https://weweb-changelog.ghost.io/content/images/2022/08/CleanShot-2022-08-18-at-12.06.17.gif)_ - -In the example above, we are telling Supabase: - -- from the table selected in the Collection – in this case the `vehicles` table – please send me the data in the `id`, `model`, and `mileage` fields -- look for the `location_id` in the `vehicles` table in the `locations` table and send me the data in the corresponding `name` field - -If we only ask for the data from the `location` field of the `vehicles` table, Supabase will only return the `id`: - -![](https://weweb-changelog.ghost.io/content/images/2022/08/CleanShot-2022-08-09-at-12.39.12@2x.png) - -**🚨 Warning 🚨** - -> If you have enabled Row-Level Security in Supabase, make sure you have also [added a Policy](https://supabase.com/docs/learn/auth-deep-dive/auth-policies) that allows users to read the data in the table. Otherwise, WeWeb won't be able to get the data. - -## Step 3: Display Supabase Data in WeWeb - -Assuming you were able to fetch data from Supabase in a WeWeb Collection, you'll be able to bind the data from that Collection on your WeWeb pages. - -In the example below, we chose to display the car model and mileage in the [Data Grid element](https://docs.weweb.io/add-elements/elements/data-grid.html) that comes out-of-the-box in WeWeb: - -![](https://weweb-changelog.ghost.io/content/images/2022/10/CleanShot-2022-10-21-at-12.31.05@2x.png) - -We chose this element because it includes a built-in inline editing mode we'll want to use later for our CRUD operations. - -#### 🔥 Pro Tip 🔥 - -> In WeWeb, you can [bind arrays of data to any Container](https://docs.weweb.io/binding-filtering/display-data.html). Just bear in mind that the first child of the Container you bind the Collection to will be the repeated item. With that in mind, you might want the first child Element to be another Container with a number of items inside like a title, description, button or image. - -## Step 4: Update a record in Supabase - -Once you've added a Supabase Collection of data to WeWeb, you might want to allow users to manipulate the data in that Collection. - -In order to do so, you'll need to create a Workflow in WeWeb. - -In the example below, we are using the "Update row" Workflow that comes by default with WeWeb's Data Grid Element. - -The trigger is `On Row update`. - -Since we added the Supabase Data Source Plugin above, we have access to all the CRUD actions available in Supabase: - -- Select -- Insert -- Update -- Upsert -- Delete - -In this case, we choose the "Update" action: - -![](https://weweb-changelog.ghost.io/content/images/2022/10/CleanShot-2022-10-13-at-15.27.20@2x.png) - -Then, in our "Update" action, we select the `vehicles` table and map the `id` to the id of the Workflow Event: - -![](https://weweb-changelog.ghost.io/content/images/2022/10/CleanShot-2022-10-13-at-17.16.51@2x.png) - -Finally, we tell WeWeb we want to update the `mileage` field in our Supabase table, and send the value in the `mileage` column of our Data Grid: - -![](https://weweb-changelog.ghost.io/content/images/2022/10/CleanShot-2022-10-13-at-17.20.24@2x.png) - -And that's it! - -If you switch to Preview mode, you will be update your Supabase table from your WeWeb Data Grid: - -![](https://weweb-changelog.ghost.io/content/images/2022/10/CleanShot-2022-10-13-at-17.35.20.gif) - -**🔥 Pro Tip 🔥** - -> By default, the fields in the Data Grid Element are Text fields but you can change the input type to Number if you need to send numerical data to your database: - -![](https://weweb-changelog.ghost.io/content/images/2022/10/CleanShot-2022-10-13-at-17.22.58@2x.png) - -## Restrict who can modify a record in Supabase - -By default, all the data in the tables that are in the `public` schema of your Supabase project can be read, updated, or deleted. - -Supabase allows you to [enable Row-Level Security](https://supabase.com/docs/learn/auth-deep-dive/auth-row-level-security) for each of your tables: - -![](https://weweb-changelog.ghost.io/content/images/2022/10/CleanShot-2022-10-13-at-16.10.39@2x.png) - -If you want to restrict certain actions to specific users or roles, you'll need to: - -- add Supabase authentication to your WeWeb project, and -- [write SQL policies in Supabase](https://supabase.com/docs/learn/auth-deep-dive/auth-policies). - -We provide a number of policy templates to get you started: - -![](https://weweb-changelog.ghost.io/content/images/2022/10/CleanShot-2022-10-13-at-16.17.32@2x.png) - -In the example below, we say that users can: - -1. update a record -2. in the "locations" table of the "public" schema -3. if they are authenticated - -![](https://weweb-changelog.ghost.io/content/images/2022/10/CleanShot-2022-10-13-at-16.19.28@2x.png) - -**🔥 Pro Tip 🔥** - -> Once you enable RLS on a Supabase table, you won't be able to access the data in a WeWeb Collection unless you've added a policy. - -![](https://weweb-changelog.ghost.io/content/images/2022/10/CleanShot-2022-10-13-at-16.28.51.gif) - -## Step 4: Add User Authentication - -Once you are able to display Supabase data in WeWeb, you might want to restrict access to certain users or display specific data based on a user's role. - -In order to do that, you'll need to add WeWeb's Supabase Auth Plugin. - -### Add Supabase Auth Plugin in WeWeb - -Supabase comes with an in-built authentication system which you can use in WeWeb. - -To add the Supabase Auth Plugin in WeWeb, go to `Plugins` > `Authentication`: - -![](https://weweb-changelog.ghost.io/content/images/2022/08/CleanShot-2022-08-09-at-15.00.47@2x.png) - -Assuming you have already provided your Supabase project URL and public API key when setting up the Supabase Data source, the only thing left to do will be to add your private API key: - -![](https://weweb-changelog.ghost.io/content/images/2022/08/CleanShot-2022-08-09-at-15.04.04@2x.png) - -In Supabase, you can find your private API key in `Settings` > `API`: - -![](https://weweb-changelog.ghost.io/content/images/2022/08/CleanShot-2022-08-09-at-15.05.40@2x.png) - -**🚨 Warning 🚨** - -> As the name suggests, you'll want to keep this API key secret! Assuming you copy it properly in the "Private API key" field of the Supabase Auth Plugin and don't use it anywhere else in your Weweb project, Weweb will never make it public. - -You will then be invited to choose a page to redirect _unauthenticated_ users, i.e. users who are NOT signed-in: - -![](https://weweb-changelog.ghost.io/content/images/2022/08/CleanShot-2022-08-09-at-15.17.43@2x.png) - -**🚨 Warning 🚨** - -> When you setup your Login Workflow, make sure you don't redirect unauthenticated users to a page that is only accessible to authenticated users. Otherwise, you'll be creating an **infinite loop** and your app will crash. - -### Create User Sign Up and Sign In Workflows - -In the `Add` > `UI kit` menu of WeWeb, you can find ready-made Sign in and Sign up Forms: - -![](https://weweb-changelog.ghost.io/content/images/2022/08/CleanShot-2022-08-19-at-11.45.02@2x.png) - -Once you've added a Form to the Canvas, you'll be able to style it whichever way you want. - -In the example below, we added an image with the logo of our project to a Sign up Form and changed the background color of the `Create Form` Container: - -![](https://weweb-changelog.ghost.io/content/images/2022/08/CleanShot-2022-08-19-at-11.49.24@2x.png) - -To allow users to sign up, you'll need to create a Sign up Workflow on the Form Container: - -![](https://weweb-changelog.ghost.io/content/images/2022/08/CleanShot-2022-08-19-at-11.50.42@2x.png) - -**🔥 Pro Tip 🔥** - -> It's important that the Workflow is on the Form Container and not the Sign up Button because we want to validate the fields of the Form when users submit it. - -In the Workflow, you will choose the `On submit` trigger and add the Supabase `Sign up` Action: - -![](https://weweb-changelog.ghost.io/content/images/2022/08/CleanShot-2022-08-19-at-11.58.23.gif) - -Then, you'll want to map the email, password, and metadata information in the Form to the email, password, and metadata in Supabase before choosing what page the new user should be redirected to: - -![](https://weweb-changelog.ghost.io/content/images/2022/08/CleanShot-2022-08-19-at-12.02.32.gif) - -In the example above, we made sure to add the user's name as an item in that user's metadata. - -In Supabase, you can find the user's metadata in JSON format in a dedicated field of the `users` table, named `raw_user_meta_data`: - -![](https://weweb-changelog.ghost.io/content/images/2022/08/CleanShot-2022-08-19-at-12.12.46@2x.png) - -The same logic will apply to any Supabase Action you want to trigger. - -## Adding User Roles & Permissions - -Now let's say we want to gate content and set different permissions based on a user's role. - -### Adding Roles in Supabase - -In Supabase, we'll need to create a `roles` table with a list of roles and a join table that links the `roles` table with our `users` table. - -First, let's create a `roles` table with three roles and make sure that each role had a UUID and a `name`: - -![](https://weweb-changelog.ghost.io/content/images/2022/08/CleanShot-2022-08-19-at-18.39.08@2x.png) - -**🚨 Warning 🚨** - -> In order for the integration to work with the Users tab in WeWeb, it is crucial that the role title is a text field named `name`. - -### Joining Roles and Users in Supabase - -Second, let's create a `userRoles` join table: - -![](https://weweb-changelog.ghost.io/content/images/2022/08/CleanShot-2022-08-19-at-18.45.10@2x.png) - -In the join table above, you can see we have an `id` field that is uniquely identifiable thanks to a UUID. - -This unique `id` is linked to a `userId`, which is also a UUID, more specifically, it is the UUID we find in the `id` field of the `users` table in the `auth` schema: - -![](https://weweb-changelog.ghost.io/content/images/2022/08/CleanShot-2022-08-19-at-18.51.00@2x.png) - -Each row in our `userRoles` table is also linked to a `roleId` which is the UUID we find in the `id` field of the `roles` table in the `public` schema: - -![](https://weweb-changelog.ghost.io/content/images/2022/08/CleanShot-2022-08-19-at-18.52.57.gif) - -### Linking Users in WeWeb to Roles and Users in Supabase - -Once we've added our list of roles in Supabase and created an empty join table to link our roles with users, it's time to go to WeWeb. - -In `Plugins` > `Supabase Auth` > `3. Roles table`, we'll click `refresh` and select the relevant Supabase tables we just created: - -![](https://weweb-changelog.ghost.io/content/images/2022/08/CleanShot-2022-08-19-at-19.00.54@2x.png) - -Once you've told WeWeb where to find the `roles` and the join table in Supabase, you'll be able to easily view and maintain user roles in the `Users` tab in WeWeb: - -![](https://weweb-changelog.ghost.io/content/images/2022/08/CleanShot-2022-08-19-at-19.03.13.gif) - -When you make a change to a User in WeWeb, it will automatically be updated in Supabase. - -## Users vs Profiles - -So far, we've showed you how to work with the default `users` table that Supabase generates in the `auth` schema when you create a new project. - -Note that, for security purposes, the information in that `users` table is not exposed on the auto-generated API. - -How does this affect your project in WeWeb? - -### Let users update their information - -Let's say you want to let authenticated users update their information, then you don't need to set up anything else in Supabase. - -You could simply create a user profile page in WeWeb and display their information when they sign in, based on the data you have in the `user` Variable: - -![](https://weweb-changelog.ghost.io/content/images/2022/08/CleanShot-2022-08-19-at-19.16.17@2x.png) - -### Display other users' information - -In some use cases, you might want to display _other_ users' information. - -For example, if you're building an HR portal in WeWeb, you might want HR employees to have access to a list of applicants and their user profiles. - -You wouldn't be able to do that with the `users` table in the `auth` schema because each user's information is only available to them. - -For such a use case, we recommend creating a `profiles` table in the `public` schema to store user data that you want to access via the API. - -In WeWeb, you would then be able to create a Collection to get data from the `profiles` table. - -Learn more about [managing user data in Supabase](https://supabase.com/docs/guides/auth/managing-user-data). - -export const Page = ({ children }) => - -export default Page diff --git a/apps/docs/pages/guides/integrations/zuplo.mdx b/apps/docs/pages/guides/integrations/zuplo.mdx deleted file mode 100644 index c66860ca73b..00000000000 --- a/apps/docs/pages/guides/integrations/zuplo.mdx +++ /dev/null @@ -1,241 +0,0 @@ -import Layout from '~/layouts/DefaultGuideLayout' - -export const meta = { - id: 'zuplo', - title: 'Zuplo', - description: 'Building a public API backed by Supabase.', -} - -[Zuplo](https://zuplo.com) is a fully-managed API gateway that offers the easiest way to securely and safely share your API. In this guide we look at how you can combine Zuplo and Supabase to create a public API with rate-limiting, a self-serve developer portal, and API-key authentication. There is an [accompanying video for this article](https://www.youtube.com/watch?v=GJSkbxMnWxE). - -![zuplo layout](/docs/img/guides/integrations/zuplo/arch.png) - -In this example we're going to work with a simple table that allows people to read and write entries to a Supabase table that contains some reviews of skis. Because this is an API for developers, we have to assume that they may be calling it from another backend service and can't login as a user using the standard Supabase method. In this scenario, API keys are often a better choice - see [Wait, you're not using API keys?](https://zuplo.com/blog/2022/05/03/you-should-be-using-api-keys/). - -We'll allow people, with a valid API key, to read data from the ski results table and to create new records. Hopefully it's obvious that there are many ways that you can extend this example to add more behavior like roles based access, with custom policies, custom handlers and more. - -## Setting up Supabase - -If you haven't already, create a new project in Supabase and create a table called ski-reviews with the following columns: - -- id (int8) -- created_at (timestamptz) -- make (text) -- model (text) -- year (int8) -- rating (int2) -- author (text) - -Manually enter a couple of rows of data, so that we have something to read from the DB. - -## The `Get all` reviews route in Zuplo - -Login to Zuplo at [portal.zuplo.com](https://portal.zuplo.com) and create a new project in Zuplo - I went with `supabase-ski-reviews`. - -Select the **File** tab and choose **Routes**. Add your first route with the following settings: - -- method: `GET` -- path: `/reviews` -- summary: `Get all reviews` -- version: `v1` -- CORS: `Anything goes` - -And in the request handler section, paste the `READ ALL ROWS` URL of your Supabase backend (you can get to this in the **API docs** section of Supabase) - -- URL Rewrite: `https://YOUR_SUPABASE_URL.supabase.co/rest/v1/ski-reviews?select=*` -- Forward Search: `unchecked` - -In order to call the Supabase backend I need to add some authentication headers to the outgoing request. - -Expand the **Policies** section of your route. Click **Add policy** on the **Request** pipeline. - -We don't want to forward any headers that the client sends us to Supabase, so find the **Clear Headers Policy** and add that to your inbound pipeline. Note, that we will allow the `content-type` header to flow through, so this should be your policy config. - -```json -{ - "export": "ClearHeadersInboundPolicy", - "module": "$import(@zuplo/runtime)", - "options": { - "exclude": ["content-type"] - } -} -``` - -Next, we need to add the credentials to the outgoing request. We'll need to get the JWT token from supabase - you'll find it in **Settings** > **API** as shown below: - -![secret_role jwt](/docs/img/guides/integrations/zuplo/secret-role.png) - -Once you've got your service_role JWT, click **Add Policy** again on the **Request** pipeline and choose the **Add/Set Headers Policy** and configure it as follows: - -```json -{ - "export": "SetHeadersInboundPolicy", - "module": "$import(@zuplo/runtime)", - "options": { - "headers": [ - { - "name": "apikey", - "value": "$env(SUPABASE_API_KEY)", - "overwrite": true - }, - { - "name": "authorization", - "value": "$env(SUPABASE_AUTHZ_HEADER)", - "overwrite": true - } - ] - } -} -``` - -Save your changes. - -Next, create two secret [environment variables](https://zuplo.com/docs/deployments/environment-variables) as follows: - -- SUPABASE_API_KEY: `"YOUR_SUPABASE_SECRET_ROLE_JWT"` -- SUPABASE_AUTHZ_HEADER: `"Bearer YOUR_SUPABASE_SECRET_ROLE_JWT"` - -Obviously, in both instances replace `YOUR_SUPABASE_SECRET_ROLE_JWT` with your service_role JWT from Supabase. - -You are now ready to invoke your API gateway and see data flow through from your Supabase backend! - -Click on the **open in browser** button shown below and you should see the JSON, flowing from Supabase in your browser 👏. - -![open in browser](/docs/img/guides/integrations/zuplo/open-in-browser.png) - -## Adding authentication - -At this point, that route is wide open to the world so we need to secure it. We'll do this using API keys. You can follow this guide [Add API key Authentication](https://zuplo.com/docs/quickstarts/add-api-key-auth). Be sure to drag the API Key authentication policy to the very top of your **Request** pipeline. Come back here when you're done. - -Welcome back! You've now learned how to secure your API with API-Keys. - -## Adding a Create route - -Next we'll add a route that allows somebody to create a review. Add another route with the following settings - -- method: `POST` -- path: `/reviews` -- summary: `Create a new review` -- version: `v1` -- CORS: `Anything goes` - -And the request handler as follows: - -- URL Rewrite: `https://YOUR_SUPABASE_URL.supabase.co/rest/v1/ski-reviews` -- Forward Search: `unchecked` - -Expand the policies section and add the same policies (note you can reuse policies by picking from the existing policies at the top of the library) - -![existing policies](/docs/img/guides/integrations/zuplo/existing-policies.png) - -- api-key-auth-inbound -- clear-headers-inbound -- set-headers-inbound - -Now your **create** route is secured and will automatically set the right headers before calling Supabase. That was easy. - -You can test this out by using the **API Test Console** to invoke your new endpoint. Go to the **API Test Console** and create a new test called `create-review.json`. - -- Method: `POST` -- Path: `/v1/reviews` -- Headers: - - `content-type`: `application/json` - - `authorization`: `Bearer YOUR_ZUPLO_API_KEY` -- Body: - -```json -{ - "make": "Rossignol", - "model": "Soul HD7", - "rating": 5, - "year": 2019 -} -``` - -![Test console](/docs/img/guides/integrations/zuplo/test-console.png) - -If you invoke your API by clicking `Test` you should see that you get a **201 Created** - congratulations! - -## Add validation to your post - -To make your API more usable and more secure it is good practice to validate incoming requests. In this case we will add a JSON Schema document and use it to validate the incoming body to our POST. - -Create a new schema document called `new-review.json`. - -![new schema](/docs/img/guides/integrations/zuplo/new-schema.png) - -This example fits the ski-reviews table we described above - -```json -{ - "$id": "http://example.com/example.json", - "type": "object", - "default": {}, - "title": "Root Schema", - "required": ["make", "model", "rating", "year"], - "additionalProperties": false, - "properties": { - "make": { - "type": "string", - "default": "", - "title": "The make Schema", - "examples": ["DPS"] - }, - "model": { - "type": "string", - "default": "", - "title": "The model Schema", - "examples": ["Pagoda"] - }, - "rating": { - "type": "integer", - "default": 0, - "title": "The rating Schema", - "examples": [5] - }, - "year": { - "type": "integer", - "default": 0, - "title": "The year Schema", - "examples": [2018] - } - }, - "examples": [ - { - "make": "DPS", - "model": "Pagoda", - "rating": 5, - "year": 2018, - "author": "Josh" - } - ] -} -``` - -Now add a new policy to **request** pipeline for your `Create new review` route. Choose the **JSON Body Validation** policy and configure it to use your newly created JSON schema document: - -```json -{ - "export": "ValidateJsonSchemaInbound", - "module": "$import(@zuplo/runtime)", - "options": { - "validator": "$import(./schemas/new-review.json)" - } -} -``` - -This policy can be dragged to the first position in your pipeline. - -Now to test this is working, go back to your API test console and change the body of your `create-review.json` test to be invalid (add a new property for example). You should find that you get a `400 Bad Request` response. - -![400 Bad Request](/docs/img/guides/integrations/zuplo/400-bad.png) - -Finally, lean back and marvel at your beautiful Developer Portal that took almost zero effort to get this far, wow! Hopefully you already found the link for this when adding API key support :) - -![Developer Portal](/docs/img/guides/integrations/zuplo/dev-portal.png) - -Zuplo can also be used to handle Supabase JWT tokens for any API, learn more at [API Authentication with Supabase JWT Tokens](https://zuplo.com/blog/2022/11/15/api-authentication-with-supabase-jwt) - -export const Page = ({ children }) => - -export default Page diff --git a/apps/docs/pages/guides/platform.mdx b/apps/docs/pages/guides/platform.mdx index 7a38fa2b167..9740bc439b6 100644 --- a/apps/docs/pages/guides/platform.mdx +++ b/apps/docs/pages/guides/platform.mdx @@ -9,7 +9,7 @@ export const meta = { Supabase is a hosted platform which makes it very simple to get started without needing to manage any infrastructure. -Visit [app.supabase.com](https://app.supabase.com) and sign in to start creating projects. +Visit [supabase.com/dashboard](https://supabase.com/dashboard) and sign in to start creating projects. ## Projects diff --git a/apps/docs/pages/guides/platform/access-control.mdx b/apps/docs/pages/guides/platform/access-control.mdx index fd33555ad4e..9ec8c238436 100644 --- a/apps/docs/pages/guides/platform/access-control.mdx +++ b/apps/docs/pages/guides/platform/access-control.mdx @@ -22,12 +22,12 @@ Create a separate organization if you need to restrict access to certain project ## Manage team members To invite others to collaborate, visit your organization's team settings in the -[Dashboard](https://app.supabase.com/projects) to send an invite link to +[Dashboard](https://supabase.com/dashboard/projects) to send an invite link to another user's email. The invite expires after 24 hours. ### Transferring ownership of an organization -Each Supabase organization can have one or more owners. If you no longer want be an owner of an organization, click **Leave team** in the members view (`https://app.supabase.com/org//settings#team`) of your organization. +Each Supabase organization can have one or more owners. If you no longer want be an owner of an organization, click **Leave team** in the members view (`https://supabase.com/dashboard/org//settings#team`) of your organization. However, you can only leave an organization when there is _at least one other owner_. If you are transferring ownership of your organization to someone else, you will need to invite the new member with the **Owner** role. You can leave the organization after they've accepted the invitation. diff --git a/apps/docs/pages/guides/platform/backups.mdx b/apps/docs/pages/guides/platform/backups.mdx index 21aef92557b..65f60334402 100644 --- a/apps/docs/pages/guides/platform/backups.mdx +++ b/apps/docs/pages/guides/platform/backups.mdx @@ -1,5 +1,5 @@ import Layout from '~/layouts/DefaultGuideLayout' -import Admonition from '~/components/Admonition' +import { Admonition } from 'ui' export const meta = { title: 'Database Backups', @@ -43,7 +43,7 @@ The PostgreSQL utility [pg_dumpall](https://www.postgresql.org/docs/current/app- ![Scheduled backups dashboard](/docs/img/backups-daily-dashboard.png) -You can access daily backups in the [Scheduled backups](https://app.supabase.com/project/_/database/backups/scheduled) settings in the Dashboard. Pro plan projects can access the last 7 days' worth of daily backups while Enterprise plan projects can access up to 30 days' worth of daily backups. Users can restore their project to any one of the backups or download them as a zipped SQL file. +You can access daily backups in the [Scheduled backups](https://supabase.com/dashboard/project/_/database/backups/scheduled) settings in the Dashboard. Pro plan projects can access the last 7 days' worth of daily backups while Enterprise plan projects can access up to 30 days' worth of daily backups. Users can restore their project to any one of the backups or download them as a zipped SQL file. ### Restoration Process [#daily-backups-restoration-process] @@ -77,7 +77,7 @@ By default, WAL files are backed up at two minute intervals. If these files cros ![PITR dashboard](/docs/img/backups-pitr-dashboard.png) -You can access PITR in the [Point in Time](https://app.supabase.com/project/_/database/backups/pitr) settings in the Dashboard. The recovery period of a project is indicated by the earliest and latest points of recoveries displayed in your preferred timezone. If need be, the maximum amount of this recovery period can be modified accordingly. +You can access PITR in the [Point in Time](https://supabase.com/dashboard/project/_/database/backups/pitr) settings in the Dashboard. The recovery period of a project is indicated by the earliest and latest points of recoveries displayed in your preferred timezone. If need be, the maximum amount of this recovery period can be modified accordingly. Note that the latest restore point of the project could be significantly far from the current time. This occurs when there has not been any recent activity in the database, and therefore no WAL file backups have been made recently. This is perfectly fine as the state of the database at the latest point of recovery would still be indicative of the state of the database at the current time given that no transactions have been made in between. diff --git a/apps/docs/pages/guides/platform/compute-add-ons.mdx b/apps/docs/pages/guides/platform/compute-add-ons.mdx index 1ef540c6ee7..d3bd4ad528a 100644 --- a/apps/docs/pages/guides/platform/compute-add-ons.mdx +++ b/apps/docs/pages/guides/platform/compute-add-ons.mdx @@ -31,7 +31,7 @@ All Postgres instances on Supabase are dedicated applications running inside ded ## Compute upgrades [#upgrades] -When considering compute upgrades, assess whether your bottlenecks are hardware-constrained or software-constrained. For example, you may want to look into [optimizing the number of connections](/docs/guides/platform/performance#optimizing-the-number-of-connections) or [examining query performance](/docs/guides/platform/performance#examining-query-performance). When you're happy with your Postgres instance's performance, then you can focus on additional compute resources. For example, you can load test your application in staging to understand your compute requirements. You can also start out on a smaller tier, [create a report](https://app.supabase.com/project/_/reports) in the Dashboard to monitor your CPU utilization, and upgrade later as needed +When considering compute upgrades, assess whether your bottlenecks are hardware-constrained or software-constrained. For example, you may want to look into [optimizing the number of connections](/docs/guides/platform/performance#optimizing-the-number-of-connections) or [examining query performance](/docs/guides/platform/performance#examining-query-performance). When you're happy with your Postgres instance's performance, then you can focus on additional compute resources. For example, you can load test your application in staging to understand your compute requirements. You can also start out on a smaller tier, [create a report](https://supabase.com/dashboard/project/_/reports) in the Dashboard to monitor your CPU utilization, and upgrade later as needed ## Disk IO @@ -58,7 +58,7 @@ Smaller compute instances can burst up to their largest throughput and IOPS for If you need consistent disk performance, choose the 4XL or larger compute add-on which has the same baseline and maximum disk throughput and IOPS. -If you're unsure of how much throughput or IOPS your application requires, you can load test your project and inspect these [metrics in the Dashboard](https://app.supabase.com/project/_/reports). If the `Disk IO % consumed` stat is more than 1%, it indicates that your workload has burst beyond the baseline IO throughput during the day. If this metric goes to 100%, the workload has used up all available disk budget and will revert to baseline performance. Projects that use any disk budget are good candidates for upgrading to a larger compute add-on with higher baseline throughput. +If you're unsure of how much throughput or IOPS your application requires, you can load test your project and inspect these [metrics in the Dashboard](https://supabase.com/dashboard/project/_/reports). If the `Disk IO % consumed` stat is more than 1%, it indicates that your workload has burst beyond the baseline IO throughput during the day. If this metric goes to 100%, the workload has used up all available disk budget and will revert to baseline performance. Projects that use any disk budget are good candidates for upgrading to a larger compute add-on with higher baseline throughput. export const Page = ({ children }) => diff --git a/apps/docs/pages/guides/platform/custom-domains.mdx b/apps/docs/pages/guides/platform/custom-domains.mdx index f968556bde6..9d751bca152 100644 --- a/apps/docs/pages/guides/platform/custom-domains.mdx +++ b/apps/docs/pages/guides/platform/custom-domains.mdx @@ -7,7 +7,7 @@ export const meta = { video: 'https://www.youtube.com/v/6rcGnW_Mh-0', } -Custom domains allow you to present a branded experience to your users. Custom domains are available as a [add-on for projects on a paid plan](https://app.supabase.com/project/_/settings/billing/subscription?panel=customDomain). Setting up a custom domain requires [Owner or Admin permissions](/docs/guides/platform/access-control#manage-team-members) for the project. Currently, you must use a subdomain (e.g., `api.example.com`, rather than `example.com`) for the purposes of this guide. +Custom domains allow you to present a branded experience to your users. Custom domains are available as a [add-on for projects on a paid plan](https://supabase.com/dashboard/project/_/settings/billing/subscription?panel=customDomain). Setting up a custom domain requires [Owner or Admin permissions](/docs/guides/platform/access-control#manage-team-members) for the project. Currently, you must use a subdomain (e.g., `api.example.com`, rather than `example.com`) for the purposes of this guide.
    -
    - -## Install `supabase-js` Client - -```bash -npm install @supabase/supabase-js -``` - -## Cursor Positions - -[Broadcast](/docs/guides/realtime/broadcast) allows a client to send messages and multiple clients to receive the messages. The broadcasted messages are ephemeral. They are not persisted to the database and are directly relayed through the Realtime servers. This is ideal for sending information like cursor positions where minimal latency is important, but persisting them is not. - -In [multiplayer.dev](https://multiplayer.dev), client's cursor positions are sent to other clients in the room. However, cursor positions will be randomly generated for this example. - -You need to get the public `anon` access token from your project's [API settings](https://app.supabase.com/project/_/settings/api). Then you can set up the Supabase client and start sending a client's cursor positions to other clients in channel `room1`: - -```js -const { createClient } = require('@supabase/supabase-js') - -const supabase = createClient('https://your-project-ref.supabase.co', 'anon-key', { - realtime: { - params: { - eventsPerSecond: 10, - }, - }, -}) - -// Channel name can be any string. -// Create channels with the same name for both the broadcasting and receiving clients. -const channel = supabase.channel('room1') - -// Subscribe registers your client with the server -channel.subscribe((status) => { - if (status === 'SUBSCRIBED') { - // now you can start broadcasting cursor positions - setInterval(() => { - channel.send({ - type: 'broadcast', - event: 'cursor-pos', - payload: { x: Math.random(), y: Math.random() }, - }) - console.log(status) - }, 100) - } -}) -``` - - - -JavaScript client has a default rate limit of 1 Realtime event every 100 milliseconds that's configured by `eventsPerSecond`. - - - -Another client can subscribe to channel `room1` and receive cursor positions: - -```js -// Supabase client setup - -// Listen to broadcast messages. -supabase - .channel('room1') - .on('broadcast', { event: 'cursor-pos' }, (payload) => console.log(payload)) - .subscribe((status) => { - if (status === 'SUBSCRIBED') { - // your callback function will now be called with the messages broadcast by the other client - } - }) -``` - - - -`type` must be `broadcast` and the `event` must match for clients subscribed to the channel. - - - -## Roundtrip Latency - -You can also configure the channel so that the server must return an acknowledgement that it received the `broadcast` message. This is useful if you want to measure the roundtrip latency: - -```js -// Supabase client setup - -const channel = supabase.channel('calc-latency', { - config: { - broadcast: { ack: true }, - }, -}) - -channel.subscribe(async (status) => { - if (status === 'SUBSCRIBED') { - const begin = performance.now() - - await channel.send({ - type: 'broadcast', - event: 'latency', - payload: {}, - }) - - const end = performance.now() - - console.log(`Latency is ${end - begin} milliseconds`) - } -}) -``` - -## Track and Display Which Users Are Online - -[Presence](/docs/guides/realtime/presence) stores and synchronize shared state across clients. The `sync` event is triggered whenever the shared state changes. The `join` event is triggered when new clients join the channel and `leave` event is triggered when clients leave. - -Each client can use the channel's `track` method to store an object in shared state. Each client can only track one object, and if `track` is called again by the same client, then the new object overwrites the previously tracked object in the shared state. You can use one client to track and display users who are online: - -```js -// Supabase client setup - -const channel = supabase.channel('online-users', { - config: { - presence: { - key: 'user1', - }, - }, -}) - -channel.on('presence', { event: 'sync' }, () => { - console.log('Online users: ', channel.presenceState()) -}) - -channel.on('presence', { event: 'join' }, ({ newPresences }) => { - console.log('New users have joined: ', newPresences) -}) - -channel.on('presence', { event: 'leave' }, ({ leftPresences }) => { - console.log('Users have left: ', leftPresences) -}) - -channel.subscribe(async (status) => { - if (status === 'SUBSCRIBED') { - const status = await channel.track({ online_at: new Date().toISOString() }) - console.log(status) - } -}) -``` - -Then you can use another client to add another user to the channel's Presence state: - -```js -// Supabase client setup - -const channel = supabase.channel('online-users', { - config: { - presence: { - key: 'user2', - }, - }, -}) - -// Presence event handlers setup - -channel.subscribe(async (status) => { - if (status === 'SUBSCRIBED') { - const status = await channel.track({ online_at: new Date().toISOString() }) - console.log(status) - } -}) -``` - -If a channel is set up without a presence key, the server generates a random UUID. `type` must be `presence` and `event` must be either `sync`, `join`, or `leave`. - -## Insert and Receive Persisted Messages - -[Postgres Changes](/docs/guides/realtime#postgres-changes) enables your client to insert, update, or delete database records and send the changes to clients. Create a `messages` table to keep track of messages created by users in specific rooms: - -```sql -create table messages ( - id serial primary key, - message text, - user_id text, - room_id text, - created_at timestamptz default now() -) - -alter table messages enable row level security; - -create policy "anon_ins_policy" -ON messages -for insert -to anon -with check (true); - -create policy "anon_sel_policy" -ON messages -for select -to anon -using (true); -``` - -If it doesn't already exist, create a `supabase_realtime` publication and add `messages` table to the publication: - -```sql -begin; - -- remove the supabase_realtime publication - drop publication if exists supabase_realtime; - - -- re-create the supabase_realtime publication with no tables and only for insert - create publication supabase_realtime with (publish = 'insert'); -commit; - --- add a table to the publication -alter publication supabase_realtime add table messages; -``` - -You can then have a client listen for changes on the `messages` table for a specific room and send and receive persisted messages: - -```js -// Supabase client setup - -const channel = supabase.channel('db-messages') - -const roomId = 'room1' -const userId = 'user1' - -channel.on( - 'postgres_changes', - { - event: 'INSERT', - schema: 'public', - table: 'messages', - filter: `room_id=eq.${roomId}`, - }, - (payload) => console.log(payload) -) - -channel.subscribe(async (status) => { - if (status === 'SUBSCRIBED') { - const res = await supabase.from('messages').insert({ - room_id: roomId, - user_id: userId, - message: 'Welcome to Realtime!', - }) - console.log(res) - } -}) -``` - -export const Page = ({ children }) => - -export default Page diff --git a/apps/docs/pages/guides/realtime/quotas.mdx b/apps/docs/pages/guides/realtime/quotas.mdx index dc083129c4f..8f9ec1e6121 100644 --- a/apps/docs/pages/guides/realtime/quotas.mdx +++ b/apps/docs/pages/guides/realtime/quotas.mdx @@ -9,7 +9,7 @@ export const meta = { -Upgrade your plan to increase your quotas. Without a spend cap, or on an Enterprise plan, some quotas are still in place to protect budgets. All quotas are configurable per project. [Contact support](https://app.supabase.com/support/new) if you need your quotas increased. Our cluster supports millions of concurrent connections and message throughput for production workloads. +Upgrade your plan to increase your quotas. Without a spend cap, or on an Enterprise plan, some quotas are still in place to protect budgets. All quotas are configurable per project. [Contact support](https://supabase.com/dashboard/support/new) if you need your quotas increased. Our cluster supports millions of concurrent connections and message throughput for production workloads. @@ -26,13 +26,13 @@ Upgrade your plan to increase your quotas. Without a spend cap, or on an Enterpr | **Broadcast payload size KB** | 256 | 3,000 | 3,000 | 3,000 | 3,000 | | **Postgres change payload size KB ([**read more**](#postgres-changes-payload-quota))** | 1,024 | 1,024 | 1,024 | 1,024 | 1,024 | -Beyond the Free and Pro plan you can customize your quotas by [contacting support](https://app.supabase.com/support/new). +Beyond the Free and Pro plan you can customize your quotas by [contacting support](https://supabase.com/dashboard/support/new). ## Client-Side Limiting Some basic WebSocket message rate limiting is implemented client-side. -For example, the [multiplayer.dev demo](/docs/guides/realtime/quickstart#cursor-positions) instantiates the Supabase client with an `eventsPerSecond` parameter. +For example, the [multiplayer.dev](https://multiplayer.dev) instantiates the Supabase client with an `eventsPerSecond` parameter. ## Quota Errors @@ -46,7 +46,7 @@ Use the [Realtime Inspector](https://realtime.supabase.com/inspector/new) to rep ### Backend Logs -If your project is being limited by a quota, check your [Realtime logs](https://app.supabase.com/project/_/database/realtime-logs). +If your project is being limited by a quota, check your [Realtime logs](https://supabase.com/dashboard/project/_/database/realtime-logs). ### WebSocket Errors diff --git a/apps/docs/pages/guides/resources/examples.mdx b/apps/docs/pages/guides/resources/examples.mdx index a414b43b63e..32c45d50c01 100644 --- a/apps/docs/pages/guides/resources/examples.mdx +++ b/apps/docs/pages/guides/resources/examples.mdx @@ -59,7 +59,7 @@ By [Fireship](https://www.youtube.com/watch?v=WiwfiVdfRIc). Build a basic Todo List with Supabase and your favorite frontend framework: - [Expo Todo List.](https://github.com/supabase/examples/tree/main/supabase-js-v1/todo-list/expo-todo-list) -- [Next.js Todo List.](https://github.com/supabase/examples/tree/main/supabase-js-v1/todo-list/nextjs-todo-list) +- [Next.js Todo List.](https://github.com/supabase/supabase/tree/master/examples/todo-list/nextjs-todo-list) - [React Todo List.](https://github.com/supabase/examples/tree/main/supabase-js-v1/todo-list/react-todo-list) - [Svelte Todo List.](https://github.com/supabase/supabase/tree/master/examples/todo-list/sveltejs-todo-list) - [Vue 3 Todo List (Typescript).](https://github.com/supabase/examples/tree/main/supabase-js-v1/todo-list/vue3-ts-todo-list) @@ -69,7 +69,7 @@ Build a basic Todo List with Supabase and your favorite frontend framework: ### Auth examples - [Supabase Auth with vanilla JavaScript.](https://github.com/supabase/examples/tree/main/supabase-js-v1/auth/javascript-auth). Use Supabase without any frontend frameworks. -- [Supabase Auth with Next.js](https://github.com/supabase/supabase/tree/master/examples/user-management/nextjs-ts-user-management). +- [Supabase Auth with Next.js](https://github.com/supabase/supabase/tree/master/examples/user-management/nextjs-user-management). - [Supabase Auth with RedwoodJS.](https://redwood-playground-auth.netlify.app/supabase) Try out Supabase authentication in the [RedwoodJS](https://redwoodjs.com) Authentication Playground complete with OAuth support and code samples. ### Collaborative @@ -110,6 +110,7 @@ Build a basic Todo List with Supabase and your favorite frontend framework: - Flutter Supabase Authentication [Blog](https://www.sandromaglione.com/2021/04/24/flutter-supabase-authentication/) - Supabase in 6 minutes [Video](https://www.youtube.com/watch?v=c8DNV9yl0mg) - Supabase React Auth UI Tutorial [Video](https://youtu.be/6ch1PtIqCUw) +- Supabase React File Upload Tutorial [Video](https://youtu.be/HvOvdD2nX1k) - Let's build SupaAuth - Build an Authentication System using Supabase, Next.js and Typescript [6-part Blog Series](https://aalam.in/blog/supabase-auth-intro-setup-next) - In-depth self-hosting guide using Nginx [Blog](https://dev.to/chronsyn/self-hosting-with-supabase-1aii) - Build an Email and Social Auth for Next JS with Supabase, Tailwind CSS 3.0 and TypeScript [Blog](https://creativedesignsguru.com/next-js-supabase-auth/) diff --git a/apps/docs/pages/guides/resources/migrating-to-supabase/amazon-rds.mdx b/apps/docs/pages/guides/resources/migrating-to-supabase/amazon-rds.mdx index b81e0266558..3a2f4315d65 100644 --- a/apps/docs/pages/guides/resources/migrating-to-supabase/amazon-rds.mdx +++ b/apps/docs/pages/guides/resources/migrating-to-supabase/amazon-rds.mdx @@ -23,10 +23,10 @@ Supabase's core is Postgres, enabling the use of row-level security and providin ## Retrieve your Supabase Host [#retrieve-supabase-host] -1. If you're new to Supabase, [create a project](https://app.supabase.com). -1. Go to the [Database settings](https://app.supabase.com/project/_/settings/database) for your project in the Supabase Dashboard. +1. If you're new to Supabase, [create a project](https://supabase.com/dashboard). +1. Go to the [Database settings](https://supabase.com/dashboard/project/_/settings/database) for your project in the Supabase Dashboard. 1. Under **Connection Info**, note your Host (`$SUPABASE_HOST`). -1. Save your password or [reset it](https://app.supabase.com/project/_/settings/database) if you forgot it. +1. Save your password or [reset it](https://supabase.com/dashboard/project/_/settings/database) if you forgot it. ![Finding Supabase host address](/docs/img/guides/resources/migrating-to-supabase/amazon-rds/supabase_dashboard.png) @@ -95,7 +95,7 @@ pgloader config.load -- If you plan on migrating a database larger than 6 GB, we recommend [contacting support](https://app.supabase.com/support/new) to ensure that you'll have the proper disk size pre-provisioned. You can read more about how the disk is managed on Supabase on the [Database usage](/docs/guides/platform/database-size#disk-management). +- If you plan on migrating a database larger than 6 GB, we recommend [contacting support](https://supabase.com/dashboard/support/new) to ensure that you'll have the proper disk size pre-provisioned. You can read more about how the disk is managed on Supabase on the [Database usage](/docs/guides/platform/database-size#disk-management). - We also recommend upgrading to at least a [Large instance](/docs/guides/platform/compute-add-ons) for the migration (you can downgrade later) to avoid running out of IO-Budget. diff --git a/apps/docs/pages/guides/resources/migrating-to-supabase/firebase-auth.mdx b/apps/docs/pages/guides/resources/migrating-to-supabase/firebase-auth.mdx index 2a4aa49db48..9b898785f38 100644 --- a/apps/docs/pages/guides/resources/migrating-to-supabase/firebase-auth.mdx +++ b/apps/docs/pages/guides/resources/migrating-to-supabase/firebase-auth.mdx @@ -32,7 +32,7 @@ git clone https://github.com/supabase-community/firebase-to-supabase.git } ``` -1. Go to the [Database settings](https://app.supabase.com/project/_/settings/database) for your project in the Supabase Dashboard. +1. Go to the [Database settings](https://supabase.com/dashboard/project/_/settings/database) for your project in the Supabase Dashboard. 1. Under **Connection Info**, copy the Host string and replace the entry in your `supabase-service.json` file. 1. Enter the password you used when you created your Supabase project in the `password` entry in the `supabase-service.json` file. diff --git a/apps/docs/pages/guides/resources/migrating-to-supabase/firebase-storage.mdx b/apps/docs/pages/guides/resources/migrating-to-supabase/firebase-storage.mdx index 1516dd2537b..6159663db96 100644 --- a/apps/docs/pages/guides/resources/migrating-to-supabase/firebase-storage.mdx +++ b/apps/docs/pages/guides/resources/migrating-to-supabase/firebase-storage.mdx @@ -21,7 +21,7 @@ git clone https://github.com/supabase-community/firebase-to-supabase.git ``` 1. In the `/storage` directory, rename [supabase-keys-sample.js](https://github.com/supabase-community/firebase-to-supabase/blob/main/storage/supabase-keys-sample.js) to `supabase-keys.js`. -1. Go to your Supabase project's [API settings](https://app.supabase.com/project/_/settings/api) in the Dashboard. +1. Go to your Supabase project's [API settings](https://supabase.com/dashboard/project/_/settings/api) in the Dashboard. 1. Copy the **Project URL** and update the `SUPABASE_URL` value in `supabase-keys.js`. 1. Under **Project API keys**, copy the **service_role** key and update the `SUPABASE_KEY` value in `supabase-keys.js`. @@ -57,7 +57,7 @@ To process in batches using multiple command-line executions, you must use the s -If the bucket doesn't exist, it's created as a `non-public` bucket. You must set permissions on this new bucket in the [Supabase Dashboard](https://app.supabase.com/project/_/storage/buckets) before users can download any files. +If the bucket doesn't exist, it's created as a `non-public` bucket. You must set permissions on this new bucket in the [Supabase Dashboard](https://supabase.com/dashboard/project/_/storage/buckets) before users can download any files. diff --git a/apps/docs/pages/guides/resources/migrating-to-supabase/firestore-data.mdx b/apps/docs/pages/guides/resources/migrating-to-supabase/firestore-data.mdx index 24dd49fa488..c8d30b87b40 100644 --- a/apps/docs/pages/guides/resources/migrating-to-supabase/firestore-data.mdx +++ b/apps/docs/pages/guides/resources/migrating-to-supabase/firestore-data.mdx @@ -31,7 +31,7 @@ git clone https://github.com/supabase-community/firebase-to-supabase.git } ``` -1. Go to the [Database settings](https://app.supabase.com/project/_/settings/database) for your project in the Supabase Dashboard. +1. Go to the [Database settings](https://supabase.com/dashboard/project/_/settings/database) for your project in the Supabase Dashboard. 1. Under **Connection Info**, copy the Host string and replace the entry in your `supabase-service.json` file. 1. Enter the password you used when you created your Supabase project in the `password` entry in the `supabase-service.json` file. diff --git a/apps/docs/pages/guides/resources/migrating-to-supabase/heroku.mdx b/apps/docs/pages/guides/resources/migrating-to-supabase/heroku.mdx index f2ed7478c6d..930ded5270a 100644 --- a/apps/docs/pages/guides/resources/migrating-to-supabase/heroku.mdx +++ b/apps/docs/pages/guides/resources/migrating-to-supabase/heroku.mdx @@ -36,8 +36,8 @@ Alternatively, use the [Heroku to Supabase migration tool](https://migrate.supab ## Retrieve your Supabase Host [#retrieve-supabase-host] -1. If you're new to Supabase, [create a project](https://app.supabase.com). -1. Go to the [Database settings](https://app.supabase.com/project/_/settings/database) for your project in the Supabase Dashboard. +1. If you're new to Supabase, [create a project](https://supabase.com/dashboard). +1. Go to the [Database settings](https://supabase.com/dashboard/project/_/settings/database) for your project in the Supabase Dashboard. 1. Under **Connection Info**, note your Host (`$SUPABASE_HOST`). ## Export your Heroku database to a file [#export-heroku-database] @@ -69,7 +69,7 @@ Run `pg_dump --help` for a full list of options. -- If you plan on migrating a database larger than 6 GB, we recommend [contacting support](https://app.supabase.com/support/new) to ensure that you'll have the proper disk size pre-provisioned. You can read more about how the disk is managed on Supabase on the [Database usage](/docs/guides/platform/database-size#disk-management). +- If you plan on migrating a database larger than 6 GB, we recommend [contacting support](https://supabase.com/dashboard/support/new) to ensure that you'll have the proper disk size pre-provisioned. You can read more about how the disk is managed on Supabase on the [Database usage](/docs/guides/platform/database-size#disk-management). - We also recommend upgrading to at least a [Large instance](/docs/guides/platform/compute-add-ons) for the migration (you can downgrade later) to avoid running out of IO-Budget. diff --git a/apps/docs/pages/guides/resources/migrating-to-supabase/render.mdx b/apps/docs/pages/guides/resources/migrating-to-supabase/render.mdx index 6d0eae2c6de..2eb6dd79214 100644 --- a/apps/docs/pages/guides/resources/migrating-to-supabase/render.mdx +++ b/apps/docs/pages/guides/resources/migrating-to-supabase/render.mdx @@ -27,10 +27,10 @@ Example: ## Retrieve your Supabase Host [#retrieve-supabase-host] -1. If you're new to Supabase, [create a project](https://app.supabase.com). -1. Go to the [Database settings](https://app.supabase.com/project/_/settings/database) for your project in the Supabase Dashboard. +1. If you're new to Supabase, [create a project](https://supabase.com/dashboard). +1. Go to the [Database settings](https://supabase.com/dashboard/project/_/settings/database) for your project in the Supabase Dashboard. 1. Under **Connection Info**, note your Host (`$SUPABASE_HOST`). -1. Save your password or [reset it](https://app.supabase.com/project/_/settings/database) if you forgot it. +1. Save your password or [reset it](https://supabase.com/dashboard/project/_/settings/database) if you forgot it. ![Finding Supabase host address](/docs/img/guides/resources/migrating-to-supabase/render/supabase_dashboard.png) @@ -85,7 +85,7 @@ Run `pg_dump --help` for a full list of options. -- If you plan on migrating a database larger than 6 GB, we recommend [contacting support](https://app.supabase.com/support/new) to ensure that you'll have the proper disk size pre-provisioned. You can read more about how the disk is managed on Supabase on the [Database usage](/docs/guides/platform/database-size#disk-management). +- If you plan on migrating a database larger than 6 GB, we recommend [contacting support](https://supabase.com/dashboard/support/new) to ensure that you'll have the proper disk size pre-provisioned. You can read more about how the disk is managed on Supabase on the [Database usage](/docs/guides/platform/database-size#disk-management). - We also recommend upgrading to at least a [Large instance](/docs/guides/platform/compute-add-ons) for the migration (you can downgrade later) to avoid running out of IO-Budget. diff --git a/apps/docs/pages/guides/self-hosting.mdx b/apps/docs/pages/guides/self-hosting.mdx index 4a6fdd2919d..aadf096723b 100644 --- a/apps/docs/pages/guides/self-hosting.mdx +++ b/apps/docs/pages/guides/self-hosting.mdx @@ -7,7 +7,7 @@ export const meta = { There are several ways to use Supabase: -- [Supabase Cloud](https://app.supabase.com): you don't need to deploy anything. We will manage and scale your infrastructure. +- [Supabase Cloud](https://supabase.com/dashboard): you don't need to deploy anything. We will manage and scale your infrastructure. - [Docker](/docs/guides/self-hosting/docker): deploy to your own infrastructure. ### Community diff --git a/apps/docs/pages/guides/self-hosting/docker.mdx b/apps/docs/pages/guides/self-hosting/docker.mdx index f496cd10efe..756081859fa 100644 --- a/apps/docs/pages/guides/self-hosting/docker.mdx +++ b/apps/docs/pages/guides/self-hosting/docker.mdx @@ -66,6 +66,18 @@ Update the `.env` file with your own secrets. In particular, these are required: - `SITE_URL`: the base URL of your site. - `SMTP_*`: mail server credentials. You can use any SMTP server. +### Adding a firewall + +We recommend using a firewall to restrict access to your Supabase setup (something like `ufw`). One thing to highlight is that Docker usually [makes changes to your iptables](https://askubuntu.com/questions/652556/uncomplicated-firewall-ufw-is-not-blocking-anything-when-using-docker). + +To disable this behavior, you can add the following to your `/etc/docker/daemon.json` file and then run `sudo service docker restart`: + +```json +{ + "iptables": false +} +``` + ## Exposing services The services running on the machine are not exposed to the internet by default. We recommend using a reverse proxy such as [NGINX](https://www.nginx.com/) or [Caddy](https://caddyserver.com/) if you want to expose the services publicly: @@ -136,6 +148,11 @@ However, you might miss important log messages such as database errors. Configur By default, Storage backend is set to `file`, which is to use local files as the storage backend. To make it work on macOS, you need to choose `VirtioFS` as the Docker container file sharing implementation (in Docker Desktop -> Preferences -> General). +### Setting up logging with the Analytics server + +Additional configuration is required for self-hosting the Analytics server. For the full setup instructions, see [Self Hosting Analytics](https://supabase.com/docs/reference/self-hosting-analytics/introduction#getting-started). + + export const Page = ({ children }) => export default Page diff --git a/apps/docs/pages/guides/storage/cdn.mdx b/apps/docs/pages/guides/storage/cdn.mdx index 8a96b745eb2..57f39faab52 100644 --- a/apps/docs/pages/guides/storage/cdn.mdx +++ b/apps/docs/pages/guides/storage/cdn.mdx @@ -7,7 +7,7 @@ export const meta = { sidebar_label: 'CDN', } -All assets uploaded to Supabase Storage are cached on a Content Delivery Network (CDN) to improve the latency for users all around the world. CDNs are a geographically distributed set of servers or **nodes** which caches content from an **origin server**. For Supabase Storage, the origin is the storage server running in the [same region as your project](https://app.supabase.com/project/_/settings/general). Aside from performance, CDNs also help with security and availability by mitigating Distributed Denial of Service and other application attacks. +All assets uploaded to Supabase Storage are cached on a Content Delivery Network (CDN) to improve the latency for users all around the world. CDNs are a geographically distributed set of servers or **nodes** which caches content from an **origin server**. For Supabase Storage, the origin is the storage server running in the [same region as your project](https://supabase.com/dashboard/project/_/settings/general). Aside from performance, CDNs also help with security and availability by mitigating Distributed Denial of Service and other application attacks. ## Basic CDN diff --git a/apps/docs/pages/guides/storage/quickstart.mdx b/apps/docs/pages/guides/storage/quickstart.mdx index 118bd2513dd..b94c588ce4b 100644 --- a/apps/docs/pages/guides/storage/quickstart.mdx +++ b/apps/docs/pages/guides/storage/quickstart.mdx @@ -7,7 +7,7 @@ export const meta = { sidebar_label: 'Quickstart', } -This guide shows the basic functionality of Supabase Storage. Find a full [example application on GitHub](https://github.com/supabase/supabase/tree/master/examples/user-management/nextjs-ts-user-management) or deploy it with [Vercel for a preview](https://vercel.com/new/git/external?repository-url=https%3A%2F%2Fgithub.com%2Fsupabase%2Fsupabase%2Ftree%2Fmaster%2Fexamples%2Fuser-management%2Fnextjs-ts-user-management&project-name=supabase-user-management&repository-name=supabase-user-management&demo-title=Supabase%20User%20Management&demo-description=An%20example%20web%20app%20using%20Supabase%20and%20Next.js&demo-url=https%3A%2F%2Fsupabase-nextjs-ts-user-management.vercel.app&demo-image=https%3A%2F%2Fi.imgur.com%2FZ3HkQqe.png&integration-ids=oac_jUduyjQgOyzev1fjrW83NYOv&external-id=nextjs-user-management). +This guide shows the basic functionality of Supabase Storage. Find a full [example application on GitHub](https://github.com/supabase/supabase/tree/master/examples/user-management/nextjs-user-management). @@ -29,7 +29,7 @@ client libraries. Here we create a bucket called "avatars": > -1. Go to the [Storage](https://app.supabase.com/project/_/storage/buckets) page in the Dashboard. +1. Go to the [Storage](https://supabase.com/dashboard/project/_/storage/buckets) page in the Dashboard. 2. Click **New Bucket** and enter a name for the bucket. 3. Click **Create Bucket**. @@ -86,7 +86,7 @@ You can upload a file from the Dashboard, or within a browser using our JS libra > -1. Go to the [Storage](https://app.supabase.com/project/_/storage/buckets) page in the Dashboard. +1. Go to the [Storage](https://supabase.com/dashboard/project/_/storage/buckets) page in the Dashboard. 2. Select the bucket you want to upload the file to. 3. Click **Upload File**. 4. Select the file you want to upload. @@ -137,7 +137,7 @@ You can download a file from the Dashboard, or within a browser using our JS lib > -1. Go to the [Storage](https://app.supabase.com/project/_/storage/buckets) page in the Dashboard. +1. Go to the [Storage](https://supabase.com/dashboard/project/_/storage/buckets) page in the Dashboard. 2. Select the bucket that contains the file. 3. Select the file that you want to download. 4. Click **Download**. @@ -184,7 +184,7 @@ To restrict access to your files you can use either the Dashboard or SQL. > -1. Go to the [Storage](https://app.supabase.com/project/_/storage/buckets) page in the Dashboard. +1. Go to the [Storage](https://supabase.com/dashboard/project/_/storage/buckets) page in the Dashboard. 2. Click **Policies** in the sidebar. 3. Click **Add Policies** in the `OBJECTS` table to add policies for Files. You can also create policies for Buckets. 4. Choose whether you want the policy to apply to downloads (SELECT), uploads (INSERT), updates (UPDATE), or deletes (DELETE). diff --git a/apps/docs/pages/index.mdx b/apps/docs/pages/index.mdx index 31c0a5a03fe..8024c9629a4 100644 --- a/apps/docs/pages/index.mdx +++ b/apps/docs/pages/index.mdx @@ -88,7 +88,7 @@ export const meta = {
    @@ -159,7 +159,7 @@ export const meta = {

    @@ -232,7 +232,7 @@ export const products = [ }, { title: 'Edge Functions', - icon: 'functions', + icon: 'edge-functions', hasLightIcon: true, href: '/guides/functions', description: diff --git a/apps/docs/pages/learn/auth-deep-dive/auth-deep-dive-jwts.mdx b/apps/docs/pages/learn/auth-deep-dive/auth-deep-dive-jwts.mdx index d33020220ed..d621ec7ade2 100644 --- a/apps/docs/pages/learn/auth-deep-dive/auth-deep-dive-jwts.mdx +++ b/apps/docs/pages/learn/auth-deep-dive/auth-deep-dive-jwts.mdx @@ -191,7 +191,7 @@ Now that you understand what JWTs are and where they're used in Supabase, you ca - [Part Three: Policies](../../learn/auth-deep-dive/auth-policies) - [Part Four: GoTrue](../../learn/auth-deep-dive/auth-gotrue) - [Part Five: Google Oauth](../../learn/auth-deep-dive/auth-google-oauth) -- Sign up for Supabase: [app.supabase.com](https://app.supabase.com) +- Sign up for Supabase: [supabase.com/dashboard](https://supabase.com/dashboard) export const Page = ({ children }) => diff --git a/apps/docs/pages/learn/auth-deep-dive/auth-google-oauth.mdx b/apps/docs/pages/learn/auth-deep-dive/auth-google-oauth.mdx index c2b91673f74..d10f98e5d73 100644 --- a/apps/docs/pages/learn/auth-deep-dive/auth-google-oauth.mdx +++ b/apps/docs/pages/learn/auth-deep-dive/auth-google-oauth.mdx @@ -97,7 +97,7 @@ For any support please get in touch at beta at [supabase.com](https://supabase.c - Watch [Part Three: Policies](../../learn/auth-deep-dive/auth-policies) - Watch [Part Four: GoTrue](../../learn/auth-deep-dive/auth-gotrue) {/* */} -- Sign up for Supabase: [app.supabase.com](https://app.supabase.com) +- Sign up for Supabase: [supabase.com/dashboard](https://supabase.com/dashboard) export const Page = ({ children }) => diff --git a/apps/docs/pages/learn/auth-deep-dive/auth-gotrue.mdx b/apps/docs/pages/learn/auth-deep-dive/auth-gotrue.mdx index ed9dd72dec7..a9531106ed8 100644 --- a/apps/docs/pages/learn/auth-deep-dive/auth-gotrue.mdx +++ b/apps/docs/pages/learn/auth-deep-dive/auth-gotrue.mdx @@ -78,7 +78,7 @@ In the next guide we'll be looking at how to setup external OAuth providers: Wat - Watch [Part Three: Policies](../../learn/auth-deep-dive/auth-policies) {/* */} - Watch [Part Five: Google Oauth](../../learn/auth-deep-dive/auth-google-oauth) -- Sign up for Supabase: [app.supabase.com](https://app.supabase.com) +- Sign up for Supabase: [supabase.com/dashboard](https://supabase.com/dashboard) export const Page = ({ children }) => diff --git a/apps/docs/pages/learn/auth-deep-dive/auth-policies.mdx b/apps/docs/pages/learn/auth-deep-dive/auth-policies.mdx index 94fe1602450..b75e75aa2d0 100644 --- a/apps/docs/pages/learn/auth-deep-dive/auth-policies.mdx +++ b/apps/docs/pages/learn/auth-deep-dive/auth-policies.mdx @@ -44,7 +44,7 @@ http://localhost:3000/#access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQ Let's break this up so that it's easier to read: ```jsx -// the base url - whatever you set in the Auth Settings in app.supabase.com dashboard +// the base url - whatever you set in the Auth Settings in supabase.com/dashboard dashboard http://localhost:3000/ // note we use the '#' (fragment) instead of '?' query param @@ -183,7 +183,7 @@ You can get as creative as you like with these policies. {/* */} - Watch [Part Four: GoTrue](../../learn/auth-deep-dive/auth-gotrue) - Watch [Part Five: Google Oauth](../../learn/auth-deep-dive/auth-google-oauth) -- Sign up for Supabase: [app.supabase.com](https://app.supabase.com) +- Sign up for Supabase: [supabase.com/dashboard](https://supabase.com/dashboard) export const Page = ({ children }) => diff --git a/apps/docs/pages/learn/auth-deep-dive/auth-row-level-security.mdx b/apps/docs/pages/learn/auth-deep-dive/auth-row-level-security.mdx index c75b446e972..81424abf45f 100644 --- a/apps/docs/pages/learn/auth-deep-dive/auth-row-level-security.mdx +++ b/apps/docs/pages/learn/auth-deep-dive/auth-row-level-security.mdx @@ -82,7 +82,7 @@ or via the Supabase Dashboard, by navigating to Auth > Policies, and clicking th ![Enable row level security in Supabase](/docs/img/auth-deep-dive-2.png) -You'll notice that both reading and writing now fail with an error like: +You'll notice that reading will return no rows and writing will now fail with an error like: ```jsx { @@ -128,7 +128,7 @@ In the next guide we will look at using Policies in combination with User Accoun - Watch [Part Three: Policies](../../learn/auth-deep-dive/auth-policies) - Watch [Part Four: GoTrue](../../learn/auth-deep-dive/auth-gotrue) - Watch [Part Five: Google Oauth](../../learn/auth-deep-dive/auth-google-oauth) -- Sign up for Supabase: [app.supabase.com](https://app.supabase.com) +- Sign up for Supabase: [supabase.com/dashboard](https://supabase.com/dashboard) export const Page = ({ children }) => diff --git a/apps/docs/public/img/ai/going-prod/lists-count--dark.png b/apps/docs/public/img/ai/going-prod/lists-count--dark.png new file mode 100644 index 00000000000..919b56b4a8c Binary files /dev/null and b/apps/docs/public/img/ai/going-prod/lists-count--dark.png differ diff --git a/apps/docs/public/img/ai/going-prod/lists-count--light.png b/apps/docs/public/img/ai/going-prod/lists-count--light.png new file mode 100644 index 00000000000..f40a98500fc Binary files /dev/null and b/apps/docs/public/img/ai/going-prod/lists-count--light.png differ diff --git a/apps/docs/public/img/ai/going-prod/size-to-rps--dark.png b/apps/docs/public/img/ai/going-prod/size-to-rps--dark.png new file mode 100644 index 00000000000..69ab4d76f51 Binary files /dev/null and b/apps/docs/public/img/ai/going-prod/size-to-rps--dark.png differ diff --git a/apps/docs/public/img/ai/going-prod/size-to-rps--light.png b/apps/docs/public/img/ai/going-prod/size-to-rps--light.png new file mode 100644 index 00000000000..b4aaf58e257 Binary files /dev/null and b/apps/docs/public/img/ai/going-prod/size-to-rps--light.png differ diff --git a/apps/docs/public/img/guides/auth-kakao/kakao-developers-consent-items-set.png b/apps/docs/public/img/guides/auth-kakao/kakao-developers-consent-items-set.png new file mode 100644 index 00000000000..b59c4616984 Binary files /dev/null and b/apps/docs/public/img/guides/auth-kakao/kakao-developers-consent-items-set.png differ diff --git a/apps/docs/public/img/guides/auth-kakao/kakao-developers-page.png b/apps/docs/public/img/guides/auth-kakao/kakao-developers-page.png new file mode 100644 index 00000000000..81d30550eb6 Binary files /dev/null and b/apps/docs/public/img/guides/auth-kakao/kakao-developers-page.png differ diff --git a/apps/docs/public/img/guides/cli/local-studio.png b/apps/docs/public/img/guides/cli/local-studio.png new file mode 100644 index 00000000000..563c5cbef58 Binary files /dev/null and b/apps/docs/public/img/guides/cli/local-studio.png differ diff --git a/apps/docs/public/img/icons/kakao-icon.svg b/apps/docs/public/img/icons/kakao-icon.svg new file mode 100644 index 00000000000..e721ec2b432 --- /dev/null +++ b/apps/docs/public/img/icons/kakao-icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/docs/public/img/icons/refine-icon.svg b/apps/docs/public/img/icons/refine-icon.svg new file mode 100644 index 00000000000..7f2e1f8d82c --- /dev/null +++ b/apps/docs/public/img/icons/refine-icon.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/apps/docs/public/img/integrations/logos/appsmith_logo.png b/apps/docs/public/img/integrations/logos/appsmith_logo.png deleted file mode 100644 index 43c73e52172..00000000000 Binary files a/apps/docs/public/img/integrations/logos/appsmith_logo.png and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/arengu_logo.jpeg b/apps/docs/public/img/integrations/logos/arengu_logo.jpeg deleted file mode 100644 index 6cbe3ed1164..00000000000 Binary files a/apps/docs/public/img/integrations/logos/arengu_logo.jpeg and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/auth0.webp b/apps/docs/public/img/integrations/logos/auth0.webp deleted file mode 100644 index 85dafcc2567..00000000000 Binary files a/apps/docs/public/img/integrations/logos/auth0.webp and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/auth0_logo.png b/apps/docs/public/img/integrations/logos/auth0_logo.png deleted file mode 100644 index 9558141f14d..00000000000 Binary files a/apps/docs/public/img/integrations/logos/auth0_logo.png and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/authsignal_logo.jpeg b/apps/docs/public/img/integrations/logos/authsignal_logo.jpeg deleted file mode 100644 index 65930d88586..00000000000 Binary files a/apps/docs/public/img/integrations/logos/authsignal_logo.jpeg and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/authsignal_logo.png b/apps/docs/public/img/integrations/logos/authsignal_logo.png deleted file mode 100644 index 51ebdeeeb4d..00000000000 Binary files a/apps/docs/public/img/integrations/logos/authsignal_logo.png and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/basedash_logo.jpeg b/apps/docs/public/img/integrations/logos/basedash_logo.jpeg deleted file mode 100644 index f4551a42378..00000000000 Binary files a/apps/docs/public/img/integrations/logos/basedash_logo.jpeg and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/bracket_logo.png b/apps/docs/public/img/integrations/logos/bracket_logo.png deleted file mode 100644 index dc56ad5382b..00000000000 Binary files a/apps/docs/public/img/integrations/logos/bracket_logo.png and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/clerk_logo.png b/apps/docs/public/img/integrations/logos/clerk_logo.png deleted file mode 100644 index 39866a000aa..00000000000 Binary files a/apps/docs/public/img/integrations/logos/clerk_logo.png and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/clerk_logo.svg b/apps/docs/public/img/integrations/logos/clerk_logo.svg deleted file mode 100644 index 57375da7f8b..00000000000 --- a/apps/docs/public/img/integrations/logos/clerk_logo.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/apps/docs/public/img/integrations/logos/cloudflare_workers_logo.jpeg b/apps/docs/public/img/integrations/logos/cloudflare_workers_logo.jpeg deleted file mode 100644 index 6d09d84acc5..00000000000 Binary files a/apps/docs/public/img/integrations/logos/cloudflare_workers_logo.jpeg and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/cloudflare_workers_logo.png b/apps/docs/public/img/integrations/logos/cloudflare_workers_logo.png deleted file mode 100644 index af9e1b1a658..00000000000 Binary files a/apps/docs/public/img/integrations/logos/cloudflare_workers_logo.png and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/clutch_logo.jpeg b/apps/docs/public/img/integrations/logos/clutch_logo.jpeg deleted file mode 100644 index dd55777b6b6..00000000000 Binary files a/apps/docs/public/img/integrations/logos/clutch_logo.jpeg and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/clutch_logo.png b/apps/docs/public/img/integrations/logos/clutch_logo.png deleted file mode 100644 index 63b4f88db08..00000000000 Binary files a/apps/docs/public/img/integrations/logos/clutch_logo.png and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/codesandbox_logo.jpeg b/apps/docs/public/img/integrations/logos/codesandbox_logo.jpeg deleted file mode 100644 index 852aba8f950..00000000000 Binary files a/apps/docs/public/img/integrations/logos/codesandbox_logo.jpeg and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/colored_logo.png b/apps/docs/public/img/integrations/logos/colored_logo.png deleted file mode 100644 index 7cbd75511ed..00000000000 Binary files a/apps/docs/public/img/integrations/logos/colored_logo.png and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/dashibase_logo.jpeg b/apps/docs/public/img/integrations/logos/dashibase_logo.jpeg deleted file mode 100644 index aefffe1b44f..00000000000 Binary files a/apps/docs/public/img/integrations/logos/dashibase_logo.jpeg and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/dashibase_logo.png b/apps/docs/public/img/integrations/logos/dashibase_logo.png deleted file mode 100644 index 359e5d8c831..00000000000 Binary files a/apps/docs/public/img/integrations/logos/dashibase_logo.png and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/dhiwise_logo.jpeg b/apps/docs/public/img/integrations/logos/dhiwise_logo.jpeg deleted file mode 100644 index 04ebaa5e0eb..00000000000 Binary files a/apps/docs/public/img/integrations/logos/dhiwise_logo.jpeg and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/dhiwise_logo.png b/apps/docs/public/img/integrations/logos/dhiwise_logo.png deleted file mode 100644 index 96264cca03e..00000000000 Binary files a/apps/docs/public/img/integrations/logos/dhiwise_logo.png and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/directus_logo.jpeg b/apps/docs/public/img/integrations/logos/directus_logo.jpeg deleted file mode 100644 index 64f9a7b5f3b..00000000000 Binary files a/apps/docs/public/img/integrations/logos/directus_logo.jpeg and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/directus_logo.png b/apps/docs/public/img/integrations/logos/directus_logo.png deleted file mode 100644 index aac383a7e79..00000000000 Binary files a/apps/docs/public/img/integrations/logos/directus_logo.png and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/doppler_logo.jpeg b/apps/docs/public/img/integrations/logos/doppler_logo.jpeg deleted file mode 100644 index 0110459330a..00000000000 Binary files a/apps/docs/public/img/integrations/logos/doppler_logo.jpeg and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/draftbit_logo.png b/apps/docs/public/img/integrations/logos/draftbit_logo.png deleted file mode 100644 index ae58788de2c..00000000000 Binary files a/apps/docs/public/img/integrations/logos/draftbit_logo.png and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/estuary_logo.jpeg b/apps/docs/public/img/integrations/logos/estuary_logo.jpeg deleted file mode 100644 index 50d3aad9f05..00000000000 Binary files a/apps/docs/public/img/integrations/logos/estuary_logo.jpeg and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/estuary_logo.png b/apps/docs/public/img/integrations/logos/estuary_logo.png deleted file mode 100644 index ab96311f616..00000000000 Binary files a/apps/docs/public/img/integrations/logos/estuary_logo.png and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/fezto-logo_logo.png b/apps/docs/public/img/integrations/logos/fezto-logo_logo.png deleted file mode 100644 index e259cc4b557..00000000000 Binary files a/apps/docs/public/img/integrations/logos/fezto-logo_logo.png and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/flutterflow_logo.png b/apps/docs/public/img/integrations/logos/flutterflow_logo.png deleted file mode 100644 index c901cacb076..00000000000 Binary files a/apps/docs/public/img/integrations/logos/flutterflow_logo.png and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/illa_logo.png b/apps/docs/public/img/integrations/logos/illa_logo.png deleted file mode 100644 index ea541c278f4..00000000000 Binary files a/apps/docs/public/img/integrations/logos/illa_logo.png and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/illa_logo.webp b/apps/docs/public/img/integrations/logos/illa_logo.webp deleted file mode 100644 index 58cfda4a568..00000000000 Binary files a/apps/docs/public/img/integrations/logos/illa_logo.webp and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/internal_logo.png b/apps/docs/public/img/integrations/logos/internal_logo.png deleted file mode 100644 index 824279374aa..00000000000 Binary files a/apps/docs/public/img/integrations/logos/internal_logo.png and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/jetadmin_logo.png b/apps/docs/public/img/integrations/logos/jetadmin_logo.png deleted file mode 100644 index 39008c72d48..00000000000 Binary files a/apps/docs/public/img/integrations/logos/jetadmin_logo.png and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/keyri_logo.jpeg b/apps/docs/public/img/integrations/logos/keyri_logo.jpeg deleted file mode 100644 index e0713d86085..00000000000 Binary files a/apps/docs/public/img/integrations/logos/keyri_logo.jpeg and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/keyri_logo.png b/apps/docs/public/img/integrations/logos/keyri_logo.png deleted file mode 100644 index 585b8c64d68..00000000000 Binary files a/apps/docs/public/img/integrations/logos/keyri_logo.png and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/logo_logo.png b/apps/docs/public/img/integrations/logos/logo_logo.png deleted file mode 100644 index b5bc166815f..00000000000 Binary files a/apps/docs/public/img/integrations/logos/logo_logo.png and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/logo_teta_logo.svg b/apps/docs/public/img/integrations/logos/logo_teta_logo.svg deleted file mode 100644 index fd9e393fa34..00000000000 --- a/apps/docs/public/img/integrations/logos/logo_teta_logo.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/apps/docs/public/img/integrations/logos/nextauth_logo.png b/apps/docs/public/img/integrations/logos/nextauth_logo.png deleted file mode 100644 index 60771efb5c2..00000000000 Binary files a/apps/docs/public/img/integrations/logos/nextauth_logo.png and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/onesignal_logo.png b/apps/docs/public/img/integrations/logos/onesignal_logo.png deleted file mode 100644 index c3d86f11297..00000000000 Binary files a/apps/docs/public/img/integrations/logos/onesignal_logo.png and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/openai_logo.png b/apps/docs/public/img/integrations/logos/openai_logo.png deleted file mode 100644 index c5596878b0b..00000000000 Binary files a/apps/docs/public/img/integrations/logos/openai_logo.png and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/ory_logo.jpeg b/apps/docs/public/img/integrations/logos/ory_logo.jpeg deleted file mode 100644 index ce10c428c95..00000000000 Binary files a/apps/docs/public/img/integrations/logos/ory_logo.jpeg and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/passage_logo.png b/apps/docs/public/img/integrations/logos/passage_logo.png deleted file mode 100644 index 270d6afa701..00000000000 Binary files a/apps/docs/public/img/integrations/logos/passage_logo.png and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/pgmustard_logo.png b/apps/docs/public/img/integrations/logos/pgmustard_logo.png deleted file mode 100644 index 806ec916bd4..00000000000 Binary files a/apps/docs/public/img/integrations/logos/pgmustard_logo.png and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/plasmic_logo.png b/apps/docs/public/img/integrations/logos/plasmic_logo.png deleted file mode 100644 index 162ca6ed517..00000000000 Binary files a/apps/docs/public/img/integrations/logos/plasmic_logo.png and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/polyscale_logo.png b/apps/docs/public/img/integrations/logos/polyscale_logo.png deleted file mode 100644 index bdfb9318127..00000000000 Binary files a/apps/docs/public/img/integrations/logos/polyscale_logo.png and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/prisma_logo.png b/apps/docs/public/img/integrations/logos/prisma_logo.png deleted file mode 100644 index b9398b4e814..00000000000 Binary files a/apps/docs/public/img/integrations/logos/prisma_logo.png and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/readyset_logo.jpeg b/apps/docs/public/img/integrations/logos/readyset_logo.jpeg deleted file mode 100644 index cc8ab4dd83e..00000000000 Binary files a/apps/docs/public/img/integrations/logos/readyset_logo.jpeg and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/replibyte_logo.jpeg b/apps/docs/public/img/integrations/logos/replibyte_logo.jpeg deleted file mode 100644 index 7212775333a..00000000000 Binary files a/apps/docs/public/img/integrations/logos/replibyte_logo.jpeg and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/replicache_logo.jpeg b/apps/docs/public/img/integrations/logos/replicache_logo.jpeg deleted file mode 100644 index 9a61bad9cc2..00000000000 Binary files a/apps/docs/public/img/integrations/logos/replicache_logo.jpeg and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/retool_logo.jpeg b/apps/docs/public/img/integrations/logos/retool_logo.jpeg deleted file mode 100644 index a39afca024a..00000000000 Binary files a/apps/docs/public/img/integrations/logos/retool_logo.jpeg and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/sequin_logo.png b/apps/docs/public/img/integrations/logos/sequin_logo.png deleted file mode 100644 index 83c9b964eba..00000000000 Binary files a/apps/docs/public/img/integrations/logos/sequin_logo.png and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/snaplet_logo.png b/apps/docs/public/img/integrations/logos/snaplet_logo.png deleted file mode 100644 index 2ac00b8ad3e..00000000000 Binary files a/apps/docs/public/img/integrations/logos/snaplet_logo.png and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/streamio_logo.jpeg b/apps/docs/public/img/integrations/logos/streamio_logo.jpeg deleted file mode 100644 index 72f1ba943ae..00000000000 Binary files a/apps/docs/public/img/integrations/logos/streamio_logo.jpeg and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/streamlit_logo.png b/apps/docs/public/img/integrations/logos/streamlit_logo.png deleted file mode 100644 index 209ceba784b..00000000000 Binary files a/apps/docs/public/img/integrations/logos/streamlit_logo.png and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/stytch_logo.png b/apps/docs/public/img/integrations/logos/stytch_logo.png deleted file mode 100644 index 2e96124c4a6..00000000000 Binary files a/apps/docs/public/img/integrations/logos/stytch_logo.png and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/supertokens_logo.png b/apps/docs/public/img/integrations/logos/supertokens_logo.png deleted file mode 100644 index 72bebc5f51e..00000000000 Binary files a/apps/docs/public/img/integrations/logos/supertokens_logo.png and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/trevorio_logo.jpeg b/apps/docs/public/img/integrations/logos/trevorio_logo.jpeg deleted file mode 100644 index 066b67230de..00000000000 Binary files a/apps/docs/public/img/integrations/logos/trevorio_logo.jpeg and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/upstash_redis_logo.jpeg b/apps/docs/public/img/integrations/logos/upstash_redis_logo.jpeg deleted file mode 100644 index 846d395ccf3..00000000000 Binary files a/apps/docs/public/img/integrations/logos/upstash_redis_logo.jpeg and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/upstash_redis_logo.png b/apps/docs/public/img/integrations/logos/upstash_redis_logo.png deleted file mode 100644 index eb0df80afd9..00000000000 Binary files a/apps/docs/public/img/integrations/logos/upstash_redis_logo.png and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/vercel_logo.jpeg b/apps/docs/public/img/integrations/logos/vercel_logo.jpeg deleted file mode 100644 index ea0dad906b6..00000000000 Binary files a/apps/docs/public/img/integrations/logos/vercel_logo.jpeg and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/vercel_logo.png b/apps/docs/public/img/integrations/logos/vercel_logo.png deleted file mode 100644 index cae9ecb1c23..00000000000 Binary files a/apps/docs/public/img/integrations/logos/vercel_logo.png and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/voltapp_logo.png b/apps/docs/public/img/integrations/logos/voltapp_logo.png deleted file mode 100644 index 57a59e9f115..00000000000 Binary files a/apps/docs/public/img/integrations/logos/voltapp_logo.png and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/weweb_logo.jpeg b/apps/docs/public/img/integrations/logos/weweb_logo.jpeg deleted file mode 100644 index e2e9a20d3f8..00000000000 Binary files a/apps/docs/public/img/integrations/logos/weweb_logo.jpeg and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/weweb_logo.png b/apps/docs/public/img/integrations/logos/weweb_logo.png deleted file mode 100644 index 4eee51ea708..00000000000 Binary files a/apps/docs/public/img/integrations/logos/weweb_logo.png and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/zapp_run_logo.jpeg b/apps/docs/public/img/integrations/logos/zapp_run_logo.jpeg deleted file mode 100644 index 1b7d2d7a2cd..00000000000 Binary files a/apps/docs/public/img/integrations/logos/zapp_run_logo.jpeg and /dev/null differ diff --git a/apps/docs/public/img/integrations/logos/zuplo_logo.png b/apps/docs/public/img/integrations/logos/zuplo_logo.png deleted file mode 100644 index 34c863e2f84..00000000000 Binary files a/apps/docs/public/img/integrations/logos/zuplo_logo.png and /dev/null differ diff --git a/apps/docs/public/img/refine-qs-countries-ui.png b/apps/docs/public/img/refine-qs-countries-ui.png new file mode 100644 index 00000000000..e062c721878 Binary files /dev/null and b/apps/docs/public/img/refine-qs-countries-ui.png differ diff --git a/apps/docs/public/img/refine-qs-welcome-page.png b/apps/docs/public/img/refine-qs-welcome-page.png new file mode 100644 index 00000000000..376740a35f7 Binary files /dev/null and b/apps/docs/public/img/refine-qs-welcome-page.png differ diff --git a/apps/docs/public/img/supabase-refine-demo.png b/apps/docs/public/img/supabase-refine-demo.png new file mode 100644 index 00000000000..6a8f15ff6ed Binary files /dev/null and b/apps/docs/public/img/supabase-refine-demo.png differ diff --git a/apps/docs/styles/main.scss b/apps/docs/styles/main.scss index d6b1d9cc720..ec6e38a814e 100644 --- a/apps/docs/styles/main.scss +++ b/apps/docs/styles/main.scss @@ -2,6 +2,10 @@ @tailwind components; @tailwind utilities; +@import './../../../packages/ui/build/css/source/global.css'; +@import './../../../packages/ui/build/css/themes/dark.css'; +@import './../../../packages/ui/build/css/themes/light.css'; + @font-face { font-family: 'custom-font'; src: url(../../../packages/common/assets/fonts/CustomFont-Book.woff2) format('woff2'), diff --git a/apps/docs/tsconfig.json b/apps/docs/tsconfig.json index c87a0e51e07..3600bbffffc 100644 --- a/apps/docs/tsconfig.json +++ b/apps/docs/tsconfig.json @@ -4,7 +4,8 @@ "noImplicitAny": false, "baseUrl": ".", "paths": { - "~/*": ["./*"] + "~/*": ["./*"], + "@ui/*": ["./../../packages/ui/src/*"] // handle ui package paths }, "target": "es2015", "lib": ["dom", "dom.iterable", "esnext"], diff --git a/apps/www/_alternatives/supabase-vs-firebase.mdx b/apps/www/_alternatives/supabase-vs-firebase.mdx index 1b0c44cb5c7..2ed8ec1883f 100644 --- a/apps/www/_alternatives/supabase-vs-firebase.mdx +++ b/apps/www/_alternatives/supabase-vs-firebase.mdx @@ -66,4 +66,4 @@ We also have guides and tools for [migrating Firebase Auth to Supabase Auth](htt These are by far the most complete Firebase to Postgres migration tools available anywhere on the web. -You can [try Supabase for free](https://app.supabase.com). If you require Enterprise level support with your project or migration, please get in touch using our [Enterprise contact form](https://forms.supabase.com/enterprise). +You can [try Supabase for free](https://supabase.com/dashboard). If you require Enterprise level support with your project or migration, please get in touch using our [Enterprise contact form](https://forms.supabase.com/enterprise). diff --git a/apps/www/_blog/2020-05-01-supabase-alpha-april-2020.mdx b/apps/www/_blog/2020-05-01-supabase-alpha-april-2020.mdx index 964cfe71934..59816be570d 100644 --- a/apps/www/_blog/2020-05-01-supabase-alpha-april-2020.mdx +++ b/apps/www/_blog/2020-05-01-supabase-alpha-april-2020.mdx @@ -11,7 +11,7 @@ tags: date: '06-01-2020' --- -Now in [Supabase](https://app.supabase.com): +Now in [Supabase](https://supabase.com/dashboard): - Set up Postgres in less than 2 minutes - Auto-generated APIs! (they are a bit flaky, go easy) @@ -58,6 +58,6 @@ Send realtime database changes to other systems, like queues or webhooks (Slack ## Follow us -Start using Supabase today: [app.supabase.com](https://app.supabase.com) +Start using Supabase today: [supabase.com/dashboard](https://supabase.com/dashboard) Make sure to star us on github! [github.com/supabase/supabase](https://github.com/supabase/supabase) diff --git a/apps/www/_blog/2020-06-01-supabase-alpha-may-2020.mdx b/apps/www/_blog/2020-06-01-supabase-alpha-may-2020.mdx index fc9d49b9ba6..b3db04f166c 100644 --- a/apps/www/_blog/2020-06-01-supabase-alpha-may-2020.mdx +++ b/apps/www/_blog/2020-06-01-supabase-alpha-may-2020.mdx @@ -56,6 +56,6 @@ This one is still very unstable, but we wanted to ship it anyway. It's a little ## Follow us -Start using Supabase today: [app.supabase.com](https://app.supabase.com) +Start using Supabase today: [supabase.com/dashboard](https://supabase.com/dashboard) Make sure to star us on github! [github.com/supabase/supabase](https://github.com/supabase/supabase) diff --git a/apps/www/_blog/2020-07-01-supabase-alpha-june-2020.mdx b/apps/www/_blog/2020-07-01-supabase-alpha-june-2020.mdx index 4baf3f31a58..f8a2d6a8e1a 100644 --- a/apps/www/_blog/2020-07-01-supabase-alpha-june-2020.mdx +++ b/apps/www/_blog/2020-07-01-supabase-alpha-june-2020.mdx @@ -93,7 +93,7 @@ We have a number of small improvements: ### Get started -- Start using Supabase today: [app.supabase.com](https://app.supabase.com) +- Start using Supabase today: [supabase.com/dashboard](https://supabase.com/dashboard) - Make sure to [star us on GitHub](https://github.com/supabase/supabase) - Follow us [on Twitter](https://twitter.com/supabase) - Become a [sponsor](https://github.com/sponsors/supabase) diff --git a/apps/www/_blog/2020-07-10-alpha-launch-postmortem.mdx b/apps/www/_blog/2020-07-10-alpha-launch-postmortem.mdx index f0186212be6..50484573981 100644 --- a/apps/www/_blog/2020-07-10-alpha-launch-postmortem.mdx +++ b/apps/www/_blog/2020-07-10-alpha-launch-postmortem.mdx @@ -51,7 +51,7 @@ We have since migrated our middleware to multiple ECS clusters, globally load-ba We serve our marketing site ([supabase.io](/)) from Netlify. It's a static-build [Docusaurus (v2)](https://v2.docusaurus.io/) site, so it had no problems (apart from one developer in Russia who couldn't access the site - it looks like some of Netlify's IP addresses are blocked there). -We serve our app ([app.supabase.com](https://app.supabase.com)) using Vercel, and the login system uses Auth0. These were both rock-solid. Before the launch we noticed that Vercel was extremely slow on their free plan, and once we upgraded to their Pro Plan for multi-region deploys it solved performance issues. It looks like they are changing their plans again so buyer beware. +We serve our app ([supabase.com/dashboard](https://supabase.com/dashboard)) using Vercel, and the login system uses Auth0. These were both rock-solid. Before the launch we noticed that Vercel was extremely slow on their free plan, and once we upgraded to their Pro Plan for multi-region deploys it solved performance issues. It looks like they are changing their plans again so buyer beware. ## The Bad diff --git a/apps/www/_blog/2020-08-02-supabase-alpha-july-2020.mdx b/apps/www/_blog/2020-08-02-supabase-alpha-july-2020.mdx index 3d450ab4765..4dbc736616a 100644 --- a/apps/www/_blog/2020-08-02-supabase-alpha-july-2020.mdx +++ b/apps/www/_blog/2020-08-02-supabase-alpha-july-2020.mdx @@ -79,7 +79,7 @@ We have a number of small improvements: ### Get started -- Start using Supabase today: [app.supabase.com](https://app.supabase.com) +- Start using Supabase today: [supabase.com/dashboard](https://supabase.com/dashboard) - Make sure to [star us on GitHub](https://github.com/supabase/supabase) - Follow us [on Twitter](https://twitter.com/supabase) - Become a [sponsor](https://github.com/sponsors/supabase) diff --git a/apps/www/_blog/2020-08-05-supabase-auth.mdx b/apps/www/_blog/2020-08-05-supabase-auth.mdx index 404b0e1d0de..708b0d4d099 100644 --- a/apps/www/_blog/2020-08-05-supabase-auth.mdx +++ b/apps/www/_blog/2020-08-05-supabase-auth.mdx @@ -95,6 +95,6 @@ Supabase has a culture of shipping early and often. Our Auth release is another ### Get started -Supabase Auth is ready for you to start using today, free of charge: [app.supabase.com](https://app.supabase.com) +Supabase Auth is ready for you to start using today, free of charge: [supabase.com/dashboard](https://supabase.com/dashboard) To see the full power of our auth system, watch [this demo](https://youtu.be/2oqIZW5S-lQ) where I deploy a secure, real-time slack clone to Vercel in less than 3 minutes. diff --git a/apps/www/_blog/2020-09-03-supabase-alpha-august-2020.mdx b/apps/www/_blog/2020-09-03-supabase-alpha-august-2020.mdx index 14678e98687..d33c6c02d6f 100644 --- a/apps/www/_blog/2020-09-03-supabase-alpha-august-2020.mdx +++ b/apps/www/_blog/2020-09-03-supabase-alpha-august-2020.mdx @@ -91,7 +91,7 @@ Our focus now is to move from Alpha to Beta. This involves stabilising our Auth ### Get started -- Start using Supabase today: [app.supabase.com](https://app.supabase.com) +- Start using Supabase today: [supabase.com/dashboard](https://supabase.com/dashboard) - Make sure to [star us on GitHub](https://github.com/supabase/supabase) - Follow us [on Twitter](https://twitter.com/supabase) - Become a [sponsor](https://github.com/sponsors/supabase) diff --git a/apps/www/_blog/2020-10-03-supabase-alpha-september-2020.mdx b/apps/www/_blog/2020-10-03-supabase-alpha-september-2020.mdx index f5af4a662ae..f24e466253c 100644 --- a/apps/www/_blog/2020-10-03-supabase-alpha-september-2020.mdx +++ b/apps/www/_blog/2020-10-03-supabase-alpha-september-2020.mdx @@ -99,7 +99,7 @@ Our focus now is to move from Alpha to Beta and we'll be improving stability, re ### Get started -- Start using Supabase today: [app.supabase.com](https://app.supabase.com/) +- Start using Supabase today: [supabase.com/dashboard](https://supabase.com/dashboard/) - Make sure to [star us on GitHub](https://github.com/supabase/supabase) - Follow us [on Twitter](https://twitter.com/supabase) - Become a [sponsor](https://github.com/sponsors/supabase) diff --git a/apps/www/_blog/2020-10-30-improved-dx.mdx b/apps/www/_blog/2020-10-30-improved-dx.mdx index d13ca01f614..786a45c585d 100644 --- a/apps/www/_blog/2020-10-30-improved-dx.mdx +++ b/apps/www/_blog/2020-10-30-improved-dx.mdx @@ -111,7 +111,7 @@ To summarise the steps: ### Get started -- Start using Supabase today: [app.supabase.com](https://app.supabase.com/) +- Start using Supabase today: [supabase.com/dashboard](https://supabase.com/dashboard/) - Make sure to [star us on GitHub](https://github.com/supabase/supabase) - Follow us [on Twitter](https://twitter.com/supabase) - Become a [sponsor](https://github.com/sponsors/supabase) diff --git a/apps/www/_blog/2020-11-02-supabase-alpha-october-2020.mdx b/apps/www/_blog/2020-11-02-supabase-alpha-october-2020.mdx index 158b81d22d5..e3778d5ba34 100644 --- a/apps/www/_blog/2020-11-02-supabase-alpha-october-2020.mdx +++ b/apps/www/_blog/2020-11-02-supabase-alpha-october-2020.mdx @@ -79,7 +79,7 @@ Our focus is still moving from Alpha to Beta and we'll be improving stability, r ### Get started -- Start using Supabase today: [app.supabase.com](https://app.supabase.com/) +- Start using Supabase today: [supabase.com/dashboard](https://supabase.com/dashboard/) - Make sure to [star us on GitHub](https://github.com/supabase/supabase) - Follow us [on Twitter](https://twitter.com/supabase) - Become a [sponsor](https://github.com/sponsors/supabase) diff --git a/apps/www/_blog/2020-12-01-supabase-alpha-november-2020.mdx b/apps/www/_blog/2020-12-01-supabase-alpha-november-2020.mdx index a4bb7858a82..15fd74a13b0 100644 --- a/apps/www/_blog/2020-12-01-supabase-alpha-november-2020.mdx +++ b/apps/www/_blog/2020-12-01-supabase-alpha-november-2020.mdx @@ -76,7 +76,7 @@ Look out for a big announcement on December 3rd. We'll also be presenting in the ### Get started -- Start using Supabase today: [app.supabase.com](https://app.supabase.com/) +- Start using Supabase today: [supabase.com/dashboard](https://supabase.com/dashboard/) - Make sure to [star us on GitHub](https://github.com/supabase/supabase) - Follow us [on Twitter](https://twitter.com/supabase) - Become a [sponsor](https://github.com/sponsors/supabase) diff --git a/apps/www/_blog/2020-12-02-case-study-monitoro.mdx b/apps/www/_blog/2020-12-02-case-study-monitoro.mdx index 0c628e8c394..df53fac79d7 100644 --- a/apps/www/_blog/2020-12-02-case-study-monitoro.mdx +++ b/apps/www/_blog/2020-12-02-case-study-monitoro.mdx @@ -8,7 +8,6 @@ author_image_url: https://github.com/roryw10.png authorURL: https://github.com/roryw10 image: /images/blog/supabase-monitoro.png tags: - - case-study - no-code date: '12-02-2020' video: https://www.youtube.com/v/8A6_pg41M2s diff --git a/apps/www/_blog/2020-12-02-case-study-tayfa.mdx b/apps/www/_blog/2020-12-02-case-study-tayfa.mdx index a4d03ff3e5e..c494482fc63 100644 --- a/apps/www/_blog/2020-12-02-case-study-tayfa.mdx +++ b/apps/www/_blog/2020-12-02-case-study-tayfa.mdx @@ -8,7 +8,6 @@ author_image_url: https://github.com/roryw10.png authorURL: https://github.com/roryw10 image: /images/blog/supabase-tayfa.png tags: - - case-study - no-code date: '12-02-2020' --- diff --git a/apps/www/_blog/2020-12-02-case-study-xendit.mdx b/apps/www/_blog/2020-12-02-case-study-xendit.mdx index 80d8822fb17..5d61a0c70d4 100644 --- a/apps/www/_blog/2020-12-02-case-study-xendit.mdx +++ b/apps/www/_blog/2020-12-02-case-study-xendit.mdx @@ -9,7 +9,6 @@ authorURL: https://github.com/roryw10 image: supabase-xendit.png thumb: supabase-xendit-thumb.jpg tags: - - case-study - fintech date: '12-02-2020' --- diff --git a/apps/www/_blog/2021-01-02-supabase-beta-december-2020.mdx b/apps/www/_blog/2021-01-02-supabase-beta-december-2020.mdx index 3b5f726509a..c78910380ec 100644 --- a/apps/www/_blog/2021-01-02-supabase-beta-december-2020.mdx +++ b/apps/www/_blog/2021-01-02-supabase-beta-december-2020.mdx @@ -102,7 +102,7 @@ We also have something exciting planned with Vercel and Stripe ... [stay tuned]( ### Get started -- Start using Supabase today: [app.supabase.com](https://app.supabase.com/) +- Start using Supabase today: [supabase.com/dashboard](https://supabase.com/dashboard/) - Make sure to [star us on GitHub](https://github.com/supabase/supabase) - Follow us [on Twitter](https://twitter.com/supabase) - Become a [sponsor](https://github.com/sponsors/supabase) diff --git a/apps/www/_blog/2021-02-02-supabase-beta-january-2021.mdx b/apps/www/_blog/2021-02-02-supabase-beta-january-2021.mdx index 976c96f08b6..7fce0b1eabf 100644 --- a/apps/www/_blog/2021-02-02-supabase-beta-january-2021.mdx +++ b/apps/www/_blog/2021-02-02-supabase-beta-january-2021.mdx @@ -123,7 +123,7 @@ We're ramping up to "Launch week" at the end of Q1, where we will be giving you ### Get started -- Start using Supabase today: [app.supabase.com](https://app.supabase.com/) +- Start using Supabase today: [supabase.com/dashboard](https://supabase.com/dashboard/) - Make sure to [star us on GitHub](https://github.com/supabase/supabase) - Follow us [on Twitter](https://twitter.com/supabase) - Subscribe to our [YouTube channel](https://www.youtube.com/c/supabase) diff --git a/apps/www/_blog/2021-02-09-case-study-roboflow.mdx b/apps/www/_blog/2021-02-09-case-study-roboflow.mdx index 97888890d83..5b33ea3b7d8 100644 --- a/apps/www/_blog/2021-02-09-case-study-roboflow.mdx +++ b/apps/www/_blog/2021-02-09-case-study-roboflow.mdx @@ -9,7 +9,6 @@ authorURL: https://github.com/roryw10 image: roboflow-og.png thumb: roboflow-website.png tags: - - case-study - AI date: '02-09-2021' --- diff --git a/apps/www/_blog/2021-02-27-cracking-postgres-interview.mdx b/apps/www/_blog/2021-02-27-cracking-postgres-interview.mdx index dbdc69fc6ee..f90e44dff41 100644 --- a/apps/www/_blog/2021-02-27-cracking-postgres-interview.mdx +++ b/apps/www/_blog/2021-02-27-cracking-postgres-interview.mdx @@ -155,7 +155,7 @@ where first_name like "S%"; If you can reason about most of the topics in this post then you'll be in a very strong position to impress with your answers to PostgreSQL interview questions. As with all programming topics however, the real learning starts when you put these things into practice. At Supabase we offer a very very fast (the fastest?) way to spin up a PostgreSQL database and start querying it, and our browser based SQL editor is getting more powerful every day. -[Get started on Supabase for free here](https://app.supabase.com) +[Get started on Supabase for free here](https://supabase.com/dashboard) Thanks Steve Chavez for providing all the good bits of this post :) diff --git a/apps/www/_blog/2021-03-02-supabase-beta-february-2021.mdx b/apps/www/_blog/2021-03-02-supabase-beta-february-2021.mdx index 750370d5c7a..1f327482d37 100644 --- a/apps/www/_blog/2021-03-02-supabase-beta-february-2021.mdx +++ b/apps/www/_blog/2021-03-02-supabase-beta-february-2021.mdx @@ -103,7 +103,7 @@ Waiting for Supabase Storage? Here's a sneak peek for the upcoming Launch Week a ### Get started -- Start using Supabase today: [app.supabase.com](https://app.supabase.com/) +- Start using Supabase today: [supabase.com/dashboard](https://supabase.com/dashboard/) - Make sure to [star us on GitHub](https://github.com/supabase/supabase) - Follow us [on Twitter](https://twitter.com/supabase) - Subscribe to our [YouTube channel](https://www.youtube.com/c/supabase) diff --git a/apps/www/_blog/2021-03-05-postgres-as-a-cron-server.mdx b/apps/www/_blog/2021-03-05-postgres-as-a-cron-server.mdx index 307339e3a02..f71abee8d97 100644 --- a/apps/www/_blog/2021-03-05-postgres-as-a-cron-server.mdx +++ b/apps/www/_blog/2021-03-05-postgres-as-a-cron-server.mdx @@ -211,7 +211,7 @@ If you're familiar with `C`, you could also help `@pramsey` to implement async f There are plenty of ways to run cron jobs these days. You can trigger them from your local machine. You can install them on a VPS. You can schedule Serverless functions. You can use a paid service. You can use GitHub Actions. -Is Postgres the best place to put your cron jobs? `¯\_(ツ)_/¯`. Postgres databases are free on Supabase and since it takes only one minute to [get started](https://app.supabase.com/), why not make your next cron server a Postgres database? +Is Postgres the best place to put your cron jobs? `¯\_(ツ)_/¯`. Postgres databases are free on Supabase and since it takes only one minute to [get started](https://supabase.com/dashboard/), why not make your next cron server a Postgres database? ## More Postgres resources diff --git a/apps/www/_blog/2021-03-08-toad-a-link-shortener-with-simple-apis-for-low-coders.mdx b/apps/www/_blog/2021-03-08-toad-a-link-shortener-with-simple-apis-for-low-coders.mdx index a87778aaeb0..c1ddf4af7e2 100644 --- a/apps/www/_blog/2021-03-08-toad-a-link-shortener-with-simple-apis-for-low-coders.mdx +++ b/apps/www/_blog/2021-03-08-toad-a-link-shortener-with-simple-apis-for-low-coders.mdx @@ -10,7 +10,6 @@ image: toadli-og.jpg thumb: toadli-website.jpg tags: - supabase - - case-study date: '03-08-2021' --- diff --git a/apps/www/_blog/2021-03-11-using-supabase-replit.mdx b/apps/www/_blog/2021-03-11-using-supabase-replit.mdx index 49dca993283..f5ef05d1413 100644 --- a/apps/www/_blog/2021-03-11-using-supabase-replit.mdx +++ b/apps/www/_blog/2021-03-11-using-supabase-replit.mdx @@ -133,4 +133,4 @@ We look forward to showing off some more Supabase + Replit examples. You can find my example repl here: [https://repl.it/@awalias/supabase-test#index.js](https://repl.it/@awalias/supabase-test#index.js) -Supabase has a free plan, head over to [https://app.supabase.com](https://app.supabase.com) to get started. +Supabase has a free plan, head over to [https://supabase.com/dashboard](https://supabase.com/dashboard) to get started. diff --git a/apps/www/_blog/2021-03-29-pricing.mdx b/apps/www/_blog/2021-03-29-pricing.mdx index f8300653ca4..1b0ccc104be 100644 --- a/apps/www/_blog/2021-03-29-pricing.mdx +++ b/apps/www/_blog/2021-03-29-pricing.mdx @@ -77,7 +77,7 @@ In the next few days, you will see your usage appear in the dashboard with an up Check back throughout the week to see what else we're launching [here](/blog/launch-week). -Sign up for the Supabase Beta [here](https://app.supabase.com). +Sign up for the Supabase Beta [here](https://supabase.com/dashboard). And check out the full pricing details [here](/pricing). And we'd love to hear your opnions on our Pricing! You can let us know via support@supabase.io. diff --git a/apps/www/_blog/2021-03-30-supabase-storage.mdx b/apps/www/_blog/2021-03-30-supabase-storage.mdx index ad9b96c392d..72921f565af 100644 --- a/apps/www/_blog/2021-03-30-supabase-storage.mdx +++ b/apps/www/_blog/2021-03-30-supabase-storage.mdx @@ -169,4 +169,4 @@ You may be itching to get started tinkering around with our new Supabase storage - A better editor to make authoring policies easier and less error prone. - Reach out to us if you would like to help out with adding storage support in one of the [community maintained client libraries](/docs/reference/javascript/installing). -Take it for a spin on our [dashboard](https://app.supabase.com/) and let us know what you think! +Take it for a spin on our [dashboard](https://supabase.com/dashboard/) and let us know what you think! diff --git a/apps/www/_blog/2021-03-31-supabase-cli.mdx b/apps/www/_blog/2021-03-31-supabase-cli.mdx index ea8d4c94ed8..9b3236d738f 100644 --- a/apps/www/_blog/2021-03-31-supabase-cli.mdx +++ b/apps/www/_blog/2021-03-31-supabase-cli.mdx @@ -224,4 +224,4 @@ Head over to the [Self Hosting Docs](/docs/guides/self-hosting) for a more compl If you require any assistance feel free to reach out in our [github discussions](https://github.com/supabase/supabase/discussions) or at support@supabase.io. -Check out the CLI VI [launched Monday 15th August, 2022](/blog/supabase-cli-v1-and-admin-api-beta), contribute to the [CLI repo](https://github.com/supabase/cli), or go here for the [hosted version](https://app.supabase.com). +Check out the CLI VI [launched Monday 15th August, 2022](/blog/supabase-cli-v1-and-admin-api-beta), contribute to the [CLI repo](https://github.com/supabase/cli), or go here for the [hosted version](https://supabase.com/dashboard). diff --git a/apps/www/_blog/2021-04-02-supabase-pgbouncer.mdx b/apps/www/_blog/2021-04-02-supabase-pgbouncer.mdx index 474d9c485bc..df6aa688c0e 100644 --- a/apps/www/_blog/2021-04-02-supabase-pgbouncer.mdx +++ b/apps/www/_blog/2021-04-02-supabase-pgbouncer.mdx @@ -92,7 +92,7 @@ This is the most granular option. Connections are returned to the pool after eve ## What's next? -Try out connection pooling now with a new project in the [dashboard.](https://app.supabase.com) For now, we do not have any plans to port this over to older projects. +Try out connection pooling now with a new project in the [dashboard.](https://supabase.com/dashboard) For now, we do not have any plans to port this over to older projects. Eventually, we will expose more PgBouncer settings to the UI such as `Pool Size`. At the moment it is set to `15`. diff --git a/apps/www/_blog/2021-04-06-supabase-beta-march-2021.mdx b/apps/www/_blog/2021-04-06-supabase-beta-march-2021.mdx index 607625976b6..1d766dbf8dc 100644 --- a/apps/www/_blog/2021-04-06-supabase-beta-march-2021.mdx +++ b/apps/www/_blog/2021-04-06-supabase-beta-march-2021.mdx @@ -116,7 +116,7 @@ For this we are working on our own [Workflow engine](/blog/supabase-workflows), ### Get started -- Start using Supabase today: [app.supabase.com](https://app.supabase.com/) +- Start using Supabase today: [supabase.com/dashboard](https://supabase.com/dashboard/) - Make sure to [star us on GitHub](https://github.com/supabase/supabase) - Follow us [on Twitter](https://twitter.com/supabase) - Subscribe to our [YouTube channel](https://www.youtube.com/c/supabase) diff --git a/apps/www/_blog/2021-05-03-supabase-beta-april-2021.mdx b/apps/www/_blog/2021-05-03-supabase-beta-april-2021.mdx index 342404f552e..817491cccb4 100644 --- a/apps/www/_blog/2021-05-03-supabase-beta-april-2021.mdx +++ b/apps/www/_blog/2021-05-03-supabase-beta-april-2021.mdx @@ -39,7 +39,7 @@ We're a developer tool, which means that Dark Mode is [extremely popular](https: While Dark mode is great, for some people it's not an option. Dark Mode is difficult to use for developers with astigmatisms, or even just working in brightly-lit environments. -So today we're shipping Light Mode. Access it in the settings of your [Dashboard](https://app.supabase.com). +So today we're shipping Light Mode. Access it in the settings of your [Dashboard](https://supabase.com/dashboard).
    diff --git a/apps/www/_blog/2022-08-24-building-a-realtime-trello-board-with-supabase-and-angular.mdx b/apps/www/_blog/2022-08-24-building-a-realtime-trello-board-with-supabase-and-angular.mdx index c63debb9185..f674ba663dc 100644 --- a/apps/www/_blog/2022-08-24-building-a-realtime-trello-board-with-supabase-and-angular.mdx +++ b/apps/www/_blog/2022-08-24-building-a-realtime-trello-board-with-supabase-and-angular.mdx @@ -46,7 +46,7 @@ Ready for a wild adventure? Then let's begin inside our Supabase account! ## Creating the Supabase Project -First of all we need a new Supabase project. If you don't have a Supabase account yet, you can [get started for free](https://app.supabase.com/)! +First of all we need a new Supabase project. If you don't have a Supabase account yet, you can [get started for free](https://supabase.com/dashboard/)! In your dashboard, click "New Project" and leave it to the default settings, but make sure you keep a copy o your Database password! diff --git a/apps/www/_blog/2022-09-07-supabase-beta-update-august-2022.mdx b/apps/www/_blog/2022-09-07-supabase-beta-update-august-2022.mdx index 31a6206daec..a12ddc5e3fd 100644 --- a/apps/www/_blog/2022-09-07-supabase-beta-update-august-2022.mdx +++ b/apps/www/_blog/2022-09-07-supabase-beta-update-august-2022.mdx @@ -117,7 +117,7 @@ Our friends from Netlify invited Ant to their new webinar series. He and Netlify - Inian shared our journey with Cloudflare. [Video](https://twitter.com/Cloudflare/status/1557728943901675520) - Supabase Tips: Introduction to Supabase Storage. [Video](https://www.youtube.com/watch?v=J9mTPY8rIXE) - Zack DeRose playing around with Supabase and Nx. [Part 1](https://www.youtube.com/watch?v=OTh5GBBfr4E) | [Part 2](https://www.youtube.com/watch?v=_5gJi_xwpzk) -- A new guide showing how to combine Supabase and Directus. [Guide](https://supabase.com/docs/guides/integrations/directus) +- A new guide showing how to combine Supabase and Directus. [Guide](https://supabase.com/partners/integrations/directus) - Supabase with Flutter course on raywenderlich. [Course](https://www.raywenderlich.com/33619647-supabase-with-flutter) - Build a full-stack app with Next.js and Supabase on the LogRocket blog. [Tutorial](https://blog.logrocket.com/build-full-stack-app-next-js-supabase/) - Supabase Crash Course by The Net Ninja. [Video Courses](https://www.youtube.com/watch?v=ydz7Dj5QHKY&list=PL4cUxeGkcC9hUb6sHthUEwG7r9VDPBMKO) @@ -146,7 +146,7 @@ If you made it this far in the blog post you deserve a treat. [Follow us on Twit ## Get started -- Start using Supabase today: **[app.supabase.com](https://app.supabase.com/)** +- Start using Supabase today: **[supabase.com/dashboard](https://supabase.com/dashboard/)** - Make sure to **[star us on GitHub](https://github.com/supabase/supabase)** - Follow us **[on Twitter](https://twitter.com/supabase)** - Subscribe to our **[YouTube channel](https://www.youtube.com/c/supabase)** diff --git a/apps/www/_blog/2022-10-05-supabase-beta-update-september-2022.mdx b/apps/www/_blog/2022-10-05-supabase-beta-update-september-2022.mdx index 8c418b43f0f..f1b3add99a3 100644 --- a/apps/www/_blog/2022-10-05-supabase-beta-update-september-2022.mdx +++ b/apps/www/_blog/2022-10-05-supabase-beta-update-september-2022.mdx @@ -96,7 +96,7 @@ If you made it this far in the blog post you deserve a treat. [Follow us on Twit ## Get started -- Start using Supabase today: **[app.supabase.com](https://app.supabase.com/)** +- Start using Supabase today: **[supabase.com/dashboard](https://supabase.com/dashboard/)** - Make sure to **[star us on GitHub](https://github.com/supabase/supabase)** - Follow us **[on Twitter](https://twitter.com/supabase)** - Subscribe to our **[YouTube channel](https://www.youtube.com/c/supabase)** diff --git a/apps/www/_blog/2022-11-02-supabase-beta-update-october-2022.mdx b/apps/www/_blog/2022-11-02-supabase-beta-update-october-2022.mdx index 5d43f927826..4c2250b41d4 100644 --- a/apps/www/_blog/2022-11-02-supabase-beta-update-october-2022.mdx +++ b/apps/www/_blog/2022-11-02-supabase-beta-update-october-2022.mdx @@ -116,7 +116,7 @@ If you made it this far in the blog post you deserve a treat. [Follow us on Twit ## Get started -- Start using Supabase today: **[app.supabase.com](https://app.supabase.com/)** +- Start using Supabase today: **[supabase.com/dashboard](https://supabase.com/dashboard/)** - Make sure to **[star us on GitHub](https://github.com/supabase/supabase)** - Follow us **[on Twitter](https://twitter.com/supabase)** - Subscribe to our **[YouTube channel](https://www.youtube.com/c/supabase)** diff --git a/apps/www/_blog/2022-11-17-fetching-and-caching-supabase-data-in-next-js-server-components.mdx b/apps/www/_blog/2022-11-17-fetching-and-caching-supabase-data-in-next-js-server-components.mdx index 01c432a7212..9dac31c3ece 100644 --- a/apps/www/_blog/2022-11-17-fetching-and-caching-supabase-data-in-next-js-server-components.mdx +++ b/apps/www/_blog/2022-11-17-fetching-and-caching-supabase-data-in-next-js-server-components.mdx @@ -52,9 +52,9 @@ Each folder within the `app` directory represents a route in our application. Ea > Learn more about [Page](https://beta.nextjs.org/docs/api-reference/file-conventions/page), [Layout](https://beta.nextjs.org/docs/api-reference/file-conventions/layout), [Loading](https://beta.nextjs.org/docs/api-reference/file-conventions/loading) and [Error](https://beta.nextjs.org/docs/api-reference/file-conventions/error) components in the [Next.js beta docs](https://beta.nextjs.org/docs). -Before we jump into fetching data, we need some data to fetch. Let’s [create a new Supabase project](https://app.supabase.com). +Before we jump into fetching data, we need some data to fetch. Let’s [create a new Supabase project](https://supabase.com/dashboard). -Once your instance is up and running, head over to the [SQL Editor](https://app.supabase.com/project/_/sql), paste in the following snippet and click `RUN`. +Once your instance is up and running, head over to the [SQL Editor](https://supabase.com/dashboard/project/_/sql), paste in the following snippet and click `RUN`. ```sql create table if not exists posts ( @@ -85,7 +85,7 @@ NEXT_PUBLIC_SUPABASE_URL=your-supabase-url NEXT_PUBLIC_SUPABASE_ANON_KEY=your-supabase-anon-key ``` -> The values for these can be found in [your project’s API settings](https://app.supabase.com/project/_/settings/api). +> The values for these can be found in [your project’s API settings](https://supabase.com/dashboard/project/_/settings/api). Lastly, we need to create a Supabase client. Create a file at `utils/supabase.ts` with the following content: @@ -382,7 +382,7 @@ But now we have loading spinners! Yuck! # Realtime -Realtime allows us to subscribe to changes in Supabase — inserted, updated or deleted posts — and update our UI dynamically. In order to receive realtime events, we need to [enable replication](https://app.supabase.com/project/_/database/replication) on the posts table. +Realtime allows us to subscribe to changes in Supabase — inserted, updated or deleted posts — and update our UI dynamically. In order to receive realtime events, we need to [enable replication](https://supabase.com/dashboard/project/_/database/replication) on the posts table. Let’s merge the two previous concepts and fetch the initial state of our posts in a Server Component, and then render a Client Component to do client-y things — like subscribe to changes in the DB and update the UI dynamically: diff --git a/apps/www/_blog/2022-11-22-flutter-authentication-and-authorization-with-rls.mdx b/apps/www/_blog/2022-11-22-flutter-authorization-with-rls.mdx similarity index 94% rename from apps/www/_blog/2022-11-22-flutter-authentication-and-authorization-with-rls.mdx rename to apps/www/_blog/2022-11-22-flutter-authorization-with-rls.mdx index 93ed654d530..25cbf2b6caa 100644 --- a/apps/www/_blog/2022-11-22-flutter-authentication-and-authorization-with-rls.mdx +++ b/apps/www/_blog/2022-11-22-flutter-authorization-with-rls.mdx @@ -1,5 +1,5 @@ --- -title: 'Flutter Authentication and Authorization with RLS' +title: 'Flutter Authorization with RLS' description: Learn how you can secure your Flutter app using Supabase Row Level Security. author: tyler_shukert image: flutter-chat-auth/supabase-flutter-auth.jpeg @@ -7,17 +7,18 @@ thumb: flutter-chat-auth/supabase-flutter-auth.jpeg tags: - flutter - auth - - mobile date: '2022-11-22' toc_depth: 3 --- This article is the second part of the Flutter tutorial series. During the series, you will learn how to build cross-platform apps without worrying about the backend. -In this article, I will show you how you can make a secure chat application by introducing authentication and authorization to the basic chat app that we created [previously](https://supabase.com/blog/flutter-tutorial-building-a-chat-app). +In this article, I will show you how you can make a secure chat application by introducing authorization to the basic chat app that we created [previously](https://supabase.com/blog/flutter-tutorial-building-a-chat-app). -We will use [Supabase](https://supabase.com/) to store the chat data and will use its [auth feature](https://supabase.com/auth) to provide a secure chat room feature. -Supabase lets us build a secure database where we can control who can access what part of the database, so your users' data is secured without worrying about the backend. +We will store the chat data on [Supabase](https://supabase.com/). +Supabase utilizes the built in authorization mechanism of PostgreSQL called [Row Level Security](https://supabase.com/docs/guides/auth/row-level-security) or RLS to prevent unauthorized access from accessing or writing data to your database. +RLS allows developers to define row-by-row conditions that evaluate to either `true` or `false` to either allow the access or deny it. +We will take a look at more specific examples of authorization using RLS throughout this article. ## What we created in the previous article @@ -174,7 +175,7 @@ For Android we edit the AndroidManifest.xml to register the deep link. ``` -We also need to set the deep link in our Supabase dashboard. Go to Authentication > URL Configuration in your dashboard and add `io.supabase.chat://login` as one of the redirect URLs. +We also need to set the deep link in our Supabase dashboard. Go to `Authentication > URL Configuration` in your dashboard and add `io.supabase.chat://login` as one of the redirect URLs. ![Deep link URL Configuration](/images/blog/flutter-chat-auth/url-configuration.png) @@ -188,7 +189,62 @@ The rooms page will load two types of data, recently added users and a list of r Let's start out by creating states for the rooms page. The rooms page would have four different states, loading, loaded, empty, and error. We will display different UI on the rooms page depending on what state it is. -Create a `lib/cubit/rooms/rooms_state.dart` file and paste the following code. +Satrt by defining the `Room` model. Create a `lib/models/room.dart` file and add the following code. + +```dart +import 'package:my_chat_app/models/message.dart'; + +class Room { + Room({ + required this.id, + required this.createdAt, + required this.otherUserId, + this.lastMessage, + }); + + /// ID of the room + final String id; + + /// Date and time when the room was created + final DateTime createdAt; + + /// ID of the user who the user is talking to + final String otherUserId; + + /// Latest message submitted in the room + final Message? lastMessage; + + Map toMap() { + return { + 'id': id, + 'createdAt': createdAt.millisecondsSinceEpoch, + }; + } + + /// Creates a room object from room_participants table + Room.fromRoomParticipants(Map map) + : id = map['room_id'], + otherUserId = map['profile_id'], + createdAt = DateTime.parse(map['created_at']), + lastMessage = null; + + Room copyWith({ + String? id, + DateTime? createdAt, + String? otherUserId, + Message? lastMessage, + }) { + return Room( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + otherUserId: otherUserId ?? this.otherUserId, + lastMessage: lastMessage ?? this.lastMessage, + ); + } +} +``` + +We will proceed with defining the states for the rooms page. Create `lib/cubit/rooms/rooms_state.dart` file and paste the following code. You may see some errors, but we will take care of them in the next step. ```dart title=lib/cubit/rooms/rooms_state.dart @@ -871,7 +927,7 @@ class _ChatBubble extends StatelessWidget { } ``` -### Step 3: Implementing Authentication in Flutter +### Step 3: Handle user sign-up/ sign-in Because we have modified the setting of our Supabase to send a confirmation email, we need to make some modifications to the register page and login page as well. @@ -1053,7 +1109,7 @@ class _RegisterPageState extends State { } ``` -Login page becomes more simple. +Login page becomes simpler. All it is doing is taking a user's email and password and logging them in. It is not doing any navigation whatsoever. This is because `LoginPage` is navigated on top of `RegisterPage`, the auth state listener on `RegisterPage` is still active, and therefore can take care of the navigation. @@ -1301,7 +1357,10 @@ class MyApp extends StatelessWidget { ### Step 4: Authorization with Row Level Security (RLS) -At this point, we seemingly have a complete app, except if we open the app right now, we will see every user's room with all the messages that have ever been sent within the app by any user. This is because we have not set up Row Level Security yet. [Row Level Security](https://supabase.com/docs/guides/auth/row-level-security) is a mechanism in PostgreSQL that allows developers to define who can perform what operation on a row-by-row basis. There are two ways we can define Row Level Security policies in Supabase, with the GUI or through SQL, but we will set them using SQL today. Let's run the following SQL to set the security policy. +At this point, we seemingly have a complete app. But if we open the app right now, we will see every users' room along with all the messages that have ever been sent. +This is because we have not set up Row Level Security to prevent users from accessing rooms that don't belong to them. +There are two ways we can define Row Level Security policies in Supabase: with the GUI or through SQL. Today we will use SQL. +Let's run the following SQL to set the security policy. ```sql -- Returns true if the signed in user is a participant of the room diff --git a/apps/www/_blog/2022-12-16-custom-domain-names.mdx b/apps/www/_blog/2022-12-16-custom-domain-names.mdx index 79ca54ad805..2578158b3c7 100644 --- a/apps/www/_blog/2022-12-16-custom-domain-names.mdx +++ b/apps/www/_blog/2022-12-16-custom-domain-names.mdx @@ -30,7 +30,7 @@ your users instead interact with Today, we're making Custom Domains generally available, and they're now accessible as a paid add-on through the Supabase dashboard. -Get your custom domain in the [Dashboard](https://app.supabase.com/project/_/settings/general) +Get your custom domain in the [Dashboard](https://supabase.com/dashboard/project/_/settings/general) # Vanity Subdomains diff --git a/apps/www/_blog/2022-12-16-launch-week-6-community-day.mdx b/apps/www/_blog/2022-12-16-launch-week-6-community-day.mdx index eb485c2bb11..57151cd6278 100644 --- a/apps/www/_blog/2022-12-16-launch-week-6-community-day.mdx +++ b/apps/www/_blog/2022-12-16-launch-week-6-community-day.mdx @@ -16,7 +16,7 @@ Supabase is a collaborative company. We work with, sponsor, and support as many ## New Integrations -While we've been working hard on new features, the community has been developing a healthy [integrations marketplace](https://supabase.com/docs/guides/integrations/integrations). Since Supabase is just Postgres, any tool which works with Postgres works with Supabase (no additional changes required). Today we're adding a few major players to the roster: +While we've been working hard on new features, the community has been developing a healthy [integrations marketplace](https://supabase.com/partners/integrations/integrations). Since Supabase is just Postgres, any tool which works with Postgres works with Supabase (no additional changes required). Today we're adding a few major players to the roster: ### FlutterFlow @@ -56,7 +56,7 @@ Watch John from the FlutterFlow team demonstrate how you can [get started with F Combining OneSignal with Supabase, you can send cloud messages to your users with any database change. -We have guides on how to get started with [Next.js and OneSignal](https://supabase.com/docs/guides/integrations/onesignal), and [Flutter and OneSignal](https://github.com/OneSignalDevelopers/onesignal-supabase-sample-integration-supabase). +We have guides on how to get started with [Next.js and OneSignal](https://supabase.com/partners/integrations/onesignal), and [Flutter and OneSignal](https://github.com/OneSignalDevelopers/onesignal-supabase-sample-integration-supabase). In the video above, William from OneSignal will show you how you can easily integrate OneSignal with your Supabase app. @@ -87,7 +87,7 @@ In [this community spotlight video](https://youtube.com/EdYQ9fF-hz4), [Thang Huu Over the past few years, we've had a number of people ask if we can add Full Text Search for other languages. PGroonga has been on our radar for a long time now, so when we reached out to them we were surprised to learn that _no_ Postgres platform currently supports them. Today we're fixing this by becoming the first. -PostgreSQL of course features excellent [Full Text Search](https://supabase.com/docs/guides/database/full-text-search) out-the-box, but it can be lacking if you need to search against languages with non-Roman characters such as Japanese, Chinese and so on. For this you can use the PGroonga extension! It's already installed on all new projects, just head to the [Database tab](https://app.supabase.com/project/_/database/extensions) on the dashboard and search or scroll for 'PGroonga' and hit enable to get started. Once enabled you can head to [their excellent documentation](https://pgroonga.github.io/tutorial/) on how to start making queries. +PostgreSQL of course features excellent [Full Text Search](https://supabase.com/docs/guides/database/full-text-search) out-the-box, but it can be lacking if you need to search against languages with non-Roman characters such as Japanese, Chinese and so on. For this you can use the PGroonga extension! It's already installed on all new projects, just head to the [Database tab](https://supabase.com/dashboard/project/_/database/extensions) on the dashboard and search or scroll for 'PGroonga' and hit enable to get started. Once enabled you can head to [their excellent documentation](https://pgroonga.github.io/tutorial/) on how to start making queries. PGroonga is backed by a collection of incredible maintainers, contributors, and a broad and diverse [community](https://pgroonga.github.io/community/). diff --git a/apps/www/_blog/2022-12-16-postgres-point-in-time-recovery.mdx b/apps/www/_blog/2022-12-16-postgres-point-in-time-recovery.mdx index dbce9c96b54..a566c13a3e0 100644 --- a/apps/www/_blog/2022-12-16-postgres-point-in-time-recovery.mdx +++ b/apps/www/_blog/2022-12-16-postgres-point-in-time-recovery.mdx @@ -17,7 +17,7 @@ When we announced Point-in-Time a few months ago, it was only available for Ente Pro-plan projects created from August 2022 onwards (excluding Northeast Asia / Seoul region), can now enable Point In Time Recovery (PITR) from the dashboard. Projects must have the "Small" compute add-on to ensure that PITR operates smoothly behind the scenes. -For older projects, don't worry - we're working on making it available for everybody. In the meantime, if you would like to have PITR enabled for your ineligible project, reach out directly [here](https://app.supabase.com/support/new), or stay tuned for further updates. +For older projects, don't worry - we're working on making it available for everybody. In the meantime, if you would like to have PITR enabled for your ineligible project, reach out directly [here](https://supabase.com/dashboard/support/new), or stay tuned for further updates. ## What is Point in Time Recovery? @@ -39,7 +39,7 @@ While all Pro-plan projects and above are backed up on a daily basis, this means ## Getting started -Enabling Point in Time Recovery can be done [in the Dashboard](https://app.supabase.com/project/_/settings/billing/subscription?panel=pitr). Retention for backups used by PITR is set to up to 7 days by default but could be increased to up to 28 days via self-serve. +Enabling Point in Time Recovery can be done [in the Dashboard](https://supabase.com/dashboard/project/_/settings/billing/subscription?panel=pitr). Retention for backups used by PITR is set to up to 7 days by default but could be increased to up to 28 days via self-serve. ## More Launch Week 6 diff --git a/apps/www/_blog/2022-12-16-vault-now-in-beta.mdx b/apps/www/_blog/2022-12-16-vault-now-in-beta.mdx index ef7a62d1640..fcc9bbec397 100644 --- a/apps/www/_blog/2022-12-16-vault-now-in-beta.mdx +++ b/apps/www/_blog/2022-12-16-vault-now-in-beta.mdx @@ -22,7 +22,7 @@ Vault is a new Postgres extension and accompanying Supabase UI that makes it saf ❇️ UPDATE JUNE 2023 ❇️ -Supabase Vault is now available on every Supabase project. [Check it out](https://app.supabase.com/project/_/settings/vault/secrets) +Supabase Vault is now available on every Supabase project. [Check it out](https://supabase.com/dashboard/project/_/settings/vault/secrets)
    @@ -89,7 +89,7 @@ Privacy is becoming one of the most important features in a modern product. Supa ### Using the Vault -To learn how to use Supabase Vault, check out the [docs](https://supabase.com/docs/guides/database/vault). Supabase Vault is now available on all [Supabase projects](https://app.supabase.com/project/_/settings/vault/secrets) (some projects will have to enable via request). +To learn how to use Supabase Vault, check out the [docs](https://supabase.com/docs/guides/database/vault). Supabase Vault is now available on all [Supabase projects](https://supabase.com/dashboard/project/_/settings/vault/secrets) (some projects will have to enable via request). ## More Launch Week 6 diff --git a/apps/www/_blog/2023-02-16-case-study-happyteams.mdx b/apps/www/_blog/2023-02-16-case-study-happyteams.mdx index cd7366094c0..47c90329823 100644 --- a/apps/www/_blog/2023-02-16-case-study-happyteams.mdx +++ b/apps/www/_blog/2023-02-16-case-study-happyteams.mdx @@ -5,7 +5,6 @@ author: rory_wilding image: 2023-02-16-case-study-happyteams/case-study-happyteams.png thumb: 2023-02-16-case-study-happyteams/case-study-happyteams.png tags: - - case-study date: '2023-02-16' toc_depth: 3 --- diff --git a/apps/www/_blog/2023-03-09-supabase-beta-update-february-2023.mdx b/apps/www/_blog/2023-03-09-supabase-beta-update-february-2023.mdx index 6b43011f606..42d8126000c 100644 --- a/apps/www/_blog/2023-03-09-supabase-beta-update-february-2023.mdx +++ b/apps/www/_blog/2023-03-09-supabase-beta-update-february-2023.mdx @@ -18,7 +18,7 @@ There’s something for everybody this month: AI, Auth, Database, Edge Functions The most popular GraphQL IDE/API explorer is now built into the dashboard! You can now explore and query your GraphQL API produced by `pg_graphql`. -[Try it now](https://app.supabase.com/project/_/api/graphiql). +[Try it now](https://supabase.com/dashboard/project/_/api/graphiql). ## Supabase + OpenAI search @@ -52,7 +52,7 @@ We rewrote the Postgres Dockerfile with multi-stage builds so that each extensio We've improved database role management. You can create, update, and delete database roles through the dashboard. Just one small step towards column-level security -[Check it out.](https://app.supabase.com/project/_/editor) +[Check it out.](https://supabase.com/dashboard/project/_/editor) ## API docs in the table editor @@ -60,7 +60,7 @@ We've improved database role management. You can create, update, and delete data API docs got a light touchup and were moved to the table editor. You can now look up API methods and generate & download type files right there ✨ -[Check it out.](https://app.supabase.com/project/_/editor) +[Check it out.](https://supabase.com/dashboard/project/_/editor) ## Quick product updates @@ -70,9 +70,9 @@ API docs got a light touchup and were moved to the table editor. You can now loo - **Database**: supabase-js now infers the response type from your query. If the inferred type is incorrect, you can use `.returns()` to override it. [Doc](https://supabase.com/docs/reference/javascript/db-returns) -- **Dashboard**: Improved database roles management, you can now create, update and delete database roles through the dashboard. [Dashboard](https://app.supabase.com/project/_/database/roles) +- **Dashboard**: Improved database roles management, you can now create, update and delete database roles through the dashboard. [Dashboard](https://supabase.com/dashboard/project/_/database/roles) -- **Dashboard**: We've provided a reference panel showing all available paths that can be queried from each respective source that improves the Logs Explorer experience. [Dashboard](https://app.supabase.com/project/_/logs/explorer) +- **Dashboard**: We've provided a reference panel showing all available paths that can be queried from each respective source that improves the Logs Explorer experience. [Dashboard](https://supabase.com/dashboard/project/_/logs/explorer) - **Edge Functions**: upgraded to Deno 1.30.3, that supports TypeScript 4.9.x and introduces `satisfies`. Thanks to [Benjamin Dobell](https://github.com/Benjamin-Dobell) 🙏. [PR](https://github.com/supabase/functions-relay/pull/30) diff --git a/apps/www/_blog/2023-04-13-supabase-auth-sso-pkce.mdx b/apps/www/_blog/2023-04-13-supabase-auth-sso-pkce.mdx index e8783c245d3..ecf8951c8e1 100644 --- a/apps/www/_blog/2023-04-13-supabase-auth-sso-pkce.mdx +++ b/apps/www/_blog/2023-04-13-supabase-auth-sso-pkce.mdx @@ -39,7 +39,7 @@ Building SSO into your application isn't necessarily hard, but does come with so ### Getting Started with SAML 2.0 -To get started, enable the [“SAML 2.0“](https://app.supabase.com/project/phcnitosaawbzytgyznx/auth/providers) [authentication method in the dashboard](https://app.supabase.com/project/_/auth/providers). We've added new commands to the [Supabase CLI](https://supabase.com/docs/guides/cli) to help with the configuration process: +To get started, enable the [“SAML 2.0“](https://supabase.com/dashboard/project/phcnitosaawbzytgyznx/auth/providers) [authentication method in the dashboard](https://supabase.com/dashboard/project/_/auth/providers). We've added new commands to the [Supabase CLI](https://supabase.com/docs/guides/cli) to help with the configuration process: ```bash $ supabase sso --help diff --git a/apps/www/_blog/2023-05-04-flutter-multi-factor-authentication.mdx b/apps/www/_blog/2023-05-04-flutter-multi-factor-authentication.mdx index ad11ad53020..c160b6251d4 100644 --- a/apps/www/_blog/2023-05-04-flutter-multi-factor-authentication.mdx +++ b/apps/www/_blog/2023-05-04-flutter-multi-factor-authentication.mdx @@ -6,6 +6,7 @@ image: flutter-mfa/flutter-mfa-thumb-og.png thumb: flutter-mfa/flutter-mfa-thumb.jpg tags: - flutter + - auth date: '2023-05-04' toc_depth: 2 --- diff --git a/apps/www/_blog/2023-06-09-supabase-beta-update-may-2023.mdx b/apps/www/_blog/2023-06-09-supabase-beta-update-may-2023.mdx index d7e31a88be1..674be2fb213 100644 --- a/apps/www/_blog/2023-06-09-supabase-beta-update-may-2023.mdx +++ b/apps/www/_blog/2023-06-09-supabase-beta-update-may-2023.mdx @@ -100,7 +100,7 @@ Bonus track: make sure to also check out [ChartGPT](https://www.chartgpt.dev/), ![Community](/images/blog/2022-june/community.jpg) -- We worked with Cloudflare on a new integration that makes it super easy to connect your Supabase database from Cloudflare Workers. [Doc](https://supabase.com/docs/guides/integrations/cloudflare-workers) +- We worked with Cloudflare on a new integration that makes it super easy to connect your Supabase database from Cloudflare Workers. [Doc](https://supabase.com/partners/integrations/cloudflare-workers) - Deno Fresh Starter: ChatGPT-style doc search. [Blog post](https://deno.com/blog/build-chatgpt-doc-search-with-supabase-fresh) diff --git a/apps/www/_blog/2023-06-27-native-mobile-auth.mdx b/apps/www/_blog/2023-06-27-native-mobile-auth.mdx new file mode 100644 index 00000000000..d9a3c7aeb43 --- /dev/null +++ b/apps/www/_blog/2023-06-27-native-mobile-auth.mdx @@ -0,0 +1,133 @@ +--- +title: Native Mobile Auth Support for Google and Apple Sign in +description: Supabase auth adds full support for native mobile sign in with Apple and Google. +author: stojan,tyler_shukert +image: native-mobile-auth/native-mobile-auth.png +thumb: native-mobile-auth/native-mobile-auth.png +tags: + - auth +date: '2023-06-27' +toc_depth: 3 +--- + +Supabase supports OAuth logins with 17 providers including Apple, Google, Microsoft, GitHub, … But for native mobile apps, this meant that developers had to use a web browser to sign in. It’s not an ideal flow for users, who are already used to signing in with the operating system’s native dialogs when possible. Today, we are excited to announce full native support for Sign in with Apple and Google on iOS and Android. But this is not all! Supabase Auth now can now be used with one-tap sign in methods like: Sign in with Apple JS, Sign in with Google for Web or even in Chrome extensions. + +## Native Sign in with Apple and Google + +Developers of native iOS and Android apps (using Flutter or React Native) can now take advantage of OS-provided authentication dialogs for Apple and Google. This is available on iOS, macOS, tvOS and watchOS apps in the Apple ecosystem, and all Android variants in the Google ecosystem. + +In full transparency, this was always sort-of possible but there were some edge cases that were not covered well with Supabase Auth. We’ve since ironed out the developer experience and made this into a fully supported feature. + +Behind the scenes, these native sign in methods use ID tokens. They’re a formalized version of a JWT that is issued by Apple or Google and contain profile information. Supabase Auth now can properly validate the ID tokens and create new or link to existing user accounts based on email similarity. + +### Using Sign in with Google in a Flutter App + +To setup Sign in with Google in your Flutter native app, you need to set up your Google Cloud project for each platform: + +- For iOS apps: [Get started with Google Sign In for iOS and macOS](https://developers.google.com/identity/sign-in/ios/start-integrating#get_an_oauth_client_id) +- For Android apps: [Get started with One Tap sign-in and sign-up for Android](https://developers.google.com/identity/one-tap/android/get-started) + +We’ve revamped the Google provider [configuration screen in the Supabase Dashboard](https://supabase.com/dashboard/project/_/auth/providers). Besides the existing OAuth flow, you can now add additional _Authorized Client IDs_ meant for native sign in. + +The client ID you obtained from the setup instructions above should be added to _Authorized Client IDs._ + +![Supabase dashboard Google provider](/images/blog/native-mobile-auth/google-setting.png) + +It really is as simple as that. + +### Using Sign in with Apple in a Flutter App + +We introduced support for native Sign in with Apple in our [previous launch week](https://supabase.com/blog/supabase-auth-sso-pkce#native-apple-login-on-ios#native-apple-login-on-ios), but today we are adding support for multiple apps on a single Supabase project. + +All you need to do is to register all of the bundle IDs of your apps in the [Supabase Dashboard](https://supabase.com/dashboard/project/_/auth/providers) under _Authorized Client IDs_ as a comma separated string. + +![Supabase dashboard Apple provider](/images/blog/native-mobile-auth/apple-setting.png) + +## Using Sign in with Apple and Google in React Native Apps + +If you use React Native to build your native apps, you can still benefit. Please check out these resources to see how you can show the native authentication dialog and obtain an ID token from the operating system: + +- https://github.com/invertase/react-native-apple-authentication +- [Expo AppleAuthentication](https://docs.expo.dev/versions/latest/sdk/apple-authentication/) +- https://github.com/react-native-google-signin/google-signin + +You would still need to configure the _Authorized Client IDs_ as shown in the Flutter examples above. + +Finally, once you’ve received a valid ID token from the operating system you can call `supabase.auth.signInWithIdToken()` to complete the sign in with your Supabase project. + +For Sign in with Apple: + +```tsx +await supabase.auth.signInWithIdToken({ + provider: 'apple', + token: '', +}) +``` + +For Sign in with Google: + +```tsx +await supabase.auth.signInWithIdToken({ + provider: 'google', + token: '', +}) +``` + +We also have some sample implementation for Expo apps in our [create-t3-turbo example](https://github.com/supabase-community/create-t3-turbo/blob/main/apps/expo/src/app/profile.tsx#L47). + +## Using Sign in with Apple and Google in Flutter Apps + +Once you have configured your Supabase instance, you can utilize third party libraries like **[sign_in_with_apple](https://pub.dev/packages/sign_in_with_apple) or [google_one_tap_sign_in](https://pub.dev/packages/google_one_tap_sign_in) to perform naive sign in, and pass the ID token to Supabase to complete the sign in.** + +For Sign in with Apple: + +```dart +final credential = await SignInWithApple.getAppleIDCredential( + scopes: [ + AppleIDAuthorizationScopes.email, + AppleIDAuthorizationScopes.fullName, + ], +); + +await supabase.auth.signInWithIdToken( + provider: Provider.apple, + idToken: credential.identityToken!, +); +``` + +And for Google: + +```dart +var result = await GoogleOneTapSignIn.handleSignIn(webClientId: ''); + +supabase.auth.signInWithIdToken( + provider: Provider.google, + idToken: result.data!.idToken!, +); +``` + +You can find more detailed instructions on the [here](https://github.com/supabase/supabase-flutter#authentication). + +## Sign in with Apple JS, Google One Tap and Chrome Extensions + +Although sign in on native platforms was the focus of the team when working on this feature, incidentally we’ve added proper support for Sign in with Apple JS, Google’s One Tap and support for authenticating within Google Chrome extensions. + +You can now take advantage of these web frameworks, most notably Google’s One Tap and Automatic Sign-in support for a frictionless onboarding experience for your users. + +All you need to do is configure the web frameworks and use the `signInWithIdToken()` method to pass the ID token provided by the Google and Apple libraries. + +For example, to use Google One tap you should first [generate an embed code for the Google Sign in Button](https://developers.google.com/identity/gsi/web/tools/configurator). Register this method as the callback that will receive the authentication response from the button: + +```tsx +async function handleSignInWithGoogle(response) { + const { data, error } = await supabase.auth.signInWithIdToken({ + token: response.credential, + nonce: 'NONCE', // must be the same one as provided in data-nonce (if any) + }) +} +``` + +## Resources + +- [Login with Apple Guide](https://supabase.com/docs/guides/auth/social-login/auth-apple) +- [Login with Google Guide](https://supabase.com/docs/guides/auth/social-login/auth-google) diff --git a/apps/www/_blog/2023-07-06-supabase-beta-update-june-2023.mdx b/apps/www/_blog/2023-07-06-supabase-beta-update-june-2023.mdx new file mode 100644 index 00000000000..6651b5bb101 --- /dev/null +++ b/apps/www/_blog/2023-07-06-supabase-beta-update-june-2023.mdx @@ -0,0 +1,107 @@ +--- +title: Supabase Beta June 2023 +description: A plethora of announcements... read till the end to find out when is the new Launch Week +author: ant_wilson +image: 2023-07-06-beta-update-june/beta-update-june-2023.png +thumb: 2023-07-06-beta-update-june/beta-update-june-2023.png +tags: + - release-notes +date: '2023-07-06' +toc_depth: 3 +--- + +Guess what? We've got some sizzling updates for you this month! Our partnership with Mozilla, Native mobile auth support, revamped billinx experience, real-time debugging magic, and great stuff from the community. Dive in now 🤿 + +## Mozilla uses Supabase to build AI Help. + +![Mozilla uses Supabase to build AI Help.](https://obuldanrptloktxcffvn.supabase.co/storage/v1/object/public/images/marketing-emails/mdn_search.png) + +MDN is one of the richest sources of developer documentation on the internet, and it just received an upgrade. Mozilla built AI Help, an AI chatbot to help with search and discovery. Mozilla chose Supabase Vector and OpenAI to power their new tool which you can try for free. + +[Read the announcement](https://developer.mozilla.org/en-US/blog/introducing-ai-help/) + +## Native Mobile Auth Support for Google and Apple Sign in + +![Native Mobile Auth Support for Google and Apple Sign in](https://obuldanrptloktxcffvn.supabase.co/storage/v1/object/public/images/marketing-emails/native-phone-support.png) + +Supabase Auth now has full native support for Sign in with Apple and Google, which means it can now be used with one-tap sign in methods like Sign in with Apple JS, Sign in with Google for Web, or even in Chrome extensions. + +[Learn more](https://supabase.com/blog/native-mobile-auth) + +## Supabase CLI: what is new? + +![Supabase CLI: what is new?](https://obuldanrptloktxcffvn.supabase.co/storage/v1/object/public/images/marketing-emails/supabase-cli-cache-hi.png) + +It’s been a busy month for the Supabase CLI. We have added a tonne of new features: + +- **Migrations:** view all your migrations inside the Dashboard with the SQL [code that was executed](https://github.com/supabase/cli/pull/1181). +- **Functions:** the CLI now [serves all Functions](https://github.com/supabase/cli/pull/1171) when developing locally. +- **Database monitoring**: we’ve added a `usage` flag for [monitoring key Postgres statistics](https://github.com/supabase/cli/pull/1070). + +[See all updates](https://github.com/supabase/cli/releases) + +## Revamped billing experience + +![Revamped billing experience](https://obuldanrptloktxcffvn.supabase.co/storage/v1/object/public/images/marketing-emails/billing.png) + +We have made huge improvements to the billing tooling inside Supabase Studio, including: + +- Easy monitoring of current usage, overage, and plan limits. +- Streamlined subscription management for upgrades or downgrades. +- Detailed usage billing breakdowns and compute instance specifications. +- And more! + +[New subscription page](https://app.supabase.com/project/_/settings/billing/subscription) | [New usage page](https://app.supabase.com/project/_/settings/billing/usage) + +## Login with Kakao + +![Login with Kakao](https://obuldanrptloktxcffvn.supabase.co/storage/v1/object/public/images/marketing-emails/kakao-social-login.png?t=2023-07-06T13%3A26%3A11.108Z) + +Added the popular social platform Kakao as new social provider. Allow your users to effortlessly sign in using their Kakao accounts and make authentication a breeze while expanding your app's reach to a wider audience. + +[Login with Kakao](https://supabase.com/docs/guides/auth/social-login/auth-kakao) + +## Quick product updates + +- [**Postgres Tooling**] Implemented parallel Index build in regular and recovery state into OrioleDB. [[PR](https://github.com/orioledb/orioledb/commit/8c4a50175cf39a97ac0555ea3f969fe7e40dba21)] +- [**Edge Functions**] Edge Functions troubleshooting guide. [[Doc](https://supabase.com/docs/guides/functions/troubleshooting)] +- [**Storage**] Object id is now returned in the response when uploading an object. [[PR](https://github.com/supabase/storage-api/pull/332)] +- [**Realtime**] A new debugging tool to test your realtime endpoints together with your JWTs and Row Level Security policies. [[Realtime Inspector](https://realtime.supabase.com/inspector/new)] +- [**Docs**] A full guide on database partitions for developers looking to scale up. [[Guide](https://supabase.com/docs/guides/database/partitions)] + +## Made with Supabase + +![Chat Thing is Made with Supabase](https://obuldanrptloktxcffvn.supabase.co/storage/v1/object/public/images/chatting-ai.png) + +[Chat Thing](https://chatthing.ai/), is an innovative project that allows you to create AI chatbots using your existing data from Notion, uploaded files, websites and more. Built by [pixelhop](https://twitter.com/pixelhopio) using Vue, Nuxt, Railway, and Supabase for database, auth, and vector store. + +## Extended Community Highlights + +![Some of the faces from the community](https://obuldanrptloktxcffvn.supabase.co/storage/v1/object/public/images/marketing-emails/flutter_auth.png) + +- Using Supabase with Cloudflare Workers. [Cloudflare TV](https://cloudflare.tv/event/using-supabase-with-cloudflare-workers/dgM90RgD) +- Build a Chatbot with Next.js, LangChain, OpenAI, and Supabase Vector. [Video Tutorial](https://www.youtube.com/watch?v=Tt45NrVIBn8) +- The refine open source Hackathon is in full swing. [Join now](https://discord.com/channels/@me/1118414473108148296/1123933930702262312) +- Flutter database and user authentication. [Video guide](https://www.youtube.com/watch?v=r7ysVtZ5Row) +- Google authentication with Expo & Supabase. [Tutorial](https://blog.spirokit.com/google-authentication-with-expo-supabase) +- Building Stripe integrations with Supabase. [Video](https://www.youtube.com/watch?v=D5kOnnB72WA) +- Build a Supabase + Drizzle-orm + Express backend. [Video Tutorial](https://www.youtube.com/watch?v=k0uC35J_xJ4) +- LogRocker blog: Authenticate React Applications: Supabase Auth. [Tutorial](https://blog.logrocket.com/authenticate-react-applications-supabase-auth/) +- Using Supabase Vault to store secrets. [Tutorial](https://makerkit.dev/blog/tutorials/supabase-vault) +- Nuxt + Supabase = Technology Stack of Dreams. [Blog post](https://dev.to/jacobandrewsky/nuxt-supabase-technology-stack-of-dreams-4dop) +- Build a to-do app with toddle and Supabase. [Video](https://www.youtube.com/watch?v=SHJ3EadcrgY) + +## We're Hiring + +Come join one of the fastest-growing open source projects ever: + +- [Front-End Developer (o11y focus)](https://boards.greenhouse.io/supabase/jobs/4910374004) +- [Rust developer](https://boards.greenhouse.io/supabase/jobs/4917893004) + +## ⚠️ Baking hot meme zone ⚠️ + +![Our favorite meme from June 2023](https://obuldanrptloktxcffvn.supabase.co/storage/v1/object/public/images/marketing-emails/beta-update-june-2023-meme.png) + +## Launch Week alert: save the date + +![Launch Week 8 is announced - From Monday 07/08 till Friday 11/08](https://obuldanrptloktxcffvn.supabase.co/storage/v1/object/public/images/marketing-emails/lw8-teaser.jpg) diff --git a/apps/www/_blog/2023-07-12-postgrest-11-1-release.mdx b/apps/www/_blog/2023-07-12-postgrest-11-1-release.mdx new file mode 100644 index 00000000000..9637dedf858 --- /dev/null +++ b/apps/www/_blog/2023-07-12-postgrest-11-1-release.mdx @@ -0,0 +1,248 @@ +--- +title: 'What is new in PostgREST v11.1?' +description: 'Impersonated Role Settings, Configurable Isolation Level, improved Bulk Insert, and more' +author: steve_chavez +image: lw6-community/postgrest.png +thumb: lw6-community/postgrest.png +tags: + - postgres +date: '2023-07-12' +toc_depth: 3 +--- + +PostgREST 11.1 is now available on the Supabase platform. Besides the [pre-release](https://supabase.com/blog/postgrest-11-prerelease) features, we’ve added configuration and querying improvements. Here is what's new: + +## Impersonated Role Settings + +Every role that passes [PostgREST JWT Authentication](https://postgrest.org/en/stable/references/auth.html#jwt-based-user-impersonation) is an _impersonated role_. On the Supabase platform, these are the `anon`, `authenticated` and `service_role` roles. + +These roles can now have settings applied with a regular `ALTER ROLE .. SET`. This is useful, for example, to prevent web users from running expensive queries. + +Let’s try it by setting a statement timeout and cost limit. + +### Statement timeout + +`statement_timeout` aborts any statement that takes more than the specified amount of time. Let’s set it for the `anon`, `authenticated` and `service_role` roles: + +```sql +-- anonymous users can run queries that take 100 milliseconds max +alter + role anon +set + statement_timeout = '100ms'; + +-- authenticated users can run queries that take 5 seconds max +alter + role authenticated +set + statement_timeout = '5s'; + +-- backend-only users can run queries that take 15 seconds max +alter + role service_role +set + statement_timeout = '15s'; +``` + +You need to reload PostgREST config cache to apply these changes. + +```sql +NOTIFY pgrst, +'reload config'; +``` + +Now, suppose you do an expensive query with the `anon` role. Like filtering on a big table's unindexed column (this will cause a full table scan): + +```jsx +const { data, error } = await supabase.from('big_table').select().eq('unindexed_column', 'value') +``` + +Then, after 5 seconds, the request will be aborted with the response: + +```json +{ + "hint": null, + "details": null, + "code": "57014", + "message": "canceling statement due to statement timeout" +} +``` + +Which is what we wanted. Note that there's already a global `statement_timeout` set but you can be more fine-grained with this feature. See [timeouts](https://supabase.com/docs/guides/database/timeouts) for more details. + +### Statement Cost Limit + +With a statement timeout, expensive queries will still get executed for a length of time. They'll consume resources until they’re terminated. + +The [pg_plan_filter](https://github.com/pgexperts/pg_plan_filter) extension (available on the Supabase platform), brings a statement cost limit. This abort queries at the planning phase, before they get executed. + +You can use it like: + +```sql +-- anonymous users can only run cheap queries +ALTER + USER anon +SET + plan_filter.statement_cost_limit = 10000; + +-- authenticated users can run more expensive queries +ALTER + USER authenticated +SET + plan_filter.statement_cost_limit = 1e6; + +-- backend-only users can run any query +ALTER + USER service_role +SET + plan_filter.statement_cost_limit = 0; + +NOTIFY pgrst, +'reload config'; + +-- reload postgREST config cache to apply changes +``` + +Let’s repeat the previous expensive query with the `anon` role. + +```jsx +const { data, error } = await supabase.from('big_table').select().eq('unindexed_column', 'value') +``` + +Then, immediately, the request will be aborted and the response will be: + +```jsx +{ + "hint": null, + "details": null, + "code": "54001", + "message": "plan cost limit exceeded" +} +``` + +Note that tuning is required to get the cost limit right. You should use the `plan_filter.statement_cost_limit` with care as it can invalidate legitimate queries. + +## Configurable Transaction Isolation Level + +By default, all queries run in a transaction with the default _read committed_ isolation level. + +You can now modify this with the `default_transaction_isolation` setting. + +If you want a function to run with _repeatable read_ isolation level: + +```sql +create function hello() +returns text as $$ + select 'hello'; +$$ language sql +set default_transaction_isolation = 'repeatable read'; +``` + +Or if you want an impersonated role to run its queries with a _serializable_ isolation level: + +```sql +alter + role service_role +set + default_transaction_isolation = 'serializable'; + +NOTIFY pgrst, +'reload config'; + +-- reload postgREST config cache +``` + +Note that the default _read committed_ is good enough for almost all use cases. Higher isolation levels incur in overhead as they use more sophisticated locking. They're only needed in special cases. + +## Bulk insert JSON with default values + +A long wanted feature was bulk inserting JSON while considering columns' default values. + +Having the following sample table. + +```sql +create table + foo ( + id bigint generated by default as identity primary key, + bar text, + baz int default 100 + ); +``` + +You can now do it like this: + +```jsx +const { error } = await supabase + .from('foo') + .insert([ + { "bar": "val1" + } + , { "bar": "val2" + , "baz": 15 + } + ], defaultToNull: false) + .select() +``` + +And the response will be: + +```json +[ + { "id": 1, "bar": "val1", "baz": 100 }, + { "id": 2, "bar": "val2", "baz": 15 } +] +``` + +As you can see, `id` and `baz` took their default values. + +## ANY/ALL filter modifiers + +As a shortcut to `OR` filters, you can now use `any` modifiers on various filters. Take the `like` filter as an example: + +{/* prettier-ignore */} +```jsx +const res = await postgrest + .from('users') + .select() + .likeAnyOf('username', ['%supa%', '%kiwi%']) +``` + +This is equivalent to the following in SQL. + +```sql +select * +from users +where username like ANY('{%supa%,%kiwi%}'); +``` + +`any` modifiers are available for the `eq,like,ilike,gt,gte,lt,lte,match,imatch` filters. + +For completeness, the `all` modifier is also included. + +## Minimal Breaking Changes from v10 + +If you only use PostgREST through Supabase client libraries (like [`supabase-js`](https://supabase.com/docs/reference/javascript/introduction)) then it's safe to upgrade to v11. If you use PostgREST with other HTTP clients (like `curl`), consider the breaking changes for this version: + +- The `Range` header is now only considered on GET requests and is ignored for any other method. Previously PostgREST responded with an error but [RFC 9110](https://www.rfc-editor.org/rfc/rfc9110.html#name-range) dictates that we should ignore the `Range` header instead. +- RPC requests no longer consider the `Prefer: params=multiple-objects` header. This header was already deprecated on [v10.1.0](https://github.com/PostgREST/postgrest/releases/tag/v10.1.0). + +By making use of [Logflare](https://logflare.app/), we detected that out of 20 thousands of projects: + +- Only 7 projects used `Range` for HTTP methods other than GET. In these cases all responses were errors so in fact this breaking change is a fix for those requests. +- None were using `Prefer: params=multiple-objects`. + +So overall the breaking changes are minimal. + +## Closing up + +There you have it, now you can make your API more secure with role settings and use higher isolation levels without resorting to direct PostgreSQL connections. + +PostgREST v11.1 is available for all Supabase projects created after 5 July 2023. Existing projects can upgrade by doing a pause/unpause. + +## More Postgres resources + +- [Storing OpenAI embeddings in Postgres with pgvector](https://supabase.com/blog/openai-embeddings-postgres-vector) +- [Choosing a Postgres Primary Key](https://supabase.com/blog/choosing-a-postgres-primary-key) +- [SQL or NoSQL? Why not use both (with PostgreSQL)?](https://supabase.com/blog/sql-or-nosql-both-with-postgresql) +- [pg_jsonschema: JSON Schema support for Postgres](https://supabase.com/blog/pg-jsonschema-a-postgres-extension-for-json-validation) +- [Implementing "seen by" functionality with Postgres](https://supabase.com/blog/seen-by-in-postgresql) diff --git a/apps/www/_blog/2023-07-13-pgvector-performance.mdx b/apps/www/_blog/2023-07-13-pgvector-performance.mdx new file mode 100644 index 00000000000..e0cd741f223 --- /dev/null +++ b/apps/www/_blog/2023-07-13-pgvector-performance.mdx @@ -0,0 +1,311 @@ +--- +title: 'pgvector 0.4.0 performance' +description: There's been lot of talk about pgvector performance lately, so we took some datasets and pushed pgvector to the limits to find out its strengths and limitations. +tags: + - AI + - performance + - postgres + - planetpg +date: '2023-07-13' +toc_depth: 2 +author: egor_romanov,pavel +image: 2023-07-13-pgvector-performance/vector-benchmarks-og.jpeg +thumb: 2023-07-13-pgvector-performance/vector-benchmarks-thumb.jpeg +--- + +There are a few pgvector benchmarks floating around the internet, most recently a [pgvector vs Qdrant](https://nirantk.com/writing/pgvector-vs-qdrant/) comparison by NirantK. We wanted to reproduce (or improve!) the results. + +There is an obvious bias here: we're a Postgres company. It's not our goal to prove that pgvector is better than Qdrant for running vector workloads. From everything we hear about Qdrant, it's fantastic. + +Our goals in this article are: + +1. To show the strengths and limitations of the _current version_ of pgvector. +2. Highlight some improvements that are coming to pgvector. +3. Prove to you that it's completely viable for production workloads and give you some tips on using it at scale. We'll show you how to run 1 million Open AI embeddings at ~1800 requests per second with 91% precision, or 670 requests per second with 98% precision. + +## Benchmark Methodology + +We've used the [ANN Benchmarks](https://github.com/erikbern/ann-benchmarks) methodology, a standard for benchmarking vector databases. + +The key elements are: + +- **Helper scripts:** a Python test runner which is responsible for data upload, index creation, and query execution. This uses [qdrant's vector-db-benchmark](https://github.com/qdrant/vector-db-benchmark) repo. The “engine” in this repo uses [Vecs](https://github.com/supabase/vecs), a Python client for pgvector. +- **Runtime:** Each test runs for at least 30-40 minutes and included a series of experiments executed at various concurrency levels. This process allowed us to gauge the engine's performance under different load types. Subsequently, we averaged the results. +- **Pre-warming RAM:** We executed 10,000 to 50,000 “warm-up” queries before each benchmark, matching the number of probes as the benchmark. Additionally, we executed about 1,000 queries with probes ranging from three to ten times the benchmark's probes. Both of these help with RAM utilization. + +
    + multi database + multi database +
    + +## Hardware + +All compute add-ons available on Supabase were used to run our benchmarks. Each add-on variant has a different allocation of RAM and CPU cores, the details of which are available in our docs. Each Supabase compute add-on comes with a specific set of optimizations ([version 2023-07](https://gist.github.com/egor-romanov/323e2847851bbd758081511785573c08)). + +| Instance | CPU | Memory | +| -------- | ----------------------- | ------ | +| 2XL | 8-core ARM (dedicated) | 32 GB | +| 4XL | 16-core ARM (dedicated) | 64 GB | +| 8XL | 32-core ARM (dedicated) | 128 GB | +| 12XL | 48-core ARM (dedicated) | 192 GB | +| 16XL | 64-core ARM (dedicated) | 256 GB | + +## Dataset + +We tested using the same dataset as the Qdrant comparison: [dbpedia-entities-openai-1M](https://huggingface.co/datasets/KShivendu/dbpedia-entities-openai-1M). This dataset includes 1M embeddings with 1536 dimensions (created using OpenAI). The embeddings are created by Wikipedia articles. It's a great dataset! + +
    + +We also have some useful [benchmarks in our docs](https://supabase.com/docs/guides/ai/choosing-compute-addon#results) for [gist-960-angular](http://corpus-texmex.irisa.fr/) (1M image embeddings, 960 dimensions) and [GloVe Reddit comments](https://nlp.stanford.edu/projects/glove/) (1.6M text embeddings, 512 dimensions). + +
    + +## Baseline + +Let's start with NirantK's results as a baseline: + +
    + multi database + multi database +
    + +They aren't very flattering! Repeating our statements above, these benchmarks are using the defaults for both engines. Our goal now is to replicate the results, and then see what improvements need to be made as developers scale up their workload. + +## Results + +Our tests mirrored NirantK's: but incorporated slight variations: + +Same: + +- We used the same dataset +- We used the same hardware: a 2XL instance on Supabase, which offers the same core and RAM count as NirantK's machine - 8 cores and 32 GB of RAM + +Changed: + +- We used the pre-warming technique described earlier. +- We used the `inner-product` distance function. +- We set `lists` constant for an index equal to 2000 instead of 1000. +- We adjusted the [Probes](https://github.com/pgvector/pgvector#query-options) in various runs to benchmark the difference (NirantK's tests used `probes = 1`). + +The resulting figures were significantly different after these changes. + +### PROBES = 10 + +With the changes above and probes set to 10, pgvector was faster and more accurate: + +- precision@10 of 0.91 +- RPS (requests per second) of 380 + +
    + multi database + multi database +
    + +### PROBES = 40 + +If we increase the probes from 10 to 40, pgvector was not just substantially faster but also boasted almost the same precision as Qdrant: + +- precision@10 of 0.98 +- RPS of 140 + +
    + multi database + multi database +
    + +### Scaling the database + +Another key takeaway is that the performance scales predictably with the size of the database. For instance, a 4XL instance achieves precision@10 of 0.98 and RPS of 270 with probes set to 40. Moreover, an 8XL compute add-on analogously obtains precision@10 of 0.98 and an RPS of 470, surpassing the results of Qdrant. + +
    + +The Qdrant benchmark uses “default” configuration and is in not indicative of its capabilities after modifying the configuration. + +
    + +
    + multi database + multi database +
    + +Although more compute is required to match Qdrant's precision and RPS levels concurrently, this is still a satisfying outcome. It means that it's not a _necessity_ to use another vector database. You can put everything in Postgres to lower your operational complexity. + +### Final results: pgvector performance + +Putting it all together, we find that we can predictably scale our database to match the performance we need. + +With a 64-core, 256 GB server we achieve ~1800 RPS and 0.91 precision. This is for pgvector 0.4.0, and we've heard that the latest version (0.4.4) already has significant improvements. We'll release those benchmarks as soon as we have them. + +
    + multi database + multi database +
    + +## Other performance factors + +It's been about 5 months since we [added](https://supabase.com/blog/openai-embeddings-postgres-vector) pgvector to the platform. Since then we've discovered a few other important things to keep in mind. + +### Increasing lists improves performance + +Another way to improve performance without throwing more compute would be to increase `lists`. + +We ran a test to measure the impact of list size: we uploaded 90,000 vectors from the Wikipedia dataset and then queried 10,000 vectors from the same dataset. The documentation recommends to use `lists` constant of `number of vectors / 1000`. In this case, it would be 90. + +But as our experiment shows, we can improve RPS if we increase `lists` (i.e. with more lists in the index we need to get less index data to get the same precision). So for 95% precision, we can take any of: + +- 3% of index data = 270 lists +- 6% of index data = 90 lists +- 13% of index data = 30 lists + +
    + multi database + multi database +
    + +This also has an important caveat: building the index takes longer with more lists. Here we measure the index build time for a dataset containing 900,000 vectors: + +So if you can afford an index build time of 1 hour or more, you can go with `lists=5000` (`number of vectors / 200`) or more! + +
    + multi database + multi database +
    + +
    + +You may need to increase `maintenance_work_mem` to be able to create an index with high values for `lists`. For example: + +```sql +SET maintenance_work_mem TO '7168 MB'; +``` + +
    + +Keeping in mind that the overall index size is almost the same, and only index build time increases, we can say that it's better to use more lists for better select queries speed. + +### Real data has higher precision than random data + +Embeddings created from “real” data are more likely to be clustered together, whereas random embeddings are more likely to be scattered. In other words, real embeddings are very far from being randomly distributed. This might seem obvious, but it's an important call-out for benchmarks. + +Embeddings generated for similarity search using “real world data” will be more correlated, so the precision will be higher as well. You can see the difference in this chart using 10,000 Wikipedia embeddings, vs 10,000 randomly-generated embeddings: + +
    + multi database + multi database +
    + +## Optimizing pgvector performance + +Armed with all this information, we can safely give you a few tips and strategies for optimizing your pgvector workloads. + +### Tips + +First, a few generic tips which you can pick and choose from: + +1. **Adjust your Postgres config.** It should be aligned with RAM & CPU cores. [Find more details here](https://gist.github.com/egor-romanov/323e2847851bbd758081511785573c08). +2. Prefer `inner-product` to `L2` or `Cosine` distances if your vectors are normalized (like `text-embedding-ada-002`). If embeddings are not normalized, `Cosine` distance should give the best results with an index. +3. **Pre-warm your database.** Implement the warm-up technique we described earlier before transitioning to production. +4. **Establish your workload.** Increasing the lists constant for the pgvector index can accelerate your queries (at the expense of a slower build). For instance, for benchmarks with OpenAI embeddings, we employed a `lists` constant of 2000 (`number of vectors / 500`) as opposed to the suggested 1000 (`number of vectors / 1000`). +5. **Benchmark your own specific workloads.** Doing this during cache warm-up helps gauge the best value for the `probes` constant, balancing precision with RPS. + +### Going into production + +Before running your pgvector workload in production, here are a few steps you can take to maximize performance. + +1. Over-provision RAM during preparation. You can scale down in step `5`, but it's better to start with a larger size to get the best results for RAM requirements. (We'd recommend at least 8XL if you're using Supabase.) +2. Upload your data to the database. If you use [`vecs`](https://supabase.com/docs/guides/ai/python/api) library, it will automatically generate an index with default parameters. +3. Run a benchmark using randomly generated queries and see the results. Again, you can use `vecs` library with the `ann-benchmarks` tool. Do it with probes set to 10 (default) and then with probes set to 100 or more, so RPS will be lower than 10. +4. Take a look at the RAM usage, and save it as a note for yourself. You would likely want to use compute add-on in the future that would have the same amount of RAM as used at the moment (both actual RAM usage and RAM used for cache and buffers). +5. Scale down your compute add-on to the one that would have the same amount of RAM as used at the moment. +6. Repeat step 3. to load the data into RAM. You should see that RPS is increased on subsequent runs, and stop when it no longer increases. Then repeat the benchmark with probes set to a higher value as well if you didn't do it before on that compute add-on size. +7. Run a benchmark using real queries and see the results. You can use `vecs` library for that as well with `ann-benchmarks` tool. Do it with probes set to 10 (default) and then gradually increase/decrease probes value until you see that both precision and RPS match your requirements. +8. If you want higher RPS and you don't expect to have frequent inserts and reindexing, you can increase `lists` constantly. You have to rebuild the index with higher lists value and repeat steps 6-7 to find the best combination of `lists` and `probes` constants to achieve the best RPS and precision values. Higher `lists` mean that index will build slower, but you can achieve better RPS and precision. Higher probes mean that select queries will be slower, but you can achieve better precision. + +## The pgvector roadmap + +pgvector is still early in development. As with any open source tool, it needs time and resources to make it better. Supabase plans to continue supporting Andrew with his development of pgvector. + +What's next on the roadmap? Andrew has an impressive list of features [planned for v0.5.0](https://github.com/pgvector/pgvector/issues/27): + +- Adding HNSW: an index with better speed & precision than IVFFlat (at a higher memory cost) +- Product quantization: better storage for IVFFLAT, improving speed and recall +- Parallel index builds: building your IVFFLAT indexes will be much faster + +## More AI resources + +- [How to build ChatGPT Plugin from scratch with Supabase Edge Runtime](https://supabase.com/blog/building-chatgpt-plugins-template) +- [Docs pgvector: Embeddings and vector similarity](https://supabase.com/docs/guides/database/extensions/pgvector) +- [pgvector vs Qdrant](https://nirantk.com/writing/pgvector-vs-qdrant) +- [Choosing Compute Add-on for AI workloads](https://supabase.com/docs/guides/ai/choosing-compute-addon) diff --git a/apps/www/_blog/2023-07-18-flutter-authentication.mdx b/apps/www/_blog/2023-07-18-flutter-authentication.mdx new file mode 100644 index 00000000000..550a9e1d7f8 --- /dev/null +++ b/apps/www/_blog/2023-07-18-flutter-authentication.mdx @@ -0,0 +1,426 @@ +--- +title: 'Getting started with Flutter authentication' +description: Learn how authentication on Flutter works through Google sign in with Supabase auth. +tags: + - flutter + - auth +date: '2023-07-18' +toc_depth: 2 +author: tyler_shukert +image: flutter-authentication/flutter-authentication.png +thumb: flutter-authentication/flutter-authentication.png +--- + +Flutter is Google’s open-source framework to develop cross-platform applications. In this article, we will take a look at how we can implement authentication using Google sign-in to secure our application using the [Supabase SDK for Flutter](https://supabase.com/docs/reference/dart/introduction). + +We will also dive into the deep ends of Open ID Connect sign-in to better understand how third-party sign-ins are being performed. You can check out the code of the sample in this article [here](https://github.com/supabase/supabase/tree/master/examples/auth/flutter-native-google-auth). + +## Prerequisites + +This article assumes you are comfortable with writing a basic application in Flutter. No knowledge of Supabase is required. + +We will use the following tools + +- [Flutter](https://docs.flutter.dev/get-started/install) - we used v3.10.5 for this article +- Supabase - create your account [here](https://database.new/) if you do not have one +- IDE of your choosing + +## What is Open ID Connect? + +We will implement third-party login with Google utilizing the Open ID Connect functionality of Supabase Auth. Open ID Connect, or OIDC is a protocol built on top of OAuth 2.0 that allows third-party applications to request the users to provide some personal information, such as name or profile image, in the form of an identity token along with an access token. This identity token can then be verified and decoded by the application to obtain that personal information. + +Supabase auth provides `signInWithIdToken` method where we can sign in a user using their ID token obtained from third-party auth providers such as Google. Upon signing a user with the `signInWithIdToken` method, Supabase automatically populates the content of the ID token in the Supabase user metadata for easy access to the information. We will be utilizing this feature in this example to display the user profile upon the user signing in. + +In today’s example, our app will make a request to Google, obtain the identity token, and we will use it to sign the user in as well as obtain basic user information. + +## What we will build + +We will build a simple app with a login screen and a home screen. The user is first presented with the login screen, and only after they sign in, can they proceed to the home screen. The login screen presents a login button that will kick off a third-party authentication flow to complete the sign-in. The profile screen displays user information such as the profile image or their full name. + +![Flutter Google sign in](/images/blog/flutter-authentication/flutter-google-sign-in.png) + +## Setup the Flutter project + +Let’s start by creating a fresh Flutter project. + +```bash +flutter create myauthapp +``` + +then we can install the dependencies. Change the working directory to the newly created app directory and run the following command to install our dependencies. + +```dart +flutter pub add supabase_flutter flutter_appauth crypto +``` + +We will use [supabase_flutter](https://pub.dev/packages/supabase_flutter) to interact with our Supabase instance. [flutter_appauth](https://pub.dev/packages/flutter_appauth) will be used to implement Google login, and [crypto](https://pub.dev/packages/crypto) is a library that has utility functions for encryption that we will use when performing OIDC logins. + +We are done installing our dependencies. Let’s set up [authentication](https://supabase.com/docs/guides/auth) now. + +## Configure Google sign-in on Supabase Auth + +We will obtain client IDs for iOS and Android from the Google Cloud console, and register them to our Supabase project. + +First, create your Google Cloud project [here](https://cloud.google.com/) if you do not have one yet. Within your Google Cloud project, follow the [Configure a Google API Console project for Android](https://developers.google.com/identity/sign-in/android/start-integrating#configure_a_project) guide and [Get an OAuth client ID for the iOS](https://developers.google.com/identity/sign-in/ios/start-integrating#get_an_oauth_client_id) guide to obtain client IDs for Android and iOS respectively. + +Once you have the client IDs, let’s add them to our Supabase dashboard. If you don’t have a Supabase project created yet, you can create one at [database.new](https://database.new) for free. The name is just an internal name, so we can call it “Auth” for now. Database Password will not be used in this example and can be reconfigured later, so press the `Generate a password` button and let Supabase generate a secure random password. No need to copy it anywhere. The region should be anywhere close to where you live, or where your users live in an actual production app. + +Lastly, for the pricing plan choose the free plan that allows you to connect with all major social OAuth providers and supports up to 50,000 monthly active users. + +![Supabase project creation](/images/blog/flutter-authentication/supabase-project-creation.png) + +Your project should be ready in a minute or two. Once your project is ready, you can open `authentication -> Providers -> Google` to set up Google auth. Toggle the `Enable Sign in with Google` switch first. Then add the two client IDs you obtained in your Google Cloud console to `Authorized Client IDs` field with a comma in between the two client IDs like this: `ANDROID_CLIENT_ID,IOS_CLIENT_ID`. + +![Supabase auth Google auth provider](/images/blog/flutter-authentication/supabase-google-provider.png) + +We also need some Android specific settings to make [flutter_appauth](https://pub.dev/packages/flutter_appauth#android-setup) work. Open `android/app/build.gradle` and find the `defaultConfig`. We need to set the reversed DNS form of the Android Client ID as the`appAuthRedirectScheme` manifest placeholder value. + +```groovy +... +android { + ... + defaultConfig { + ... + manifestPlaceholders += [ + // *account_id* will be unique for every single app + 'appAuthRedirectScheme': 'com.googleusercontent.apps.*account_id*' + ] + } +} +``` + +That is it for setting up our [Supabase auth to prepare for Google sign-in](https://supabase.com/docs/guides/auth/social-login/auth-google#using-native-sign-in). + +Finally, we can initialize Supabase in our Flutter application with the credentials of our Supabase instance. Update your `main.dart` file and add `Supabase.initialize()` in the `main` function like the following. Note that you will see some errors since the home screen is set to the `LoginScreen`, which we will create later. + +```dart +import 'package:flutter/material.dart'; +import 'package:myauthapp/screens/login_screen.dart'; +import 'package:supabase_flutter/supabase_flutter.dart'; + +void main() async { + /// TODO: update Supabase credentials with your own + await Supabase.initialize( + url: 'YOUR_SUPABASE_URL', + anonKey: 'YOUR_ANON_KEY', + ); + runApp(const MyApp()); +} + +final supabase = Supabase.instance.client; + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + @override + Widget build(BuildContext context) { + return MaterialApp( + debugShowCheckedModeBanner: false, + title: 'Flutter Auth', + theme: ThemeData( + colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), + useMaterial3: true, + ), + home: const LoginScreen(), + ); + } +} +``` + +You can find your Supabase URL and Anon key in `Settings -> API` from your [Supabase dashboard](https://supabase.com/dashboard/project/_/settings/api). + +![Supabase credentials](/images/blog/flutter-authentication/supabase-credentials.png) + +## Create the Login Screen + +We will have two screens for this app, `LoginScreen` and `ProfileScreen`. `LoginScreen` presents a single sign-in button for the user to perform Google sign-in. Create a `lib/screens/login_screen.dart` file add add the following. + +```dart +import 'dart:convert'; +import 'dart:io'; +import 'dart:math'; + +import 'package:crypto/crypto.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_appauth/flutter_appauth.dart'; +import 'package:myauthapp/main.dart'; +import 'package:myauthapp/screens/profile_screen.dart'; +import 'package:supabase_flutter/supabase_flutter.dart'; + +class LoginScreen extends StatefulWidget { + const LoginScreen({super.key}); + + @override + State createState() => _LoginScreenState(); +} + +class _LoginScreenState extends State { + @override + void initState() { + _setupAuthListener(); + super.initState(); + } + + void _setupAuthListener() { + supabase.auth.onAuthStateChange.listen((data) { + final event = data.event; + if (event == AuthChangeEvent.signedIn) { + Navigator.of(context).pushReplacement( + MaterialPageRoute( + builder: (context) => const ProfileScreen(), + ), + ); + } + }); + } + + /// Function to generate a random 16 character string. + String _generateRandomString() { + final random = Random.secure(); + return base64Url.encode(List.generate(16, (_) => random.nextInt(256))); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Login'), + ), + body: Center( + child: ElevatedButton( + onPressed: () async { + const appAuth = FlutterAppAuth(); + + // Just a random string + final rawNonce = _generateRandomString(); + final hashedNonce = + sha256.convert(utf8.encode(rawNonce)).toString(); + + /// TODO: update the iOS and Android client ID with your own. + /// + /// Client ID that you registered with Google Cloud. + /// You will have two different values for iOS and Android. + final clientId = + Platform.isIOS ? 'IOS_CLIENT_ID' : 'ANDROID_CLIENT_ID'; + + /// Set as reversed DNS form of Google Client ID + `:/` for Google login + final redirectUrl = '${clientId.split('.').reversed.join('.')}:/'; + + /// Fixed value for google login + const discoveryUrl = + 'https://accounts.google.com/.well-known/openid-configuration'; + + // authorize the user by opening the concent page + final result = await appAuth.authorize( + AuthorizationRequest( + clientId, + redirectUrl, + discoveryUrl: discoveryUrl, + nonce: hashedNonce, + scopes: [ + 'openid', + 'email', + 'profile', + ], + ), + ); + + if (result == null) { + throw 'No result'; + } + + // Request the access and id token to google + final tokenResult = await appAuth.token( + TokenRequest( + clientId, + redirectUrl, + authorizationCode: result.authorizationCode, + discoveryUrl: discoveryUrl, + codeVerifier: result.codeVerifier, + nonce: result.nonce, + scopes: [ + 'openid', + 'email', + ], + ), + ); + + final idToken = tokenResult?.idToken; + + if (idToken == null) { + throw 'No idToken'; + } + + await supabase.auth.signInWithIdToken( + provider: Provider.google, + idToken: idToken, + nonce: rawNonce, + ); + }, + child: const Text('Google login'), + ), + ), + ); + } +} +``` + +In terms of UI, this page is very simple, it just has a basic `Scaffold` with an `AppBar`, and has a button right in the middle of the body. Upon pressing the button, Google sign in flow starts. The user is presented with a Google authentication screen where they will complete the consent to allow our application to sign the user in using a Google account, as well as allow us to view some personal information. + +![Google sign in](/images/blog/flutter-authentication/google-sign-in.png) + +Let’s break down what is going on within the `onPressed` callback of the sign in button. + +First, we are generating a [nonce](https://openid.net/specs/openid-connect-core-1_0.html#SelfIssuedDiscovery:~:text=auth_time%20response%20parameter.)-,nonce,-String%20value%20used), which is essentially just a random string. This string is later passed to Google after being hashed to verify that the ID token has not been tampered to prevent a man-in-the-middle attack. + +```dart +// Random string to verify the integrity of the ID Token +final rawNonce = _generateRandomString(); +final hashedNonce = sha256.convert(utf8.encode(rawNonce)).toString(); +``` + +`clientId` and `applicationId` are the app-specific values. These will be used to the authentication request to Google later on. + +```dart + /// Client ID that you registered with Google Cloud. + /// You will have two different values for iOS and Android. + final clientId = Platform.isIOS ? 'IOS_CLIENT_ID' : 'ANDROID_CLIENT_ID'; +``` + +`redirectUrl` is the URL at which the user will be redirected after a successful authentication request. For Google sign-in, we can set it to the reversed DNS form of the client ID followed by `:/`. `discoveryUrl` is a URL provided by Google that contains information about their Open ID configuration. + +```dart +/// Set as reversed DNS form of Google Client ID + `:/` for Google login +final redirectUrl = '${clientId.split('.').reversed.join('.')}:/'; + +/// Fixed value for google login +const discoveryUrl = 'https://accounts.google.com/.well-known/openid-configuration'; +``` + +Then we are sending an authorization request to Google. This is where the user is taken to Google’s page to perform sign in and consent our app to obtain some personal information. Note that we are requesting three scopes here. `openid` and `email` are required by Supabase auth to complete the sign-in process. `profile` is not required by Supabase auth, but we are requesting it to display some profile information on the profile screen later on. We do not actually obtain the requested information in this step though. All we are doing is requesting an access token that has permission to obtain the personal information we have requested. + +```dart +// Authorize the user by opening the concent page +final result = await appAuth.authorize( + AuthorizationRequest( + clientId, + redirectUrl, + discoveryUrl: discoveryUrl, + nonce: hashedNonce, + scopes: [ + 'openid', + 'email', + 'profile', + ], + ), +); +``` + +Using the authorization token obtained in the previous step, we make the final request to Google’s auth server to obtain the personal information we asked for earlier. We get an ID token in return, which contains the personal information. + +```dart +// Request the access and id token to google +final tokenResult = await appAuth.token( + TokenRequest( + clientId, + redirectUrl, + authorizationCode: result.authorizationCode, + discoveryUrl: discoveryUrl, + codeVerifier: result.codeVerifier, + nonce: result.nonce, + scopes: [ + 'openid', + 'email', + 'profile', + ], + ), +); +``` + +And lastly, we pass the ID token we obtained from Google to Supabase to complete the sign-in on Supabase auth. Once the user is signed in, the auth state listener in the `initState` fires and takes the user to the `ProfileScreen`. + +```dart +await supabase.auth.signInWithIdToken( + provider: Provider.google, + idToken: idToken, + nonce: rawNonce, +); +``` + +## Create the Profile Screen + +The `ProfileScreen` will be just a simple UI presenting some of the information we obtained in the `LoginPage`. We can access the user data with `supabase.auth.currentUser`, where Supabase has saved the personal information in a property called `userMetadata`. In this example, we are displaying the `avatar_url` and `full_name` to display a basic profile page. Create a `lib/screens/profile_screen.dart` file and add the following. + +```dart +import 'package:flutter/material.dart'; +import 'package:myauthapp/main.dart'; +import 'package:myauthapp/screens/login_screen.dart'; + +class ProfileScreen extends StatelessWidget { + const ProfileScreen({super.key}); + + @override + Widget build(BuildContext context) { + final user = supabase.auth.currentUser; + final profileImageUrl = user?.userMetadata?['avatar_url']; + final fullName = user?.userMetadata?['full_name']; + return Scaffold( + appBar: AppBar( + title: const Text('Profile'), + actions: [ + TextButton( + onPressed: () async { + await supabase.auth.signOut(); + if (context.mounted) { + Navigator.of(context).pushReplacement( + MaterialPageRoute(builder: (context) => const LoginScreen()), + ); + } + }, + child: const Text('Sign out'), + ) + ], + ), + body: Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + if (profileImageUrl != null) + ClipOval( + child: Image.network( + profileImageUrl, + width: 100, + height: 100, + fit: BoxFit.cover, + ), + ), + const SizedBox(height: 16), + Text( + fullName ?? '', + style: Theme.of(context).textTheme.headlineMedium, + ), + const SizedBox(height: 32), + ], + ), + ), + ); + } +} +``` + +And with that, we now have a basic working personalized application that utilizes Google sign-in. + +![Flutter Google sign in app](/images/blog/flutter-authentication/flutter-google-sign-in.png) + +## Conclusion + +In this post, we learned how to implement authentication in a Flutter application using Google sign-in and the Supabase SDK for Flutter. We also delved into the Open ID Connect functionality, which allows third-party sign-ins and the retrieval of personal information through identity tokens. + +You can also check out the [Flutter reference documents](https://supabase.com/docs/reference/dart/installing) to see how you can use `supabase-flutter` to implement a Postgres database, Storage, Realtime, and more. + +## More Flutter and Supabase resources + +- [supabase_flutter package](https://pub.dev/packages/supabase_flutter) +- [Build a chat application using Flutter and Supabase](https://supabase.com/blog/flutter-tutorial-building-a-chat-app) +- [Securing your Flutter apps with Multi-Factor Authentication](https://supabase.com/blog/flutter-multi-factor-authentication) +- [How to build a real-time multiplayer game with Flutter Flame](https://supabase.com/blog/flutter-real-time-multiplayer-game) diff --git a/apps/www/_customers/mendableai.mdx b/apps/www/_customers/mendableai.mdx index bcaa5e6cf50..44d70141b7e 100644 --- a/apps/www/_customers/mendableai.mdx +++ b/apps/www/_customers/mendableai.mdx @@ -34,8 +34,9 @@ about: Mendable is Chat Powered Search for Documentation. Mendable was experiencing tremendous success, growing Weekly Active Users nearly 300% since March. They needed a tool to store and search through large amounts of vector data to improve the efficiency and accuracy of their similarity search operations. They tried Faiss, Weaviate, and Pinecone, but found them to be expensive and not very intuitive, especially when it came to storing metadata along with the vectors. -Why they chose Supabase: -Mendable discovered that Supabase supports pgvector and found it to be a simple and cost-effective solution. They were impressed with the open source nature of Supabase, as well as its ability to store metadata alongside the vectors. They also appreciated the intuitive interface and ease of use. +## Why they chose Supabase + +Mendable discovered that Supabase supports [pgvector](https://supabase.com/docs/guides/database/extensions/pgvector) and found it to be a simple and cost-effective solution. They were impressed with the open source nature of Supabase, as well as its ability to store metadata alongside the vectors. They also appreciated the intuitive interface and ease of use. We tried other vector databases - we tried Faiss, we tried Weaviate, we tried Pinecone. We found @@ -45,18 +46,18 @@ Mendable discovered that Supabase supports pgvector and found it to be a simple ## What They Built -Using Supabase and pgvector, Mendable was able to build a more efficient and accurate search function for their AI chatbot. By storing vector data alongside metadata in Supabase, Mendable was able to quickly and easily search through their customers documentation to find the most relevant responses to queries. They found that Supabase's solution was just as performant as dedicated vector databases, but without the high cost. +Using [Supabase Vector](https://supabase.com/vector), Mendable was able to build a more efficient and accurate search function for their AI chatbot. By storing vector data alongside metadata in Supabase, Mendable was able to quickly and easily search through their customers documentation to find the most relevant responses to queries. They found that Supabase's solution was just as performant as dedicated vector databases, but without the high cost. ## The Results Thanks to Supabase and pgvector, Mendable was able to significantly improve the efficiency and accuracy of their Chat Powered Search for Documentation. They were able to build faster and more cost-effectively using Supabase’s open source stack. -## Tech stack - -Mendable's tech stack includes React, Next.js, Express, Vercel, and Supabase. - We looked at the alternatives and chose Supabase because it’s open source, it’s simpler, and, for all the ways we need use it, Supabase has been just as performant - if not more performant - than the other vector databases. + +## Tech stack + +Mendable's tech stack includes React, Next.js, Express, Vercel, and Supabase. diff --git a/apps/www/_customers/mobbin.mdx b/apps/www/_customers/mobbin.mdx index 6a8345ed0c7..ae7c0334ee8 100644 --- a/apps/www/_customers/mobbin.mdx +++ b/apps/www/_customers/mobbin.mdx @@ -98,4 +98,4 @@ Migrating to Supabase helped the team at Mobbin instantly improve the end-user e They now add new features with confidence, and they continue to help creators all over the globe find inspiration for their next project. You can [check out Mobbin on their website](https://mobbin.design/browse/ios/apps). -[Sign up](https://app.supabase.com/) for Supabase's free plan and set up a scalable backend in less than 2 minutes. +[Sign up](https://supabase.com/dashboard/) for Supabase's free plan and set up a scalable backend in less than 2 minutes. diff --git a/apps/www/_customers/replenysh.mdx b/apps/www/_customers/replenysh.mdx index ca6aef5898f..4d70d4674b3 100644 --- a/apps/www/_customers/replenysh.mdx +++ b/apps/www/_customers/replenysh.mdx @@ -88,4 +88,4 @@ Replenysh clients include brands with global recognition and millions of custome ## Supabase help Replenysh continue to scale -Supabase turbo-charged Replenysh's development time with a seamless end-user mobile login experience. They are ready to help major brands engage with their users through participation in the circular economy. With Supabase, the team knows they have a slick auth experience, reduced DevOps overhead, and can continue to scale with Postgres. You can test out the Supabase developer experience today by [starting a new project on the free plan!.](https://app.supabase.com) +Supabase turbo-charged Replenysh's development time with a seamless end-user mobile login experience. They are ready to help major brands engage with their users through participation in the circular economy. With Supabase, the team knows they have a slick auth experience, reduced DevOps overhead, and can continue to scale with Postgres. You can test out the Supabase developer experience today by [starting a new project on the free plan!.](https://supabase.com/dashboard) diff --git a/apps/www/components/BackedBy.tsx b/apps/www/components/BackedBy.tsx index f796653e736..af93fc30156 100644 --- a/apps/www/components/BackedBy.tsx +++ b/apps/www/components/BackedBy.tsx @@ -1,5 +1,6 @@ import React from 'react' import { useRouter } from 'next/router' +import Image from 'next/image' const BackedBy = ({ className, @@ -23,26 +24,42 @@ const BackedBy = ({ backed by
    - Y Combinator - Mozilla - Coatue - Felicis +
    + Y Combinator +
    +
    + Mozilla +
    +
    + Coatue +
    +
    + Felicis +
    diff --git a/apps/www/components/Carousels/ImageCarousel.tsx b/apps/www/components/Carousels/ImageCarousel.tsx index ef544266da7..802a1bf4ff5 100644 --- a/apps/www/components/Carousels/ImageCarousel.tsx +++ b/apps/www/components/Carousels/ImageCarousel.tsx @@ -1,6 +1,6 @@ import { Button, IconCornerRightUp, Tabs } from 'ui' import { useRouter } from 'next/router' -import { useState } from 'react' +import { useRef, useState } from 'react' // Import Swiper React components import { Swiper, SwiperSlide } from 'swiper/react' @@ -11,6 +11,7 @@ import 'swiper/swiper.min.css' import Image from 'next/image' import TextLink from '../TextLink' import ImageCarouselStyles from './ImageCarousel.module.css' +import { useInView } from 'framer-motion' interface Content { title: string @@ -29,6 +30,8 @@ interface ImageCarouselProps { } function ImageCarousel(props: ImageCarouselProps) { + const sectionRef = useRef(null) + const isInView = useInView(sectionRef, { margin: '75%', once: true }) // base path for images const { basePath } = useRouter() @@ -66,7 +69,7 @@ function ImageCarousel(props: ImageCarouselProps) { ) return ( -
    +
    @@ -116,7 +119,7 @@ function ImageCarousel(props: ImageCarouselProps) { height="960" /> )} - {content.youtube_id && ( + {isInView && content.youtube_id && (