diff --git a/apps/reference/_api/intro.md b/apps/reference/_api/intro.md index 8f9bd942142..27e2690b58e 100644 --- a/apps/reference/_api/intro.md +++ b/apps/reference/_api/intro.md @@ -1,16 +1,16 @@ --- slug: / sidebar_position: 1 -sidebar_label: Supabase API +sidebar_label: Management API --- -# Supabase API +# Management API -The Supabase API allows you to manage your projects programmatically. +The Management API allows you to manage your projects programmatically. ## Status -The Supabase API is in `beta`. It is usable in it's current state, but it's likely that there will be breaking changes. +The Management API is in `beta`. It is usable in it's current state, but it's likely that there will be breaking changes. ## Authentication diff --git a/apps/reference/_cli/intro.mdx b/apps/reference/_cli/intro.mdx index 8ac2186ca6e..949ce05cfb8 100644 --- a/apps/reference/_cli/intro.mdx +++ b/apps/reference/_cli/intro.mdx @@ -25,5 +25,5 @@ The CLI is still under development, but it contains all the functionality for wo - [Install the Supabase CLI](/docs/guides/cli) - [Source code](https://github.com/supabase/cli) - [Known bugs and issues](https://github.com/supabase/cli/issues) -- [Supabase CLI v1 and Admin API Beta](https://supabase.com/blog/supabase-cli-v1-and-admin-api-beta) -- [The CLI team announcing CLI V1 and Admin API Beta - Video](https://www.youtube.com/watch?v=OpPOaJI_Z28) +- [Supabase CLI v1 and Management API Beta](https://supabase.com/blog/supabase-cli-v1-and-admin-api-beta) +- [The CLI team announcing CLI V1 and Management API Beta - Video](https://www.youtube.com/watch?v=OpPOaJI_Z28) diff --git a/apps/reference/_supabase_js/upgrade-guide.mdx b/apps/reference/_supabase_js/upgrade-guide.mdx index 6edc071b8cb..71f46c8151b 100644 --- a/apps/reference/_supabase_js/upgrade-guide.mdx +++ b/apps/reference/_supabase_js/upgrade-guide.mdx @@ -378,7 +378,7 @@ const userListener = supabase.from('users') ```ts -const userListener = supabase.channel('public:user') +const userListener = supabase.channel('all-users-changes') .on( 'postgres_changes', { event: '*', schema: 'public', table: 'user' }, @@ -412,3 +412,32 @@ supabase.removeChannel(userListener) ``` + +#### onOpen + + + + + +```ts +supabase.realtime.onOpen(() => console.log('Connected to socket')) +``` + + + + +```ts +supabase.channel('any') + .subscribe((status) => { + if (status === 'SUBSCRIBED') { + console.log('Connected to channel') + } + }) +``` + + diff --git a/apps/reference/_supabase_js/working-with-types.md b/apps/reference/_supabase_js/working-with-types.md index 519650ed995..311c0f30883 100644 --- a/apps/reference/_supabase_js/working-with-types.md +++ b/apps/reference/_supabase_js/working-with-types.md @@ -8,13 +8,29 @@ id: typescript-support ## Generating types -You can use our CLI to generate types: +You can use the [Supabase CLI](/docs/guides/cli) to generate types directly from your Postgres database’s schema: + +### Generate types from a remote database + +Install the [Supabase CLI](/docs/guides/cli#installation) locally, and then run: + +```bash +supabase login +supabase projects list +supabase gen types typescript --project-id > lib/database.types.ts +``` + +### Generate types from a local database + +Set up your [local development environment](/docs/guides/cli/local-development), and then run: ```bash supabase start supabase gen types typescript --local > lib/database.types.ts ``` +For additional methods, see the [CLI Reference Docs](/docs/reference/cli/usage#supabase-gen-types). + These types are generated directly from your database. Given a table `public.movies`, the definition will provide the following data: ```ts diff --git a/apps/reference/docs/guides/api.mdx b/apps/reference/docs/guides/api.mdx index b4e3e48bd09..abae0380c72 100644 --- a/apps/reference/docs/guides/api.mdx +++ b/apps/reference/docs/guides/api.mdx @@ -1,6 +1,6 @@ --- id: api -title: APIs +title: Data APIs description: Auto-generating and Realtime APIs. sidebar_label: Overview --- @@ -9,9 +9,7 @@ import Tabs from '@theme/Tabs' import TabItem from '@theme/TabItem' import useBaseUrl from '@docusaurus/useBaseUrl' -## Overview - -Supabase generates three types of API directly from your database schema. +Supabase auto-generates three types of API directly from your database schema. - REST - interact with your database through a restful interface. - Realtime - listen to database changes. @@ -25,7 +23,7 @@ The APIs are: - **Fast.**
Our benchmarks for basic reads are more than 300% faster than Firebase. The API is a very thin layer on top of Postgres, which does most of the heavy lifting. - **Scalable.**
The API can serve thousands of simultaneous requests, and works well for Serverless workloads. -### REST API {#rest-api-overview} +## REST API {#rest-api-overview} Supabase provides a RESTful API using [PostgREST](https://postgrest.org/). This is a very thin API layer on top of Postgres. It provides everything you need from a CRUD API: @@ -45,7 +43,7 @@ It provides everything you need from a CRUD API: > -### GraphQL API {#graphql-api-overview} +## GraphQL API {#graphql-api-overview} :::note @@ -55,7 +53,7 @@ GraphQL is in Beta, and may have breaking changes. It is only available on self- GraphQL in Supabase works through [pg_graphql](https://supabase.com/blog/pg-graphql), an open source PostgreSQL extension for GraphQL. -### Realtime API {#realtime-api-overview} +## Realtime API {#realtime-api-overview} 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. diff --git a/apps/reference/docs/guides/auth/auth-email.mdx b/apps/reference/docs/guides/auth/auth-email.mdx index 4f982891630..c4c24a828a6 100644 --- a/apps/reference/docs/guides/auth/auth-email.mdx +++ b/apps/reference/docs/guides/auth/auth-email.mdx @@ -16,12 +16,9 @@ Setting up Email logins for your Supabase application. ## Configure email settings -- Go to your [Supabase Project Dashboard](https://app.supabase.com) -- In the left sidebar, click the `Authentication` icon (near the top) -- Click `Settings` from the list to go to the `Authentication Settings` page -- Enter the final (hosted) URL of your app under `Site URL` -- Under `Email Auth` turn `Enable Email Signup` to ON -- Click `Save` +1. Go to the [Authentication Settings](https://app.supabase.com/project/_/auth/settings) page in the Dashboard. +1. For **Site URL**, enter the final (hosted) URL of your app. +1. Under Auth Providers, **enable email provider**. :::note Self hosting diff --git a/apps/reference/docs/guides/auth/auth-magic-link.mdx b/apps/reference/docs/guides/auth/auth-magic-link.mdx index 865d4394300..3d09f2e726b 100644 --- a/apps/reference/docs/guides/auth/auth-magic-link.mdx +++ b/apps/reference/docs/guides/auth/auth-magic-link.mdx @@ -19,12 +19,9 @@ Setting up Magic Link logins for your Supabase application. ## Add Magic Link into your Supabase Project -- Go to your [Supabase Project Dashboard](https://app.supabase.com) -- In the left sidebar, click the `Authentication` icon (near the top) -- Click `Settings` from the list to go to the `Authentication Settings` page -- Enter the final (hosted) URL of your app under `Site URL` (this is important) -- Under `Email Auth` turn `Enable Email Signup` to ON -- Click `Save` +1. Go to the [Authentication Settings](https://app.supabase.com/project/_/auth/settings) page in the Dashboard. +1. For **Site URL**, enter the final (hosted) URL of your app. +1. Under Auth Providers, **enable email provider**. ## Add login code to your client app diff --git a/apps/reference/docs/guides/auth/row-level-security.mdx b/apps/reference/docs/guides/auth/row-level-security.mdx index a103c6a676e..d12ff68f464 100644 --- a/apps/reference/docs/guides/auth/row-level-security.mdx +++ b/apps/reference/docs/guides/auth/row-level-security.mdx @@ -49,42 +49,6 @@ Returns the ID of the user making the request. Returns the JWT of the user making the request. -### `auth.role()` - -:::caution - -Deprecated - -::: - -The `auth.role()` function has been deprecated in favour of using the `TO` field, natively supported within Postgres. - -```sql --- DEPRECATED -create policy "Public profiles are viewable by everyone." -on profiles for select using ( - auth.role() = 'authenticated' or auth.role() = 'anon' -); - --- RECOMMENDED -create policy "Public profiles are viewable by everyone." -on profiles for select -to authenticated, anon -using ( - true -); -``` - -### `auth.email()` - -:::caution - -Deprecated. Use `auth.jwt() ->> 'email'` instead. - -::: - -Returns the email of the user making the request. - ## Examples Here are some examples to show you the power of PostgreSQL's RLS. @@ -361,3 +325,41 @@ Tip: Make sure to enable RLS for all your tables, so that your tables are inacce Supabase provides special "Service" keys, which can be used to bypass all RLS. These should never be used in the browser or exposed to customers, but they are useful for administrative tasks. + +## Deprecated features + +We have deprecate some functions to ensure better performance and extensibilty of RLS policies. + +### `auth.role()` + +:::caution + +Deprecated: The `auth.role()` function has been deprecated in favour of using the `TO` field, natively supported within Postgres. + +::: + +```sql +-- DEPRECATED +create policy "Public profiles are viewable by everyone." +on profiles for select using ( + auth.role() = 'authenticated' or auth.role() = 'anon' +); + +-- RECOMMENDED +create policy "Public profiles are viewable by everyone." +on profiles for select +to authenticated, anon +using ( + true +); +``` + +### `auth.email()` + +:::caution + +Deprecated. Use `auth.jwt() ->> 'email'` instead. + +::: + +Returns the email of the user making the request. diff --git a/apps/reference/docs/guides/database/connecting-to-postgres.mdx b/apps/reference/docs/guides/database/connecting-to-postgres.mdx index e6882cf3883..b059d3dbc67 100644 --- a/apps/reference/docs/guides/database/connecting-to-postgres.mdx +++ b/apps/reference/docs/guides/database/connecting-to-postgres.mdx @@ -12,11 +12,11 @@ Supabase provides several options for programmatically connecting to your Postgr - Direct connections using Postgres' standard connection system. - Connection pooling using PgBouncer. -### API vs Direct vs Pooling +### Direct vs Pooling vs API -- The API is an auto-generated REST interface. You should use this for all browser and application interactions. - A "direct connection" is when a connection is made to the database using Postgres' native connection implementation. You should use this for tools which are always alive - usually installed on a long-running server. - A "connection pool" is a system (external to Postgres) which keeps connections "open". You should use this for serverless functions and tools which disconnect from the database frequently. +- The API is an auto-generated REST interface. You should use this for all browser and application interactions. The API server internally handles a connection pool. Why would you use a connection pool? Primarily because the way that Postgres handles connections isn't very scalable for a large number of _temporary_ connections. You can use these simple questions to determine which connection method to use: diff --git a/apps/reference/docs/guides/database/database-webhooks.mdx b/apps/reference/docs/guides/database/database-webhooks.mdx new file mode 100644 index 00000000000..6828a0bbc7b --- /dev/null +++ b/apps/reference/docs/guides/database/database-webhooks.mdx @@ -0,0 +1,74 @@ +--- +id: webhooks +title: 'Database Webhooks' +description: Trigger external payloads on database events. +--- + +import Tabs from '@theme/Tabs' +import TabItem from '@theme/TabItem' + +Database Webhooks allow you to send real-time data from your database to another system whenever a table event occurs. + + +You can hook into three table events: `INSERT`, `UPDATE`, and `DELETE`. All events are fired _after_ a database row is changed. + +Database Webhooks are very similar to triggers, and that's because Database Webhooks are just a convenience wrapper around triggers +using the [pg_net](/docs/guides/database/extensions/pgnet) extension. This extension is asynchronous, and therefore will not block your database changes for long-running network requests. + +This video demonstrates how you can create a new customer in Stripe each time a row is inserted into a `profiles` table: + +:::note + +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. 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. + +We currently support HTTP webhooks. These are sent as a `POST` request with a JSON payload. + +## Payload + +The payload is automatically generated from the underlying table record: + +```typescript +type InsertPayload = { + type: 'INSERT' + table: string + schema: string + record: TableRecord + old_record: null +} +type UpdatePayload = { + type: 'UPDATE' + table: string + schema: string + record: TableRecord + old_record: TableRecord +} +type DeletePayload = { + type: 'DELETE' + table: string + schema: string + record: null + old_record: TableRecord +} +``` + +## Resources + +- [pg_net](/docs/guides/database/extensions/pgnet): an async networking extension for PostgreSQL diff --git a/apps/reference/docs/guides/database/extensions/pgcron.mdx b/apps/reference/docs/guides/database/extensions/pgcron.mdx new file mode 100644 index 00000000000..e7032bf9bd9 --- /dev/null +++ b/apps/reference/docs/guides/database/extensions/pgcron.mdx @@ -0,0 +1,99 @@ +--- +id: pgcron +title: 'pg_cron: Job Scheduling' +description: 'pgnet: a simple cron-based job scheduler for PostgreSQL that runs inside the database.' +--- + +import Tabs from '@theme/Tabs' +import TabItem from '@theme/TabItem' + +The `pg_cron` extension is a simple cron-based job scheduler for PostgreSQL that runs inside the database. + +## Usage + +### Enable the extension + + + + + +1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard. +2. Click on **Extensions** in the sidebar. +3. Search for "pg_cron" and enable the extension. + + + + +```sql +-- Example: enable the "pg_cron" extension +create extension pg_cron with schema extensions; + +-- If you're planning to use a non-superuser role to schedule jobs, +-- ensure that they are granted access to the cron schema and its underlying objects beforehand. +-- Failure to do so would result in jobs by these roles to not run at all. + +grant usage on schema cron to {{DB user}}; +grant all privileges on all tables in schema cron to {{DB user}}; + + +-- Example: disable the "pg_cron" extension +drop extension if exists pg_cron; +``` + + + + +### Syntax + +The schedule uses the standard cron syntax, in which \* means "run every time period", and a specific number means "but only at this time": + +```bash + ┌───────────── min (0 - 59) + │ ┌────────────── hour (0 - 23) + │ │ ┌─────────────── day of month (1 - 31) + │ │ │ ┌──────────────── month (1 - 12) + │ │ │ │ ┌───────────────── day of week (0 - 6) (0 to 6 are Sunday to + │ │ │ │ │ Saturday, or use names; 7 is also Sunday) + │ │ │ │ │ + │ │ │ │ │ + * * * * * +``` + +## Examples + +### Delete data every week + +Delete old data on Saturday at 3:30am (GMT): + +```sql +select cron.schedule ( + 'webhook-every-minute', -- name of the cron job + '* * * * *', -- every minute + $$ delete from events where event_time < now() - interval '1 week' $$ +); +``` + +### Run a vacuum every day + +Vacuum every day at 3:00am (GMT) + +```sql +SELECT cron.schedule('nightly-vacuum', '0 3 * * *', 'VACUUM'); +``` + +### Unschedule a job + +Unschedules a job called `'nightly-vacuum'` + +```sql +SELECT cron.unschedule('nightly-vacuum'); +``` + +## Resources + +- [pg_cron GitHub Repository](https://github.com/citusdata/pg_cron) diff --git a/apps/reference/docs/guides/database/extensions/pgnet.mdx b/apps/reference/docs/guides/database/extensions/pgnet.mdx new file mode 100644 index 00000000000..341af926e44 --- /dev/null +++ b/apps/reference/docs/guides/database/extensions/pgnet.mdx @@ -0,0 +1,254 @@ +--- +id: pgnet +title: 'pg_net: Async Networking' +description: 'pg_net: an async networking extension for PostgreSQL.' +--- + +import Tabs from '@theme/Tabs' +import TabItem from '@theme/TabItem' + +:::caution + +The pg_net API is in beta. Functions signatures may change. + +::: + +[pg_net](https://github.com/supabase/pg_net/) is a PostgreSQL extension exposing a SQL interface for async networking with a focus on scalability and UX. + +It differs from the `http` extension in that it is asynchronous by default. This makes it useful in blocking functions (like triggers). + +## Usage + +### Enable the extension + + + + + +1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard. +2. Click on **Extensions** in the sidebar. +3. Search for "pg_net" and enable the extension. + + + + +```sql +-- Example: enable the "pg_net" extension +create schema if not exists net; +create extension pg_net with schema net; + +-- Example: disable the "plv8" extension +drop extension if exists pg_net; +drop schema net; +``` + +Even though the SQL code is `create extension`, this is the equivalent of "enabling the extension". +To disable an extension, call `drop extension`. + +Procedural languages are automatically installed within `pg_catalog`, so you don't need to specify a schema. + + + + +## `http_get` {#http_get} + +Creates an HTTP GET request returning the request's ID. HTTP requests are not started until the transaction is committed. + +### Signature + +:::caution + +This is a Postgres SECURITY DEFINER function. + +::: + +```sql +net.http_get( + -- url for the request + url text, + -- key/value pairs to be url encoded and appended to the `url` + params jsonb default '{}'::jsonb, + -- key/values to be included in request headers + headers jsonb default '{}'::jsonb, + -- WARNING: this is currently ignored, so there is no timeout + -- the maximum number of milliseconds the request may take before being cancelled + timeout_milliseconds int default 1000 +) + -- request_id reference + returns bigint + + strict + volatile + parallel safe + language plpgsql +``` + +### Usage + +```sql +select net.http_get('https://news.ycombinator.com') as request_id; +request_id +---------- + 1 +(1 row) +``` + +After triggering `http_get`, use [`http_get_result`](#http_get_result) to get the result of the request. + +## `http_post` {#http_post} + +Creates an HTTP POST request with a JSON body, returning the request's ID. HTTP requests are not started until the transaction is committed. + +The body's character set encoding matches the database's `server_encoding` setting. + +### Signature + +:::caution + +This is a Postgres SECURITY DEFINER function + +::: + +```sql +net.http_post( + -- url for the request + url text, + -- body of the POST request + body jsonb default '{}'::jsonb, + -- key/value pairs to be url encoded and appended to the `url` + params jsonb default '{}'::jsonb, + -- key/values to be included in request headers + headers jsonb default '{"Content-Type": "application/json"}'::jsonb, + -- WARNING: this is currently ignored, so there is no timeout + -- the maximum number of milliseconds the request may take before being cancelled + timeout_milliseconds int default 1000 +) + -- request_id reference + returns bigint + + volatile + parallel safe + language plpgsql +``` + +### Usage + +```sql +select + net.http_post( + url:='https://httpbin.org/post', + body:='{"hello": "world"}'::jsonb + ) as request_id; +request_id +---------- + 1 +(1 row) +``` + +After triggering `http_post`, use [`http_get_result`](#http_get_result) to get the result of the request. + +## `http_collect_response` {#http_collect_response} + +Given a `request_id` reference, retrieves the response. + +When `async:=false` is set it is recommended that [statement_timeout](https://www.postgresql.org/docs/13/runtime-config-client.html) is set for the maximum amount of time the caller is willing to wait in case the response is slow to populate. + +### Signature + +:::caution + +This is a Postgres SECURITY DEFINER function + +::: + +```sql +net.http_collect_response( + -- request_id reference + request_id bigint, + -- when `true`, return immediately. when `false` wait for the request to complete before returning + async bool default true +) + -- http response composite wrapped in a result type + returns net.http_response_result + + strict + volatile + parallel safe +``` + +### Usage + +:::caution + +`net.http_collect_response` must be in a separate transaction from the calls to `net.http_` + +::: + +```sql +select + net.http_post( + url:='https://httpbin.org/post', + body:='{"hello": "world"}'::jsonb + ) as request_id; +request_id +---------- + 1 +(1 row) + +select * from net.http_collect_response(1, async:=false); +status | message | response +--------+---------+---------- +SUCCESS ok ( + status_code := 200, + headers := '{"date": ...}', + body := '{"args": ...}' + )::net.http_response_result + + +select + (response).body::json +from + net.http_collect_response(request_id:=1); + body +------------------------------------------------------------------- + { + "args": {}, + "data": "{\"hello\": \"world\"}", + "files": {}, + "form": {}, + "headers": { + "Accept": "*/*", + "Content-Length": "18", + "Content-Type": "application/json", + "Host": "httpbin.org", + "User-Agent": "pg_net/0.2", + "X-Amzn-Trace-Id": "Root=1-61031a5c-7e1afeae69bffa8614d8e48e" + }, + "json": { + "hello": "world" + }, + "origin": "135.63.38.488", + "url": "https://httpbin.org/post" + } +(1 row) +``` + +Where `response` is a composite: + +```sql +status_code integer +headers jsonb +body text +``` + +Possible values for `net.http_response_result.status` are `('PENDING', 'SUCCESS', 'ERROR')` + +## Resources + +- Source code: [github.com/supabase/pg_net](https://github.com/supabase/pg_net/) +- Official Docs: [supabase.github.io/pg_net](https://supabase.github.io/pg_net/) diff --git a/apps/reference/docs/guides/database/tables.mdx b/apps/reference/docs/guides/database/tables.mdx index 1f0e0857329..ed9292a9f3e 100644 --- a/apps/reference/docs/guides/database/tables.mdx +++ b/apps/reference/docs/guides/database/tables.mdx @@ -245,7 +245,7 @@ For example, if you wanted to load a CSV file into your movies table: You would [connect](../../guides/database/connecting-to-postgres#direct-connections) to your database directly and load the file with the COPY command: ```bash -psql -h DATABASE_URL -p 5432 postgres -U postgres \ +psql -h DATABASE_URL -p 5432 -d postgres -U postgres \ -c "COPY movies FROM './movies.csv';" ``` diff --git a/apps/reference/docs/guides/features.mdx b/apps/reference/docs/guides/features.mdx index bd3ae948058..e41d5f1f752 100755 --- a/apps/reference/docs/guides/features.mdx +++ b/apps/reference/docs/guides/features.mdx @@ -133,7 +133,7 @@ Globally distributed TypeScript functions to execute custom business logic. [Doc Use our CLI to develop your project locally and deploy to the Supabase Platform. [Docs](/docs/reference/cli). -### Admin API +### Management API Manage your projects programmatically. [Docs](/docs/reference/api). @@ -154,9 +154,9 @@ Both Postgres and the Supabase Platform are production-ready. Some tools we offe | Database | Point-in-Time Recovery | `alpha` | | Database | Vault | `alpha` | | Studio | | `GA` | -| Realtime | Postgres CDC | `beta` | -| Realtime | Broadcast | `alpha` | -| Realtime | Presence | `alpha` | +| Realtime | Postgres CDC | `GA` | +| Realtime | Broadcast | `beta` | +| Realtime | Presence | `beta` | | Storage | Backend (S3) | `GA` | | Storage | API | `beta` | | Storage | CDN | `beta` | @@ -165,7 +165,7 @@ Both Postgres and the Supabase Platform are production-ready. Some tools we offe | Auth | Passwordless | `beta` | | Auth | Next.js Auth Helpers | `alpha` | | Auth | SvelteKit Auth Helpers | `alpha` | -| Admin API | | `beta` | +| Management API | | `beta` | | CLI | | `beta` | | Client Library: JavaScript | | `GA` | | Client Library: Dart | | `beta` | diff --git a/apps/reference/docs/guides/functions.mdx b/apps/reference/docs/guides/functions.mdx index 0391adce248..8bcab320554 100644 --- a/apps/reference/docs/guides/functions.mdx +++ b/apps/reference/docs/guides/functions.mdx @@ -263,6 +263,10 @@ serve(async (req) => { }) ``` +### Using HTTP Methods + +Edge Functions supports `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, and `OPTIONS`. A function can be designed to perform different actions based on a request's HTTP method. See the [example on building a RESTful service](https://github.com/supabase/supabase/tree/master/examples/edge-functions/supabase/functions/restful-tasks) to learn how to handle different HTTP methods in your function. + ## Limitations - Deno Deploy limitations @@ -270,5 +274,4 @@ serve(async (req) => { - Cannot write to File System - Edge Functions - Local development - only one function at a time - - Supabase Functions only supports `POST`, `OPTIONS`, and `GET` requests. - Serving of HTML content is not supported (`GET` requests that return `text/html` will be rewritten to `text/plain`). diff --git a/apps/reference/docs/guides/functions/examples.mdx b/apps/reference/docs/guides/functions/examples.mdx index e56dc4da517..39a48de9a0b 100644 --- a/apps/reference/docs/guides/functions/examples.mdx +++ b/apps/reference/docs/guides/functions/examples.mdx @@ -26,6 +26,16 @@ const examples = [ description: 'Full example for using Supabase and Stripe, with Flutter.', href: 'https://github.com/supabase-community/flutter-stripe-payments-with-supabase-functions', }, + { + name: 'Building a RESTful Service API', + description: 'Learn how to use HTTP methods and paths to build a RESTful service for managing tasks.', + href: 'https://github.com/supabase/supabase/blob/master/examples/edge-functions/supabase/functions/restful-tasks/index.ts', + }, + { + name: 'Working with Supabase Storage', + description: 'An example on reading a file from Supbase Storage.', + href: 'https://github.com/supabase/supabase/blob/master/examples/edge-functions/supabase/functions/read-storage/index.ts', + } ] You can find a list of useful [Edge Function Examples](https://github.com/supabase/supabase/tree/master/examples/edge-functions) in our GitHub repository. diff --git a/apps/reference/docs/guides/platform/migrating-between-projects.mdx b/apps/reference/docs/guides/platform/migrating-between-projects.mdx new file mode 100644 index 00000000000..b8e34c012c4 --- /dev/null +++ b/apps/reference/docs/guides/platform/migrating-between-projects.mdx @@ -0,0 +1,131 @@ +--- +id: migrating-and-upgrading-projects +title: 'Migrating and Upgrading Projects' +description: Upgrade your project to the latest version of Supabase. +sidebar_label: 'Migrating and upgrading' +--- + +Supabase ships fast and we endeavor to add all new features to existing projects wherever possible. +In some cases, access to new features require upgrading or migrating your Supabase project. + +## Upgrade your project + +When you pause and restore a project, the restored database includes the latest features. This method _does_ include downtime, so be aware that your project will be inaccessible for a short period of time. + +1. On the [General Settings](https://app.supabase.com/project/_/settings/general) page in the Dashboard, click **Pause project**. You will be redirected to the home screen as your project is pausing. This process can take several minutes. +1. After your project is paused, click **Restore project**. The restoration can take several minutes depending on how much data your database has. You will receive an email once the restoration is complete. + +## Migrate your project + +Migrating projects can be achieved using standard PostgreSQL tooling. This is particularly useful for older projects (e.g. to use a newer Postgres version). + +### Before you begin + +- Install [Postgres](https://www.postgresql.org/download/) so you can run `psql` and `pg_dump`. +- Create a new [Supabase project](https://app.supabase.com). +- Store the old project's database URL as `$OLD_DB_URL` and the new project's as `$NEW_DB_URL`. + +### Migrate the database + +1. Enable [Database Webhooks](https://app.supabase.com/project/_/database/hooks) in your new project if you enabled them in your old project. +2. In your new project, enable all extensions that were enabled in your old project. +3. Run the following command from your terminal: + +```sh +set -euo pipefail + +pg_dump \ + --clean \ + --if-exists \ + --quote-all-identifiers \ + --exclude-table-data 'storage.objects' \ + --exclude-schema 'extensions|graphql|graphql_public|net|pgbouncer|pgsodium|pgsodium_masks|realtime|supabase_functions|pg_toast|pg_catalog|information_schema' \ + --schema '*' \ + --dbname "$OLD_DB_URL" \ +| sed 's/^DROP SCHEMA IF EXISTS "auth";$/-- DROP SCHEMA IF EXISTS "auth";/' \ +| sed 's/^DROP SCHEMA IF EXISTS "storage";$/-- DROP SCHEMA IF EXISTS "storage";/' \ +| sed 's/^CREATE SCHEMA "auth";$/-- CREATE SCHEMA "auth";/' \ +| sed 's/^CREATE SCHEMA "storage";$/-- CREATE SCHEMA "storage";/' \ +| sed 's/^ALTER DEFAULT PRIVILEGES FOR ROLE "supabase_admin"/-- ALTER DEFAULT PRIVILEGES FOR ROLE "supabase_admin"/' \ +> dump.sql + +psql \ + --single-transaction \ + --variable ON_ERROR_STOP=1 \ + --file dump.sql \ + --dbname "$NEW_DB_URL" +``` + +### Enable publication on tables + +Replication for Realtime is disabled for all tables in your new project. On the [Replication](https://app.supabase.com/project/_/database/replication) page in the Dashboard, select your new project and enable replication for tables that were enabled in your old project. + +### Migrate Storage objects + +The new project has the old project's Storage buckets, but the Storage objects need to be migrated manually. Use this script to move storage objects from one project to another. If you have more than 10k objects, we can move the objects for you. Just contact us at [support@supabase.com](mailto:support@supabase.com). + +```js +// npm install @supabase/supabase-js@1 +const { createClient } = require('@supabase/supabase-js') + +const OLD_PROJECT_URL = 'https://xxx.supabase.co' +const OLD_PROJECT_SERVICE_KEY = 'old-project-service-key-xxx' + +const NEW_PROJECT_URL = 'https://yyy.supabase.co' +const NEW_PROJECT_SERVICE_KEY = 'new-project-service-key-yyy' + +;(async () => { + const oldSupabaseRestClient = createClient( + OLD_PROJECT_URL, + OLD_PROJECT_SERVICE_KEY, + { + schema: 'storage', + } + ) + const oldSupabaseClient = createClient( + OLD_PROJECT_URL, + OLD_PROJECT_SERVICE_KEY + ) + const newSupabaseClient = createClient( + NEW_PROJECT_URL, + NEW_PROJECT_SERVICE_KEY + ) + + // make sure you update max_rows in postgrest settings if you have a lot of objects + // or paginate here + const { data: oldObjects, error } = await oldSupabaseRestClient + .from('objects') + .select() + if (error) { + console.log('error getting objects from old bucket') + throw error + } + + for (const objectData of oldObjects) { + console.log(`moving ${objectData.id}`) + try { + const { data, error: downloadObjectError } = + await oldSupabaseClient.storage + .from(objectData.bucket_id) + .download(objectData.name) + if (downloadObjectError) { + throw downloadObjectError + } + + const { _, error: uploadObjectError } = await newSupabaseClient.storage + .from(objectData.bucket_id) + .upload(objectData.name, data, { + upsert: true, + contentType: objectData.metadata.mimetype, + cacheControl: objectData.metadata.cacheControl, + }) + if (uploadObjectError) { + throw uploadObjectError + } + } catch (err) { + console.log('error moving ', objectData) + console.log(err) + } + } +})() +``` diff --git a/apps/reference/docs/guides/with-nextjs.mdx b/apps/reference/docs/guides/with-nextjs.mdx index 2cfe4430752..82492aca8a0 100644 --- a/apps/reference/docs/guides/with-nextjs.mdx +++ b/apps/reference/docs/guides/with-nextjs.mdx @@ -14,6 +14,17 @@ import ProjectSetup from './_partials/project_setup.mdx' ![Supabase User Management example](/img/user-management-demo.png) +### Video Guide + +
+ +
+ ### GitHub Should you get stuck while working through the guide, refer to [this repo](https://github.com/supabase/supabase/tree/master/examples/user-management/nextjs-ts-user-management). diff --git a/apps/reference/docs/reference.mdx b/apps/reference/docs/reference.mdx index 6b292769084..f3718731732 100644 --- a/apps/reference/docs/reference.mdx +++ b/apps/reference/docs/reference.mdx @@ -34,7 +34,7 @@ Reference documentation for the official Supabase client libraries, APIs, and to {/* API */}
- +
{/* */} diff --git a/apps/reference/nav/_referenceSidebars.js b/apps/reference/nav/_referenceSidebars.js index f88427304a1..b87a4b84ff5 100644 --- a/apps/reference/nav/_referenceSidebars.js +++ b/apps/reference/nav/_referenceSidebars.js @@ -109,12 +109,6 @@ const sidebars = { }, ], }, - { - type: 'category', - label: 'APIs', - collapsed: true, - items: ['guides/api', 'guides/api/generating-types'], - }, { type: 'category', label: 'Database', @@ -124,20 +118,28 @@ const sidebars = { 'guides/database/connecting-to-postgres', 'guides/database/tables', 'guides/database/functions', + 'guides/database/webhooks', 'guides/database/full-text-search', - 'guides/database/migrating-between-projects', // 'guides/database/json', // 'guides/database/arrays', // 'guides/database/sql-to-api', + { + type: 'category', + label: 'Data APIs', + collapsed: true, + items: ['guides/api', 'guides/api/generating-types'], + }, { type: 'category', label: 'Extensions', collapsed: true, items: [ 'guides/database/extensions', - 'guides/database/extensions/plv8', 'guides/database/extensions/http', + 'guides/database/extensions/pgcron', + 'guides/database/extensions/pgnet', 'guides/database/extensions/pgtap', + 'guides/database/extensions/plv8', 'guides/database/extensions/uuid-ossp', ], }, @@ -191,6 +193,7 @@ const sidebars = { 'guides/platform/disk-usage', 'guides/platform/logs', 'guides/platform/metrics', + 'guides/platform/migrating-and-upgrading-projects', 'guides/platform/performance', 'guides/platform/permissions', 'going-into-prod', @@ -285,7 +288,7 @@ const sidebars = { href: '/reference/dart', }, { type: 'link', label: 'Supabase CLI', href: '/reference/cli' }, - { type: 'link', label: 'Supabase API', href: '/reference/api' }, + { type: 'link', label: 'Management API', href: '/reference/api' }, ], }, // TODO: add back in with typedocs diff --git a/apps/temp-docs/.gitignore b/apps/temp-docs/.gitignore new file mode 100644 index 00000000000..820d2d9ff0a --- /dev/null +++ b/apps/temp-docs/.gitignore @@ -0,0 +1,21 @@ +# Dependencies +/node_modules + +# Production +/build + +# Generated files +.cache-loader + +# Misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +**/*/generated \ No newline at end of file diff --git a/apps/temp-docs/_api/intro.md b/apps/temp-docs/_api/intro.md new file mode 100644 index 00000000000..8f9bd942142 --- /dev/null +++ b/apps/temp-docs/_api/intro.md @@ -0,0 +1,37 @@ +--- +slug: / +sidebar_position: 1 +sidebar_label: Supabase API +--- + +# Supabase API + +The Supabase API allows you to manage your projects programmatically. + +## Status + +The Supabase API is in `beta`. It is usable in it's current state, but it's likely that there will be breaking changes. + +## Authentication + +All API requests require a Supabase Personal token to be included in the Authorization header: `Authorization Bearer + + + + + + + + +## Organizations {#organizations} + +Organization endpoints + + + + + +### List all organizations {#list-all-organizations} + +``` +GET https://api.supabase.com/v1/organizations +``` + + + + + + + + + + + + + + +#### Responses + + + + + + + + + + +```json +{ + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": [ + "id", + "name" + ] + } + } +} +``` + + + + + + + + +Unexpected error listing organizations + + + + + + + + + + + +
+ + + + +### Create an organization {#create-an-organization} + +``` +POST https://api.supabase.com/v1/organizations +``` + + + + + + + + + + + +#### Body Parameters + +```json +{ + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": [ + "name" + ] + } +} +``` + + + + +#### Responses + + + + + + + + + + +```json +{ + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": [ + "id", + "name" + ] + } +} +``` + + + + + + + + +Unexpected error creating an organization + + + + + + + + + + + +
+ + + +## Projects {#projects} + +Project endpoints + + + + + +### List all projects {#list-all-projects} + +``` +GET https://api.supabase.com/v1/projects +``` + + + + + + + + + + + + + + +#### Responses + + + + + + + + + + +```json +{ + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "organization_id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "region": { + "type": "string" + }, + "created_at": { + "type": "string" + } + }, + "required": [ + "id", + "organization_id", + "name", + "region", + "created_at" + ] + } + } +} +``` + + + + + + + + +
+ + + + +### Create a project {#create-a-project} + +``` +POST https://api.supabase.com/v1/projects +``` + + + + + + + + + + + +#### Body Parameters + +```json +{ + "schema": { + "type": "object", + "properties": { + "db_pass": { + "type": "string" + }, + "name": { + "type": "string" + }, + "organization_id": { + "type": "string" + }, + "plan": { + "type": "string", + "enum": [ + "free", + "pro" + ] + }, + "region": { + "type": "string", + "enum": [ + "us-east-1", + "us-west-1", + "ap-southeast-1", + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-2", + "eu-west-1", + "eu-west-2", + "eu-central-1", + "ca-central-1", + "ap-south-1", + "sa-east-1" + ] + }, + "kps_enabled": { + "type": "boolean" + } + }, + "required": [ + "db_pass", + "name", + "organization_id", + "plan", + "region" + ] + } +} +``` + + + + +#### Responses + + + + + + + + + + +```json +{ + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "organization_id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "region": { + "type": "string" + }, + "created_at": { + "type": "string" + } + }, + "required": [ + "id", + "organization_id", + "name", + "region", + "created_at" + ] + } +} +``` + + + + + + + + +
+ + + + +### List all functions {#list-all-functions} + +``` +GET https://api.supabase.com/v1/projects/{ref}/functions +``` + + + + +#### Path Parameters + +
    + +
  • +

    + + ref + + + required + + + + +

    + +
    + Project ref +
    +
  • + +
+ + + + + + + + + + + +#### Responses + + + + + + + + + + +```json +{ + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "slug": { + "type": "string" + }, + "name": { + "type": "string" + }, + "status": { + "enum": [ + "ACTIVE", + "REMOVED", + "THROTTLED" + ], + "type": "string" + }, + "version": { + "type": "number" + }, + "created_at": { + "type": "number" + }, + "updated_at": { + "type": "number" + }, + "verify_jwt": { + "type": "boolean" + } + }, + "required": [ + "id", + "slug", + "name", + "status", + "version", + "created_at", + "updated_at" + ] + } + } +} +``` + + + + + + + + + + + + + + + + + + + + +Failed to retrieve project's functions + + + + + + + + + + + +
+ + + + +### Create a function {#create-a-function} + +``` +POST https://api.supabase.com/v1/projects/{ref}/functions +``` + + + + +#### Path Parameters + +
    + +
  • +

    + + ref + + + required + + + + +

    + +
    + Project ref +
    +
  • + +
+ + + + + + + + +#### Body Parameters + +```json +{ + "schema": { + "type": "object", + "properties": { + "slug": { + "type": "string", + "pattern": "/^[A-Za-z0-9_-]+$/" + }, + "name": { + "type": "string" + }, + "body": { + "type": "string" + }, + "verify_jwt": { + "type": "boolean" + } + }, + "required": [ + "slug", + "name", + "body" + ] + } +} +``` + + + + +#### Responses + + + + + + + + + + +```json +{ + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "slug": { + "type": "string" + }, + "name": { + "type": "string" + }, + "status": { + "enum": [ + "ACTIVE", + "REMOVED", + "THROTTLED" + ], + "type": "string" + }, + "version": { + "type": "number" + }, + "created_at": { + "type": "number" + }, + "updated_at": { + "type": "number" + }, + "verify_jwt": { + "type": "boolean" + } + }, + "required": [ + "id", + "slug", + "name", + "status", + "version", + "created_at", + "updated_at" + ] + } +} +``` + + + + + + + + + + + + + + + + + + + + +Failed to create project's function + + + + + + + + + + + +
+ + + + +### Retrieve a function {#retrieve-a-function} + +``` +GET https://api.supabase.com/v1/projects/{ref}/functions/{function_slug} +``` + + + + +#### Path Parameters + +
    + +
  • +

    + + ref + + + required + + + + +

    + +
    + Project ref +
    +
  • + +
  • +

    + + function_slug + + + required + + + + +

    + +
    + Function slug +
    +
  • + +
+ + + + + + + + + + + +#### Responses + + + + + + + + + + +```json +{ + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "slug": { + "type": "string" + }, + "name": { + "type": "string" + }, + "status": { + "enum": [ + "ACTIVE", + "REMOVED", + "THROTTLED" + ], + "type": "string" + }, + "version": { + "type": "number" + }, + "created_at": { + "type": "number" + }, + "updated_at": { + "type": "number" + }, + "verify_jwt": { + "type": "boolean" + }, + "body": { + "type": "string" + } + }, + "required": [ + "id", + "slug", + "name", + "status", + "version", + "created_at", + "updated_at" + ] + } +} +``` + + + + + + + + + + + + + + + + + + + + +Failed to retrieve function with given slug + + + + + + + + + + + +
+ + + + +### Update a function {#update-a-function} + +``` +PATCH https://api.supabase.com/v1/projects/{ref}/functions/{function_slug} +``` + + + + +#### Path Parameters + +
    + +
  • +

    + + ref + + + required + + + + +

    + +
    + Project ref +
    +
  • + +
  • +

    + + function_slug + + + required + + + + +

    + +
    + Function slug +
    +
  • + +
+ + + + + + + + +#### Body Parameters + +```json +{ + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "body": { + "type": "string" + }, + "verify_jwt": { + "type": "boolean" + } + } + } +} +``` + + + + +#### Responses + + + + + + + + + + +```json +{ + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "slug": { + "type": "string" + }, + "name": { + "type": "string" + }, + "status": { + "enum": [ + "ACTIVE", + "REMOVED", + "THROTTLED" + ], + "type": "string" + }, + "version": { + "type": "number" + }, + "created_at": { + "type": "number" + }, + "updated_at": { + "type": "number" + }, + "verify_jwt": { + "type": "boolean" + } + }, + "required": [ + "id", + "slug", + "name", + "status", + "version", + "created_at", + "updated_at" + ] + } +} +``` + + + + + + + + + + + + + + + + + + + + +Failed to update function with given slug + + + + + + + + + + + +
+ + + + +### Delete a function {#delete-a-function} + +``` +DELETE https://api.supabase.com/v1/projects/{ref}/functions/{function_slug} +``` + + + + +#### Path Parameters + +
    + +
  • +

    + + ref + + + required + + + + +

    + +
    + Project ref +
    +
  • + +
  • +

    + + function_slug + + + required + + + + +

    + +
    + Function slug +
    +
  • + +
+ + + + + + + + + + + +#### Responses + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Failed to delete function with given slug + + + + + + + + + + + +
+ + + + +### List all secrets {#list-all-secrets} + +``` +GET https://api.supabase.com/v1/projects/{ref}/secrets +``` + + + + +#### Path Parameters + +
    + +
  • +

    + + ref + + + required + + + + +

    + +
    + Project ref +
    +
  • + +
+ + + + + + + + + + + +#### Responses + + + + + + + + + + +```json +{ + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "required": [ + "name", + "value" + ] + } + } +} +``` + + + + + + + + + + + + + + + + + + + + +Failed to retrieve project's secrets + + + + + + + + + + + +
+ + + + +### Bulk create secrets {#bulk-create-secrets} + +``` +POST https://api.supabase.com/v1/projects/{ref}/secrets +``` + + + + +#### Path Parameters + +
    + +
  • +

    + + ref + + + required + + + + +

    + +
    + Project ref +
    +
  • + +
+ + + + + + + + +#### Body Parameters + +```json +{ + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "value": { + "type": "string", + "pattern": "/^(?!SUPABASE_).*/" + } + }, + "required": [ + "name", + "value" + ] + } + } +} +``` + + + + +#### Responses + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Failed to create project's secrets + + + + + + + + + + + +
+ + + + +### Bulk delete secrets {#bulk-delete-secrets} + +``` +DELETE https://api.supabase.com/v1/projects/{ref}/secrets +``` + + + + +#### Path Parameters + +
    + +
  • +

    + + ref + + + required + + + + +

    + +
    + Project ref +
    +
  • + +
+ + + + + + + + +#### Body Parameters + +```json +{ + "schema": { + "type": "array", + "items": { + "type": "string" + } + } +} +``` + + + + +#### Responses + + + + + + + + + + +```json +{ + "schema": { + "type": "object" + } +} +``` + + + + + + + + + + + + + + + + + + + + +Failed to delete secrets with given names + + + + + + + + + + + +
+ diff --git a/apps/temp-docs/docs/javascript/.gitkeep b/apps/temp-docs/_api_versioned_docs/.gitkeep similarity index 100% rename from apps/temp-docs/docs/javascript/.gitkeep rename to apps/temp-docs/_api_versioned_docs/.gitkeep diff --git a/apps/temp-docs/docs/reference/javascript/.gitkeep b/apps/temp-docs/_api_versioned_sidebars/.gitkeep similarity index 100% rename from apps/temp-docs/docs/reference/javascript/.gitkeep rename to apps/temp-docs/_api_versioned_sidebars/.gitkeep diff --git a/apps/temp-docs/_api_versions.json b/apps/temp-docs/_api_versions.json new file mode 100644 index 00000000000..fe51488c706 --- /dev/null +++ b/apps/temp-docs/_api_versions.json @@ -0,0 +1 @@ +[] diff --git a/apps/temp-docs/_cli/intro.mdx b/apps/temp-docs/_cli/intro.mdx new file mode 100644 index 00000000000..8ac2186ca6e --- /dev/null +++ b/apps/temp-docs/_cli/intro.mdx @@ -0,0 +1,29 @@ +--- +id: intro +slug: / +sidebar_position: 1 +sidebar_label: Supabase CLI +hide_table_of_contents: true +--- + +# Supabase CLI + +The Supabase CLI provides tools to develop your project locally and deploy to the Supabase Platform. +The CLI is still under development, but it contains all the functionality for working with your Supabase projects and the Supabase Platform. + +- Run Supabase locally: [`supabase start`](https://supabase.com/docs/reference/cli/usage#supabase-start) +- Manage database migrations: [`supabase migration`](https://supabase.com/docs/reference/cli/usage#supabase-migration) +- CI/CD for releasing to production: [`supabase db push`](https://supabase.com/docs/reference/cli/usage#supabase-db-push) +- Manage your Supabase projects: [`supabase projects`](https://supabase.com/docs/reference/cli/usage#supabase-projects) +- Generate types directly from your database schema: [`supabase gen types`](https://supabase.com/docs/reference/cli/usage#supabase-gen) + - A [community-supported GitHub Action](https://github.com/lyqht/generate-supabase-db-types-github-action) to generate TypeScript types +- Shell autocomplete: [`supabase completion`](https://supabase.com/docs/reference/cli/usage#supabase-completion) + - A [community-supported Fig autocomplete spec](https://fig.io/manual/supabase) for macOS terminal + +## Additional Links + +- [Install the Supabase CLI](/docs/guides/cli) +- [Source code](https://github.com/supabase/cli) +- [Known bugs and issues](https://github.com/supabase/cli/issues) +- [Supabase CLI v1 and Admin API Beta](https://supabase.com/blog/supabase-cli-v1-and-admin-api-beta) +- [The CLI team announcing CLI V1 and Admin API Beta - Video](https://www.youtube.com/watch?v=OpPOaJI_Z28) diff --git a/apps/temp-docs/_cli/release-notes.mdx b/apps/temp-docs/_cli/release-notes.mdx new file mode 100644 index 00000000000..e10337f8d04 --- /dev/null +++ b/apps/temp-docs/_cli/release-notes.mdx @@ -0,0 +1,7 @@ +--- +id: release-notes +--- + +# Release Notes + +All release notes can be found in the [GitHub Releases page](https://github.com/supabase/cli/releases). diff --git a/apps/temp-docs/_cli_versioned_docs/.gitkeep b/apps/temp-docs/_cli_versioned_docs/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/apps/temp-docs/_cli_versioned_sidebars/.gitkeep b/apps/temp-docs/_cli_versioned_sidebars/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/apps/temp-docs/_cli_versions.json b/apps/temp-docs/_cli_versions.json new file mode 100644 index 00000000000..fe51488c706 --- /dev/null +++ b/apps/temp-docs/_cli_versions.json @@ -0,0 +1 @@ +[] diff --git a/apps/temp-docs/_gotrue/config.mdx b/apps/temp-docs/_gotrue/config.mdx new file mode 100644 index 00000000000..9a00a5a380e --- /dev/null +++ b/apps/temp-docs/_gotrue/config.mdx @@ -0,0 +1,35 @@ +--- +id: config +slug: /config +title: Configuration +toc_max_heading_level: 3 +--- + + + +A `config.toml` file is generated after running `supabase init`. + +This file is located in the `supabase` folder under `supabase/config.toml`. + + + + + + +## General {#general} + + + +### `project_id` {#project_id} + +A string used to distinguish different Supabase projects on the same host. Defaults to the working directory name when running `supabase init`. + + +
    +
  • Required: true
  • +
  • Default: None
  • +
+ +
+ + diff --git a/apps/temp-docs/_gotrue/intro.mdx b/apps/temp-docs/_gotrue/intro.mdx new file mode 100644 index 00000000000..cb9a386f235 --- /dev/null +++ b/apps/temp-docs/_gotrue/intro.mdx @@ -0,0 +1,22 @@ +--- +slug: / +sidebar_position: 1 +sidebar_label: Auth Server +--- + +# Supabase Auth Server + +The Supabase Auth Server (GoTrue) is a JSON Web Token (JWT)-based API for managing users and issuing access tokens. + +GoTrue is an open-source API written in Golang, that acts as a self-standing API service for handling user registration and authentication for JAM projects. It's based on OAuth2 and JWT and handles user signup, authentication, and custom user data. + +## Client libraries + +- [JavaScript](https://github.com/supabase/gotrue-js) +- [Dart](https://github.com/supabase/gotrue-dart) + +## Additional Links + +- [Source Code](https://github.com/supabase/gotrue) +- [Known bugs and issues](https://github.com/supabase/gotrue/issues) +- [Auth Guides](https://supabase.com/docs/guides/auth) diff --git a/apps/temp-docs/_gotrue/release-notes.mdx b/apps/temp-docs/_gotrue/release-notes.mdx new file mode 100644 index 00000000000..cb164542845 --- /dev/null +++ b/apps/temp-docs/_gotrue/release-notes.mdx @@ -0,0 +1,7 @@ +--- +id: release-notes +--- + +# Release Notes + +All release notes can be found in the [GitHub Releases](https://github.com/supabase/gotrue/releases) page. diff --git a/apps/temp-docs/_gotrue/usage.mdx b/apps/temp-docs/_gotrue/usage.mdx new file mode 100644 index 00000000000..64eed5538f0 --- /dev/null +++ b/apps/temp-docs/_gotrue/usage.mdx @@ -0,0 +1,18 @@ +--- +id: usage +slug: /usage +title: Usage +toc_max_heading_level: 3 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + + + + +Documentation of the gotrue API. + + + + diff --git a/apps/temp-docs/_gotrue_versioned_docs/.gitkeep b/apps/temp-docs/_gotrue_versioned_docs/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/apps/temp-docs/_gotrue_versioned_sidebars/.gitkeep b/apps/temp-docs/_gotrue_versioned_sidebars/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/apps/temp-docs/_gotrue_versions.json b/apps/temp-docs/_gotrue_versions.json new file mode 100644 index 00000000000..fe51488c706 --- /dev/null +++ b/apps/temp-docs/_gotrue_versions.json @@ -0,0 +1 @@ +[] diff --git a/apps/temp-docs/_storage/intro.mdx b/apps/temp-docs/_storage/intro.mdx new file mode 100644 index 00000000000..4c038f6c7aa --- /dev/null +++ b/apps/temp-docs/_storage/intro.mdx @@ -0,0 +1,28 @@ +--- +slug: / +sidebar_position: 1 +sidebar_label: Storage Server +--- + +# Supabase Storage Server + +An S3 compatible object storage service that integrates with Postgres. + +- Uses Postgres as it's datastore for storing metadata +- Authorization rules are written as Postgres Row Level Security policies +- Integrates with S3 as the storage backend (with more in the pipeline!) +- Extremely lightweight and performant + +Read [this post](https://supabase.com/blog/supabase-storage) on why we decided to build a new object storage service. + +## Client libraries + +- [JavaScript](https://github.com/supabase/storage-js) +- [Dart](https://github.com/supabase/storage-dart) + +## Additional Links + +- [Source Code](https://github.com/supabase/storage-api) +- [Known bugs and issues](https://github.com/supabase/storage-js/issues) +- [Storage Guides](https://supabase.com/docs/guides/storage) +- [OpenAPI Docs](https://supabase.github.io/storage-api/) diff --git a/apps/temp-docs/_storage/release-notes.mdx b/apps/temp-docs/_storage/release-notes.mdx new file mode 100644 index 00000000000..8dad269f0d9 --- /dev/null +++ b/apps/temp-docs/_storage/release-notes.mdx @@ -0,0 +1,7 @@ +--- +id: release-notes +--- + +# Release Notes + +All release notes can be found in the [GitHub Releases](https://github.com/supabase/storage-api/releases) page. diff --git a/apps/temp-docs/_storage_versioned_docs/.gitkeep b/apps/temp-docs/_storage_versioned_docs/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/apps/temp-docs/_storage_versioned_sidebars/.gitkeep b/apps/temp-docs/_storage_versioned_sidebars/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/apps/temp-docs/_storage_versions.json b/apps/temp-docs/_storage_versions.json new file mode 100644 index 00000000000..fe51488c706 --- /dev/null +++ b/apps/temp-docs/_storage_versions.json @@ -0,0 +1 @@ +[] diff --git a/apps/temp-docs/_supabase_dart/initializing.mdx b/apps/temp-docs/_supabase_dart/initializing.mdx new file mode 100644 index 00000000000..b0d69813f45 --- /dev/null +++ b/apps/temp-docs/_supabase_dart/initializing.mdx @@ -0,0 +1,43 @@ +--- +id: initializing +title: 'Initializing' +slug: initializing +--- + +import Tabs from '@theme/Tabs' +import TabItem from '@theme/TabItem' + +## Flutter + +For `supabase-flutter`, you will be using the static `initialize()` method on `Supabase` class. + +### Flutter `initialize()` + +```dart title="main.dart" +Future main() async { + await Supabase.initialize(url: 'https://xyzcompany.supabase.co', anonKey: 'public-anon-key'); + runApp(MyApp()); +} +``` + +### Access `SupabaseClient` instance + +Once you initialize Supabase in your `main()` method, you can access the `SupabaseClient` instance from anywhere in your app. + +```dart +final supabase = Supabase.instance.client; +``` + +## Other Dart Projects + +You can initialize a new Supabase client using the `SupabaseClient()` method. + +The Supabase client is your entrypoint to the rest of the Supabase functionality +and is the easiest way to interact with everything we offer within the Supabase ecosystem. + +### Dart `SupabaseClient()` + +```dart +final supabase = SupabaseClient('https://xyzcompany.supabase.co', 'public-anon-key'); +``` + diff --git a/apps/temp-docs/_supabase_dart/installing.mdx b/apps/temp-docs/_supabase_dart/installing.mdx new file mode 100644 index 00000000000..8c49c1fd9f7 --- /dev/null +++ b/apps/temp-docs/_supabase_dart/installing.mdx @@ -0,0 +1,24 @@ +--- +id: installing +title: 'Installing' +slug: installing +--- + +import Tabs from '@theme/Tabs' +import TabItem from '@theme/TabItem' + +## Flutter + +For Flutter project, you can use [supabase_flutter](https://pub.dev/packages/supabase_flutter) package. + +```bash +flutter pub add supabase_flutter +``` + +## Other Dart projects + +For non-Flutter projects using Dart, such as server-side Dart or Angular-Dart, you can use the [supabase](https://pub.dev/packages/supabase) package. + +```bash +dart pub add supabase +``` \ No newline at end of file diff --git a/apps/temp-docs/_supabase_dart/intro.mdx b/apps/temp-docs/_supabase_dart/intro.mdx new file mode 100644 index 00000000000..d728b447d2e --- /dev/null +++ b/apps/temp-docs/_supabase_dart/intro.mdx @@ -0,0 +1,27 @@ +--- +id: intro +slug: / +sidebar_label: Supabase Flutter Library +--- + +# Supabase Flutter Library + +This reference documents every object and method available in Supabase's isomorphic Flutter library, [`supabase-flutter`](https://pub.dev/packages/supabase_flutter). + +You can use the `supabase-flutter` library to: + +- interact with your Postgres database +- listen to database changes +- invoke Deno Edge Functions +- build login and user management functionality +- manage large files + +## For non-Flutter projects + +We also have [supabase-dart](https://github.com/supabase-community/supabase-dart) for non-Flutter Dart projects, such as server-side Dart or Angular-Dart. +supabase-dart shares most of the APIs with supabase-flutter without being dependent on Flutter so that you can use Supabase anywhere you can run Dart! + +## Additional Links + +- Source Code: [github.com/supabase/supabase-flutter](https://github.com/supabase/supabase-flutter) +- [Known bugs and issues](https://github.com/supabase/supabase-flutter/issues) diff --git a/apps/temp-docs/_supabase_dart_versioned_docs/version-v0/intro.mdx b/apps/temp-docs/_supabase_dart_versioned_docs/version-v0/intro.mdx new file mode 100644 index 00000000000..ed80828fa05 --- /dev/null +++ b/apps/temp-docs/_supabase_dart_versioned_docs/version-v0/intro.mdx @@ -0,0 +1,27 @@ +--- +id: intro +slug: / +sidebar_label: Supabase Dart Library +--- + +# Supabase Dart Library + +:::caution +You're viewing the docs for an older version of the `supabase-flutter` library. +::: + + +This reference documents every object and method available in Supabase's isomorphic Dart library, `supabase-dart`. + +You can use the `supabase-dart` library to: + +- interact with your Postgres database +- listen to database changes +- invoke Deno Edge Functions +- build login and user management functionality +- manage large files + +## Additional Links + +- Source Code: [github.com/supabase/supabase-dart](https://github.com/supabase/supabase-dart) +- [Known bugs and issues](https://github.com/supabase/supabase-dart/issues) diff --git a/apps/temp-docs/_supabase_dart_versioned_sidebars/version-v0-sidebars.json b/apps/temp-docs/_supabase_dart_versioned_sidebars/version-v0-sidebars.json new file mode 100644 index 00000000000..44acc0181ad --- /dev/null +++ b/apps/temp-docs/_supabase_dart_versioned_sidebars/version-v0-sidebars.json @@ -0,0 +1,120 @@ +{ + "sidebar": [ + { + "type": "category", + "label": "Getting Started", + "items": ["intro", "generated/installing", "generated/initializing"], + "collapsed": true + }, + { + "type": "category", + "label": "Auth", + "items": [ + "generated/auth-signup", + "generated/auth-signin", + "generated/auth-signinwithprovider", + "generated/auth-signout", + "generated/auth-session", + "generated/auth-user", + "generated/auth-update", + "generated/auth-onauthstatechange", + "generated/reset-password-email" + ], + "collapsed": true + }, + { + "type": "category", + "label": "Functions", + "items": ["generated/invoke"], + "collapsed": true + }, + { + "type": "category", + "label": "Database", + "items": [ + "generated/select", + "generated/insert", + "generated/update", + "generated/upsert", + "generated/delete", + "generated/rpc" + ], + "collapsed": true + }, + { + "type": "category", + "label": "Realtime", + "items": [ + "generated/subscribe", + "generated/removesubscription", + "generated/getsubscriptions", + "generated/stream" + ], + "collapsed": true + }, + { + "type": "category", + "label": "Storage", + "items": [ + "generated/storage-createbucket", + "generated/storage-getbucket", + "generated/storage-listbuckets", + "generated/storage-updatebucket", + "generated/storage-deletebucket", + "generated/storage-emptybucket", + "generated/storage-from-upload", + "generated/storage-from-download", + "generated/storage-from-list", + "generated/storage-from-update", + "generated/storage-from-move", + "generated/storage-from-remove", + "generated/storage-from-createsignedurl", + "generated/storage-from-getpublicurl" + ], + "collapsed": true + }, + { + "type": "category", + "label": "Modifiers", + "items": [ + "generated/using-modifiers", + "generated/limit", + "generated/order", + "generated/range", + "generated/single" + ], + "collapsed": true + }, + { + "type": "category", + "label": "Filters", + "items": [ + "generated/using-filters", + "generated/or", + "generated/not", + "generated/match", + "generated/eq", + "generated/neq", + "generated/gt", + "generated/gte", + "generated/lt", + "generated/lte", + "generated/like", + "generated/ilike", + "generated/is_", + "generated/in_", + "generated/contains", + "generated/containedby", + "generated/rangelt", + "generated/rangegt", + "generated/rangegte", + "generated/rangelte", + "generated/rangeadjacent", + "generated/overlaps", + "generated/textsearch", + "generated/filter" + ], + "collapsed": true + } + ] +} diff --git a/apps/temp-docs/_supabase_dart_versions.json b/apps/temp-docs/_supabase_dart_versions.json new file mode 100644 index 00000000000..574c045ecaf --- /dev/null +++ b/apps/temp-docs/_supabase_dart_versions.json @@ -0,0 +1 @@ +["v0"] diff --git a/apps/temp-docs/docs/javascript/installing.mdx b/apps/temp-docs/_supabase_js/installing.mdx similarity index 73% rename from apps/temp-docs/docs/javascript/installing.mdx rename to apps/temp-docs/_supabase_js/installing.mdx index 80d1b5cf909..96b254beca2 100644 --- a/apps/temp-docs/docs/javascript/installing.mdx +++ b/apps/temp-docs/_supabase_js/installing.mdx @@ -1,42 +1,37 @@ --- id: installing -title: "Installing" +title: 'Installing' slug: installing custom_edit_url: https://github.com/supabase/supabase/edit/master/web/spec/supabase.yml --- -import Tabs from '@theme/Tabs'; -import TabsPanel from '@theme/TabsPanel'; +import Tabs from '@theme/Tabs' +import TabItem from '@theme/TabItem' -All JavaScript libraries are built directly by the Supabase team. +All JavaScript libraries are built directly by the Supabase team. Other languages are built by the community and supported by Supabase. ## JavaScript Via NPM + ```bash npm install @supabase/supabase-js ``` Via Yarn + ```bash yarn add @supabase/supabase-js ``` -Find the source code on [GitHub](https://github.com/supabase/supabase-js). +Find the source code on [GitHub](https://github.com/supabase/supabase-js). Or via CDN + ```js //or ``` - -## Python - -```bash -pip install supabase-py -``` - -Find the source code on [GitHub](https://github.com/supabase/supabase-py). \ No newline at end of file diff --git a/apps/temp-docs/_supabase_js/intro.md b/apps/temp-docs/_supabase_js/intro.md new file mode 100644 index 00000000000..bec6b4f975e --- /dev/null +++ b/apps/temp-docs/_supabase_js/intro.md @@ -0,0 +1,25 @@ +--- +slug: / +sidebar_position: 1 +sidebar_label: Supabase JavaScript Library +hide_table_of_contents: true +--- + +# Supabase JavaScript Library + +This reference documents every object and method available in Supabase's isomorphic JavaScript library, `supabase-js`. + +You can use the `supabase-js` library to: + +- interact with your Postgres database +- listen to database changes +- invoke Deno Edge Functions +- build login and user management functionality +- manage large files + +## Additional Links + +- Source Code: [github.com/supabase/supabase-js](https://github.com/supabase/supabase-js) +- TypeDoc: [supabase.github.io/supabase-js](https://supabase.github.io/supabase-js/v2/) +- NPM: [npmjs.com/package/@supabase/supabase-js](https://www.npmjs.com/package/@supabase/supabase-js) +- [Known bugs and issues](https://github.com/supabase/supabase-js/issues) diff --git a/apps/temp-docs/_supabase_js/release-notes.md b/apps/temp-docs/_supabase_js/release-notes.md new file mode 100644 index 00000000000..715215f2197 --- /dev/null +++ b/apps/temp-docs/_supabase_js/release-notes.md @@ -0,0 +1,211 @@ +--- +id: release-notes +--- + +# Release Notes + +Supabase.js v2 release notes. + +## 2.0.0 + +Install the latest with `npm install @supabase/supabase-js`. + +### Explicit constructor options + +All client specific options within the constructor are keyed to the library: [PR](https://github.com/supabase/supabase-js/pull/458): + +```jsx +const supabase = createClient(apiURL, apiKey, { + db: { + schema: 'public', + }, + auth: { + storage: AsyncStorage, + autoRefreshToken: true, + persistSession: true, + detectSessionInUrl: true, + }, + realtime: { + channels, + endpoint, + }, + global: { + fetch: customFetch, + headers: DEFAULT_HEADERS, + }, +}) +``` + +### Typescript support + +The libraries now support typescript. + +```ts +// v2 - definitions are injected in `createClient()` +import type { Database } from './DatabaseDefinitions' +const supabase = createClient(SUPABASE_URL, ANON_KEY) +const { data } = await supabase.from('messages').select().match({ id: 1 }) + +// v1 -- previously definitions were injected in the `from()` method +supabase.from('messages').select('*') +``` + +Types can be generated via the CLI: + +```bash +supabase start +supabase gen types typescript --local > DatabaseDefinitions.ts +``` + +### Data operations return minimal + +`.insert()` / `.upsert()` / `.update()` / `.delete()` don't return rows by default: [PR](https://github.com/supabase/postgrest-js/pull/276). + +Previously, these methods return inserted/updated/deleted rows by default (which caused [some confusion](https://github.com/supabase/supabase/discussions/1548)), and you can opt to not return it by specifying `returning: 'minimal'`. Now the default behavior is to not return rows. To return inserted/updated/deleted rows, add a `.select()` call at the end, e.g.: + +```sql +const { data, error } = await supabase + .from('my_table') + .delete() + .eq('id', 1) + .select() +``` + +### New ordering defaults + +`.order()` now defaults to Postgres’s default: [PR](https://github.com/supabase/postgrest-js/pull/283). + +Previously `nullsFirst` defaults to `false` , meaning `null`s are ordered last. This is bad for performance if e.g. the column uses an index with `NULLS FIRST` (which is the default direction for indexes). + +### Cookies and localstorage namespace + +Storage key name in the Auth library has changed to include project reference which means that existing websites that had their JWT expiry set to a longer time could find their users logged out with this upgrade. + +```jsx +const defaultStorageKey = `sb-${new URL(this.authUrl).hostname.split('.')[0]}-auth-token` +``` + +### New Auth Types + +Typescript typings have been reworked. `Session` interface now guarantees that it will always have an `access_token`, `refresh_token` and `user` + +```jsx +interface Session { + provider_token?: string | null + access_token: string + expires_in?: number + expires_at?: number + refresh_token: string + token_type: string + user: User +} +``` + +### New Auth methods + +We're removing the `signIn()` method in favor of more explicit function signatures: +`signInWithPassword()`, `signInWithPasswordless()`, and `signInWithOtp()`. + +```ts +// v2 +const { data } = await supabase.auth.signInWithPassword({ + email: 'hello@example', + password: 'pass', +}) +// v1 +const { data } = await supabase.auth.signIn({ + email: 'hello@example', + password: 'pass', +}) +``` + +### New Realtime methods + +There is a new `channel()` method in the Realtime library, which will be used for our Multiplayer updates. + +```ts +supabaseClient + .channel('any_string_you_want') + .on('presence', { event: 'track' }, (payload) => { + console.log(payload) + }) + .subscribe() + +supabaseClient + .channel('any_string_you_want') + .on( + 'postgres_changes', + { + event: 'INSERT', + schema: 'public', + table: 'movies', + }, + (payload) => { + console.log(payload) + } + ) + .subscribe() +``` + +We will deprecate the `.from().on().subscribe()` method previosuly used for listening to postgres changes. + +### Deprecated setAuth() + +Deprecated and removed `setAuth()` . To set a custom `access_token` jwt instead, pass the custom header into the `createClient()` method provided: ([PR](https://github.com/supabase/gotrue-js/pull/340)) + +### All changes + +- `supabase-js` + - `shouldThrowOnError` has been removed until all the client libraries support this option ([PR](https://github.com/supabase/supabase-js/pull/490)). +- `postgrest-js` + - TypeScript typings have been reworked [PR](https://github.com/supabase/postgrest-js/pull/279) + - Use `undefined` instead of `null` for function params, types, etc. (https://github.com/supabase/postgrest-js/pull/278) + - Some features are now obsolete: (https://github.com/supabase/postgrest-js/pull/275) + - filter shorthands (e.g. `cs` vs. `contains`) + - `body` in response (vs. `data`) + - `upsert`ing through the `.insert()` method + - `auth` method on `PostgrestClient` + - client-level `throwOnError` +- `gotrue-js` + - `supabase-js` client allows passing a `storageKey` param which will allow the user to set the key used in local storage for storing the session. By default, this will be namespace-d with the supabase project ref. ([PR](https://github.com/supabase/supabase-js/pull/460)) + - `signIn` method is now split into `signInWithPassword` , `signInWithPasswordless` , `signInWithOAuth` ([PR](https://github.com/supabase/gotrue-js/pull/304)) + - Deprecated and removed `session()` , `user()` in favour of using `getSession()` instead. `getSession()` will always return a valid session if a user is already logged in, meaning no more random logouts. ([PR](https://github.com/supabase/gotrue-js/pull/299)) + - Deprecated and removed setting for `multitab` support because `getSession()` and gotrue’s reuse interval setting takes care of session management across multiple tabs ([PR](https://github.com/supabase/gotrue-js/pull/366)) + - No more throwing of random errors, gotrue-js v2 always returns a custom error type: ([PR](https://github.com/supabase/gotrue-js/pull/341)) + - `AuthSessionMissingError` + - Indicates that a session is expected but missing + - `AuthNoCookieError` + - Indicates that a cookie is expected but missing + - `AuthInvalidCredentialsError` + - Indicates that the incorrect credentials were passed + - Renamed the `api` namespace to `admin` , the `admin` namespace will only contain methods that should only be used in a trusted server-side environment with the service role key + - Moved `resetPasswordForEmail` , `getUser` and `updateUser` to the `GoTrueClient` which means they will be accessible from the `supabase.auth` namespace in `supabase-js` instead of having to do `supabase.auth.api` to access them + - Removed `sendMobileOTP` , `sendMagicLinkEmail` in favor of `signInWithOtp` + - Removed `signInWithEmail`, `signInWithPhone` in favor of `signInWithPassword` + - Removed `signUpWithEmail` , `signUpWithPhone` in favor of `signUp` + - Replaced `update` with `updateUser` +- `storage-js` + - Return types are more strict. Functions types used to indicate that the data returned could be null even if there was no error. We now make use of union types which only mark the data as null if there is an error and vice versa. ([PR](https://github.com/supabase/storage-js/pull/60)) + - The `upload` and `update` function returns the path of the object uploaded as the `path` parameter. Previously the returned value had the bucket name prepended to the path which made it harder to pass the value on to other storage-js methods since all methods take the bucket name and path separately. We also chose to call the returned value `path` instead of `Key` ([PR](https://github.com/supabase/storage-js/pull/75)) + - `getPublicURL` only returns the public URL inside the data object. This keeps it consistent with our other methods of returning only within the data object. No error is returned since this method cannot does not throw an error ([PR](https://github.com/supabase/storage-js/pull/93)) + - signed urls are returned as `signedUrl` instead of `signedURL` in both `createSignedUrl` and `createSignedUrls` ([PR](https://github.com/supabase/storage-js/pull/94)) + - Encodes URLs returned by `createSignedUrl`, `createSignedUrls` and `getPublicUrl` ([PR](https://github.com/supabase/storage-js/pull/86)) + - `createsignedUrl` used to return a url directly and and within the data object. This was inconsistent. Now we always return values only inside the data object across all methods. ([PR](https://www.notion.so/LW5-supabase-js-v2-7b0bfcdf571d4f20b9b7a9308883f24b)) + - `createBucket` returns a data object instead of the name of the bucket directly. ([PR](https://github.com/supabase/storage-js/pull/89)) + - Fixed types for metadata ([PR](https://github.com/supabase/storage-js/pull/90)) + - Better error types make it easier to track down what went wrong quicker. + - `SupabaseStorageClient` is no longer exported. Use `StorageClient` instead. ([PR](https://github.com/supabase/storage-js/pull/92)). +- `realtime-js` + - `RealtimeSubscription` class no longer exists and replaced by `RealtimeChannel`. + - `RealtimeClient`'s `disconnect` method now returns type of `void` . It used to return type of `Promise<{ error: Error | null; data: boolean }`. + - Removed `removeAllSubscriptions` and `removeSubscription` methods from `SupabaseClient` class. + - Removed `SupabaseRealtimeClient` class. + - Removed `SupabaseQueryBuilder` class. + - Removed `SupabaseEventTypes` type. + - Thinking about renaming this to something like `RealtimePostgresChangeEvents` and moving it to `realtime-js` v2. + - Removed `.from(’table’).on(’INSERT’, () ⇒ {}).subscribe()` in favor of new Realtime client API. +- `functions-js` + - supabase-js v1 only threw an error if the fetch call itself threw an error (network errors, etc) and not if the function returned HTTP errors like 400s or 500s. We have changed this behaviour to return an error if your function throws an error. + - We have introduced new error types to distinguish between different kinds of errors. A `FunctionsHttpError` error is returned if your function throws an error, `FunctionsRelayError` if the Supabase Relay has an error processing your function and `FunctionsFetchError` if there is a network error in calling your function. + - The correct content-type headers are automatically attached when sending the request if you don’t pass in a `Content-Type` header and pass in an argument to your function. We automatically attach the content type for `Blob`, `ArrayBuffer`, `File`, `FormData` ,`String` . If it doesn’t match any of these we assume the payload is `json` , we serialise the payload as JSON and attach the content type as `application/json`. + - `responseType` does not need to be explicitly passed in. We parse the response based on the `Content-Type` response header sent by the function. We support parsing the responses as `text`, `json`, `blob`, `form-data` and are parsed as `text` by default. diff --git a/apps/temp-docs/_supabase_js/upgrade-guide.mdx b/apps/temp-docs/_supabase_js/upgrade-guide.mdx new file mode 100644 index 00000000000..c3a148c534c --- /dev/null +++ b/apps/temp-docs/_supabase_js/upgrade-guide.mdx @@ -0,0 +1,461 @@ +--- +id: upgrade-guide +title: Upgrade to supabase-js v2 +description: 'Learn how to upgrade to supabase-js v2.' +--- + +import Tabs from '@theme/Tabs' +import TabItem from '@theme/TabItem' + +supabase-js v2 focuses on "quality-of-life" improvements for developers and addresses some of the largest pain points in v1. v2 includes type support, a rebuilt Auth library with async methods, improved errors, and more. + +No new features will be added to supabase-js v1 , but we'll continuing merging security fixes to v1, with maintenance patches for the next 3 months. + +## Upgrade the client library + +```sh +npm install @supabase/supabase-js@2 +``` + +_Optionally_ if you are using custom configuration with `createClient` then follow below: + + + + + +```ts title="src/supabaseClient.ts" +const supabaseClient = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, { + schema: 'custom', + persistSession: false, +}) +``` + + + + + +```ts title="src/supabaseClient.ts" +const supabaseClient = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, { + db: { + schema: 'custom', + }, + auth: { + persistSession: true, + }, +}) +``` + + + + +Read more about the [constructor options](/docs/reference/javascript/release-notes#explicit-constructor-options). + +### Auth methods + +The signIn() method has been deprecated in favor of more explicit method signatures to help with type hinting. Previously it was difficult for developers to know what they were missing (e.g., a lot of developers didn't realize they could use passwordless magic links). + +#### Sign in with email and password + + + + + +```ts +const { user, error } = await supabase.auth.signIn({ email, password }) +``` + + + + + +```ts +const { + data: { user }, + error, +} = await supabase.auth.signInWithPassword({ email, password }) +``` + + + + +#### Sign in with magic link + + + + + +```ts +const { error } = await supabase.auth.signIn({ email }) +``` + + + + + +```ts +const { error } = await supabase.auth.signInWithOtp({ email }) +``` + + + + +#### Sign in with a third-party provider + + + + + +```ts +const { error } = await supabase.auth.signIn({ provider }) +``` + + + + + +```ts +const { error } = await supabase.auth.signInWithOAuth({ provider }) +``` + + + + +#### Sign in with phone + + + + + +```ts +const { error } = await supabase.auth.signIn({ phone, password }) +``` + + + + + +```ts +const { error } = await supabase.auth.signInWithPassword({ phone, password }) +``` + + + + +#### Sign in with phone using OTP + + + + + +```ts +const { error } = await supabase.auth.api.sendMobileOTP(phone) +``` + + + + + +```ts +const { data, error } = await supabase.auth.signInWithOtp({ phone }) + +// After receiving a SMS with a OTP. +const { data, error } = await supabase.auth.verifyOtp({ phone, token }) +``` + + + + +#### Reset password for email + + + + + +```ts +const { data, error } = await supabase.auth.api.resetPasswordForEmail(email) +``` + + + + + +```ts +const { data, error } = await supabase.auth.resetPasswordForEmail(email) +``` + + + + +#### Get the user's current session + + + + + +```ts +const session = supabase.auth.session() +``` + + + + + +```ts +const { + data: { session }, +} = await supabase.auth.getSession() +``` + + + + +#### Get the logged-in user + + + + + +```ts +const user = supabase.auth.user() +``` + + + + + +```ts +const { + data: { session }, +} = await supabase.auth.getSession() +const { user } = session +``` + + + + +#### Update user data for a logged-in user + + + + + +```ts +const { user, error } = await supabase.auth.update({ attributes }) +``` + + + + + +```ts +const { + data: { user }, + error, +} = await supabase.auth.updateUser({ attributes }) +``` + + + + +#### Use a custom `access_token` JWT with Supabase + + + + + +```ts +const { user, error } = supabase.auth.setAuth(access_token) +``` + + + + + +```ts +const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, { + global: { + headers: { + Authorization: `Bearer ${access_token}`, + }, + }, +}) +``` + + + + +### Data methods + +`.insert()` / `.upsert()` / `.update()` / `.delete()` don't return rows by default: [PR](https://github.com/supabase/postgrest-js/pull/276). + +Previously, these methods return inserted/updated/deleted rows by default (which caused [some confusion](https://github.com/supabase/supabase/discussions/1548)), and you can opt to not return it by specifying `returning: 'minimal'`. Now the default behavior is to not return rows. To return inserted/updated/deleted rows, add a `.select()` call at the end. + +#### Insert and return data + + + + + +```ts +const { data, error } = await supabase.auth.insert({ new_data }) +``` + + + + + +```ts +const { data, error } = await supabase.auth.insert({ new_data }).select() +``` + + + + +#### Update and return data + + + + + +```ts +const { data, error } = await supabase.auth.update({ new_data }).eq('id', id) +``` + + + + + +```ts +const { data, error } = await supabase.auth.update({ new_data }).eq('id', id).select() +``` + + + + +### Realtime methods + +#### Subscribe + + + + + +```ts +const userListener = supabase + .from('users') + .on('*', (payload) => handleAllEventsPayload(payload.new)) + .subscribe() +``` + + + + + +```ts +const userListener = supabase.channel('public:user') + .on( + 'postgres_changes', + { event: '*', schema: 'public', table: 'user' } + (payload) => handleAllEventsPayload() + ) + .subscribe() +``` + + + + +#### Unsubscribe + + + + + +```ts +userListener.unsubscribe() +``` + + + + + +```ts +supabase.removeChannel('public:users') +``` + + + diff --git a/apps/temp-docs/_supabase_js/working-with-types.md b/apps/temp-docs/_supabase_js/working-with-types.md new file mode 100644 index 00000000000..cb564843bba --- /dev/null +++ b/apps/temp-docs/_supabase_js/working-with-types.md @@ -0,0 +1,81 @@ +--- +id: typescript-support +--- + +# Typescript Support + +`supabase-js` supports Typescript. + +## Generating types + +You can use our CLI to generate types: + +```bash +supabase start +supabase gen types typescript --local > lib/database.types.ts +``` + +These types are generated directly from your database. Given a table `public.movies`, the definition will provide the following data: + +```ts +interface Database { + public: { + Tables: { + movies: { + Row: {} // The data expected to be returned from a "select" statement. + Insert: {} // The data expected passed to an "insert" statement. + Update: {} // The data expected passed to an "update" statement. + } + } + } +} +``` + +There is a difference between `selects`, `inserts`, and `updates`, because often you will set default values in your database for specific columns. +With default values you do not need to send any data over the network, even if that column is a "required" field. Our type system is granular +enough to handle these situations. + +## Injecting type definitions + +You can enrich the supabase client with the types you generated with Supabase. + +```ts +import { createClient } from '@supabase/supabase-js' +import { Database } from 'lib/database.types' + +const supabase = createClient(process.env.SUPABASE_URL, process.env.SUPABASE_ANON_KEY) +``` + +## Type hints + +`supabase-js` always returns a `data` object (for success), and an `error` response (for unsuccessful requests). +This provides a simple interface to get the relevant types returned from any function: + +```ts +export async function getMovies() { + return await supabase.from('movies').select(`id, title`) +} + +type MoviesResponse = Awaited> +export type MoviesResponseSuccess = MoviesResponse['data'] +export type MoviesResponseError = MoviesResponse['error'] +``` + +## Nested tables + +For advanced queries such as nested tables, you may want to construct your own types. + +```ts +import supabase from '~/lib/supabase' +import type { Database } from '~/lib/database.types' + +async function getMovies() { + return await supabase.from('movies').select('id, title, actors(*)') +} + +type Actors = Database['public']['Tables']['actors']['Row'] +type MoviesResponse = Awaited> +type MoviesResponseSuccess = MoviesResponse['data'] & { + actors: Actors[] +} +``` diff --git a/apps/temp-docs/_supabase_js_versioned_docs/version-v1/generating-types.mdx b/apps/temp-docs/_supabase_js_versioned_docs/version-v1/generating-types.mdx new file mode 100644 index 00000000000..033cfdb9165 --- /dev/null +++ b/apps/temp-docs/_supabase_js_versioned_docs/version-v1/generating-types.mdx @@ -0,0 +1,42 @@ +--- +id: generating-types +title: 'Generating Types' +slug: generating-types +custom_edit_url: https://github.com/supabase/supabase/edit/master/spec/supabase_js_v1.yml +--- + +import Tabs from '@theme/Tabs' +import TabItem from '@theme/TabItem' + +Supabase will soon release native type generators that dump your database types for various languages. For now, we support TypeScript [through third-party tools](/docs/guides/api/generating-types). + +## Usage with TypeScript + +`supabase-js` ships with type definitions for usage with TypeScript and for convenient IntelliSense auto-complete and documentation in your editor. + +When using TypeScript, you can pass the type of database row as a type parameter to the `from` method to get better auto-completion support down the chain. +If you don't provide a type for the row you need to explicitly pass `from('tableName')`. + +```ts +type Message = { + id: number + inserted_at: string + message: string + user_id: string + channel_id: number + author: { username: string } +} + +const response = await supabase + .from('messages') // Message maps to the type of the row in your database. + .select('*, author:user_id(username)') + .match({ channel_id: 2 }) // Your IDE will be able to help with auto-completion. +response.data // Response data will be of type Array. + +// If you don't provide a type for the row you need to explicitly pass `from('tableName')`. +const response = await supabase + .from('messages') + .select('*, author:user_id(username)') + .match({ channel_id: 2 }) +response.data // Response data will be of type Array. +``` diff --git a/apps/temp-docs/_supabase_js_versioned_docs/version-v1/index.mdx b/apps/temp-docs/_supabase_js_versioned_docs/version-v1/index.mdx new file mode 100644 index 00000000000..8f37f5853e8 --- /dev/null +++ b/apps/temp-docs/_supabase_js_versioned_docs/version-v1/index.mdx @@ -0,0 +1,11 @@ +--- +id: index +title: 'Supabase Client' +slug: supabase-client +custom_edit_url: ../../spec/supabase_js_v1.yml +--- + +import Tabs from '@theme/Tabs' +import TabItem from '@theme/TabItem' + +Supabase JavaScript. diff --git a/apps/temp-docs/docs/reference/javascript/initializing.mdx b/apps/temp-docs/_supabase_js_versioned_docs/version-v1/initializing.mdx similarity index 68% rename from apps/temp-docs/docs/reference/javascript/initializing.mdx rename to apps/temp-docs/_supabase_js_versioned_docs/version-v1/initializing.mdx index 39bd9890b98..711f03f2aed 100644 --- a/apps/temp-docs/docs/reference/javascript/initializing.mdx +++ b/apps/temp-docs/_supabase_js_versioned_docs/version-v1/initializing.mdx @@ -1,24 +1,20 @@ --- id: initializing -title: "Initializing" +title: 'Initializing' slug: initializing -custom_edit_url: https://github.com/supabase/supabase/edit/master/web/spec/supabase.yml +custom_edit_url: https://github.com/supabase/supabase/edit/master/spec/supabase_js_v1.yml --- -import Tabs from '@theme/Tabs'; -import TabsPanel from '@theme/TabsPanel'; +import Tabs from '@theme/Tabs' +import TabItem from '@theme/TabItem' You can initialize a new Supabase client using the `createClient()` method. The Supabase client is your entrypoint to the rest of the Supabase functionality and is the easiest way to interact with everything we offer within the Supabase ecosystem. - - - ## Parameters -
  • @@ -41,7 +37,6 @@ The unique Supabase URL which is supplied when you create a new project in your
  • -
  • @@ -62,7 +57,6 @@ The unique Supabase Key which is supplied when you create a new project in your

  • -
  • @@ -77,7 +71,7 @@ The unique Supabase Key which is supplied when you create a new project in your

    -No description provided. +No description provided.
    @@ -85,55 +79,22 @@ No description provided.
- - - - - - - - - - - - - ## Examples ### createClient() - - - - - - ```js import { createClient } from '@supabase/supabase-js' -// Create a single supabase client for interacting with your database -const supabase = createClient('https://xyzcompany.supabase.co', 'public-anon-key') +// Create a single supabase client for interacting with your database +const supabase = createClient( + 'https://xyzcompany.supabase.co', + 'public-anon-key' +) ``` - - - - - ### With additional parameters - - - - - - ```js import { createClient } from '@supabase/supabase-js' @@ -142,70 +103,49 @@ const options = { headers: { 'x-my-custom-header': 'my-app-name' }, autoRefreshToken: true, persistSession: true, - detectSessionInUrl: true + detectSessionInUrl: true, } -const supabase = createClient("https://xyzcompany.supabase.co", "public-anon-key", options) +const supabase = createClient( + 'https://xyzcompany.supabase.co', + 'public-anon-key', + options +) ``` - - - - - ### API schemas - - - - - - ```js import { createClient } from '@supabase/supabase-js' // Provide a custom schema. Defaults to "public". -const supabase = createClient('https://xyzcompany.supabase.co', 'public-anon-key', { - schema: 'other_schema' -}) +const supabase = createClient( + 'https://xyzcompany.supabase.co', + 'public-anon-key', + { + schema: 'other_schema', + } +) ``` -By default the API server points to the `public` schema. You can enable other database schemas within the Dashboard. +By default the API server points to the `public` schema. You can enable other database schemas within the Dashboard. Go to `Settings > API > Schema` and add the schema which you want to expose to the API. Note: each client connection can only access a single schema, so the code above can access the `other_schema` schema but cannot access the `public` schema. - - - - - ### Custom `fetch` implementation - - - - - - ```js import { createClient } from '@supabase/supabase-js' -const supabase = createClient('https://xyzcompany.supabase.co', 'public-anon-key', { - fetch: fetch.bind(globalThis) -}) +const supabase = createClient( + 'https://xyzcompany.supabase.co', + 'public-anon-key', + { + fetch: fetch.bind(globalThis), + } +) ``` -`supabase-js` uses the [`cross-fetch`](https://www.npmjs.com/package/cross-fetch) library to make HTTP requests, -but an alternative `fetch` implementation can be provided as an option. +`supabase-js` uses the [`cross-fetch`](https://www.npmjs.com/package/cross-fetch) library to make HTTP requests, +but an alternative `fetch` implementation can be provided as an option. This is most useful in environments where `cross-fetch` is not compatible (for instance Cloudflare Workers). - - - - - \ No newline at end of file diff --git a/apps/temp-docs/_supabase_js_versioned_docs/version-v1/installing.mdx b/apps/temp-docs/_supabase_js_versioned_docs/version-v1/installing.mdx new file mode 100644 index 00000000000..4441c7253d7 --- /dev/null +++ b/apps/temp-docs/_supabase_js_versioned_docs/version-v1/installing.mdx @@ -0,0 +1,37 @@ +--- +id: installing +title: 'Installing' +slug: installing +custom_edit_url: https://github.com/supabase/supabase/edit/master/spec/supabase_js_v1.yml +--- + +import Tabs from '@theme/Tabs' +import TabItem from '@theme/TabItem' + +All JavaScript libraries are built directly by the Supabase team. + +Other languages are built by the community and supported by Supabase. + +## JavaScript + +Via NPM + +```bash +npm install @supabase/supabase-js +``` + +Via Yarn + +```bash +yarn add @supabase/supabase-js +``` + +Find the source code on [GitHub](https://github.com/supabase/supabase-js). + +Or via CDN + +```js + +//or + +``` diff --git a/apps/temp-docs/_supabase_js_versioned_docs/version-v1/intro.md b/apps/temp-docs/_supabase_js_versioned_docs/version-v1/intro.md new file mode 100644 index 00000000000..97230e84aea --- /dev/null +++ b/apps/temp-docs/_supabase_js_versioned_docs/version-v1/intro.md @@ -0,0 +1,20 @@ +--- +id: intro +title: 'Supabase JavaScript Library' +slug: / +sidebar_label: Supabase JavaScript Library +--- + +:::caution +You're viewing the docs for an older version of the `supabase-js` library. +::: + +This reference documents every object and method available in Supabase's isomorphic JavaScript library, `supabase-js`. + +You can use the `supabase-js` library to: + +- interact with your Postgres database +- listen to database changes +- invoke Deno Edge Functions +- build login and user management functionality +- manage large files diff --git a/apps/temp-docs/_supabase_js_versioned_sidebars/.gitkeep b/apps/temp-docs/_supabase_js_versioned_sidebars/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/apps/temp-docs/_supabase_js_versioned_sidebars/version-v1-sidebars.json b/apps/temp-docs/_supabase_js_versioned_sidebars/version-v1-sidebars.json new file mode 100644 index 00000000000..8687644761e --- /dev/null +++ b/apps/temp-docs/_supabase_js_versioned_sidebars/version-v1-sidebars.json @@ -0,0 +1,138 @@ +{ + "sidebar": [ + { + "type": "category", + "label": "Getting Started", + "items": ["intro", "installing", "initializing", "generating-types"], + "collapsed": false + }, + { + "type": "category", + "label": "Auth", + "items": [ + "generated/auth-signup", + "generated/auth-signin", + "generated/auth-signout", + "generated/auth-session", + "generated/auth-user", + "generated/auth-update", + "generated/auth-setauth", + "generated/auth-onauthstatechange", + "generated/auth-api-getuser", + "generated/auth-api-resetpasswordforemail" + ], + "collapsed": true + }, + { + "type": "category", + "label": "Auth (Server Only)", + "items": [ + "generated/auth-api-listusers", + "generated/auth-api-createuser", + "generated/auth-api-deleteuser", + "generated/auth-api-generatelink", + "generated/auth-api-inviteuserbyemail", + "generated/auth-api-sendmobileotp", + "generated/auth-api-updateuserbyid" + ], + "collapsed": true + }, + { + "type": "category", + "label": "Functions", + "items": ["generated/invoke"], + "collapsed": true + }, + { + "type": "category", + "label": "Database", + "items": [ + "generated/select", + "generated/insert", + "generated/update", + "generated/upsert", + "generated/delete", + "generated/rpc" + ], + "collapsed": true + }, + { + "type": "category", + "label": "Realtime", + "items": [ + "generated/subscribe", + "generated/removesubscription", + "generated/removeallsubscriptions", + "generated/getsubscriptions" + ], + "collapsed": true + }, + { + "type": "category", + "label": "Storage", + "items": [ + "generated/storage-createbucket", + "generated/storage-getbucket", + "generated/storage-listbuckets", + "generated/storage-updatebucket", + "generated/storage-deletebucket", + "generated/storage-emptybucket", + "generated/storage-from-upload", + "generated/storage-from-download", + "generated/storage-from-list", + "generated/storage-from-update", + "generated/storage-from-move", + "generated/storage-from-copy", + "generated/storage-from-remove", + "generated/storage-from-createsignedurl", + "generated/storage-from-createsignedurls", + "generated/storage-from-getpublicurl" + ], + "collapsed": true + }, + { + "type": "category", + "label": "Modifiers", + "items": [ + "generated/using-modifiers", + "generated/limit", + "generated/order", + "generated/range", + "generated/single", + "generated/maybesingle" + ], + "collapsed": true + }, + { + "type": "category", + "label": "Filters", + "items": [ + "generated/using-filters", + "generated/or", + "generated/not", + "generated/match", + "generated/eq", + "generated/neq", + "generated/gt", + "generated/gte", + "generated/lt", + "generated/lte", + "generated/like", + "generated/ilike", + "generated/is", + "generated/in", + "generated/contains", + "generated/containedby", + "generated/rangelt", + "generated/rangegt", + "generated/rangegte", + "generated/rangelte", + "generated/rangeadjacent", + "generated/overlaps", + "generated/textsearch", + "generated/filter" + ], + "collapsed": true + } + ] +} diff --git a/apps/temp-docs/_supabase_js_versions.json b/apps/temp-docs/_supabase_js_versions.json new file mode 100644 index 00000000000..868a38e32ae --- /dev/null +++ b/apps/temp-docs/_supabase_js_versions.json @@ -0,0 +1 @@ +["v1"] diff --git a/apps/temp-docs/components/AuthProviders.tsx b/apps/temp-docs/components/AuthProviders.tsx index aa984248e90..5e5ffa71fd9 100644 --- a/apps/temp-docs/components/AuthProviders.tsx +++ b/apps/temp-docs/components/AuthProviders.tsx @@ -1,5 +1,5 @@ import authProviders from '../data/authProviders' -import { Card } from '@supabase/ui' +import { Card } from 'ui' import Link from 'next/link' export default function AuthProviders() { diff --git a/apps/temp-docs/components/ButtonCard.tsx b/apps/temp-docs/components/ButtonCard.tsx new file mode 100644 index 00000000000..8ac61494589 --- /dev/null +++ b/apps/temp-docs/components/ButtonCard.tsx @@ -0,0 +1,35 @@ +import React from 'react' +import Link from 'next/link' + +export default function ButtonCard({ + children = undefined, + icon = undefined, + title, + description = '', + to, + layout = 'vertical', +}) { + return ( + + + {children ? ( + children + ) : ( +
+ {icon && typeof icon == 'string' ? ( + {title} + ) : ( + icon + )} +

{title}

+

{description}

+
+ )} +
+ + ) +} diff --git a/apps/temp-docs/components/CodeBlock/CodeBlock.tsx b/apps/temp-docs/components/CodeBlock/CodeBlock.tsx index df5ead184b6..103a242a107 100644 --- a/apps/temp-docs/components/CodeBlock/CodeBlock.tsx +++ b/apps/temp-docs/components/CodeBlock/CodeBlock.tsx @@ -1,12 +1,13 @@ import { Light as SyntaxHighlighter } from 'react-syntax-highlighter' import monokaiCustomTheme from './CodeBlock.utils' -import { Button, IconCheck, IconCopy } from '@supabase/ui' +import { Button, IconCheck, IconCopy } from 'ui' import CopyToClipboard from 'react-copy-to-clipboard' import js from 'react-syntax-highlighter/dist/cjs/languages/hljs/javascript' import py from 'react-syntax-highlighter/dist/cjs/languages/hljs/python' import sql from 'react-syntax-highlighter/dist/cjs/languages/hljs/sql' import { useState } from 'react' +import { useTheme } from '../Providers' interface Props { lang: 'js' | 'sql' | 'py' @@ -16,8 +17,10 @@ interface Props { children?: string size?: 'small' | 'medium' | 'large' } - function CodeBlock(props: Props) { + const { isDarkMode } = useTheme() + const monokaiTheme = monokaiCustomTheme(isDarkMode) + const [copied, setCopied] = useState(false) const handleCopy = () => { @@ -42,26 +45,31 @@ function CodeBlock(props: Props) { // const large = props.size === 'large' ? true : false const large = false + // don't show line numbers if bash == lang + const showLineNumbers = lang !== 'bash' + return (
{!props.hideCopy && props.children ? ( -
+
+ +
+ ) +} + +export default DarkModeToggle diff --git a/apps/temp-docs/components/Footer.tsx b/apps/temp-docs/components/Footer.tsx index a64a96c7668..0be8c2c5194 100644 --- a/apps/temp-docs/components/Footer.tsx +++ b/apps/temp-docs/components/Footer.tsx @@ -1,128 +1,132 @@ -import Image from 'next/image' +import FooterLinks from 'data/footer.json' +import SectionContainer from './SectionContainer' +import DarkModeToggle from './DarkModeToggle' import Link from 'next/link' -import footer from '../data/footer.json' import { useTheme } from './Providers' +import { Badge } from 'ui' +import Image from 'next/image' +import * as supabaseLogoWordmarkDark from 'common/assets/images/supabase-logo-wordmark--dark.png' +import * as supabaseLogoWordmarkLight from 'common/assets/images/supabase-logo-wordmark--light.png' const Footer = () => { const { isDarkMode } = useTheme() return ( ) } diff --git a/apps/temp-docs/components/Frameworks.tsx b/apps/temp-docs/components/Frameworks.tsx new file mode 100644 index 00000000000..69eb20ac53e --- /dev/null +++ b/apps/temp-docs/components/Frameworks.tsx @@ -0,0 +1,99 @@ +import ButtonCard from './ButtonCard' + +const Frameworks = () => { + const frameworks = [ + { + name: 'Angular', + // logo: AngularLogo, + href: '/guides/with-angular', + }, + { + name: 'Expo', + // logo: ExpoLogo, + href: '/guides/with-expo', + }, + { + name: 'Flutter', + // logo: FlutterLogo, + href: '/guides/with-flutter', + }, + { + name: 'JavaScript', + // logo: JavascriptLogo, + href: '/reference/javascript/installing#javascript', + }, + { + name: 'Next.js', + themed: true, + logo: { + dark: '/img/libraries/nextjs-dark-icon.svg', + light: '/img/libraries/nextjs-light-icon.svg', + }, + href: '/guides/with-nextjs', + }, + { + name: 'React', + // logo: ReactLogo, + href: '/guides/with-react', + }, + { + name: 'RedwoodJS', + // logo: RedwoodJsLogo, + href: '/guides/with-redwoodjs', + }, + { + name: 'SolidJS', + // logo: SolidJSLogo, + href: '/guides/with-solidjs', + }, + { + name: 'Svelte', + // logo: SvelteLogo, + href: '/guides/with-svelte', + }, + { + name: 'Vue', + // logo: VuejsLogo, + href: '/guides/with-vue-3', + }, + ] + return ( +
+ {frameworks.map((x) => ( +
+ + // ) : ( + // + // ) + // } + /> +
+ ))} +
+ ) +} + +export default Frameworks diff --git a/apps/temp-docs/components/Navigation/Nav.constants.ts b/apps/temp-docs/components/Navigation/Nav.constants.ts new file mode 100644 index 00000000000..7a65550e57a --- /dev/null +++ b/apps/temp-docs/components/Navigation/Nav.constants.ts @@ -0,0 +1,313 @@ +import SupabaseJsV1Nav from 'data/nav/supabase-js/v1' +import SupabaseJsV2Nav from 'data/nav/supabase-js/v2' +import SupabaseDartV0Nav from 'data/nav/supabase-dart/v0' +import SupabaseDartV1Nav from 'data/nav/supabase-dart/v1' +import SupabaseCLINav from 'data/nav/supabase-cli' +import SupabaseAPINav from 'data/nav/supabase-api' +import AuthServerNav from 'data/nav/auth-server' +import StorageServerNav from 'data/nav/storage-server' + +export interface NavMenu { + [key: string]: NavMenuGroup[] +} + +export interface NavMenuGroup { + label: string + items: NavMenuSection[] +} + +export interface NavMenuSection { + name: string + url?: string + items: NavMenuSection[] +} + +export const menuItems: NavMenu = { + docs: [ + { + label: 'Overview', + items: [ + { name: 'Introduction', url: '/', items: [] }, + { name: 'Features', url: '/guides/features', items: [] }, + { name: 'Architecture', url: '/architecture', items: [] }, + ], + }, + { + label: 'Quickstarts', + items: [ + { name: 'Angular', url: '/guides/with-angular', items: [] }, + { name: 'Expo', url: '/guides/with-expo', items: [] }, + { name: 'Flutter', url: '/guides/with-flutter', items: [] }, + { name: 'Ionic Angular', url: '/guides/with-ionic-angular', items: [] }, + { name: 'Ionic React', url: '/guides/with-ionic-react', items: [] }, + { name: 'Ionic Vue', url: '/guides/with-ionic-vue', items: [] }, + { name: 'Next.js', url: '/guides/with-nextjs', items: [] }, + { name: 'Nuxt 3', url: '/guides/with-nuxt-3', items: [] }, + { name: 'React', url: '/guides/with-react', items: [] }, + { name: 'RedwoodJS', url: '/guides/with-redwoodjs', items: [] }, + { name: 'SolidJS', url: '/guides/with-solidjs', items: [] }, + { name: 'Svelte', url: '/guides/with-svelte', items: [] }, + { name: 'SvelteKit', url: '/guides/with-sveltekit', items: [] }, + { name: 'Vue 3', url: '/guides/with-vue-3', items: [] }, + ], + }, + { + label: 'CLI', + items: [ + { name: 'Overview', url: '/guides/cli', items: [] }, + { name: 'Local Development', url: '/guides/cli/local-development', items: [] }, + { name: 'Managing Environments', url: '/guides/cli/managing-environments', items: [] }, + ], + }, + { + label: 'Auth', + items: [ + { name: 'Overview', url: '/guides/auth', items: [] }, + { + name: 'Authentication', + url: undefined, + items: [ + { name: 'Login with Email', url: '/guides/auth/auth-email', items: [] }, + { name: 'Login with Magic Link', url: '/guides/auth/auth-magic-link', items: [] }, + { name: 'Login with Apple', url: '/guides/auth/auth-apple', items: [] }, + { name: 'Login with Azure', url: '/guides/auth/auth-azure', items: [] }, + { name: 'Login with Bitbucket', url: '/guides/auth/auth-bitbucket', items: [] }, + { name: 'Login with Discord', url: '/guides/auth/auth-discord', items: [] }, + { name: 'Login with Facebook', url: '/guides/auth/auth-facebook', items: [] }, + { name: 'Login with Github', url: '/guides/auth/auth-github', items: [] }, + { name: 'Login with Gitlab', url: '/guides/auth/auth-gitlab', items: [] }, + { name: 'Login with Google', url: '/guides/auth/auth-google', items: [] }, + { name: 'Login with Keycloak', url: '/guides/auth/auth-keycloak', items: [] }, + { name: 'Login with LinkedIn', url: '/guides/auth/auth-linkedin', items: [] }, + { name: 'Login with Notion', url: '/guides/auth/auth-notion', items: [] }, + { name: 'Login with Slack', url: '/guides/auth/auth-slack', items: [] }, + { name: 'Login with Spotify', url: '/guides/auth/auth-spotify', items: [] }, + { name: 'Login with Twitch', url: '/guides/auth/auth-twitch', items: [] }, + { name: 'Login with Twitter', url: '/guides/auth/auth-twitter', items: [] }, + { name: 'Login with WorkOS', url: '/guides/auth/auth-workos', items: [] }, + { name: 'Phone Auth with Twilio', url: '/guides/auth/twilio', items: [] }, + { name: 'Phone Auth with Vonage', url: '/guides/auth/vonage', items: [] }, + { name: 'Phone Auth with MessageBird', url: '/guides/auth/messagebird', items: [] }, + ], + }, + { + name: 'Authorization', + url: undefined, + items: [ + { name: 'Row Level Security', url: '/guides/auth/row-level-security', items: [] }, + { name: 'Managing User Data', url: '/guides/auth/managing-user-data', items: [] }, + { name: 'Enable Captcha Protection', url: '/guides/auth/auth-captcha', items: [] }, + { name: 'Server-side Rendering', url: '/guides/auth/server-side-rendering', items: [] }, + { name: 'Multi-Factor Authentication', url: '/guides/auth/auth-mfa', items: [] }, + ], + }, + { + name: 'Auth Helpers', + url: undefined, + items: [ + { name: 'Overview', url: '/guides/auth/auth-helpers', items: [] }, + { name: 'Auth UI', url: '/guides/auth/auth-helpers/auth-ui', items: [] }, + { name: 'Next.js', url: '/guides/auth/auth-helpers/nextjs', items: [] }, + { name: 'SvelteKit', url: '/guides/auth/auth-helpers/sveltekit', items: [] }, + ], + }, + { + name: 'Deep Dive', + url: undefined, + items: [ + { name: 'Part One: JWTs', url: 'learn/auth-deep-dive/auth-deep-dive-jwts', items: [] }, + { + name: 'Part Two: Row Level Security', + url: 'learn/auth-deep-dive/auth-row-level-security', + items: [], + }, + { name: 'Part Three: Policies', url: 'learn/auth-deep-dive/auth-policies', items: [] }, + { name: 'Part Four: GoTrue', url: 'learn/auth-deep-dive/auth-gotrue', items: [] }, + { + name: 'Part Fibe: Google OAuth', + url: 'learn/auth-deep-dive/auth-google-oauth', + items: [], + }, + ], + }, + ], + }, + { + label: 'APIs', + items: [ + { name: 'Overview', url: '/guides/api', items: [] }, + { name: 'Generating Types', url: '/guides/api/generating-types', items: [] }, + ], + }, + { + label: 'Database', + items: [ + { name: 'Overview', url: '/guides/database', items: [] }, + { name: 'Database Connections', url: '/guides/database/connecting-to-postgres', items: [] }, + { name: 'Tables and Data', url: '/guides/database/tables', items: [] }, + { name: 'Database Functions', url: '/guides/database/functions', items: [] }, + { name: 'Full Text Search', url: '/guides/database/full-text-search', items: [] }, + { + name: 'Migrating Between Projects', + url: '/guides/platform/migrating-and-upgrading-projects', + items: [], + }, + { + name: 'Extensions', + url: undefined, + items: [ + { name: 'Overview', url: '/guides/database/extensions', items: [] }, + { + name: 'plv8: Javascript Language', + url: '/guides/database/extensions/plv8', + items: [], + }, + { name: 'http: RESTful Client', url: '/guides/database/extensions/http', items: [] }, + { name: 'pgTAP: Unit Testing', url: '/guides/database/extensions/pgtap', items: [] }, + { + name: 'uuid-ossp: Unique Identifiers', + url: '/guides/database/extensions/uuid-ossp', + items: [], + }, + ], + }, + { + name: 'Configuration', + url: undefined, + items: [ + { name: 'Timeouts', url: '/guides/database/timeouts', items: [] }, + { name: 'Replication', url: '/guides/database/replication', items: [] }, + { name: 'Passwords', url: '/guides/database/managing-passwords', items: [] }, + { name: 'Timezones', url: '/guides/database/managing-timezones', items: [] }, + ], + }, + ], + }, + { + label: 'Edge Functions', + items: [ + { name: 'Overview', url: '/guides/functions', items: [] }, + { name: 'Auth', url: '/guides/functions/auth', items: [] }, + { name: 'Examples', url: '/guides/functions/examples', items: [] }, + { name: 'CI/CD Workflow', url: '/guides/functions/cicd-workflow', items: [] }, + ], + }, + { + label: 'Realtime', + items: [ + { name: 'Overview', url: '/guides/realtime', items: [] }, + { name: 'Quickstart', url: '/guides/realtime/quickstart', items: [] }, + { name: 'Postgres CDC', url: '/guides/realtime/postgres-cdc', items: [] }, + ], + }, + { + label: 'Storage', + items: [ + { name: 'Overview', url: '/guides/storage', items: [] }, + { name: 'CDN', url: '/guides/storage-cdn', items: [] }, + ], + }, + { + label: 'Platform', + items: [ + { name: 'Overview', url: '/guides/hosting/platform', items: [] }, + { name: 'Custom Domains', url: '/guides/platform/custom-domains', items: [] }, + { name: 'Disk Space Usage', url: '/guides/platform/disk-usage', items: [] }, + { name: 'Logging', url: '/guides/platform/logs', items: [] }, + { name: 'Metrics', url: '/guides/platform/metrics', items: [] }, + { name: 'Performance', url: '/guides/platform/performance', items: [] }, + { name: 'Permissions', url: '/guides/platform/permissions', items: [] }, + { name: 'Production Readiness', url: '/guides/platform/going-into-prod', items: [] }, + ], + }, + { + label: 'Self Hosting', + items: [ + { name: 'Overview', url: '/guides/hosting/overview', items: [] }, + { name: 'Docker', url: '/guides/hosting/docker', items: [] }, + ], + }, + { + label: 'Migrate to Supabase', + items: [ + { name: 'Firebase Auth', url: '/guides/migrations/firebase-auth', items: [] }, + { name: 'Firestore Data', url: '/guides/migrations/firestore-data', items: [] }, + { name: 'Firebase Storage', url: '/guides/migrations/firebase-storage', items: [] }, + { name: 'Herokuy', url: '/guides/migrations/heroku', items: [] }, + ], + }, + { + label: 'Integrations', + items: [ + { name: 'Supabase Marketplace', url: '/guides/integrations/integrations', items: [] }, + { + name: 'Auth', + url: undefined, + items: [ + { name: 'Auth0', url: '/guides/integration/auth0', items: [] }, + { name: 'Authsignal', url: '/guides/integration/authsignal', items: [] }, + { name: 'Clerk', url: '/guides/integration/clerk', items: [] }, + { name: 'keyri', url: '/guides/integration/keyri', items: [] }, + { name: 'Stytch', url: '/guides/integration/stytch', items: [] }, + { name: 'SuperTokens', url: '/guides/integration/supertokens', items: [] }, + ], + }, + { + name: 'Caching / Offline-first', + url: undefined, + items: [{ name: 'Polyscale', url: '/guides/integrations/polyscale', items: [] }], + }, + { + name: 'Developer Tools', + url: undefined, + items: [ + { name: 'pgMustard', url: '/guides/integrations/pgmustard', items: [] }, + { name: 'Prisma', url: '/guides/integrations/prisma', items: [] }, + { name: 'Sequin', url: '/guides/integrations/sequin', items: [] }, + { name: 'Snaplet', url: '/guides/integrations/snaplet', items: [] }, + { name: 'Vercel', url: '/guides/integrations/vercel', items: [] }, + ], + }, + { + name: 'Low-code', + url: undefined, + items: [ + { name: 'Appsmith', url: '/guides/integrations/appsmith', items: [] }, + { name: 'Dashibase', url: '/guides/integrations/dashibase', items: [] }, + { name: 'DhiWise', url: '/guides/integrations/dhiwise', items: [] }, + { name: 'Directus', url: '/guides/integrations/directus', items: [] }, + { name: 'Draftbit', url: '/guides/integrations/draftbit', items: [] }, + { name: 'Plasmic', url: '/guides/integrations/plasmic', items: [] }, + ], + }, + ], + }, + ], + reference: [ + { + label: 'Official', + items: [ + { name: 'Reference Documentation', url: '/reference', items: [] }, + { name: 'Supabase Javascript Library', url: '/reference/javascript', items: [] }, + { name: 'Supabase Flutter Library', url: '/reference/dart', items: [] }, + { name: 'Supabase CLI', url: '/reference/cli', items: [] }, + { name: 'Supabase API', url: '/reference/api', items: [] }, + ], + }, + { + label: 'Self-hosting', + items: [ + { name: 'Auth Server', url: '/reference/auth', items: [] }, + { name: 'Storage Server', url: '/reference/storage', items: [] }, + ], + }, + ], + 'reference/javascript': SupabaseJsV2Nav, + 'reference/javascript/v1': SupabaseJsV1Nav, + 'reference/dart': SupabaseDartV1Nav, + 'reference/dart/v0': SupabaseDartV0Nav, + 'reference/cli': SupabaseCLINav, + 'reference/api': SupabaseAPINav, + 'reference/auth': AuthServerNav, + 'reference/storage': StorageServerNav, +} diff --git a/apps/temp-docs/components/Navigation/NavBar.tsx b/apps/temp-docs/components/Navigation/NavBar.tsx new file mode 100644 index 00000000000..e8a3f40e9be --- /dev/null +++ b/apps/temp-docs/components/Navigation/NavBar.tsx @@ -0,0 +1,157 @@ +import Image from 'next/image' +import Link from 'next/link' +import { useState, useEffect } from 'react' +import { IconMenu, IconSearch, Input, IconCommand, Button, IconMoon, IconSun } from 'ui' +import { useTheme } from '../Providers' + +const NavBar = ({ currentPage }: { currentPage: string }) => { + const { isDarkMode, toggleTheme } = useTheme() + const [mounted, setMounted] = useState(false) + + useEffect(() => { + setMounted(true) + }, [isDarkMode]) + + const pageLinks = [ + { text: 'Guides', key: 'docs', link: '/' }, + { text: 'Reference', key: 'reference', link: '/reference' }, + ] + + const toggleDarkMode = () => { + localStorage.setItem('supabaseDarkMode', (!isDarkMode).toString()) + toggleTheme() + + const key = localStorage.getItem('supabaseDarkMode') + document.documentElement.className = key === 'true' ? 'dark' : '' + } + + return ( + + ) +} +export default NavBar diff --git a/apps/temp-docs/components/Navigation/SideBar.tsx b/apps/temp-docs/components/Navigation/SideBar.tsx new file mode 100644 index 00000000000..96c0bbc4721 --- /dev/null +++ b/apps/temp-docs/components/Navigation/SideBar.tsx @@ -0,0 +1,199 @@ +import Link from 'next/link' +import Image from 'next/image' +import { useRouter } from 'next/router' +import { IconChevronRight, IconArrowLeft } from '~/../../packages/ui' +import { NavMenuGroup, NavMenuSection } from './Nav.constants' +import * as Accordion from '@radix-ui/react-accordion' + +const SideBar = ({ menuItems = [] }: { menuItems: any }) => { + const { asPath } = useRouter() + const pathSegments = asPath.split('/') + + const isInReferencePages = pathSegments.includes('reference') && pathSegments.length >= 3 + const references = { + javascript: { name: 'supabase-js', icon: '/docs/img/libraries/javascript-icon.svg' }, + dart: { name: 'Flutter', icon: '/docs/img/libraries/dart-icon.svg' }, + cli: { name: 'CLI', icon: '/docs/img/libraries/cli-icon.svg' }, + api: { name: 'API', icon: '/docs/img/libraries/api-icon.svg' }, + } + const referenceMeta = pathSegments.length >= 3 ? references[pathSegments[2]] : undefined + + const currentSection: NavMenuGroup = menuItems.find((group) => { + const foundItem = group.items.find((section) => { + if (section.items.length > 0) { + const foundSubItem = section.items.find((item) => { + if (item.url === asPath) return item + }) + if (foundSubItem) return section + } else { + if (section.url === asPath) return section + } + }) + if (foundItem) return group + }) + + const currentSubSection: NavMenuSection = + currentSection !== undefined + ? currentSection.items.find((section) => { + if (section.items.length === 0) { + return undefined + } else { + return section.items.find((item) => { + if (item.url === asPath) return item + }) + } + }) + : undefined + + console.log({ currentSection, currentSubSection }) + + return ( +
+ {isInReferencePages && ( + <> + + +
+ + All Reference Docs +
+
+ + {referenceMeta !== undefined && ( +
+
+ {referenceMeta.name} +
+

{referenceMeta.name}

+
+ )} + + )} + {menuItems.length === 1 ? ( +
+ {menuItems[0].items.map((item) => ( + + +
+ {item.name} +
+
+ + ))} +
+ ) : ( + + {menuItems.map((group: NavMenuGroup) => ( + + + + + {group.label} + + + + {group.items.map((section: NavMenuSection) => { + if (section.items.length === 0) { + return ( + + +
+ {section.name} +
+
+ + ) + } else { + return ( + + + + + + {section.name} + + + + {section.items.map((item: NavMenuSection) => ( + + +
+ {item.name} +
+
+ + ))} +
+
+
+ ) + } + })} +
+
+ ))} +
+ )} +
+ ) +} + +export default SideBar diff --git a/apps/temp-docs/components/Search.tsx b/apps/temp-docs/components/Search.tsx new file mode 100644 index 00000000000..115d31c3245 --- /dev/null +++ b/apps/temp-docs/components/Search.tsx @@ -0,0 +1,209 @@ +import { useState, useCallback, useRef, createContext, useContext, useEffect } from 'react' +import { createPortal } from 'react-dom' +import Link from 'next/link' +import Head from 'next/head' +import { useRouter } from 'next/router' +import { DocSearchModal } from '@docsearch/react' +import clsx from 'clsx' +import { useActionKey } from '~/hooks/useActionKey' + +const INDEX_NAME = 'tailwindcss_dev' +const API_KEY = '5fc87cef58bb80203d2207578309fab6' +const APP_ID = 'KNPXZI5B0M' + +const SearchContext = createContext(null) + +export function SearchProvider({ children }: any) { + const router = useRouter() + const [isOpen, setIsOpen] = useState(false) + const [initialQuery, setInitialQuery] = useState(null) + + const onOpen = useCallback(() => { + setIsOpen(true) + }, [setIsOpen]) + + const onClose = useCallback(() => { + setIsOpen(false) + }, [setIsOpen]) + + const onInput = useCallback( + (e) => { + setIsOpen(true) + setInitialQuery(e.key) + }, + [setIsOpen, setInitialQuery] + ) + + useDocSearchKeyboardEvents({ + isOpen, + onOpen, + onClose, + // @ts-ignore + onInput, + }) + + return ( + <> + + + + + {children} + + {isOpen && + createPortal( + { + return items.map((item, index) => { + // We transform the absolute URL into a relative URL to + // leverage Next's preloading. + const a = document.createElement('a') + a.href = item.url + + const hash = a.hash === '#content-wrapper' ? '' : a.hash + + return { + ...item, + url: `${a.pathname}${hash}`, + __is_result: () => true, + __is_parent: () => item.type === 'lvl1' && items.length > 1 && index === 0, + __is_child: () => + item.type !== 'lvl1' && + items.length > 1 && + items[0].type === 'lvl1' && + index !== 0, + __is_first: () => index === 1, + __is_last: () => index === items.length - 1 && index !== 0, + } + }) + }} + />, + document.body + )} + + ) +} + +// @ts-ignore +function Hit({ hit, children }) { + return ( + + + {children} + + + ) +} + +// @ts-ignore +export function SearchButton({ children, ...props }) { + let searchButtonRef = useRef() + let actionKey = useActionKey() + // @ts-ignore + let { onOpen, onInput } = useContext(SearchContext) + + useEffect(() => { + // @ts-ignore + function onKeyDown(event) { + if (searchButtonRef && searchButtonRef.current === document.activeElement && onInput) { + if (/[a-zA-Z0-9]/.test(String.fromCharCode(event.keyCode))) { + onInput(event) + } + } + } + window.addEventListener('keydown', onKeyDown) + return () => { + window.removeEventListener('keydown', onKeyDown) + } + }, [onInput, searchButtonRef]) + + return ( + // @ts-ignore + + ) +} + +// @ts-ignore +function useDocSearchKeyboardEvents({ isOpen, onOpen, onClose }) { + useEffect(() => { + // @ts-ignore + function onKeyDown(event) { + function open() { + // We check that no other DocSearch modal is showing before opening + // another one. + if (!document.body.classList.contains('DocSearch--active')) { + onOpen() + } + } + + if ( + (event.keyCode === 27 && isOpen) || + (event.key === 'k' && (event.metaKey || event.ctrlKey)) || + (!isEditingContent(event) && event.key === '/' && !isOpen) + ) { + event.preventDefault() + + if (isOpen) { + onClose() + } else if (!document.body.classList.contains('DocSearch--active')) { + open() + } + } + } + + window.addEventListener('keydown', onKeyDown) + return () => { + window.removeEventListener('keydown', onKeyDown) + } + }, [isOpen, onOpen, onClose]) +} + +// @ts-ignore +function isEditingContent(event) { + let element = event.target + let tagName = element.tagName + return ( + element.isContentEditable || + tagName === 'INPUT' || + tagName === 'SELECT' || + tagName === 'TEXTAREA' + ) +} diff --git a/apps/temp-docs/components/SectionContainer.tsx b/apps/temp-docs/components/SectionContainer.tsx new file mode 100644 index 00000000000..db5ec705165 --- /dev/null +++ b/apps/temp-docs/components/SectionContainer.tsx @@ -0,0 +1,19 @@ +import classNames from 'classnames' + +interface Props { + children: React.ReactNode + className?: string +} + +const SectionContainer = ({ children, className }: Props) => ( +
+ {children} +
+) + +export default SectionContainer diff --git a/apps/temp-docs/components/TabPanel.tsx b/apps/temp-docs/components/TabPanel.tsx index 5d18bd9dd44..9427d116692 100644 --- a/apps/temp-docs/components/TabPanel.tsx +++ b/apps/temp-docs/components/TabPanel.tsx @@ -1,4 +1,4 @@ -import { Tabs } from '@supabase/ui' +import { Tabs } from 'ui' export default function TabPanel(props: { id: string; label: string | undefined; children: any }) { return ( diff --git a/apps/temp-docs/components/TabWrapper.tsx b/apps/temp-docs/components/TabWrapper.tsx index 1d3861e9899..091f7d055aa 100644 --- a/apps/temp-docs/components/TabWrapper.tsx +++ b/apps/temp-docs/components/TabWrapper.tsx @@ -1,4 +1,4 @@ -import { Tabs } from '@supabase/ui' +import { Tabs } from 'ui' const TabWrapper = (props: any) => { return ( diff --git a/apps/temp-docs/components/ThemeToggle.tsx b/apps/temp-docs/components/ThemeToggle.tsx deleted file mode 100644 index fc88fd28ef4..00000000000 --- a/apps/temp-docs/components/ThemeToggle.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { useTheme } from './Providers' -import { IconSun, IconMoon } from '@supabase/ui' - -function DarkModeToggle() { - const { isDarkMode, toggleTheme } = useTheme() - - const toggleDarkMode = () => { - localStorage.setItem('supabaseDarkMode', (!isDarkMode).toString()) - toggleTheme() - - const key = localStorage.getItem('supabaseDarkMode') - document.documentElement.className = key === 'true' ? 'dark' : '' - } - - return ( - - ) -} - -export default DarkModeToggle diff --git a/apps/temp-docs/components/index.tsx b/apps/temp-docs/components/index.tsx index 170d44a98ff..21de059e999 100644 --- a/apps/temp-docs/components/index.tsx +++ b/apps/temp-docs/components/index.tsx @@ -1,22 +1,21 @@ -import LinkCard from './LinkCard' -import LinkCardsWrapper from './LinkCardsWrapper' -import { Tabs } from '@supabase/ui' -import Sponsor from './Sponsor' -import SponsorsWrapper from './SponsorsWrapper' +import { Tabs, Alert } from 'ui' +// import Tabs from '@theme/Tabs' +// import TabItem from '@theme/TabItem' + import CodeBlock from './CodeBlock/CodeBlock' -import AuthProviders from './AuthProviders' +import ButtonCard from './ButtonCard' +import Frameworks from './Frameworks' +import { H3, H4 } from './CustomMDX' const components = { - // LinkCard, - // LinkCardsWrapper, - // SponsorsWrapper, - // Sponsor, - // AuthProviders, - // Tabs: (props: any) => , - // TabsPanel: (props: any) => { - // return - // }, - // code: (props: any) => , + h3: H3, + h4: H4, + Alert, + ButtonCard, + Frameworks, + Tabs, + TabPanel: (props: any) => {props.children}, + code: (props: any) => , } export default components diff --git a/apps/temp-docs/components/layouts/Layout.tsx b/apps/temp-docs/components/layouts/Layout.tsx deleted file mode 100644 index 12ef773b83a..00000000000 --- a/apps/temp-docs/components/layouts/Layout.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import Head from 'next/head' -import NavBar from '../nav/NavBar' -import Footer from '../Footer' - -const Layout = ({ - children, - meta, - currentPage, -}: { - meta: { title: string; description: string } - children: React.ReactNode - currentPage: string -}) => { - return ( - <> - - {meta?.title} | Supabase - - - - - - - - -
- -
- {children} -
-
-
- - ) -} - -export default Layout diff --git a/apps/temp-docs/components/nav/NavBar.tsx b/apps/temp-docs/components/nav/NavBar.tsx deleted file mode 100644 index 33a525c62f9..00000000000 --- a/apps/temp-docs/components/nav/NavBar.tsx +++ /dev/null @@ -1,103 +0,0 @@ -import { useState, useEffect } from 'react' -import { - IconMenu, - IconGitHub, - IconTwitter, - IconSearch, - Input, - IconCommand, - Button, -} from '@supabase/ui' -import Image from 'next/image' -import Link from 'next/link' -import { useTheme } from '../Providers' -import ThemeToggle from '../ThemeToggle' - -const NavBar = ({ currentPage }: { currentPage: string }) => { - const [mounted, setMounted] = useState(false) - const { isDarkMode } = useTheme() - - useEffect(() => { - setMounted(true) - }, [isDarkMode]) - - const pageLinks = [ - { text: 'Overview', active: currentPage == 'Docs', link: '/' }, - { text: 'Guides', active: currentPage == 'Guides', link: '/guides' }, - { - text: 'Reference', - active: currentPage == 'Reference', - link: '/reference/javascript/supabase-client', - }, - { text: 'Discussions', active: currentPage == 'Discussions', link: '/discussions' }, - ] - - return ( - - ) -} -export default NavBar diff --git a/apps/temp-docs/components/nav/SideBar.tsx b/apps/temp-docs/components/nav/SideBar.tsx deleted file mode 100644 index de6fd0b5226..00000000000 --- a/apps/temp-docs/components/nav/SideBar.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { Menu } from '@supabase/ui' -import Link from 'next/link' -import { useRouter } from 'next/router' - -const SideBar = ({ menuItems }: { menuItems: any }) => { - const { asPath } = useRouter() - return ( -
- -
- {Object.keys(menuItems).map((key) => { - return ( -
- - {menuItems[key].map( - (item: { link: string; text: string; sections: Array }) => ( - - {Array.isArray(item.sections) ? ( - <> - - {item.sections.map((section) => ( - - - - {section.text} - - - - ))} - - ) : ( - - - -
- {item.text} -
-
-
- - )} -
- ) - )} -
- ) - })} -
-
-
- ) -} - -export default SideBar diff --git a/apps/temp-docs/components/nav/menu-items.json b/apps/temp-docs/components/nav/menu-items.json deleted file mode 100644 index d8b5a4bf13e..00000000000 --- a/apps/temp-docs/components/nav/menu-items.json +++ /dev/null @@ -1,807 +0,0 @@ -{ - "Docs": { - "Overview": [ - { "text": "Introduction", "link": "/" }, - { "text": "Architecture", "link": "/architecture" }, - { "text": "Database", "link": "/guides/database" }, - { "text": "Auth", "link": "/guides/auth" }, - { "text": "Storage", "link": "/guides/storage" }, - { "text": "APIs", "link": "/guides/apis" }, - { "text": "Examples and Resources", "link": "/guides/examples" } - ], - "Tutorials": [ - { "text": "Quickstart: Angular", "link": "/guides/with-angular" }, - { "text": "Quickstart: Next.js", "link": "/guides/with-nextjs" }, - { "text": "Quickstart: Nuxt 3", "link": "/guides/with-nuxt-3" }, - { "text": "Quickstart: Flutter", "link": "/guides/with-flutter" }, - { "text": "Quickstart: React", "link": "/guides/with-react" }, - { "text": "Quickstart: RedwoodJS", "link": "/guides/with-redwoodjs" }, - { "text": "Quickstart: Svelte", "link": "/guides/with-svelte" }, - { "text": "Quickstart: Vue 3", "link": "/guides/with-vue-3" } - ], - "See Also": [ - { "text": "FAQs", "link": "/faqs" }, - { "text": "Going into Prod Checklist", "link": "/going-into-prod" }, - { "text": "Contributing", "link": "/handbook/contributing" }, - { "text": "SupaSquad", "link": "/handbook/supasquad" }, - { "text": "Terms of Service", "link": "/company/terms" }, - { "text": "Privacy Policy", "link": "/company/privacy" }, - { "text": "Acceptable Use Policy", "link": "/company/aup" } - ] - }, - "Guides": { - "Guides": [ - { "text": "Overview", "link": "/guides" }, - { "text": "Local Development", "link": "/guides/local-development" } - ], - "Database": [ - { "text": "Introduction", "link": "/guides/database/introduction" }, - { "text": "Tables", "link": "/guides/database/tables" }, - { "text": "Functions", "link": "/guides/database/functions" }, - { "text": "Full Text Search", "link": "/guides/database/full-text-search" }, - { - "text": "Extensions", - "sections": [ - { - "text": "Overview", - "link": "/guides/database/extensions" - }, - { - "text": "plv8: Javascript Language", - "link": "/guides/database/extensions/plv8" - }, - { - "text": "http: RESTful Client", - "link": "/guides/database/extensions/http" - }, - { - "text": "uuid-ossp: Unique Identifiers", - "link": "/guides/database/extensions/uuid-ossp" - } - ] - }, - { - "text": "Connecting to Postgres", - "sections": [ - { - "text": "Overview", - "link": "/guides/database/connecting/connecting-to-postgres" - }, - { - "text": "Direct Connections", - "link": "/guides/database/connecting/direct-connections" - }, - { - "text": "Connection Pooling", - "link": "/guides/database/connecting/connection-pooling" - } - ] - }, - { - "text": "Configuration", - "sections": [ - { - "text": "Timeouts", - "link": "/guides/database/timeouts" - }, - { - "text": "Direct Connections", - "link": "/guides/database/replication" - }, - { - "text": "Passwords", - "link": "/guides/database/managing-passwords" - }, - { - "text": "Timezones", - "link": "/guides/database/managing-timezones" - } - ] - } - ], - "Auth": [ - { - "text": "Introduction", - "link": "/guides/auth/intro" - }, - { - "text": "Authentication", - "sections": [ - { "text": "Login With Email", "link": "/guides/auth/auth-email" }, - { "text": "Login With Magic Link", "link": "/guides/auth/auth-magic-link" }, - { "text": "Login With Apple", "link": "/guides/auth/auth-apple" }, - { "text": "Login With Bitbucket", "link": "/guides/auth/auth-bitbucket" }, - { "text": "Login With Discord", "link": "/guides/auth/auth-discord" }, - { "text": "Login With Facebook", "link": "/guides/auth/auth-facebook" }, - { "text": "Login With GitHub", "link": "/guides/auth/auth-gitHub" }, - { "text": "Login With GitLab", "link": "/guides/auth/auth-gitLab" }, - { "text": "Login With Google", "link": "/guides/auth/auth-google" }, - { "text": "Login With Slack", "link": "/guides/auth/auth-slack" }, - { "text": "Login With Spotify", "link": "/guides/auth/auth-spotify" }, - { "text": "Login With Twitter", "link": "/guides/auth/auth-twitter" }, - { "text": "Login With Twitch", "link": "/guides/auth/auth-twitch" }, - { "text": "Phone Auth With Twilio", "link": "/guides/auth/auth-twilio" }, - { "text": "Phone Auth With MessageBird", "link": "/guides/auth/auth-messageBird" } - ] - }, - { - "text": "Authorization", - "sections": [ - { "text": "Row Level Security", "link": "/guides/auth/row-level-security" }, - { "text": "Managing User Data", "link": "/guides/auth/managing-user-data" } - ] - }, - { - "text": "Deep Dive", - "link": [ - { "text": "Part One: JWTs", "link": "/guides/auth-deep-dive/auth-deep-dive-jwts" }, - { - "text": "Part Two: Row Level Security", - "link": "/guides/auth-deep-dive/auth-row-level-security" - }, - { "text": "Part Three: Policies", "link": "/guides/auth-deep-dive/auth-policies" }, - { "text": "Part Four: Gotrue", "link": "/guides/auth-deep-dive/auth-gotrue" }, - { "text": "Part Five: Google Oauth", "link": "/guides/auth-deep-dive/auth-google-auth" } - ] - } - ], - "Integrations": [ - { - "text": "Auth0", - "link": "/guides/integrations/auth0" - }, - { - "text": "Draftbit", - "link": "/guides/integrations/draftbit" - }, - { - "text": "Prisma", - "link": "/guides/integrations/prisma" - }, - { - "text": "Vercel", - "link": "/guides/integrations/vercel" - } - ], - "Self Hosting": [ - { "text": "Overview", "link": "/guides/hosting/overview" }, - { "text": "Supabase Platform", "link": "/guides/hosting/platform" }, - { "text": "With Docker", "link": "/guides/hosting/docker" } - ] - }, - "Reference": { - "JAVASCRIPT": [ - { - "text": "Getting Started", - "sections": [ - { "text": "Getting Started", "link": "/reference/javascript/index" }, - { "text": "Installing", "link": "/reference/javascript/installing" }, - { "text": "Initializing", "link": "/reference/javascript/initializing" }, - { "text": "Generating Types", "link": "/reference/javascript/generating-types" } - ] - }, - { - "text": "Auth", - "sections": [ - { "text": "signUp()", "link": "/reference/javascript/auth-signup" }, - { "text": "signIn()", "link": "/reference/javascript/auth-signin" }, - { "text": "signOut()", "link": "/reference/javascript/auth-signout" }, - { "text": "session()", "link": "/reference/javascript/auth-session" }, - { "text": "user()", "link": "/reference/javascript/auth-user" }, - { "text": "update()", "link": "/reference/javascript/auth-update" }, - { "text": "setAuth()", "link": "/reference/javascript/auth-setauth" }, - { "text": "onAuthStateChange()", "link": "/reference/javascript/auth-onauthstatechange" }, - { "text": "getUser()", "link": "/reference/javascript/auth-api-getuser" }, - { "text": "Reset Password(Email)", "link": "/reference/javascript/reset-password-email" } - ] - }, - { - "text": "Auth (Server Only)", - "sections": [ - { "text": "createUser()", "link": "/reference/javascript/auth-api-createuser" }, - { "text": "deleteUser()", "link": "/reference/javascript/auth-api-deleteuser" }, - { "text": "generateLink()", "link": "/reference/javascript/auth-api-generatelink" }, - { - "text": "inviteUserByEmail()", - "link": "/reference/javascript/auth-api-inviteuserbyemail" - }, - { "text": "sendMobileOTP()", "link": "/reference/javascript/auth-api-sendmobileotp" }, - { - "text": "resetPasswordForEmail()", - "link": "/reference/javascript/auth-api-resetpasswordforemail" - } - ] - }, - { - "text": "Database", - "sections": [ - { - "text": "Fetch data: select()", - "link": "/reference/javascript/select" - }, - { - "text": "Create data: insert()", - "link": "/reference/javascript/insert" - }, - { - "text": "Modify data: update()", - "link": "/reference/javascript/update" - }, - { - "text": "Upsert data: upsert()", - "link": "/reference/javascript/upsert" - }, - { - "text": "Delete data: delete()", - "link": "/reference/javascript/delete" - }, - { - "text": "Postgres functions: rpc()", - "link": "/reference/javascript/rpc" - } - ] - }, - { - "text": "Realtime", - "sections": [ - { - "text": "on().subscribe()", - "link": "/reference/javascript/subscribe" - }, - { - "text": "removeSubscription()", - "link": "/reference/javascript/removesubscription" - }, - { - "text": "removeAllSubscriptions()", - "link": "/reference/javascript/removeallsubscriptions" - }, - { - "text": "getSubscriptions()", - "link": "/reference/javascript/getsubscriptions" - } - ] - }, - { - "text": "Storage", - "sections": [ - { - "text": "createBucket()", - "link": "/reference/javascript/storage-createbucket" - }, - { - "text": "getBucket()", - "link": "/reference/javascript/storage-getbucket" - }, - { - "text": "listBuckets()", - "link": "/reference/javascript/storage-listbuckets" - }, - { - "text": "updateBucket()", - "link": "/reference/javascript/storage-updatebucket" - }, - { - "text": "deleteBucket()", - "link": "/reference/javascript/storage-deletebucket" - }, - { - "text": "emptyBucket()", - "link": "/reference/javascript/storage-emptybucket" - }, - { - "text": "from.upload()", - "link": "/reference/javascript/storage-from-upload" - }, - { - "text": "from.download()", - "link": "/reference/javascript/storage-from-download" - }, - { - "text": "from.list()", - "link": "/reference/javascript/storage-from-list" - }, - { - "text": "from.update()", - "link": "/reference/javascript/storage-from-update" - }, - { - "text": "from.move()", - "link": "/reference/javascript/storage-from-move" - }, - { - "text": "from.remove()", - "link": "/reference/javascript/storage-from-remove" - }, - { - "text": "from.createSignedUrl()", - "link": "/reference/javascript/storage-from-createsignedurl" - }, - { - "text": "from.getPublicUrl()", - "link": "/reference/javascript/storage-from-getpublicurl" - } - ] - }, - { - "text": "Modifiers", - "sections": [ - { - "text": "Using Modifiers", - "link": "/reference/javascript/using-modifiers" - }, - { - "text": "limit()", - "link": "/reference/javascript/limit" - }, - { - "text": "order()", - "link": "/reference/javascript/order" - }, - { - "text": "range()", - "link": "/reference/javascript/range" - }, - { - "text": "single()", - "link": "/reference/javascript/single" - }, - { - "text": "maybeSingle()", - "link": "/reference/javascript/maybesingle" - } - ] - }, - { - "text": "Filters", - "sections": [ - { - "text": "Using Filters", - "link": "/reference/javascript/using-filters" - }, - { - "text": "or()", - "link": "/reference/javascript/or" - }, - { - "text": "not()", - "link": "/reference/javascript/not" - }, - { - "text": "match()", - "link": "/reference/javascript/match" - }, - { - "text": "eq()", - "link": "/reference/javascript/eq" - }, - { - "text": "neq()", - "link": "/reference/javascript/neq" - }, - { - "text": "gt()", - "link": "/reference/javascript/gt" - }, - { - "text": "gte()", - "link": "/reference/javascript/gte" - }, - { - "text": "lt()", - "link": "/reference/javascript/lt" - }, - { - "text": "lte()", - "link": "/reference/javascript/lte" - }, - { - "text": "like()", - "link": "/reference/javascript/like" - }, - { - "text": "ilike()", - "link": "/reference/javascript/ilike" - }, - { - "text": "is()", - "link": "/reference/javascript/is" - }, - { - "text": "in()", - "link": "/reference/javascript/in" - }, - { - "text": "contains()", - "link": "/reference/javascript/contains" - }, - { - "text": "containedBy()", - "link": "/reference/javascript/containedby" - }, - { - "text": "rangeLt()", - "link": "/reference/javascript/rangelt" - }, - { - "text": "rangeGt()", - "link": "/reference/javascript/rangegt" - }, - { - "text": "rangeGte()", - "link": "/reference/javascript/rangegte" - }, - { - "text": "rangeLte()", - "link": "/reference/javascript/rangelte" - }, - - { - "text": "rangeAdjacent()", - "link": "/reference/javascript/rangeadjacent" - }, - { - "text": "overlaps()", - "link": "/reference/javascript/overlaps" - }, - { - "text": "textSearch()", - "link": "/reference/javascript/textsearch" - }, - { - "text": "filter()", - "link": "/reference/javascript/filter" - } - ] - } - ], - "DART": [ - { - "text": "Getting Started", - "sections": [ - { "text": "Installing", "link": "/reference/dart/installing" }, - { "text": "Initializing", "link": "/reference/dart/initializing" } - ] - }, - { - "text": "Auth", - "sections": [ - { "text": "signUp()", "link": "/reference/dart/auth-signup" }, - { "text": "signIn()", "link": "/reference/dart/auth-signin" }, - { "text": "signOut()", "link": "/reference/dart/auth-signout" }, - { "text": "session()", "link": "/reference/dart/auth-session" }, - { "text": "user()", "link": "/reference/dart/auth-user" }, - { "text": "update()", "link": "/reference/dart/auth-update" }, - { "text": "setAuth()", "link": "/reference/dart/auth-setauth" }, - { "text": "onAuthStateChange()", "link": "/reference/dart/auth-onauthsatechange" }, - { "text": "Reset Password(Email)", "link": "/reference/dart/reset-password-email" } - ] - }, - - { - "text": "Database", - "sections": [ - { - "text": "Fetch data: select()", - "link": "/reference/dart/select" - }, - { - "text": "Create data: insert()", - "link": "/reference/dart/insert" - }, - { - "text": "Modify data: update()", - "link": "/reference/dart/update" - }, - { - "text": "Upsert data: upsert()", - "link": "/reference/dart/upsert" - }, - { - "text": "Delete data: delete()", - "link": "/reference/dart/delete" - }, - { - "text": "Postgres functions: rpc()", - "link": "/reference/dart/rpc" - } - ] - }, - { - "text": "Realtime", - "sections": [ - { - "text": "on().subscribe()", - "link": "/reference/dart/subscribe" - }, - { - "text": "removeSubscription()", - "link": "/reference/dart/removesubscription" - }, - { - "text": "getSubscriptions()", - "link": "/reference/dart/getsubscriptions" - } - ] - }, - { - "text": "Storage", - "sections": [ - { - "text": "createBucket()", - "link": "/reference/dart/storage-createbucket" - }, - { - "text": "getBucket()", - "link": "/reference/dart/storage-getbucket" - }, - { - "text": "listBuckets()", - "link": "/reference/dart/storage-listbuckets" - }, - { - "text": "updateBucket()", - "link": "/reference/dart/storage-updatebucket" - }, - { - "text": "deleteBucket()", - "link": "/reference/dart/storage-deletebucket" - }, - { - "text": "emptyBucket()", - "link": "/reference/dart/storage-emptybucket" - }, - { - "text": "from.upload()", - "link": "/reference/dart/storage-from-upload" - }, - { - "text": "from.download()", - "link": "/reference/dart/storage-from-download" - }, - { - "text": "from.list()", - "link": "/reference/dart/storage-from-list" - }, - { - "text": "from.update()", - "link": "/reference/dart/storage-from-update" - }, - { - "text": "from.move()", - "link": "/reference/dart/storage-from-move" - }, - { - "text": "from.remove()", - "link": "/reference/dart/storage-from-remove" - }, - { - "text": "from.createSignedUrl()", - "link": "/reference/dart/storage-from-createsignedurl" - }, - { - "text": "from.getPublicUrl()", - "link": "/reference/dart/storage-from-getpublicurl" - } - ] - }, - { - "text": "Modifiers", - "sections": [ - { - "text": "Using Modifiers", - "link": "/reference/dart/using-modifiers" - }, - { - "text": "limit()", - "link": "/reference/dart/limit" - }, - { - "text": "order()", - "link": "/reference/dart/order" - }, - { - "text": "range()", - "link": "/reference/dart/range" - }, - { - "text": "single()", - "link": "/reference/dart/single" - }, - { - "text": "maybeSingle()", - "link": "/reference/dart/maybesingle" - } - ] - }, - { - "text": "Filters", - "sections": [ - { - "text": "Using Filters", - "link": "/reference/dart/using-filters" - }, - { - "text": "or()", - "link": "/reference/dart/or" - }, - { - "text": "not()", - "link": "/reference/dart/not" - }, - { - "text": "match()", - "link": "/reference/dart/match" - }, - { - "text": "eq()", - "link": "/reference/dart/eq" - }, - { - "text": "neq()", - "link": "/reference/dart/neq" - }, - { - "text": "gt()", - "link": "/reference/dart/gt" - }, - { - "text": "gte()", - "link": "/reference/dart/gte" - }, - { - "text": "lt()", - "link": "/reference/dart/lt" - }, - { - "text": "lte()", - "link": "/reference/dart/lte" - }, - { - "text": "like()", - "link": "/reference/dart/like" - }, - { - "text": "ilike()", - "link": "/reference/dart/ilike" - }, - { - "text": "is_()", - "link": "/reference/dart/is_" - }, - { - "text": "in_()", - "link": "/reference/dart/in_" - }, - { - "text": "contains()", - "link": "/reference/dart/contains" - }, - { - "text": "containedBy()", - "link": "/reference/dart/containedby" - }, - { - "text": "rangeLt()", - "link": "/reference/dart/rangelt" - }, - { - "text": "rangeGt()", - "link": "/reference/dart/rangegt" - }, - { - "text": "rangeGte()", - "link": "/reference/dart/rangegte" - }, - { - "text": "rangeLte()", - "link": "/reference/dart/rangelte" - }, - - { - "text": "rangeAdjacent()", - "link": "/reference/dart/rangeadjacent" - }, - { - "text": "overlaps()", - "link": "/reference/dart/overlaps" - }, - { - "text": "textSearch()", - "link": "/reference/dart/textsearch" - }, - { - "text": "filter()", - "link": "/reference/dart/filter" - } - ] - } - ], - "CLI": [ - { - "text": "About", - "sections": [{ "text": "About", "link": "/reference/cli/about" }] - }, - { - "text": "Command reference", - "sections": [ - { - "text": "supabase help", - "link": "/reference/cli/supabase-help" - }, - - { - "text": "supabase init", - "link": "/reference/cli/supabase-init" - }, - { - "text": "supabase start", - "link": "/reference/cli/supabase-start" - }, - { - "text": "supabase stop", - "link": "/reference/cli/supabase-stop" - }, - { - "text": "supabase eject", - "link": "/reference/cli/supabase-eject" - }, - - { - "text": "supabase db branch list", - "link": "/reference/cli/supabase-db-branch-list" - }, - { - "text": "supabase db branch create", - "link": "/reference/cli/supabase-db-branch-create" - }, - { - "text": "supabase db branch delete", - "link": "/reference/cli/supabase-db-branch-delete" - }, - { - "text": "supabase db switch", - "link": "/reference/cli/supabase-db-switch" - }, - { - "text": "supabase db changes", - "link": "/reference/cli/supabase-db-changes" - }, - { - "text": "supabase db commit", - "link": "/reference/cli/supabase-db-commit" - }, - { - "text": "supabase db reset", - "link": "/reference/cli/supabase-db-reset" - }, - { - "text": "supabase db remote set", - "link": "/reference/cli/supabase-db-remote-set" - }, - { - "text": "supabase db remote commit", - "link": "/reference/cli/supabase-db-remote-commit" - }, - { - "text": "supabase db push", - "link": "/reference/cli/supabase-db-push" - }, - { - "text": "supabase migration new", - "link": "/reference/cli/supabase-migration-new" - } - ] - }, - { - "text": "Config reference", - "sections": [ - { - "text": "Config reference", - "link": "/reference/cli/config-reference" - } - ] - } - ] - } -} diff --git a/apps/temp-docs/components/pagination.tsx b/apps/temp-docs/components/pagination.tsx index 8953ae9684e..9dbaaffe7a1 100644 --- a/apps/temp-docs/components/pagination.tsx +++ b/apps/temp-docs/components/pagination.tsx @@ -1,5 +1,5 @@ import Link from 'next/link' -import { IconArrowLeft, IconArrowRight } from '@supabase/ui' +import { IconArrowLeft, IconArrowRight } from 'ui' const pagination = ({ currentPage, totalCount }: { currentPage: number; totalCount: number }) => { // TODO: not sure if this is the most efficient way to do this. may need to refactor. diff --git a/apps/temp-docs/data/nav/auth-server.ts b/apps/temp-docs/data/nav/auth-server.ts new file mode 100644 index 00000000000..8dfa1bbe110 --- /dev/null +++ b/apps/temp-docs/data/nav/auth-server.ts @@ -0,0 +1,12 @@ +const Nav = [ + { + label: 'Auth', + items: [ + { name: 'Auth Server', url: '/reference/auth', items: [] }, + { name: 'Configuration', url: '/reference/auth/config', items: [] }, + { name: 'Release Notes', url: '/reference/auth/release-notes', items: [] }, + ], + }, +] + +export default Nav diff --git a/apps/temp-docs/data/nav/storage-server.ts b/apps/temp-docs/data/nav/storage-server.ts new file mode 100644 index 00000000000..8bfbe9cff46 --- /dev/null +++ b/apps/temp-docs/data/nav/storage-server.ts @@ -0,0 +1,13 @@ +const Nav = [ + { + label: 'Storage', + items: [ + { name: 'Storage Server', url: '/reference/storage', items: [] }, + { name: 'Usage', url: '/reference/storage/usage', items: [] }, + { name: 'Configuration', url: '/reference/storage/config', items: [] }, + { name: 'Release Notes', url: '/reference/storage/release-notes', items: [] }, + ], + }, +] + +export default Nav diff --git a/apps/temp-docs/data/nav/supabase-api.ts b/apps/temp-docs/data/nav/supabase-api.ts new file mode 100644 index 00000000000..e0bad91376b --- /dev/null +++ b/apps/temp-docs/data/nav/supabase-api.ts @@ -0,0 +1,12 @@ +const Nav = [ + { + label: 'API', + items: [ + { name: 'Supabase API', url: '/reference/api', items: [] }, + { name: 'Usage', url: '/reference/api/usage', items: [] }, + { name: 'Release Notes', url: '/reference/api/release-notes', items: [] }, + ], + }, +] + +export default Nav diff --git a/apps/temp-docs/data/nav/supabase-cli.ts b/apps/temp-docs/data/nav/supabase-cli.ts new file mode 100644 index 00000000000..65a5bce9419 --- /dev/null +++ b/apps/temp-docs/data/nav/supabase-cli.ts @@ -0,0 +1,13 @@ +const Nav = [ + { + label: 'CLI', + items: [ + { name: 'Supabase CLI', url: '/reference/cli', items: [] }, + { name: 'Usage', url: '/reference/cli/usage', items: [] }, + { name: 'Configuration', url: '/reference/cli/config', items: [] }, + { name: 'Release Notes', url: '/reference/cli/release-notes', items: [] }, + ], + }, +] + +export default Nav diff --git a/apps/temp-docs/data/nav/supabase-dart/v0.ts b/apps/temp-docs/data/nav/supabase-dart/v0.ts new file mode 100644 index 00000000000..0835a840306 --- /dev/null +++ b/apps/temp-docs/data/nav/supabase-dart/v0.ts @@ -0,0 +1,132 @@ +const Nav = [ + { + label: 'Getting Started', + items: [ + { name: 'Supabase Dart Library', url: '/reference/dart/v0', items: [] }, + { name: 'Installing', url: '/reference/dart/v0/installing', items: [] }, + { name: 'Initializing', url: '/reference/dart/v0/initializing', items: [] }, + ], + }, + { + label: 'Auth', + items: [ + { name: 'signUp()', url: '/reference/dart/v0/auth-signup', items: [] }, + { name: 'signIn()', url: '/reference/dart/v0/auth-signin', items: [] }, + { + name: 'signInWithProvider()', + url: '/reference/dart/v0/auth-signinwithprovider', + items: [], + }, + { name: 'signOut()', url: '/reference/dart/v0/auth-signout', items: [] }, + { name: 'session()', url: '/reference/dart/v0/auth-session', items: [] }, + { name: 'user()', url: '/reference/dart/v0/auth-user', items: [] }, + { name: 'update()', url: '/reference/dart/v0/auth-update', items: [] }, + { + name: 'onAuthStateChange()', + url: '/reference/dart/v0/auth-onauthstatechanged', + items: [], + }, + { + name: 'Reset Password (Email)', + url: '/reference/dart/v0/reset-password-email', + items: [], + }, + ], + }, + { + label: 'Functions', + items: [{ name: 'invoke()', url: '/reference/dart/v0/invoke', items: [] }], + }, + { + label: 'Database', + items: [ + { name: 'Fetch data: select()', url: '/reference/dart/v0/select', items: [] }, + { name: 'Create data: insert()', url: '/reference/dart/v0/insert', items: [] }, + { name: 'Modify data: update()', url: '/reference/dart/v0/update', items: [] }, + { name: 'Upsert data: upsert()', url: '/reference/dart/v0/upsert', items: [] }, + { name: 'Delete data: delete()', url: '/reference/dart/v0/delete', items: [] }, + { name: 'Stored Procedures: rpc()', url: '/reference/dart/v0/rpc', items: [] }, + ], + }, + { + label: 'Realtime', + items: [ + { name: 'stream', url: '/reference/dart/v0/stream', items: [] }, + { name: 'on().subscribe()', url: '/reference/dart/v0/subscribe', items: [] }, + { + name: 'removeSubscription()', + url: '/reference/dart/v0/removesubscription', + items: [], + }, + { name: 'getSubscriptions()', url: '/reference/dart/v0/getsubscriptions', items: [] }, + ], + }, + { + label: 'Storage', + items: [ + { name: 'createBucket()', url: '/reference/dart/v0/storage-createbucket', items: [] }, + { name: 'getBucket()', url: '/reference/dart/v0/storage-getbucket', items: [] }, + { name: 'listBuckets()', url: '/reference/dart/v0/storage-listbuckets', items: [] }, + { name: 'updateBucket()', url: '/reference/dart/v0/storage-updatebucket', items: [] }, + { name: 'deleteBucket()', url: '/reference/dart/v0/storage-deletebucket', items: [] }, + { name: 'emptyBucket()', url: '/reference/dart/v0/storage-emptybucket', items: [] }, + { name: 'from.upload()', url: '/reference/dart/v0/storage-from-upload', items: [] }, + { name: 'from.download()', url: '/reference/dart/v0/storage-from-download', items: [] }, + { name: 'from.list()', url: '/reference/dart/v0/storage-from-list', items: [] }, + { name: 'from.update()', url: '/reference/dart/v0/storage-from-update', items: [] }, + { name: 'from.move()', url: '/reference/dart/v0/storage-from-move', items: [] }, + { name: 'from.remove()', url: '/reference/dart/v0/storage-from-remove', items: [] }, + { + name: 'from.createSignedUrl()', + url: '/reference/dart/v0/storage-from-createsignedurl', + items: [], + }, + { + name: 'from.getPublicUrl()', + url: '/reference/dart/v0/storage-from-getpublicurl', + items: [], + }, + ], + }, + { + label: 'Filters', + items: [ + { name: 'Using Filters', url: '/reference/dart/v0/using-filters', items: [] }, + { name: 'eq()', url: '/reference/dart/v0/eq', items: [] }, + { name: 'neq()', url: '/reference/dart/v0/neq', items: [] }, + { name: 'gt()', url: '/reference/dart/v0/gt', items: [] }, + { name: 'gte()', url: '/reference/dart/v0/gte', items: [] }, + { name: 'lt()', url: '/reference/dart/v0/lt', items: [] }, + { name: 'lte()', url: '/reference/dart/v0/lte', items: [] }, + { name: 'like()', url: '/reference/dart/v0/like', items: [] }, + { name: 'ilike()', url: '/reference/dart/v0/ilike', items: [] }, + { name: 'is_()', url: '/reference/dart/v0/is_', items: [] }, + { name: 'in_()', url: '/reference/dart/v0/in_', items: [] }, + { name: 'contains()', url: '/reference/dart/v0/contains', items: [] }, + { name: 'containedBy()', url: '/reference/dart/v0/containedby', items: [] }, + { name: 'rangeGt()', url: '/reference/dart/v0/rangegt', items: [] }, + { name: 'rangeGte()', url: '/reference/dart/v0/rangegte', items: [] }, + { name: 'rangeLt()', url: '/reference/dart/v0/rangelt', items: [] }, + { name: 'rangeLte()', url: '/reference/dart/v0/rangelte', items: [] }, + { name: 'rangeAdjacent()', url: '/reference/dart/v0/rangeadjacent', items: [] }, + { name: 'overlaps()', url: '/reference/dart/v0/overlaps', items: [] }, + { name: 'textSearch()', url: '/reference/dart/v0/textsearch', items: [] }, + { name: 'match()', url: '/reference/dart/v0/match', items: [] }, + { name: 'not()', url: '/reference/dart/v0/not', items: [] }, + { name: 'or()', url: '/reference/dart/v0/or', items: [] }, + { name: 'filter()', url: '/reference/dart/v0/filter', items: [] }, + ], + }, + { + label: 'Modifiers', + items: [ + { name: 'Using Modifiers', url: '/reference/dart/v0/using-modifiers', items: [] }, + { name: 'order()', url: '/reference/dart/v0/order', items: [] }, + { name: 'limit()', url: '/reference/dart/v0/limit', items: [] }, + { name: 'range()', url: '/reference/dart/v0/range', items: [] }, + { name: 'single()', url: '/reference/dart/v0/single', items: [] }, + ], + }, +] + +export default Nav diff --git a/apps/temp-docs/data/nav/supabase-dart/v1.ts b/apps/temp-docs/data/nav/supabase-dart/v1.ts new file mode 100644 index 00000000000..2c1031693ec --- /dev/null +++ b/apps/temp-docs/data/nav/supabase-dart/v1.ts @@ -0,0 +1,130 @@ +const Nav = [ + { + label: 'Getting Started', + items: [ + { name: 'Supabase Flutter Library', url: '/reference/dart', items: [] }, + { name: 'Installing', url: '/reference/dart/installing', items: [] }, + { name: 'Initializing', url: '/reference/dart/initializing', items: [] }, + { name: 'Upgrading to supabase-flutter v1', url: '/reference/dart/upgrade-guide', items: [] }, + ], + }, + { + label: 'Auth', + items: [ + { name: 'signUp()', url: '/reference/dart/auth-signup', items: [] }, + { + name: 'signInWithPassword()', + url: '/reference/dart/auth-signinwithpassword', + items: [], + }, + { name: 'signInWithOtp()', url: '/reference/dart/auth-signinwithotp', items: [] }, + { name: 'signInWithOAuth()', url: '/reference/dart/auth-signinwithoauth', items: [] }, + { name: 'signOut()', url: '/reference/dart/auth-signout', items: [] }, + { name: 'verifyOtp()', url: '/reference/dart/auth-verifyotp', items: [] }, + { name: 'currentSession', url: '/reference/dart/auth-currentsession', items: [] }, + { name: 'currentUser', url: '/reference/dart/auth-currentuser', items: [] }, + { name: 'updateUser()', url: '/reference/dart/auth-updateuser', items: [] }, + { name: 'setSession()', url: '/reference/dart/auth-setSession', items: [] }, + { + name: 'resetPasswordForEmail()', + url: '/reference/dart/auth-resetpasswordforemail', + items: [], + }, + ], + }, + { + label: 'Functions', + items: [{ name: 'invoke()', url: '/reference/dart/invoke', items: [] }], + }, + { + label: 'Database', + items: [ + { name: 'Fetch data: select()', url: '/reference/dart/select', items: [] }, + { name: 'Create data: insert()', url: '/reference/dart/insert', items: [] }, + { name: 'Modify data: update()', url: '/reference/dart/update', items: [] }, + { name: 'Upsert data: upsert()', url: '/reference/dart/upsert', items: [] }, + { name: 'Delete data: delete()', url: '/reference/dart/delete', items: [] }, + { name: 'Stored Procedures: rpc()', url: '/reference/dart/rpc', items: [] }, + { + name: 'Filters', + url: undefined, + items: [ + { name: 'Using Filters', url: '/reference/dart/using-filters', items: [] }, + { name: 'eq()', url: '/reference/dart/eq', items: [] }, + { name: 'neq()', url: '/reference/dart/neq', items: [] }, + { name: 'gt()', url: '/reference/dart/gt', items: [] }, + { name: 'gte()', url: '/reference/dart/gte', items: [] }, + { name: 'lt()', url: '/reference/dart/lt', items: [] }, + { name: 'lte()', url: '/reference/dart/lte', items: [] }, + { name: 'like()', url: '/reference/dart/like', items: [] }, + { name: 'ilike()', url: '/reference/dart/ilike', items: [] }, + { name: 'is_()', url: '/reference/dart/is_', items: [] }, + { name: 'in_()', url: '/reference/dart/in_', items: [] }, + { name: 'contains()', url: '/reference/dart/contains', items: [] }, + { name: 'containedBy()', url: '/reference/dart/containedby', items: [] }, + { name: 'rangeGt()', url: '/reference/dart/rangegt', items: [] }, + { name: 'rangeGte()', url: '/reference/dart/rangegte', items: [] }, + { name: 'rangeLt()', url: '/reference/dart/rangelt', items: [] }, + { name: 'rangeLte()', url: '/reference/dart/rangelte', items: [] }, + { name: 'rangeAdjacent()', url: '/reference/dart/rangeadjacent', items: [] }, + { name: 'overlaps()', url: '/reference/dart/overlaps', items: [] }, + { name: 'textSearch()', url: '/reference/dart/textsearch', items: [] }, + { name: 'match()', url: '/reference/dart/match', items: [] }, + { name: 'not()', url: '/reference/dart/not', items: [] }, + { name: 'or()', url: '/reference/dart/or', items: [] }, + { name: 'filter()', url: '/reference/dart/filter', items: [] }, + ], + }, + { + name: 'Modifiers', + url: undefined, + items: [ + { name: 'Using Modifiers', url: '/reference/dart/using-modifiers', items: [] }, + { name: 'order()', url: '/reference/dart/order', items: [] }, + { name: 'limit()', url: '/reference/dart/limit', items: [] }, + { name: 'range()', url: '/reference/dart/range', items: [] }, + { name: 'single()', url: '/reference/dart/single', items: [] }, + ], + }, + ], + }, + { + label: 'Realtime', + items: [ + { name: 'stream()', url: '/reference/dart/stream', items: [] }, + { name: 'on().subscribe()', url: '/reference/dart/subscribe', items: [] }, + { name: 'removeChannel()', url: '/reference/dart/removechannel', items: [] }, + { name: 'removeAllChannels()', url: '/reference/dart/removeallchannels', items: [] }, + { name: 'getChannels()', url: '/reference/dart/getchannels', items: [] }, + ], + }, + { + label: 'Storage', + items: [ + { name: 'createBucket()', url: '/reference/dart/storage-createbucket', items: [] }, + { name: 'getBucket()', url: '/reference/dart/storage-getbucket', items: [] }, + { name: 'listBuckets()', url: '/reference/dart/storage-listbuckets', items: [] }, + { name: 'updateBucket()', url: '/reference/dart/storage-updatebucket', items: [] }, + { name: 'deleteBucket()', url: '/reference/dart/storage-deletebucket', items: [] }, + { name: 'emptyBucket()', url: '/reference/dart/storage-emptybucket', items: [] }, + { name: 'from.upload()', url: '/reference/dart/storage-from-upload', items: [] }, + { name: 'from.download()', url: '/reference/dart/storage-from-download', items: [] }, + { name: 'from.list()', url: '/reference/dart/storage-from-list', items: [] }, + { name: 'from.update()', url: '/reference/dart/storage-from-update', items: [] }, + { name: 'from.move()', url: '/reference/dart/storage-from-move', items: [] }, + { name: 'from.remove()', url: '/reference/dart/storage-from-remove', items: [] }, + { + name: 'from.createSignedUrl()', + url: '/reference/dart/storage-from-createsignedurl', + items: [], + }, + { + name: 'from.getPublicUrl()', + url: '/reference/dart/storage-from-getpublicurl', + items: [], + }, + ], + }, +] + +export default Nav diff --git a/apps/temp-docs/data/nav/supabase-js/v1.ts b/apps/temp-docs/data/nav/supabase-js/v1.ts new file mode 100644 index 00000000000..828563e796a --- /dev/null +++ b/apps/temp-docs/data/nav/supabase-js/v1.ts @@ -0,0 +1,169 @@ +const Nav = [ + { + label: 'Getting Started', + items: [ + { name: 'Supabase Javascript Library', url: '/reference/javascript/v1', items: [] }, + { name: 'Installing', url: '/reference/javascript/v1/installing', items: [] }, + { name: 'Initializing', url: '/reference/javascript/v1/initializing', items: [] }, + { name: 'Generating Types', url: '/reference/javascript/v1/generating-types', items: [] }, + ], + }, + { + label: 'Auth', + items: [ + { name: 'signUp()', url: '/reference/javascript/v1/auth-signup', items: [] }, + { name: 'signIn()', url: '/reference/javascript/v1/auth-signin', items: [] }, + { name: 'signOut()', url: '/reference/javascript/v1/auth-signout', items: [] }, + { name: 'session()', url: '/reference/javascript/v1/auth-session', items: [] }, + { name: 'user()', url: '/reference/javascript/v1/auth-user', items: [] }, + { name: 'update()', url: '/reference/javascript/v1/auth-update', items: [] }, + { name: 'setAuth()', url: '/reference/javascript/v1/auth-setauth', items: [] }, + { + name: 'onAuthStateChange()', + url: '/reference/javascript/v1/auth-onauthstatechanged', + items: [], + }, + { name: 'getUser()', url: '/reference/javascript/v1/auth-api-getuser', items: [] }, + { + name: 'resetPasswordForEmail()', + url: '/reference/javascript/v1/auth-api-resetpasswordforemail', + items: [], + }, + ], + }, + { + label: 'Auth (Server Only)', + items: [ + { name: 'listUsers()', url: '/reference/javascript/v1/auth-api-listusers', items: [] }, + { name: 'createUser()', url: '/reference/javascript/v1/auth-api-createuser', items: [] }, + { name: 'deleteUser()', url: '/reference/javascript/v1/auth-api-deleteuser', items: [] }, + { + name: 'generateLink()', + url: '/reference/javascript/v1/auth-api-generatelink', + items: [], + }, + { + name: 'inviteUserByEmail()', + url: '/reference/javascript/v1/auth-api-inviteuserbyemail', + items: [], + }, + { + name: 'sendMobileOTP()', + url: '/reference/javascript/v1/auth-api-sendmobileotp', + items: [], + }, + { + name: 'updateUserById()', + url: '/reference/javascript/v1/auth-api-updateuserbyid', + items: [], + }, + ], + }, + { + label: 'Functions', + items: [{ name: 'invoke()', url: '/reference/javascript/v1/invoke', items: [] }], + }, + { + label: 'Database', + items: [ + { name: 'Fetch data: select()', url: '/reference/javascript/v1/select', items: [] }, + { name: 'Create data: insert()', url: '/reference/javascript/v1/insert', items: [] }, + { name: 'Modify data: update()', url: '/reference/javascript/v1/update', items: [] }, + { name: 'Upsert data: upsert()', url: '/reference/javascript/v1/upsert', items: [] }, + { name: 'Delete data: delete()', url: '/reference/javascript/v1/delete', items: [] }, + { name: 'Postgres functions: rpc()', url: '/reference/javascript/v1/rpc', items: [] }, + ], + }, + { + label: 'Realtime', + items: [ + { name: 'on().subscribe()', url: '/reference/javascript/v1/subscribe', items: [] }, + { + name: 'removeSubscription()', + url: '/reference/javascript/v1/removesubscription', + items: [], + }, + { + name: 'removeAllSubscriptions()', + url: '/reference/javascript/v1/removeallsubscriptions', + items: [], + }, + { name: 'getSubscriptions()', url: '/reference/javascript/v1/getsubscriptions', items: [] }, + ], + }, + { + label: 'Storage', + items: [ + { name: 'createBucket()', url: '/reference/javascript/v1/storage-createbucket', items: [] }, + { name: 'getBucket()', url: '/reference/javascript/v1/storage-getbucket', items: [] }, + { name: 'listBuckets()', url: '/reference/javascript/v1/storage-listbuckets', items: [] }, + { name: 'updateBucket()', url: '/reference/javascript/v1/storage-updatebucket', items: [] }, + { name: 'deleteBucket()', url: '/reference/javascript/v1/storage-deletebucket', items: [] }, + { name: 'emptyBucket()', url: '/reference/javascript/v1/storage-emptybucket', items: [] }, + { name: 'from.upload()', url: '/reference/javascript/v1/storage-from-upload', items: [] }, + { name: 'from.download()', url: '/reference/javascript/v1/storage-from-download', items: [] }, + { name: 'from.list()', url: '/reference/javascript/v1/storage-from-list', items: [] }, + { name: 'from.update()', url: '/reference/javascript/v1/storage-from-update', items: [] }, + { name: 'from.move()', url: '/reference/javascript/v1/storage-from-move', items: [] }, + { name: 'from.copy()', url: '/reference/javascript/v1/storage-from-copy', items: [] }, + { name: 'from.remove()', url: '/reference/javascript/v1/storage-from-remove', items: [] }, + { + name: 'from.createSignedUrl()', + url: '/reference/javascript/v1/storage-from-createsignedurl', + items: [], + }, + { + name: 'from.createSignedUrls()', + url: '/reference/javascript/v1/storage-from-createsignedurls', + items: [], + }, + { + name: 'from.getPublicUrl()', + url: '/reference/javascript/v1/storage-from-getpublicurl', + items: [], + }, + ], + }, + { + label: 'Filters', + items: [ + { name: 'Using Filters', url: '/reference/javascript/v1/using-filters', items: [] }, + { name: 'eq()', url: '/reference/javascript/v1/eq', items: [] }, + { name: 'neq()', url: '/reference/javascript/v1/neq', items: [] }, + { name: 'gt()', url: '/reference/javascript/v1/gt', items: [] }, + { name: 'gte()', url: '/reference/javascript/v1/gte', items: [] }, + { name: 'lt()', url: '/reference/javascript/v1/lt', items: [] }, + { name: 'lte()', url: '/reference/javascript/v1/lte', items: [] }, + { name: 'like()', url: '/reference/javascript/v1/like', items: [] }, + { name: 'ilike()', url: '/reference/javascript/v1/ilike', items: [] }, + { name: 'is()', url: '/reference/javascript/v1/is', items: [] }, + { name: 'in()', url: '/reference/javascript/v1/in', items: [] }, + { name: 'contains()', url: '/reference/javascript/v1/contains', items: [] }, + { name: 'containedBy()', url: '/reference/javascript/v1/containedby', items: [] }, + { name: 'rangeGt()', url: '/reference/javascript/v1/rangegt', items: [] }, + { name: 'rangeGte()', url: '/reference/javascript/v1/rangegte', items: [] }, + { name: 'rangeLt()', url: '/reference/javascript/v1/rangelt', items: [] }, + { name: 'rangeLte()', url: '/reference/javascript/v1/rangelte', items: [] }, + { name: 'rangeAdjacent()', url: '/reference/javascript/v1/rangeadjacent', items: [] }, + { name: 'overlaps()', url: '/reference/javascript/v1/overlaps', items: [] }, + { name: 'textSearch()', url: '/reference/javascript/v1/textsearch', items: [] }, + { name: 'match()', url: '/reference/javascript/v1/match', items: [] }, + { name: 'not()', url: '/reference/javascript/v1/not', items: [] }, + { name: 'or()', url: '/reference/javascript/v1/or', items: [] }, + { name: 'filter()', url: '/reference/javascript/v1/filter', items: [] }, + ], + }, + { + label: 'Modifiers', + items: [ + { name: 'Using Modifiers', url: '/reference/javascript/v1/using-modifiers', items: [] }, + { name: 'order()', url: '/reference/javascript/v1/order', items: [] }, + { name: 'limit()', url: '/reference/javascript/v1/limit', items: [] }, + { name: 'range()', url: '/reference/javascript/v1/range', items: [] }, + { name: 'single()', url: '/reference/javascript/v1/single', items: [] }, + { name: 'maybeSingle()', url: '/reference/javascript/v1/maybesingle', items: [] }, + ], + }, +] + +export default Nav diff --git a/apps/temp-docs/data/nav/supabase-js/v2.ts b/apps/temp-docs/data/nav/supabase-js/v2.ts new file mode 100644 index 00000000000..e367be12e08 --- /dev/null +++ b/apps/temp-docs/data/nav/supabase-js/v2.ts @@ -0,0 +1,166 @@ +const Nav = [ + { + label: 'Getting Started', + items: [ + { name: 'Supabase Javascript Library', url: '/reference/javascript', items: [] }, + { name: 'Installing', url: '/reference/javascript/installing', items: [] }, + { name: 'Initializing', url: '/reference/javascript/initializing', items: [] }, + { name: 'Typescript Support', url: '/reference/javascript/typescript-support', items: [] }, + { name: 'Release Notes', url: '/reference/javascript/release-notes', items: [] }, + { + name: 'Upgrading to supabase-js v2', + url: '/reference/javascript/upgrade-guide', + items: [], + }, + ], + }, + { + label: 'Auth', + items: [ + { name: 'signUp()', url: '/reference/javascript/auth-signup', items: [] }, + { + name: 'signInWithPassword()', + url: '/reference/javascript/auth-signinwithpassword', + items: [], + }, + { name: 'signInWithOtp()', url: '/reference/javascript/auth-signinwithotp', items: [] }, + { name: 'signInWithOAuth()', url: '/reference/javascript/auth-signinwithoauth', items: [] }, + { name: 'signOut()', url: '/reference/javascript/auth-signout', items: [] }, + { name: 'verifyOtp()', url: '/reference/javascript/auth-verifyotp', items: [] }, + { name: 'getSession()', url: '/reference/javascript/auth-getsession', items: [] }, + { name: 'getUser()', url: '/reference/javascript/auth-getuser', items: [] }, + { name: 'updateUser()', url: '/reference/javascript/auth-updateuser', items: [] }, + { name: 'setSession()', url: '/reference/javascript/auth-setSession', items: [] }, + { + name: 'resetPasswordForEmail()', + url: '/reference/javascript/auth-resetpasswordforemail', + items: [], + }, + ], + }, + { + label: 'Auth (Server Only)', + items: [ + { name: 'Overview', url: '/reference/javascript/supabase-auth-admin-api', items: [] }, + { name: 'listUsers()', url: '/reference/javascript/auth-admin-listusers', items: [] }, + { name: 'createUser()', url: '/reference/javascript/auth-admin-createuser', items: [] }, + { name: 'deleteUser()', url: '/reference/javascript/auth-admin-deleteuser', items: [] }, + { name: 'generateLink()', url: '/reference/javascript/auth-admin-generatelink', items: [] }, + { + name: 'inviteUserByEmail()', + url: '/reference/javascript/auth-admin-inviteuserbyemail', + items: [], + }, + { name: 'getUserById()', url: '/reference/javascript/auth-admin-getuserbyid', items: [] }, + { + name: 'updateUserById()', + url: '/reference/javascript/auth-admin-updateuserbyid', + items: [], + }, + ], + }, + { + label: 'Database', + items: [ + { name: 'Fetch data: select()', url: '/reference/javascript/select', items: [] }, + { name: 'Create data: insert()', url: '/reference/javascript/insert', items: [] }, + { name: 'Modify data: update()', url: '/reference/javascript/update', items: [] }, + { name: 'Upsert data: upsert()', url: '/reference/javascript/upsert', items: [] }, + { name: 'Delete data: delete()', url: '/reference/javascript/delete', items: [] }, + { name: 'Postgres functions: rpc()', url: '/reference/javascript/rpc', items: [] }, + { + name: 'Filters', + url: undefined, + items: [ + { name: 'Using Filters', url: '/reference/javascript/using-filters', items: [] }, + { name: 'eq()', url: '/reference/javascript/eq', items: [] }, + { name: 'neq()', url: '/reference/javascript/neq', items: [] }, + { name: 'gt()', url: '/reference/javascript/gt', items: [] }, + { name: 'gte()', url: '/reference/javascript/gte', items: [] }, + { name: 'lt()', url: '/reference/javascript/lt', items: [] }, + { name: 'lte()', url: '/reference/javascript/lte', items: [] }, + { name: 'like()', url: '/reference/javascript/like', items: [] }, + { name: 'ilike()', url: '/reference/javascript/ilike', items: [] }, + { name: 'is()', url: '/reference/javascript/is', items: [] }, + { name: 'in()', url: '/reference/javascript/in', items: [] }, + { name: 'contains()', url: '/reference/javascript/contains', items: [] }, + { name: 'containedBy()', url: '/reference/javascript/containedby', items: [] }, + { name: 'rangeGt()', url: '/reference/javascript/rangegt', items: [] }, + { name: 'rangeGte()', url: '/reference/javascript/rangegte', items: [] }, + { name: 'rangeLt()', url: '/reference/javascript/rangelt', items: [] }, + { name: 'rangeLte()', url: '/reference/javascript/rangelte', items: [] }, + { name: 'rangeAdjacent()', url: '/reference/javascript/rangeadjacent', items: [] }, + { name: 'overlaps()', url: '/reference/javascript/overlaps', items: [] }, + { name: 'textSearch()', url: '/reference/javascript/textsearch', items: [] }, + { name: 'match()', url: '/reference/javascript/match', items: [] }, + { name: 'not()', url: '/reference/javascript/not', items: [] }, + { name: 'or()', url: '/reference/javascript/or', items: [] }, + { name: 'filter()', url: '/reference/javascript/filter', items: [] }, + ], + }, + { + name: 'Modifiers', + url: undefined, + items: [ + { name: 'Using Modifiers', url: '/reference/javascript/using-modifiers', items: [] }, + { name: 'select()', url: '/reference/javascript/db-modifiers-select', items: [] }, + { name: 'order()', url: '/reference/javascript/order', items: [] }, + { name: 'limit()', url: '/reference/javascript/limit', items: [] }, + { name: 'range()', url: '/reference/javascript/range', items: [] }, + { name: 'abortSignal()', url: '/reference/javascript/db-abortsignal', items: [] }, + { name: 'single()', url: '/reference/javascript/single', items: [] }, + { name: 'maybeSingle()', url: '/reference/javascript/maybesingle', items: [] }, + { name: 'csv()', url: '/reference/javascript/db-csv', items: [] }, + ], + }, + ], + }, + { + label: 'Functions', + items: [{ name: 'invoke()', url: '/reference/javascript/invoke', items: [] }], + }, + { + label: 'Realtime', + items: [ + { name: 'on().subscribe()', url: '/reference/javascript/subscribe', items: [] }, + { name: 'removeChannel()', url: '/reference/javascript/removechannel', items: [] }, + { name: 'removeAllChannels()', url: '/reference/javascript/removeallchannels', items: [] }, + { name: 'getChannels()', url: '/reference/javascript/getchannels', items: [] }, + ], + }, + { + label: 'Storage', + items: [ + { name: 'createBucket()', url: '/reference/javascript/storage-createbucket', items: [] }, + { name: 'getBucket()', url: '/reference/javascript/storage-getbucket', items: [] }, + { name: 'listBuckets()', url: '/reference/javascript/storage-listbuckets', items: [] }, + { name: 'updateBucket()', url: '/reference/javascript/storage-updatebucket', items: [] }, + { name: 'deleteBucket()', url: '/reference/javascript/storage-deletebucket', items: [] }, + { name: 'emptyBucket()', url: '/reference/javascript/storage-emptybucket', items: [] }, + { name: 'from.upload()', url: '/reference/javascript/storage-from-upload', items: [] }, + { name: 'from.download()', url: '/reference/javascript/storage-from-download', items: [] }, + { name: 'from.list()', url: '/reference/javascript/storage-from-list', items: [] }, + { name: 'from.update()', url: '/reference/javascript/storage-from-update', items: [] }, + { name: 'from.move()', url: '/reference/javascript/storage-from-move', items: [] }, + { name: 'from.copy()', url: '/reference/javascript/storage-from-copy', items: [] }, + { name: 'from.remove()', url: '/reference/javascript/storage-from-remove', items: [] }, + { + name: 'from.createSignedUrl()', + url: '/reference/javascript/storage-from-createsignedurl', + items: [], + }, + { + name: 'from.createSignedUrls()', + url: '/reference/javascript/storage-from-createsignedurls', + items: [], + }, + { + name: 'from.getPublicUrl()', + url: '/reference/javascript/storage-from-getpublicurl', + items: [], + }, + ], + }, +] + +export default Nav diff --git a/apps/temp-docs/docs/architecture.mdx b/apps/temp-docs/docs/architecture.mdx index 706b3319a55..bb6b76121ec 100644 --- a/apps/temp-docs/docs/architecture.mdx +++ b/apps/temp-docs/docs/architecture.mdx @@ -1,6 +1,6 @@ --- -title: 'Architecture' -description: 'Supabase Architecture' +title: 'Architecture', +description: 'Supabase Architecture', --- # Architecture diff --git a/apps/temp-docs/docs/faqs.mdx b/apps/temp-docs/docs/faqs.mdx index 3131951766b..92cfe6b2b27 100644 --- a/apps/temp-docs/docs/faqs.mdx +++ b/apps/temp-docs/docs/faqs.mdx @@ -1,11 +1,12 @@ --- -title: 'FAQs' -description: 'Supabase frequently asked questions' +title: 'FAQs', +description: 'Supabase frequently asked questions', --- # FAQs ### Have you built `[Firebase feature]` yet? + We're building as fast as we can! And we are even adding a few things that Firebase doesn't have, as we go. Here are the features we have built: - A dashboard/UI for spinning up and managing your database in less than one minute. @@ -19,10 +20,13 @@ We're building as fast as we can! And we are even adding a few things that Fireb We started Supabase because we love the functionality of Firebase, but we personally experienced the scaling issues that many others experienced. We chose Postgres because it's well-trusted and it has phenomenal scalability. Our goal is to make Postgres as easy to use as Firebase, so that you no longer have to choose between usability and scalability. Also, we're sure that once you start using Postgres, you'll love it more than any other database. ### How do I host Supabase? + Supabase is an amalgamation of five open source tools (and growing). Some of these tools are made by Supabase (like our [Realtime Server](https://github.com/supabase/realtime)), some we support indirectly (like [PostgREST](http://postgrest.org/en/v7.0.0/)), and some are third-party tools (like [Kong](https://github.com/Kong/kong)). All of the tools we use in Supabase are MIT, Apache 2.0, or PostgreSQL licensed. You can use the docker-compose script [here](https://github.com/supabase/supabase/tree/master/docker) to build Supabase on your own environment, and find detailed instructions [here](https://supabase.com/docs/guides/hosting/overview). ### Do you support `[some other database]`? + At the moment, we only support PostgreSQL. It's unlikely we'll ever move away from Postgres; however, you can [vote on a new database](https://github.com/supabase/supabase/issues/6) if you want us to start development. ### Do you have a library for `[some other language]`? + We currently have a JavaScript library. You can [vote on a new client library](https://github.com/supabase/supabase/issues/5) for your favorite language. diff --git a/apps/temp-docs/docs/going-into-prod.mdx b/apps/temp-docs/docs/going-into-prod.mdx index 9f93fe8a95d..3d916b02105 100644 --- a/apps/temp-docs/docs/going-into-prod.mdx +++ b/apps/temp-docs/docs/going-into-prod.mdx @@ -1,7 +1,7 @@ --- -id: going-into-prod -title: Going into Prod Checklist -description: 'Things to do before making your app publicly available' +id: 'going-into-prod', +title: 'Going into Prod Checklist', +description: 'Things to do before making your app publicly available', --- # Going into Prod Checklist @@ -15,39 +15,39 @@ After developing your project, and deciding it's time to Go Live With Real Users ## Security - Ensure RLS is enabled - - Tables that do not have RLS enabled with reasonable policies allow any client to access and modify their data. This is unlikely to be what you want in the majority of cases. - - [Learn more about RLS](/docs/guides/auth/row-level-security) + - Tables that do not have RLS enabled with reasonable policies allow any client to access and modify their data. This is unlikely to be what you want in the majority of cases. + - [Learn more about RLS](/docs/guides/auth/row-level-security) - Ensure replication is disabled for tables containing sensitive data: - - Today, Realtime does not respect RLS policies and any client with your anon key can listen to changes on tables where replication is enabled. - - Go to the Database > Replication page in the Supabase Dashboard to manage these settings - - We are launching [RLS for realtime](https://github.com/supabase/walrus) soon, but in the meantime you should only use it for tables containing public data (scoreboards, blog posts, etc.) + - Today, Realtime does not respect RLS policies and any client with your anon key can listen to changes on tables where replication is enabled. + - Go to the Database > Replication page in the Supabase Dashboard to manage these settings + - We are launching [RLS for realtime](https://github.com/supabase/walrus) soon, but in the meantime you should only use it for tables containing public data (scoreboards, blog posts, etc.) - Enable 2FA on Github - - Since your Github account gives you administrative rights to your Supabase project, you should protect it with a strong password and 2FA using a U2F key or a TOTP app. + - Since your Github account gives you administrative rights to your Supabase project, you should protect it with a strong password and 2FA using a U2F key or a TOTP app. - Ensure email confirmations are enabled in the Auth > Settings page - Use a custom SMTP server for auth emails so that your users can see that the mails are coming from a trusted domain (preferably the same domain that your app is hosted on). (grab SMTP credentials from any major email provider such as SendGrid, AWS SES, etc.) -- Think hard about how *you* would abuse your service as an attacker, and mitigate +- Think hard about how _you_ would abuse your service as an attacker, and mitigate - Review these [common cybersecurity threats](https://auth0.com/docs/security/prevent-threats) ## Performance - Ensure that you have suitable indices to cater to your common query patterns - - [learn more about indexes in postgres](https://www.enterprisedb.com/postgres-tutorials/overview-postgresql-indexes) - - pg_stat_statements can help you [identify hot or slow queries](https://www.virtual-dba.com/blog/postgresql-performance-identifying-hot-and-slow-queries/) + - [learn more about indexes in postgres](https://www.enterprisedb.com/postgres-tutorials/overview-postgresql-indexes) + - pg_stat_statements can help you [identify hot or slow queries](https://www.virtual-dba.com/blog/postgresql-performance-identifying-hot-and-slow-queries/) - Perform load testing (preferably on a staging env) - - tools like [k6](https://k6.io/) can simulate traffic from many different users + - tools like [k6](https://k6.io/) can simulate traffic from many different users - If you find you require more resources, contact support@supabase.io to upgrade to PAYG for bigger servers ## Availability - Use your own SMTP credentials so that you have full control over the deliverability of your transactional auth emails (see Auth > Settings) - - you can grab SMTP credentials from any major email provider such as SendGrid, AWS SES, etc. - - the default rate limit for auth emails provided by Supabase is 30 new users per hour, if doing a major public announcement you will likely require more than this + - you can grab SMTP credentials from any major email provider such as SendGrid, AWS SES, etc. + - the default rate limit for auth emails provided by Supabase is 30 new users per hour, if doing a major public announcement you will likely require more than this - If your application is on the free tier and is **not** expected to be queried at least once every 7 days, then it may be paused by Supabase to save on server resources - - you can restore paused projects from the Supabase dashboard - - upgrade to Pro to guarantee that your project will not be paused for inactivity + - you can restore paused projects from the Supabase dashboard + - upgrade to Pro to guarantee that your project will not be paused for inactivity - Database backups are not available for download on the free tier - - You can set up your own backup systems using tools like [pg_dump](https://www.postgresqltutorial.com/postgresql-backup-database/) or [wal-g](https://github.com/wal-g/wal-g) - - Nightly backups for Pro tier projects are available on the Supabase dashboard for up to 7 days + - You can set up your own backup systems using tools like [pg_dump](https://www.postgresqltutorial.com/postgresql-backup-database/) or [wal-g](https://github.com/wal-g/wal-g) + - Nightly backups for Pro tier projects are available on the Supabase dashboard for up to 7 days - upgrading to the Supabase Pro Tier will give you access to email support on support@supabase.io This checklist is always growing so be sure to check back frequently, and also feel free to suggest additions and amendments by making a PR on [Github](https://github.com/supabase/supabase). diff --git a/apps/temp-docs/docs/guides/_partials/project_setup.mdx b/apps/temp-docs/docs/guides/_partials/project_setup.mdx new file mode 100644 index 00000000000..271a3c426cb --- /dev/null +++ b/apps/temp-docs/docs/guides/_partials/project_setup.mdx @@ -0,0 +1,66 @@ +import Tabs from '@theme/Tabs' +import TabItem from '@theme/TabItem' +import UserManagementSQLTemplate from './user_management_quickstart_sql_template.md' + +## Project set up + +Before we start building we're going to set up our Database and API. This is as simple as starting a new Project in Supabase +and then creating a "schema" inside the database. + +### Create a project + +1. Go to [app.supabase.com](https://app.supabase.com). +1. Click on "New Project". +1. Enter your project details. +1. Wait for the new database to launch. + +### Set up the database schema + +Now we are going to set up the database schema. We can use the "User Management Starter" quickstart in the SQL Editor, +or you can just copy/paste the SQL from below and run it yourself. + + + + +1. Go to the [SQL Editor](https://app.supabase.com/project/_/sql) page in the Dashboard. +2. Click **User Management Starter**. +3. Click **Run**. + + + + + + + + + +### Get the API Keys + +Now that you've created some database tables, you are ready to insert data using the auto-generated API. +We just need to get the URL and `anon` key from the API settings. + +1. Go to the [Settings](https://app.supabase.com/project/_/settings) page in the Dashboard. +2. Click **API** in the sidebar. +3. Find your API `URL`, `anon`, and `service_role` keys on this page. + + diff --git a/apps/temp-docs/docs/guides/_partials/quickstart_intro.md b/apps/temp-docs/docs/guides/_partials/quickstart_intro.md new file mode 100644 index 00000000000..36b56131364 --- /dev/null +++ b/apps/temp-docs/docs/guides/_partials/quickstart_intro.md @@ -0,0 +1,11 @@ +## Intro + +This example provides the steps to build a basic user management app. It includes: + +- Supabase [Database](/docs/guides/database): a Postgres database for storing your user data. +- Supabase [Auth](/docs/guides/auth): users can sign in with magic links (no passwords, only email). +- Supabase [Storage](/docs/guides/storage): users can upload a photo. +- [Row Level Security](/docs/guides/auth#row-level-security): data is protected so that individuals can only access their own data. +- Instant [APIs](/docs/guides/api): APIs will be automatically generated when you create your database tables. + +By the end of this guide you'll have an app which allows users to login and update some basic profile details: diff --git a/apps/temp-docs/docs/guides/_partials/user_management_quickstart_sql_template.md b/apps/temp-docs/docs/guides/_partials/user_management_quickstart_sql_template.md new file mode 100644 index 00000000000..ba55c4aeab3 --- /dev/null +++ b/apps/temp-docs/docs/guides/_partials/user_management_quickstart_sql_template.md @@ -0,0 +1,55 @@ +```sql +-- Create a table for public profiles +create table profiles ( + id uuid references auth.users not null primary key, + updated_at timestamp with time zone, + username text unique, + full_name text, + avatar_url text, + website text, + + constraint username_length check (char_length(username) >= 3) +); +-- Set up Row Level Security (RLS) +-- See https://supabase.com/docs/guides/auth/row-level-security for more details. +alter table profiles + enable row level security; + +create policy "Public profiles are viewable by everyone." on profiles + for select using (true); + +create policy "Users can insert their own profile." on profiles + for insert with check (auth.uid() = id); + +create policy "Users can update own profile." on profiles + for update using (auth.uid() = id); + +-- This trigger automatically creates a profile entry when a new user signs up via Supabase Auth. +-- See https://supabase.com/docs/guides/auth/managing-user-data#using-triggers for more details. +create function public.handle_new_user() +returns trigger as $$ +begin + insert into public.profiles (id, full_name, avatar_url) + values (new.id, new.raw_user_meta_data->>'full_name', new.raw_user_meta_data->>'avatar_url'); + return new; +end; +$$ language plpgsql security definer; +create trigger on_auth_user_created + after insert on auth.users + for each row execute procedure public.handle_new_user(); + +-- Set up Storage! +insert into storage.buckets (id, name) + values ('avatars', 'avatars'); + +-- Set up access controls for storage. +-- See https://supabase.com/docs/guides/storage#policy-examples for more details. +create policy "Avatar images are publicly accessible." on storage.objects + for select using (bucket_id = 'avatars'); + +create policy "Anyone can upload an avatar." on storage.objects + for insert with check (bucket_id = 'avatars'); + +create policy "Anyone can update their own avatar." on storage.objects + for update using (auth.uid() = owner) with check (bucket_id = 'avatars'); +``` diff --git a/apps/temp-docs/docs/guides/api.mdx b/apps/temp-docs/docs/guides/api.mdx new file mode 100644 index 00000000000..0f0c67e93c6 --- /dev/null +++ b/apps/temp-docs/docs/guides/api.mdx @@ -0,0 +1,391 @@ +--- +id: api +title: APIs +description: Auto-generating and Realtime APIs. +sidebar_label: Overview +--- + +import Tabs from '@theme/Tabs' +import TabItem from '@theme/TabItem' +// import useBaseUrl from '@docusaurus/useBaseUrl' + +## Overview + +Supabase generates three types of API directly from your database schema. + +- REST - interact with your database through a restful interface. +- Realtime - listen to database changes. +- GraphQL - [in beta](https://supabase.com/blog/pg-graphql). + +The APIs are: + +- **Instant and auto-generated.**
As you update your database the changes are immediately accessible through your API. +- **Self documenting.**
Supabase generates documentation in the Dashboard which updates as you make database changes. +- **Secure.**
The API is configured to work with PostgreSQL's Row Level Security, provisioned behind an API gateway with key-auth enabled. +- **Fast.**
Our benchmarks for basic reads are more than 300% faster than Firebase. The API is a very thin layer on top of Postgres, which does most of the heavy lifting. +- **Scalable.**
The API can serve thousands of simultaneous requests, and works well for Serverless workloads. + +### REST API {#rest-api-overview} + +Supabase provides a RESTful API using [PostgREST](https://postgrest.org/). This is a very thin API layer on top of Postgres. +It provides everything you need from a CRUD API: + +- Basic CRUD operations +- Deeply nested joins, allowing you to fetch data from multiple tables in a single fetch +- Works with Postgres Views +- Works with Postgres Functions +- Works with the Postgres security model - including Row Level Security, Roles, and Grants. + +
+ +
+ +### GraphQL API {#graphql-api-overview} + +:::note + +GraphQL is in Beta, and may have breaking changes. It is only available on self-hosted setups and Supabase projects created after 28th March 2022. + +::: + +GraphQL in Supabase works through [pg_graphql](https://supabase.com/blog/pg-graphql), an open source PostgreSQL extension for GraphQL. + +### Realtime API {#realtime-api-overview} + +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. + +## Getting started + +All APIs are auto-created from Database tables. After you have added tables or functions to your database, you can use the APIs provided. + +### Creating API Routes + +API routes are automatically created when you create Postgres Tables, Views, or Functions. + +Let's create our first +API route by creating a table called `todos` to store tasks. +This creates a corresponding route `todos` which can accept `GET`, `POST`, `PATCH`, & `DELETE` requests. + + + + + +1. Go to the [Table editor](https://app.supabase.com/project/_/editor) page in the Dashboard. +1. Click **New Table** and create a table with the name `todos`. +1. Click **Save**. +1. Click **New Column** and create a column with the name `task` and type `text`. +1. Click **Save**. + + + + + + +```sql +-- Create a table called "todos" with a column to store tasks. + +create table todos ( + id bigint generated by default as identity primary key, + task text check (char_length(task) > 3) +); +``` + + + + +### API URL and Keys + +Every Supabase project has a unique API URL. Your API is secured behind an API gateway which requires an API Key for every request. + +1. Go to the [Settings](https://app.supabase.com/project/_/settings/general) page in the Dashboard. +2. Click **API** in the sidebar. +3. Find your API `URL`, `anon`, and `service_role` keys on this page. + + + +The REST API and the GraphQL API are both accessible through this URL: + +- REST: `https://.supabase.co/rest/v1` +- GraphQL: `https://.supabase.co/graphql/v1` + +Both of these routes require the `anon` key to be passed through an `apikey` header. + +#### API Keys + +You are provided with two keys: + +- an `anon` key, which is safe to be used in a browser context. +- a `service_role` key, which should only be used on a server. This key can bypass Row Level Security. NEVER use this key in a browser. + +### Accessing the docs in the Dashboard + +#### REST API {#rest-api-dashboard-docs} + +Supabase generates documentation in the [Dashboard](https://app.supabase.com) which updates as you make database changes. +Let's view the documentation for a `countries` table which we created in our database. + +1. Go to the [API](https://app.supabase.com/project/_/api) page in the Dashboard. +2. Find the `countries` table under **Tables and Views** in the sidebar. +3. Switch between the JavaScript and the cURL docs using the tabs. + + + +#### GraphQL + +The GraphQL Endpoint that we provide (`https://.supabase.co/graphql/v1`) is compatible with any GraphiQL implementation that can pass an `apikey` header. +Some suggested applications: + +- [paw.cloud](https://paw.cloud) +- [insomnia.rest](https://insomnia.rest) +- [postman.com/graphql](https://www.postman.com/graphql/) +- Self-hosted GraphiQL: GraphiQL can be served through a simple HTML file. See [this discussion](https://github.com/supabase/supabase/discussions/6144) for more details. + +## Using the API + +### REST API + +You can interact with your API directly via HTTP requests, or you can use the client libraries which we provide. + +Let's see how to make a request to the `todos` table which we created in the first step, +using the API URL (`SUPABASE_URL`) and Key (`SUPABASE_ANON_KEY`) we provided: + + + + + +```javascript +// Initialize the JS client +import { createClient } from '@supabase/supabase-js' +const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY) + +// Make a request +const { data: todos, error } = await supabase.from('todos').select('*') +``` + + + + +```bash +# Append /rest/v1/ to your URL, and then use the table name as the route +curl '/rest/v1/todos' \ +-H "apikey: " \ +-H "Authorization: Bearer " +``` + + + + +JS Reference: [select()](../reference/javascript/select), +[insert()](../reference/javascript/insert), +[update()](../reference/javascript/update), +[upsert()](../reference/javascript/upsert), +[delete()](../reference/javascript/delete), +[rpc()](../reference/javascript/rpc) (call Postgres functions). + +### GraphQL API + +:::note + +To rebuild your GraphQL schema from the SQL schema, call `select graphql.rebuild_schema();`. +Be sure to rebuild the GraphQL schema after altering the SQL schema. + +::: + +You can use any GraphQL client with the Supabase GraphQL API. For our GraphQL example we will use [urql](https://formidable.com/open-source/urql/docs/). + + + + + +```javascript +import { createClient, useQuery } from 'urql' + +// Prepare API key and Authorization header +const headers = { + apikey: , + authorization: `Bearer ${ +} + +// Create GraphQL client +// See: https://formidable.com/open-source/urql/docs/basics/react-preact/#setting-up-the-client +const client = createClient({ + url: '/graphql/v1', + fetchOptions: function createFetchOptions() { + return { headers } + }, +}) + +// Prepare our GraphQL query +const TodosQuery = ` + query { + todosCollection { + edges { + node { + id + title + } + } + } + } +` + +// Query for the data (React) +const [result, reexecuteQuery] = useQuery({ + query: TodosQuery, +}) + +// Read the result +const { data, fetching, error } = result +``` + + + + +```bash +# Append /graphql/v1/ to your URL, and then use the table name as the route +curl --request POST '/graphql/v1' \ +-H 'apikey: ' \ +-H 'Authorization: Bearer ' \ +-d '{ "query":"{ todos(first: 3) { edges { node { id } } } }" }' +``` + + + + +### Realtime API + +By default Realtime is disabled on your database. Let's turn on Realtime for the `todos` table. + + + + + +1. Go to the [Database](https://app.supabase.com/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. + + + + + + +```sql +alter publication supabase_realtime add table todos; +``` + + + + +From the client, we can listen to any new data that is inserted into the `todos` table: + +```javascript +// Initialize the JS client +import { createClient } from '@supabase/supabase-js' +const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY) + +// Create a function to handle inserts +const handleInserts = (payload) => { + console.log('Change received!', payload) +} + +// Listen to inserts +const { data: todos, error } = await supabase.from('todos').on('INSERT', handleInserts).subscribe() +``` + +Use [subscribe()](/docs/reference/javascript/subscribe) to listen to database changes. +The Realtime API works through PostgreSQL's replication functionality. Postgres sends database changes to a [publication](/docs/guides/database/replication#publications) +called `supabase_realtime`, and by managing this publication you can control which data is broadcast. + +## API Security + +### Securing your Routes + +Your API is designed to work with Postgres Row Level Security (RLS). If you use Supabase [Auth](/guides/auth), you can restrict data based on the logged-in user. +To control access to your data, you can use [Policies](/guides/auth#policies). +When you create a table in Postgres, Row Level Security is disabled by default. To enable RLS: + + + + + +1. Go to the [Authentication](https://app.supabase.com/project/_/auth/users) page in the Dashboard. +2. Click on **Policies** in the sidebar. +3. Select **Enable RLS** to enable Row Level Security. + + + + +```sql +alter table todos enable row level security; +``` + + + + +### The `service_role` key + +Never expose the `service_role` key in a browser or anywhere where a user can see it. This Key is designed to bypass Row Level Security - so it should only be used on a private server. + +We have [partnered with GitHub](https://github.blog/changelog/2022-03-28-supabase-is-now-a-github-secret-scanning-partner/) to scan for Supabase `service_role` keys pushed to public repositories. +If they detect any keys with service_role privileges being pushed to GitHub, they will forward the API key to us, so that we can automatically revoke the detected secrets and notify you, protecting your data against malicious actors. + +### Safeguards towards accidental deletes and updates + +For all projects, by default, the Postgres extension [safeupdate](https://github.com/eradman/pg-safeupdate) is enabled for all queries coming from the API. +This ensures that any `delete()` or `update()` would fail if there are no accompanying filters provided. +To confirm that safeupdate is enabled for queries going through the API of your project, the following query could be run: + +```sql +select usename,useconfig from pg_shadow where usename = 'authenticator' ; +``` + +The expected value for `useconfig` should be: + +``` +["session_preload_libraries=supautils, safeupdate"] +``` diff --git a/apps/temp-docs/docs/reference/javascript/generating-types.mdx b/apps/temp-docs/docs/guides/api/generating-types.mdx similarity index 52% rename from apps/temp-docs/docs/reference/javascript/generating-types.mdx rename to apps/temp-docs/docs/guides/api/generating-types.mdx index 70f24120dd4..7c1ccdb41f8 100644 --- a/apps/temp-docs/docs/reference/javascript/generating-types.mdx +++ b/apps/temp-docs/docs/guides/api/generating-types.mdx @@ -1,47 +1,12 @@ --- id: generating-types -title: "Generating Types" -slug: generating-types -custom_edit_url: https://github.com/supabase/supabase/edit/master/web/spec/supabase.yml +title: 'Generating Types' +description: How to generate types for your API and Supabase libraries. --- -import Tabs from '@theme/Tabs'; -import TabsPanel from '@theme/TabsPanel'; +Supabase APIs are generated from your database, which means that we can use database introspection to generate type-safe API definitions. -Supabase will soon release native type generators that dump your database types for various languages. For now, we support TypeScript through a third-party tool. - -## Usage with TypeScript - -`supabase-js` ships with type definitions for usage with TypeScript and for convenient IntelliSense auto-complete and documentation in your editor. - -When using TypeScript, you can pass the type of database row as a type parameter to the `from` method to get better auto-completion support down the chain. -If you don't provide a type for the row you need to explicitly pass `from('tableName')`. - -```ts -type Message = { - id: number; - inserted_at: string; - message: string; - user_id: string; - channel_id: number; - author: { username: string }; -} - -const response = await supabase - .from('messages') // Message maps to the type of the row in your database. - .select('*, author:user_id(username)') - .match({ channel_id: 2 }) // Your IDE will be able to help with auto-completion. -response.data // Response data will be of type Array. - -// If you don't provide a type for the row you need to explicitly pass `from('tableName')`. -const response = await supabase - .from('messages') - .select('*, author:user_id(username)') - .match({ channel_id: 2 }) -response.data // Response data will be of type Array. -``` - -### Generate database types from OpenAPI specification +### Generating types from OpenAPI specification Supabase generates an OpenAPI specification file for your database which can be used to generate your data types for usage with TypeScript. @@ -61,40 +26,49 @@ Important notes: - Since the generator uses JSON API, there is no way to determine if a column is an Array. It will generate array types as `string`, even though Supabase handles this automatically and returns arrays. You can fix this manually in the files by changing the type, e.g. `names: string` -> `names: string[]` -- The types won't automatically stay in sync with your database, so make sure to regenerate your types after your make changes to your database. +- The types won't automatically stay in sync with your database, so make sure to regenerate your types after you make changes to your database. After you have generated your types, you can use them in your TypeScript projects: ```ts -import { NextApiRequest, NextApiResponse } from "next"; -import { createClient } from "@supabase/supabase-js"; -import { definitions } from "../../types/supabase"; +import { NextApiRequest, NextApiResponse } from 'next' +import { createClient } from '@supabase/supabase-js' +import { definitions } from '../../types/supabase' const supabase = createClient( process.env.NEXT_PUBLIC_SUPABASE_URL, process.env.SUPABASE_SECRET_KEY -); +) export default async (req: NextApiRequest, res: NextApiResponse) => { const allOnlineUsers = await supabase - .from("users") - .select("*") - .eq("status", "ONLINE"); - res.status(200).json(allOnlineUsers); -}; + .from('users') + .select('*') + .eq('status', 'ONLINE') + res.status(200).json(allOnlineUsers) +} ``` -### Update types automatically with GitHub Actions +For more advance type-support, check out [`postgrest-js-tools`](https://github.com/mzalevski/postgrest-js-tools). + +## Update types automatically with GitHub Actions One way to keep your type definitions in sync with your database is to set up a GitHub action that runs on a schedule. -Add a script to your package.json to generate the types: +The following script can be run in your terminal to produce the file `types/database/index.ts`. ``` -"update-types": "npx openapi-typescript https://your-project.supabase.co/rest/v1/?apikey=your-anon-key --output types/database/index.ts" +npx openapi-typescript https://your-project.supabase.co/rest/v1/?apikey=your-anon-key --output types/database/index.ts ``` -In your repo, create the file `.github/workflows/update-types.yml`. Add the following snippet into this file to define the action. If there is a change to your definitions, this script will commit the change to your repo. +You can add this script to your `package.json` and run it using `npm run update-types`: + +``` +"update-types": "npx openapi-typescript \"${SUPABASE_URL}/rest/v1/?apikey=${SUPABASE_ANON_KEY}\" --version=2 --output types/database/index.ts" +``` + +You can use GitHub actions to generate this file automatically. This script will commit the change to your repo every night. +Create a file `.github/workflows/update-types.yml` and add the following snippet into this file to define the action along with the environment variables. ```yml name: Update database types @@ -107,6 +81,9 @@ on: jobs: update: runs-on: ubuntu-latest + env: + SUPABASE_URL: ${{secrets.SUPABASE_URL}} + SUPABASE_ANON_KEY: ${{secrets.SUPABASE_ANON_KEY}} steps: - uses: actions/checkout@v2 with: @@ -133,4 +110,10 @@ jobs: with: github_token: ${{ secrets.GITHUB_TOKEN }} branch: ${{ github.ref }} -``` \ No newline at end of file +``` + +Alternatively, you can use a community-supported GitHub action: [generate-supabase-db-types-github-action](https://github.com/lyqht/generate-supabase-db-types-github-action). + +## Resources + +- [Generating Supabase types with GitHub Actions](https://blog.esteetey.dev/how-to-create-and-test-a-github-action-that-generates-types-from-supabase-database) diff --git a/apps/temp-docs/docs/guides/apis.mdx b/apps/temp-docs/docs/guides/apis.mdx deleted file mode 100644 index cac8fc84f2c..00000000000 --- a/apps/temp-docs/docs/guides/apis.mdx +++ /dev/null @@ -1,192 +0,0 @@ ---- -title: 'APIs' -description: 'Supabase APIs' ---- - -# APIs - -## Overview - -Supabase generates APIs directly from your database schema. The API is: - -- Instant and auto-generated: as you update your database the changes are immediately accessible through your API. -- Self documenting: Supabase generates documentation in the Dashboard which updates as you make database changes. -- Secure: the API is configured to work with PostgreSQL's Row Level Security, provisioned behind an API gateway with key-auth enabled. -- Fast: our benchmarks for basic reads are more than 300% faster than Firebase. The API is a very thin layer on top of Postgres, which does most of the heavy lifting. -- Scalable: the API can serve thousands of simultaneous requests, and works well for Serverless workloads. - -### RESTful API - -Supabase provides a RESTful API using [PostgREST](https://postgrest.org/). This is a very thin API layer on top of Postgres. It provides everything you need from a CRUD API: - -- Basic CRUD operations -- Deeply nested joins, allowing you to fetch data from multiple tables in a single fetch -- Works with Postgres Views -- Works with Postgres Functions -- Works with the Postgres security model - including Row Level Security, Roles, and Grants. - - - -### Realtime API - -Supabase provides a [Realtime](https://github.com/supabase/realtime) API using 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. - -## Getting started - -After you have added tables or functions to your database, you can use the API. - -### Creating API Routes - -API routes are automatically created when you create Postgres Tables, Views, or Functions. - -Let's create our first API route by creating a table called `todos` (which will store some public user information). This will create a corresponding route `todos` which can accept `GET`, `POST`, `PATCH`, & `DELETE` requests. - - - - - - -### API URL and Keys - -Every Supabase project has a unique API URL. Your API is secured behind an API gateway which requires an API Key for every request. - -You can find the Keys inside the Dashboard. - - -``` -1. Go to the "Settings" section. -2. Click "API" in the sidebar. -3. Find your API URL in this page. -4. Find your "anon" and "service_role" keys on this page. -``` - -**Details and links:** - -You are provided with two keys initially: - -- an `anon` key, which is safe to be used in a browser context. -- a `service_role` key, which should only be used on a server. This key can bypass Row Level Security. - -### Accessing the Docs - -Supabase generates documentation in the Dashboard which updates as you make database changes. - -Let's view the documentation for the todos table which we created in the first step. - - - -``` -1. Go to the "API" section. -2. Find "todos" in the "Tables and Views" section. -3. Switch between the Javascript and the cURL docs using the tabs. -``` - -### Using the API - -You can interact with your API directly via HTTP requests, or you can use the client libraries which we provide. - -Let's see how to make a request to the `todos` table which we created in the first step, using the API URL (`[SUPABASE_URL]`) and Key (`[SUPABASE_ANON_KEY]`) we provided: - - - - -**Details and links:** - -- When you create a table in Postgres, Row Level Security is disabled by default. Make sure you secure it by [enabling RLS](https://supabase.com/docs/guides/api#securing-your-routes). -- Never expose the service_role key in a browser or anywhere where a user can see it. -- JS Reference: [select()](https://supabase.com/docs/reference/javascript/select), [insert()](https://supabase.com/docs/reference/javascript/insert), [update()](https://supabase.com/docs/reference/javascript/update), [upsert()](https://supabase.com/docs/reference/javascript/upsert), [delete()](https://supabase.com/docs/reference/javascript/delete), [rpc()](https://supabase.com/docs/reference/javascript/rpc) (call Postgres functions). - -### Managing Realtime -The Realtime API works through PostgreSQL's replication functionality. Postgres sends database changes to a "publication" called `supabase_realtime`, and by managing this publication you can control which data is broadcast. - -By default Realtime is disabled on your database. Let's turn on Realtime for the `todos` table. - - - - -**Details and links:** - -- You should only turn on realtime for Public tables (where all data should be accessible). -- [JS Reference](https://supabase.com/docs/reference/javascript/subscribe): Subscribe to database changes using the realtime client - -### Securing your Routes - - - -**Details and links:** -- Your API is designed to work with Postgres Row Level Security. If you use Supabase [Auth](https://supabase.com/docs/guides/auth), you can restrict data based on the logged-in user. -- To control access to your data, you can use [Policies](https://supabase.com/docs/guides/auth#policies). - - - - diff --git a/apps/temp-docs/docs/guides/auth.mdx b/apps/temp-docs/docs/guides/auth.mdx index eb664dfc758..d5810112520 100644 --- a/apps/temp-docs/docs/guides/auth.mdx +++ b/apps/temp-docs/docs/guides/auth.mdx @@ -1,61 +1,141 @@ --- -title: 'Auth' -description: 'Supabase auth' +id: auth +title: Auth +description: Use Supabase to Authenticate and Authorize your users. +sidebar_label: Overview --- -# Auth +import Link from '@docusaurus/Link' +import Tabs from '@theme/Tabs' +import TabItem from '@theme/TabItem' +import providers from '../../data/authProviders' +import ButtonCard from '@site/src/components/ButtonCard' +import useBaseUrl from '@docusaurus/useBaseUrl' - +## Overview -## User Management +There are two parts to every Auth system: -Supabase makes it simple to manage your users. +- **Authentication:** should this person be allowed in? If yes, who are they? +- **Authorization:** once they are in, what are they allowed to do? - +Supabase Auth is designed to work either as a standalone product, or deeply integrated with the other Supabase products. +Postgres is at the heart of everything we do, and the Auth system follows this principle. We leverage Postgres' built-in Auth functionality wherever possible. -When users sign up, Supabase assigns them a unique ID. You can reference this ID anywhere in your database. For example, you might create a '`profiles`' table referencing id in the `auth.users` table using a `user_id` field. +Here's a quick, 2 minute tour of the Auth features built-in to Supabase: -Supabase provides the routes to [sign up](https://supabase.com/docs/reference/javascript/auth-signup), [log in](https://supabase.com/docs/reference/javascript/auth-signin), [log out](https://supabase.com/docs/reference/javascript/auth-signout), and manage users in your apps and websites. +
+ +
-## Third Party Logins +## Authentication -We currently support the following OAuth providers: +You can authenticate your users in several ways: - +- Email & password. +- Magic links (one-click logins). +- Social providers. +- Phone logins. -You can enable providers by navigating to Authentication > Settings > External OAuth Providers and inputting your `Client ID` and `Secret` for each. +### Providers - +We provide a suite of Providers and login methods, as well as [Auth helpers](/docs/guides/auth/auth-helpers/). -## Row Level Security +
+
+ {providers.map((x) => ( +
+ +
+
+ {x.logo && {x.name}} +

{x.name}

+

+ {x.official ? ( + Official + ) : ( + Unofficial + )} +

+
+
+
+ Platform: + {x.platform.toString()} +
+
+ Self-Hosted: + {x.selfHosted.toString()} +
+
+
+
+
+ ))} +
+
+ +### Configure third-party providers + +You can enable third-party providers with the click of a button by navigating to Authentication > Settings > Auth Providers and inputting your `Client ID` and `Secret` for each. + +![OAuth Logins.](/img/supabase-oauth-logins.png) + +## Authorization + +When you need granular authorization rules, nothing beats PostgreSQL's Row Level Security (RLS). + +Policies are PostgreSQL's rule engine. They are incredibly powerful and flexible, allowing you to write complex SQL rules which fit your unique business needs. + +Get started with our [Row Level Security Guides](/docs/guides/auth/row-level-security). + +### Row Level Security Authentication only gets you so far. When you need granular authorization rules, nothing beats PostgreSQL's [Row Level Security (RLS)](https://www.postgresql.org/docs/current/ddl-rowsecurity.html). Supabase makes it simple to turn RLS on and off. -
- `, + templateUrl: './account.component.html', + styleUrls: ['./account.component.css'], }) export class AccountComponent implements OnInit { loading = false - profile: Profile | undefined + profile!: Profile - @Input() session: Session | undefined + @Input() + session!: AuthSession - constructor(private readonly supabase: SupabaseService) {} + updateProfileForm = this.formBuilder.group({ + username: '', + website: '', + avatar_url: '', + }) - ngOnInit() { - this.getProfile() + constructor( + private readonly supabase: SupabaseService, + private formBuilder: FormBuilder + ) {} + + async ngOnInit(): Promise { + await this.getProfile() + + const { username, website, avatar_url } = this.profile + this.updateProfileForm.patchValue({ + username, + website, + avatar_url, + }) } async getProfile() { try { this.loading = true - let { data: profile, error, status } = await this.supabase.profile + const { user } = this.session + let { data: profile, error, status } = await this.supabase.profile(user) if (error && status !== 406) { throw error @@ -367,18 +275,34 @@ export class AccountComponent implements OnInit { this.profile = profile } } catch (error) { - alert(error.message) + if (error instanceof Error) { + alert(error.message) + } } finally { this.loading = false } } - async updateProfile(username: string, website: string, avatar_url: string = '') { + async updateProfile(): Promise { try { this.loading = true - await this.supabase.updateProfile({ username, website, avatar_url }) + const { user } = this.session + + const username = this.updateProfileForm.value.username as string + const website = this.updateProfileForm.value.website as string + const avatar_url = this.updateProfileForm.value.avatar_url as string + + const { error } = await this.supabase.updateProfile({ + id: user.id, + username, + website, + avatar_url, + }) + if (error) throw error } catch (error) { - alert(error.message) + if (error instanceof Error) { + alert(error.message) + } } finally { this.loading = false } @@ -390,6 +314,37 @@ export class AccountComponent implements OnInit { } ``` +```html title="src/app/account/account.component.html" +
+
+ + +
+
+ + +
+
+ + +
+ +
+ +
+ +
+ +
+
+``` + ### Launch! Now that we have all the components in place, let's update **AppComponent**: @@ -400,16 +355,12 @@ import { SupabaseService } from './supabase.service' @Component({ selector: 'app-root', - template: ` -
- - - - -
- `, + templateUrl: './app.component.html', + styleUrls: ['./app.component.css'], }) export class AppComponent implements OnInit { + title = 'angular-user-management' + session = this.supabase.session constructor(private readonly supabase: SupabaseService) {} @@ -420,6 +371,41 @@ export class AppComponent implements OnInit { } ``` +```html title="src/app/app.component.html" +
+ + + + +
+``` + +`app.module.ts` also needs to be modified to include the `ReactiveFormsModule` from the `@angular/forms` package. + +```ts title="src/app/app.module.ts" +import { NgModule } from '@angular/core' +import { BrowserModule } from '@angular/platform-browser' + +import { AppComponent } from './app.component' +import { AuthComponent } from './auth/auth.component' +import { AccountComponent } from './account/account.component' +import { ReactiveFormsModule } from '@angular/forms' +import { AvatarComponent } from './avatar/avatar.component' + +@NgModule({ + declarations: [ + AppComponent, + AuthComponent, + AccountComponent, + AvatarComponent, + ], + imports: [BrowserModule, ReactiveFormsModule], + providers: [], + bootstrap: [AppComponent], +}) +export class AppModule {} +``` + Once that's done, run this in a terminal window: ```bash @@ -428,56 +414,33 @@ npm run start And then open the browser to [localhost:4200](http://localhost:4200) and you should see the completed app. -![Supabase Angular](/docs/img/supabase-angular-demo.png) +![Supabase Angular](/img/supabase-angular-demo.png) ## Bonus: Profile photos -Every Supabase project is configured with [Storage](/guides/storage) for managing large files like photos and videos. +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. Create an **AvatarComponent** with `ng g c avatar` Angular CLI command. -```ts title="src/app/avatar.component.ts" -import { Component, EventEmitter, Input, Output } from '@angular/core' -import { SupabaseService } from './supabase.service' -import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser' +```ts title="src/app/avatar/avatar.component.ts" +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core' +import { SafeResourceUrl, DomSanitizer } from '@angular/platform-browser' +import { SupabaseService } from '../supabase.service' @Component({ selector: 'app-avatar', - template: ` -
- Avatar -
-
-
- - -
- `, + templateUrl: './avatar.component.html', + styleUrls: ['./avatar.component.css'], }) -export class AvatarComponent { +export class AvatarComponent implements OnInit { _avatarUrl: SafeResourceUrl | undefined uploading = false @Input() - set avatarUrl(url: string | undefined) { + set avatarUrl(url: string | null) { if (url) { this.downloadImage(url) } @@ -485,14 +448,25 @@ export class AvatarComponent { @Output() upload = new EventEmitter() - constructor(private readonly supabase: SupabaseService, private readonly dom: DomSanitizer) {} + constructor( + private readonly supabase: SupabaseService, + private readonly dom: DomSanitizer + ) {} + + ngOnInit(): void {} async downloadImage(path: string) { try { const { data } = await this.supabase.downLoadImage(path) - this._avatarUrl = this.dom.bypassSecurityTrustResourceUrl(URL.createObjectURL(data)) + if (data instanceof Blob) { + this._avatarUrl = this.dom.bypassSecurityTrustResourceUrl( + URL.createObjectURL(data) + ) + } } catch (error) { - console.error('Error downloading image: ', error.message) + if (error instanceof Error) { + console.error('Error downloading image: ', error.message) + } } } @@ -505,13 +479,14 @@ export class AvatarComponent { const file = event.target.files[0] const fileExt = file.name.split('.').pop() - const fileName = `${Math.random()}.${fileExt}` - const filePath = `${fileName}` + const filePath = `${Math.random()}.${fileExt}` await this.supabase.uploadAvatar(filePath, file) this.upload.emit(filePath) } catch (error) { - alert(error.message) + if (error instanceof Error) { + alert(error.message) + } } finally { this.uploading = false } @@ -519,19 +494,74 @@ export class AvatarComponent { } ``` +```html title="src/app/avatar/avatar.component.html" +
+ Avatar +
+
+
+ + +
+``` + ### Add the new widget And then we can add the widget on top of the **AccountComponent** html template: -```ts title="src/app/account.component.ts" -template: ` - - +```html title="src/app/account.component.html" +
+ + + +
+``` - -` +And add an `updateAvatar` function along with an `avatarUrl` getter to the **AccountComponent** typescript file: + +```ts title="src/app/account.component.ts" +@Component({ + selector: 'app-account', + templateUrl: './account.component.html', + styleUrls: ['./account.component.css'], +}) +export class AccountComponent implements OnInit { + // ... + get avatarUrl() { + return this.updateProfileForm.value.avatar_url as string + } + + async updateAvatar(event: string): Promise { + this.updateProfileForm.patchValue({ + avatar_url: event, + }) + await this.updateProfile() + } + // ... +} ``` ## Next steps diff --git a/apps/temp-docs/docs/guides/with-expo.mdx b/apps/temp-docs/docs/guides/with-expo.mdx new file mode 100644 index 00000000000..598fbf8f03d --- /dev/null +++ b/apps/temp-docs/docs/guides/with-expo.mdx @@ -0,0 +1,542 @@ +--- +id: with-expo +title: 'Quickstart: Expo' +description: Learn how to use Supabase in your React Native App. +sidebar_label: Expo +--- + +import Tabs from '@theme/Tabs' +import TabItem from '@theme/TabItem' +import QuickstartIntro from './_partials/quickstart_intro.md' +import ProjectSetup from './_partials/project_setup.mdx' + + + +![Supabase User Management example](/img/supabase-flutter-demo.png) + +### GitHub + +Should you get stuck while working through the guide, refer to [this repo](https://github.com/supabase/supabase/tree/master/examples/user-management/expo-user-management). + + + +## Building the App + +Let's start building the React Native app from scratch. + +### Initialize a React Native app + +We can use [`expo`](https://docs.expo.dev/get-started/create-a-new-app/) to initialize +an app called `expo-user-management`: + +```bash +npx create-expo-app -t expo-template-blank-typescript expo-user-management + +cd expo-user-management +``` + +Then let's install the additional dependencies: [supabase-js](https://github.com/supabase/supabase-js) + +```bash +npm install @supabase/supabase-js +npm install react-native-elements @react-native-async-storage/async-storage react-native-url-polyfill +``` + +Now let's create a helper file to initialize the Supabase client. +We need the API URL and the `anon` key that you copied [earlier](#get-the-api-keys). +These variables will be exposed on the browser, and that's completely fine since we have +[Row Level Security](/docs/guides/auth#row-level-security) enabled on our Database. + +```ts title="lib/supabase.ts" +import AsyncStorage from '@react-native-async-storage/async-storage' +import { createClient } from '@supabase/supabase-js' + +const supabaseUrl = YOUR_REACT_NATIVE_SUPABASE_URL +const supabaseAnonKey = YOUR_REACT_NATIVE_SUPABASE_ANON_KEY + +export const supabase = createClient(supabaseUrl, supabaseAnonKey, { + auth: { + storage: AsyncStorage as any, + autoRefreshToken: true, + persistSession: true, + detectSessionInUrl: false, + }, +}) +``` + +### Set up a Login component + +Let's set up a React Native component to manage logins and sign ups. +Users would be able to sign in with their email and password. + +```tsx title="components/Auth.tsx" +import React, { useState } from 'react' +import { Alert, StyleSheet, View } from 'react-native' +import { supabase } from '../lib/supabase' +import { Button, Input } from 'react-native-elements' + +export default function Auth() { + const [email, setEmail] = useState('') + const [password, setPassword] = useState('') + const [loading, setLoading] = useState(false) + + async function signInWithEmail() { + setLoading(true) + const { error } = await supabase.auth.signInWithPassword({ + email: email, + password: password, + }) + + if (error) Alert.alert(error.message) + setLoading(false) + } + + async function signUpWithEmail() { + setLoading(true) + const { error } = await supabase.auth.signUp({ + email: email, + password: password, + }) + + if (error) Alert.alert(error.message) + setLoading(false) + } + + return ( + + + setEmail(text)} + value={email} + placeholder="email@address.com" + autoCapitalize={'none'} + /> + + + setPassword(text)} + value={password} + secureTextEntry={true} + placeholder="Password" + autoCapitalize={'none'} + /> + + + -
- + + + + ) +} +export default MyApp +``` + + + + +Wrap your `pages/_app.tsx` component with the `SessionContextProvider` component: + +```jsx title="pages/_app.tsx" +import { createBrowserSupabaseClient } from '@supabase/auth-helpers-nextjs' +// highlight-next-line +import { SessionContextProvider, Session } from '@supabase/auth-helpers-react' + +function MyApp({ + Component, + pageProps, +}: AppProps<{ + // highlight-next-line + initialSession: Session, +}>) { + const [supabaseClient] = useState(() => createBrowserSupabaseClient()) + + return ( + + + + ) +} +export default MyApp +``` + +See the [Auth Helpers docs](/docs/guides/auth/auth-helpers/nextjs#usage-with-typescript) for more details on usage with TypeScript. + + + + +#### Supabase Auth UI + +We can use the [Supabase Auth UI](https://supabase.com/docs/guides/auth/auth-helpers/auth-ui) a pre-built React component for authenticating users via OAuth, email, and magic links. + +Install the Supabase Auth UI for React + +```bash +npm install @supabase/auth-ui-react +``` + +Add the `Auth` component to your home page + +```jsx title="pages/index.js" +import { Auth, ThemeSupa } from '@supabase/auth-ui-react' +import { useSession, useSupabaseClient } from '@supabase/auth-helpers-react' + +const Home = () => { + const session = useSession() + const supabase = useSupabaseClient() + + return ( +
+ {!session ? ( + + ) : ( +

Account page will go here.

+ )}
) } + +export default Home ``` ### Account page After a user is signed in we can allow them to edit their profile details and manage their account. -Let's create a new component for that called `Account.js`. + + -```jsx title="components/Account.js" +Let's create a new component for that called `Account.js` within a `components` folder. + +```tsx title="components/Account.js" import { useState, useEffect } from 'react' -import { supabase } from '../utils/supabaseClient' +import { useUser, useSupabaseClient } from '@supabase/auth-helpers-react' export default function Account({ session }) { + const supabase = useSupabaseClient() + const user = useUser() const [loading, setLoading] = useState(true) const [username, setUsername] = useState(null) const [website, setWebsite] = useState(null) @@ -276,7 +235,6 @@ export default function Account({ session }) { async function getProfile() { try { setLoading(true) - const user = supabase.auth.user() let { data, error, status } = await supabase .from('profiles') @@ -294,7 +252,8 @@ export default function Account({ session }) { setAvatarUrl(data.avatar_url) } } catch (error) { - alert(error.message) + alert('Error loading user data!') + console.log(error) } finally { setLoading(false) } @@ -303,25 +262,21 @@ export default function Account({ session }) { async function updateProfile({ username, website, avatar_url }) { try { setLoading(true) - const user = supabase.auth.user() const updates = { id: user.id, username, website, avatar_url, - updated_at: new Date(), + updated_at: new Date().toISOString(), } - let { error } = await supabase.from('profiles').upsert(updates, { - returning: 'minimal', // Don't return the value after inserting - }) - - if (error) { - throw error - } + let { error } = await supabase.from('profiles').upsert(updates) + if (error) throw error + alert('Profile updated!') } catch (error) { - alert(error.message) + alert('Error updating the data!') + console.log(error) } finally { setLoading(false) } @@ -334,7 +289,7 @@ export default function Account({ session }) {
- +
-
@@ -372,33 +330,176 @@ export default function Account({ session }) { } ``` + + + +Let's create a new component for that called `Account.tsx` within a `components` folder. + +```tsx title="components/Account.tsx" +import { useState, useEffect } from 'react' +import { + useUser, + useSupabaseClient, + Session, +} from '@supabase/auth-helpers-react' +import { Database } from '../utils/database.types' +type Profiles = Database['public']['Tables']['profiles']['Row'] + +export default function Account({ session }: { session: Session }) { + const supabase = useSupabaseClient() + const user = useUser() + const [loading, setLoading] = useState(true) + const [username, setUsername] = useState(null) + const [website, setWebsite] = useState(null) + const [avatar_url, setAvatarUrl] = useState(null) + + useEffect(() => { + getProfile() + }, [session]) + + async function getProfile() { + try { + setLoading(true) + if (!user) throw new Error('No user') + + let { data, error, status } = await supabase + .from('profiles') + .select(`username, website, avatar_url`) + .eq('id', user.id) + .single() + + if (error && status !== 406) { + throw error + } + + if (data) { + setUsername(data.username) + setWebsite(data.website) + setAvatarUrl(data.avatar_url) + } + } catch (error) { + alert('Error loading user data!') + console.log(error) + } finally { + setLoading(false) + } + } + + async function updateProfile({ + username, + website, + avatar_url, + }: { + username: Profiles['username'] + website: Profiles['website'] + avatar_url: Profiles['avatar_url'] + }) { + try { + setLoading(true) + if (!user) throw new Error('No user') + + const updates = { + id: user.id, + username, + website, + avatar_url, + updated_at: new Date().toISOString(), + } + + let { error } = await supabase.from('profiles').upsert(updates) + if (error) throw error + alert('Profile updated!') + } catch (error) { + alert('Error updating the data!') + console.log(error) + } finally { + setLoading(false) + } + } + + return ( +
+
+ + +
+
+ + setUsername(e.target.value)} + /> +
+
+ + setWebsite(e.target.value)} + /> +
+ +
+ +
+ +
+ +
+
+ ) +} +``` + +
+ + ### Launch! Now that we have all the components in place, let's update `pages/index.js`: ```jsx title="pages/index.js" -import { useState, useEffect } from 'react' -import { supabase } from '../utils/supabaseClient' -import Auth from '../components/Auth' +import type { NextPage } from 'next' +import { Auth, ThemeSupa } from '@supabase/auth-ui-react' +import { useSession, useSupabaseClient } from '@supabase/auth-helpers-react' +// highlight-next-line import Account from '../components/Account' -export default function Home() { - const [session, setSession] = useState(null) - - useEffect(() => { - setSession(supabase.auth.session()) - - supabase.auth.onAuthStateChange((_event, session) => { - setSession(session) - }) - }, []) +const Home: NextPage = () => { + const session = useSession() + const supabase = useSupabaseClient() return (
- {!session ? : } + {!session ? ( + + ) : ( + // highlight-next-line + + )}
) } + +export default Home ``` Once that's done, run this in a terminal window: @@ -409,8 +510,6 @@ npm run dev And then open the browser to [localhost:3000](http://localhost:3000) and you should see the completed app. -![Supabase User demo](/docs/img/supabase-react-demo.png) - ## Bonus: Profile photos Every Supabase project is configured with [Storage](/docs/guides/storage) for managing large files like @@ -418,14 +517,23 @@ 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: +Let's create an avatar widget for the user so that they can upload a profile photo. We can start by creating a new component: + + + ```jsx title="components/Avatar.js" -import { useEffect, useState } from 'react' -import { supabase } from '../utils/supabaseClient' +import React, { useEffect, useState } from 'react' +import { useSupabaseClient } from '@supabase/auth-helpers-react' -export default function Avatar({ url, size, onUpload }) { +export default function Avatar({ uid, url, size, onUpload }) { + const supabase = useSupabaseClient() const [avatarUrl, setAvatarUrl] = useState(null) const [uploading, setUploading] = useState(false) @@ -435,18 +543,20 @@ export default function Avatar({ url, size, onUpload }) { async function downloadImage(path) { try { - const { data, error } = await supabase.storage.from('avatars').download(path) + const { data, error } = await supabase.storage + .from('avatars') + .download(path) if (error) { throw error } const url = URL.createObjectURL(data) setAvatarUrl(url) } catch (error) { - console.log('Error downloading image: ', error.message) + console.log('Error downloading image: ', error) } } - async function uploadAvatar(event) { + const uploadAvatar = async (event) => { try { setUploading(true) @@ -456,10 +566,12 @@ export default function Avatar({ url, size, onUpload }) { const file = event.target.files[0] const fileExt = file.name.split('.').pop() - const fileName = `${Math.random()}.${fileExt}` + const fileName = `${uid}.${fileExt}` const filePath = `${fileName}` - let { error: uploadError } = await supabase.storage.from('avatars').upload(filePath, file) + let { error: uploadError } = await supabase.storage + .from('avatars') + .upload(filePath, file, { upsert: true }) if (uploadError) { throw uploadError @@ -467,7 +579,8 @@ export default function Avatar({ url, size, onUpload }) { onUpload(filePath) } catch (error) { - alert(error.message) + alert('Error uploading avatar!') + console.log(error) } finally { setUploading(false) } @@ -483,7 +596,10 @@ export default function Avatar({ url, size, onUpload }) { style={{ height: size, width: size }} /> ) : ( -
+
)}