Merge pull request #11754 from supabase/master

Prod Deploy Dashboard
This commit is contained in:
Joshen Lim authored and GitHub committed 2023-01-24 14:51:39 +08:00
commit 6c1a5e16c2
147 files changed
+8676 -1133

No files matched your search

+2 -1
View File
@@ -4,4 +4,5 @@ node_modules/
.env.local
.env.production
.env.dev
.env.*
.env.*
.git
+1 -1
View File
@@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2022 Supabase
Copyright 2023 Supabase
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+7 -12
View File
@@ -1,7 +1,7 @@
import React, { useState } from 'react'
import KJUR from 'jsrsasign'
import CodeBlock from './CodeBlock/CodeBlock'
import { Button } from 'ui'
import { Button, Select, Input } from 'ui'
const JWT_HEADER = { alg: 'HS256', typ: 'JWT' }
const now = new Date()
@@ -46,9 +46,8 @@ export default function JwtGenerator({}) {
<div>
<div className="grid mb-8">
<label for="secret">JWT Secret:</label>
<input
<Input
id="secret"
className="border rounded bg-gray-200 w-full"
type="text"
placeholder="JWT Secret (at least 32 characters)"
value={jwtSecret}
@@ -57,19 +56,15 @@ export default function JwtGenerator({}) {
</div>
<div className="grid mb-8">
<label for="service">Preconfigured Payload:</label>
<select
id="service"
onChange={handleKeySelection}
className="border rounded bg-gray-200 w-full"
>
<option value="anon">ANON_KEY</option>
<option value="service">SERVICE_KEY</option>
</select>
<Select id="service" onChange={handleKeySelection}>
<Select.Option value="anon">ANON_KEY</Select.Option>
<Select.Option value="service">SERVICE_KEY</Select.Option>
</Select>
</div>
<div className="grid mb-8">
<label for="token">Payload:</label>
<textarea
<Input.TextArea
id="token"
type="text"
rows="5"
@@ -264,6 +264,7 @@ export const menuItems: NavMenu = {
{ name: 'Compute Add-ons', url: '/guides/platform/compute-add-ons', items: [] },
{ name: 'Custom Domains', url: '/guides/platform/custom-domains', items: [] },
{ name: 'Database Usage', url: '/guides/platform/database-usage', items: [] },
{ name: 'HTTP Status Codes', url: '/guides/platform/http-status-codes', items: [] },
{ name: 'Logging', url: '/guides/platform/logs', items: [] },
{ name: 'Metrics', url: '/guides/platform/metrics', items: [] },
{
@@ -275,6 +276,7 @@ export const menuItems: NavMenu = {
{ name: 'Performance Tuning', url: '/guides/platform/performance', items: [] },
{ name: 'Permissions', url: '/guides/platform/permissions', items: [] },
{ name: 'Production Readiness', url: '/guides/platform/going-into-prod', items: [] },
{ name: 'Troubleshooting', url: '/guides/platform/troubleshooting', items: [] },
],
},
{
@@ -339,6 +341,7 @@ export const menuItems: NavMenu = {
{ name: 'Draftbit', url: '/guides/integrations/draftbit', items: [] },
{ name: 'Plasmic', url: '/guides/integrations/plasmic', items: [] },
{ name: 'WeWeb', url: '/guides/integrations/weweb', items: [] },
{ name: 'ILLA', url: '/guides/integrations/illa', items: [] },
],
},
],
@@ -94,6 +94,12 @@ const home = [
href: '/reference/dart/introduction',
level: 'reference_dart',
},
{
label: 'Python',
icon: '/img/icons/menu/reference-python',
href: '/reference/python/introduction',
level: 'reference_python',
},
{
label: 'Tools Reference',
},
@@ -48,6 +48,7 @@ export const gettingstarted = {
{ name: 'ReactJS', url: '/guides/getting-started/quickstarts/reactjs', items: [] },
{ name: 'NextJS', url: '/guides/getting-started/quickstarts/nextjs', items: [] },
{ name: 'Flutter', url: '/guides/getting-started/quickstarts/flutter', items: [] },
{ name: 'SvelteKit', url: '/guides/getting-started/quickstarts/sveltekit', items: [] },
],
},
{
@@ -320,6 +321,11 @@ export const auth = {
{ 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: 'Next.js Server Components',
url: '/guides/auth/auth-helpers/nextjs-server-components',
items: [],
},
{ name: 'Remix', url: '/guides/auth/auth-helpers/remix', items: [] },
{ name: 'SvelteKit', url: '/guides/auth/auth-helpers/sveltekit', items: [] },
],
@@ -500,6 +506,7 @@ export const platform = {
items: [
{ name: 'Access Control', url: '/guides/platform/access-control', items: [] },
{ name: 'Database Usage', url: '/guides/platform/database-usage', items: [] },
{ name: 'HTTP Status Codes', url: '/guides/platform/http-status-codes', items: [] },
{ name: 'Logging', url: '/guides/platform/logs', items: [] },
{ name: 'Metrics', url: '/guides/platform/metrics', items: [] },
{
@@ -535,6 +542,17 @@ export const platform = {
url: undefined,
items: [{ name: 'Production Readiness', url: '/guides/platform/going-into-prod', items: [] }],
},
{
name: 'Troubleshooting',
url: undefined,
items: [
{
name: 'HTTP and Project Issues',
url: '/guides/platform/troubleshooting',
items: [],
},
],
},
],
}
@@ -688,6 +706,7 @@ export const integrations = {
{ name: 'Directus', url: '/guides/integrations/directus', items: [] },
{ name: 'Draftbit', url: '/guides/integrations/draftbit', items: [] },
{ name: 'Plasmic', url: '/guides/integrations/plasmic', items: [] },
{ name: 'ILLA', url: '/guides/integrations/illa', items: [] },
],
},
{
@@ -775,6 +794,13 @@ export const reference_dart_v1 = {
parent: '/reference',
}
export const reference_python_v2 = {
icon: 'reference-python',
title: 'Python',
url: '/guides/reference/python',
parent: '/reference',
}
export const reference_cli = {
icon: 'reference-cli',
title: 'Supabase CLI',
@@ -11,6 +11,9 @@ import spec_js_v1 from '~/../../spec/supabase_js_v1.yml' assert { type: 'yml' }
import spec_dart_v1 from '~/../../spec/supabase_dart_v1.yml' assert { type: 'yml' }
// @ts-expect-error
import spec_dart_v0 from '~/../../spec/supabase_dart_v0.yml' assert { type: 'yml' }
// @ts-expect-error
import spec_python_v2 from '~/../../spec/supabase_py_v2.yml' assert { type: 'yml' }
// import { gen_v3 } from '~/lib/refGenerator/helpers'
import apiCommonSections from '~/../../spec/common-api-sections.json'
import cliCommonSections from '~/../../spec/common-cli-sections.json'
@@ -47,6 +50,7 @@ export type RefIdOptions =
| 'reference_javascript_v2'
| 'reference_dart_v0'
| 'reference_dart_v1'
| 'reference_python_v2'
| 'reference_cli'
| 'reference_api'
| 'reference_self_hosting_auth'
@@ -56,6 +60,7 @@ export type RefIdOptions =
export type RefKeyOptions =
| 'javascript'
| 'dart'
| 'python'
| 'cli'
| 'api'
| 'self-hosting-auth'
@@ -65,16 +70,6 @@ export type RefKeyOptions =
const NavigationMenu = () => {
const router = useRouter()
let version = ''
if (router.asPath.includes('v1')) {
version = '_v1'
}
if (router.asPath.includes('v0')) {
version = '_v0'
}
function handleRouteChange(url: string) {
switch (url) {
case `/docs`:
@@ -126,6 +121,10 @@ const NavigationMenu = () => {
case url.includes(`/docs/reference/dart`) && url:
menuState.setMenuLevelId('reference_dart_v1')
break
// puthon v2 (latest)
case url.includes(`/docs/reference/python`) && url:
menuState.setMenuLevelId('reference_python_v2')
break
case url.includes(`/docs/reference/cli`) && url:
menuState.setMenuLevelId('reference_cli')
break
@@ -175,6 +174,7 @@ const NavigationMenu = () => {
const isReference_Javascript_V2 = 'reference_javascript_v2' === level
const isReference_Dart_V0 = 'reference_dart_v0' === level
const isReference_Dart_V1 = 'reference_dart_v1' === level
const isReference_Python_V2 = 'reference_python_v2' === level
const isReference_Cli = 'reference_cli' === level
const isReference_Api = 'reference_api' === level
const isReference_Self_Hosting_Auth = 'reference_self_hosting_auth' === level
@@ -229,6 +229,14 @@ const NavigationMenu = () => {
lib="dart"
spec={spec_dart_v1}
/>
<NavigationMenuRefList
key={'reference-python-menu-v2'}
id={'reference_python_v2'}
active={isReference_Python_V2}
commonSections={libCommonSections}
lib="python"
spec={spec_python_v2}
/>
{/* // Tools */}
<NavigationMenuRefList
key={'reference-cli-menu'}
@@ -25,6 +25,10 @@ const NavigationMenuRefList: React.FC<INavigationMenuRefList> = ({
active,
spec,
}) => {
const filteredSections = commonSections.filter((section) => {
return !section.excludes?.includes(id)
})
return (
<div
className={[
@@ -38,7 +42,7 @@ const NavigationMenuRefList: React.FC<INavigationMenuRefList> = ({
!active ? 'opacity-0 invisible absolute h-0 overflow-hidden' : '',
].join(' ')}
>
<NavigationMenuRefListItems id={id} lib={lib} commonSections={commonSections} spec={spec} />
<NavigationMenuRefListItems id={id} lib={lib} commonSections={filteredSections} spec={spec} />
</div>
)
}
@@ -179,7 +179,7 @@ const Content: React.FC<INavigationMenuRefList> = ({ id, lib, commonSections, sp
if (!sections) console.error('no common sections imported')
const menu = NavItems[id]
console.log(menu)
const databaseFunctions = find(sections, { title: 'Database' })
? find(sections, { title: 'Database' }).items
: []
@@ -1,25 +1,12 @@
import { IconChevronRight } from '~/../../packages/ui'
import ReactMarkdown from 'react-markdown'
import { IconChevronRight, Tabs } from '~/../../packages/ui'
// @ts-expect-error
import spec from '~/../../spec/cli_v1_commands.yaml' assert { type: 'yml' }
import CodeBlock from '~/components/CodeBlock/CodeBlock'
import Options from '~/components/Options'
import Param from '~/components/Params'
import RefSubLayout from '~/layouts/ref/RefSubLayout'
interface ICommonFunc {
id: string
title: string
slug: string
product: string
libs: string
items: ICommonFunc[]
}
interface IRefFunctionSection {
funcData: ICommonFunc
commonFuncData: ICommonFunc
spec: any
}
import RefDetailCollapse from './RefDetailCollapse'
export type Flag = {
id: string
@@ -37,16 +24,25 @@ export type AcceptedValue = {
description?: string
}
export type Example = {
id: string
name: string
code: string
response: string
description?: string
}
export type Command = {
id: string
title: string
description: string
flags?: Flag[]
summary: string
tags?: []
links?: []
subcommands?: []
tags?: string[]
links?: string[]
subcommands?: string[]
usage?: string
examples?: Example[]
}
const CliCommandSection = (props) => {
@@ -70,17 +66,17 @@ const CliCommandSection = (props) => {
' mb-16',
].join(' ')}
>
<p className="capitalize mb-4 scroll-mt-16 mt-0 text-scale-1100 text-base">
{command.summary}
</p>
{command.description ? (
<div className="prose">
<ReactMarkdown>{command.description}</ReactMarkdown>
</div>
) : (
<p className="capitalize mb-4 scroll-mt-16 mt-0 text-scale-1100 text-base">
{command.summary}
</p>
)}
</header>
{/* {command.usage && (
<CodeBlock language="bash" className="relative">
{command.usage}
</CodeBlock>
)} */}
{command.subcommands.length > 0 && (
<div className="mb-3">
<h3 className="text-lg text-scale-1200 mb-3">Available Commands</h3>
@@ -126,13 +122,82 @@ const CliCommandSection = (props) => {
</div>
</div>
</RefSubLayout.Details>
<RefSubLayout.Examples>
{command.usage && (
<CodeBlock language="bash" className="relative">
{command.usage}
</CodeBlock>
)}
</RefSubLayout.Examples>
{(command.examples || command.usage) && (
<RefSubLayout.Examples>
<div className="overflow-hidden w-full">
<Tabs
defaultActiveId={`${command.id}-basic-usage`}
size="tiny"
type="rounded-pills"
scrollable
>
{command.examples ? (
command.examples.map((example) => {
const exampleId = `${command.id}-${example.id}`
return (
<Tabs.Panel
id={exampleId}
key={exampleId}
label={example.name}
className="flex flex-col gap-3"
>
<CodeBlock
className="useless-code-block-class"
language="bash"
hideLineNumbers={true}
>
{example.code}
</CodeBlock>
<RefDetailCollapse
id={`${exampleId}-response`}
label="Response"
defaultOpen={false}
>
<CodeBlock
className="useless-code-block-class rounded !rounded-tl-none !rounded-tr-none border border-scale-500"
language="bash"
hideLineNumbers={true}
>
{example.response}
</CodeBlock>
</RefDetailCollapse>
{example.description && (
<RefDetailCollapse
id={`${exampleId}-notes`}
label="Notes"
defaultOpen={false}
>
<div className="bg-scale-300 border border-scale-500 rounded !rounded-tl-none !rounded-tr-none prose max-w-none px-5 py-2">
<ReactMarkdown className="text-sm">{example.description}</ReactMarkdown>
</div>
</RefDetailCollapse>
)}
</Tabs.Panel>
)
})
) : (
// TODO: remove this block once all commands have examples
<Tabs.Panel
id={`${command.id}-basic-usage`}
key={`${command.id}-basic-usage`}
label="Basic usage"
className="flex flex-col gap-3"
>
<CodeBlock
className="useless-code-block-class"
language="bash"
hideLineNumbers={true}
>
{command.usage}
</CodeBlock>
</Tabs.Panel>
)}
</Tabs>
</div>
</RefSubLayout.Examples>
)}
</RefSubLayout.Section>
)
}
@@ -15,6 +15,7 @@ const CliGlobalFlagsHandler = () => {
return (
<Param
{...flag}
id={`${spec.id}-${flag.id}`}
isOptional={flag.required === undefined ? true : !flag.required}
></Param>
)
+1 -1
View File
@@ -17,7 +17,7 @@ hideTitle: true
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`](/docs/reference/cli/usage#supabase-start)
- Run Supabase locally: [`supabase init`](/docs/reference/cli/usage#supabase-init) and [`supabase start`](/docs/reference/cli/usage#supabase-start)
- Manage database migrations: [`supabase migration`](/docs/reference/cli/usage#supabase-migration)
- CI/CD for releasing to production: [`supabase db push`](/docs/reference/cli/usage#supabase-db-push)
- Manage your Supabase projects: [`supabase projects`](/docs/reference/cli/usage#supabase-projects)
-50
View File
@@ -1,50 +0,0 @@
---
id: initializing
title: 'Initializing'
slug: initializing
---
<RefSubLayout.EducationRow>
<RefSubLayout.Details>
Initialize Supabase with the static `initialize()` method on `Supabase` class.
</RefSubLayout.Details>
<RefSubLayout.Examples>
<Tabs
size="small"
type="underlined"
defaultActiveId="flutter"
>
<TabPanel id="flutter" label="Flutter">
```dart main.dart
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Supabase.initialize(
url: 'https://xyzcompany.supabase.co',
anonKey: 'public-anon-key',
);
runApp(MyApp());
}
```
</TabPanel>
<TabPanel id="dart" label="Other Dart Project">
```dart
final supabase = SupabaseClient(
'https://xyzcompany.supabase.co',
'public-anon-key',
);
```
</TabPanel>
</Tabs>
</RefSubLayout.Examples>
</RefSubLayout.EducationRow>
@@ -0,0 +1,21 @@
---
id: introduction
title: Introduction
hideTitle: true
---
<div className="flex items-start gap-6 not-prose" id="introduction">
<img src="/docs/img/icons/menu/reference-python.svg" className="w-8 h-8 rounded" />
<div className="flex flex-col gap-2">
<h1 className="text-3xl text-scale-1200 m-0">Python Client Library</h1>
<h2 className="text-base font-mono text-scale-1100">@supabase-community/supabase-py</h2>
</div>
</div>
<div className="max-w-xl">
This reference documents every object and method available in the
[supabase-py](https://github.com/supabase-community/supabase-py) library from the Supabase
community. You can use `supabase-py` to interact with your Postgres database, listen to database
changes, invoke Deno Edge Functions, build login and user management functionality, and manage
large files.
</div>
@@ -0,0 +1,10 @@
---
id: release-notes
title: Release Notes
---
The community is actively working on the library and we will be upgrading the Authentication library, `gotrue-py`, to mirror the Supabase-js v2 lib.
## Storage Transformations
We currently support [image transformations](https://supabase.com/docs/guides/storage/image-transformations) in our storage library.
+4
View File
@@ -71,6 +71,10 @@ const levelsData = {
icon: '/docs/img/icons/menu/reference-dart',
name: 'Dart Reference v0.0',
},
reference_python_v2: {
icon: '/docs/img/icons/menu/reference-python',
name: 'Python Reference v2.0',
},
reference_cli: {
icon: '/docs/img/icons/menu/reference-cli',
name: 'CLI Reference',
@@ -8,6 +8,15 @@ export const meta = {
Supabase APIs are generated from your database, which means that we can use database introspection to generate type-safe API definitions.
<div className="video-container">
<iframe
src="https://www.youtube-nocookie.com/embed/7CqlTU9aOR4"
frameBorder="1"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
></iframe>
</div>
## Generating types using Supabase CLI
The Supabase CLI is a single binary Go application that provides everything you need to setup a local development environment.
@@ -10,6 +10,8 @@ export const meta = {
This submodule provides experimental convenience helpers for implementing user authentication in Next.js Server Components - the `app` directory. For examples using the `pages` directory check out [Auth Helpers in Next.js](/docs/guides/auth/auth-helpers/nextjs).
> For a complete implementation example, check out [this repo](https://github.com/supabase/auth-helpers/tree/main/examples/nextjs-server-components).
> To learn more about fetching and caching Supabase data with Next.js 13 Server Components, check out our [blog](/blog/fetching-and-caching-supabase-data-in-next-js-server-components) or [live stream](https://www.youtube.com/watch?v=QH0P5xZt5wY).
## Install the Next.js helper library
@@ -52,6 +54,8 @@ NEXT_PUBLIC_SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY
## Creating a Supabase Client
### Server-side
<Tabs
scrollable
size="small"
@@ -60,23 +64,13 @@ NEXT_PUBLIC_SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY
>
<TabPanel id="js" label="JavaScript">
Create a new file at `/utils/supabase-browser.js` and populate with the following:
```js title="/utils/supabase-browser.js"
import { createBrowserSupabaseClient } from '@supabase/auth-helpers-nextjs'
export default createBrowserSupabaseClient()
```
This will be used any time we need to create a Supabase client _client-side_ - in `useEffect`, for example.
Create a new file at `/utils/supabase-server.js` and populate with the following:
```js title="/utils/supabase-server.js"
import { headers, cookies } from 'next/headers'
import { createServerComponentSupabaseClient } from '@supabase/auth-helpers-nextjs'
export default () =>
export const createClient = () =>
createServerComponentSupabaseClient({
headers,
cookies,
@@ -85,33 +79,19 @@ export default () =>
> This needs to export a function, as the headers and cookies are not populated with values until the Server Component is requesting data.
This will be used any time we need to create a Supabase client _server-side_ - in a Server Component, for example.
</TabPanel>
<TabPanel id="ts" label="TypeScript">
Create a new file at `/utils/supabase-browser.ts` and populate with the following:
```ts
import { createBrowserSupabaseClient } from '@supabase/auth-helpers-nextjs'
import { Database } from '../lib/database.types'
export default createBrowserSupabaseClient<Database>()
```
> TypeScript types can be [generated with the Supabase CLI](https://supabase.com/docs/reference/javascript/typescript-support) and passed to `createBrowserSupabaseClient` to add type support to the Supabase client.
This will be used any time we need to create a Supabase client _client-side_ - in `useEffect`, for example.
Create a new file at `/utils/supabase-server.ts` and populate with the following:
```ts title="/utils/supabase-server.ts"
import { headers, cookies } from 'next/headers'
import { createServerComponentSupabaseClient } from '@supabase/auth-helpers-nextjs'
import { Database } from '../lib/database.types'
export default () =>
import type { Database } from '../lib/database.types'
export const createClient = () =>
createServerComponentSupabaseClient<Database>({
headers,
cookies,
@@ -122,14 +102,12 @@ export default () =>
> This needs to export a function, as the headers and cookies are not populated with values until the Server Component is requesting data.
This will be used any time we need to create a Supabase client _server-side_ - in a Server Component, for example.
</TabPanel>
</Tabs>
## Middleware
This will be used any time we need to create a Supabase client _server-side_ - in a Server Component, for example.
Middleware runs before every route declared in the `matcher` array. Since we don't have access to set cookies or headers from Server Components, we need to create a Middleware Supabase client and refresh the user's session by calling `getSession()`.
Next, we need a middleware file to refresh the user's session on navigation.
<Tabs
scrollable
@@ -139,7 +117,7 @@ Middleware runs before every route declared in the `matcher` array. Since we don
>
<TabPanel id="js" label="JavaScript">
Create a new file at `/app/middleware.js` and populate with the following:
Create a new `/app/middleware.js` file and populate with the following:
```jsx title="middleware.js"
import { createMiddlewareSupabaseClient } from '@supabase/auth-helpers-nextjs'
@@ -156,27 +134,25 @@ export async function middleware(req) {
return res
}
export const config = {
matcher: ['/optional-session', '/required-session', '/realtime'],
}
```
</TabPanel>
<TabPanel id="ts" label="TypeScript">
Create a new file at `/app/middleware.ts` and populate with the following:
Create a new `/app/middleware.ts` file and populate with the following:
```tsx title="middleware.ts"
import { createMiddlewareSupabaseClient } from '@supabase/auth-helpers-nextjs'
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
import type { Database } from '../lib/database.types'
export async function middleware(req: NextRequest) {
const res = NextResponse.next()
const supabase = createMiddlewareSupabaseClient({ req, res })
const supabase = createMiddlewareSupabaseClient<Database>({ req, res })
const {
data: { session },
@@ -184,20 +160,34 @@ export async function middleware(req: NextRequest) {
return res
}
export const config = {
matcher: ['/optional-session', '/required-session', '/realtime'],
}
```
> TypeScript types can be [generated with the Supabase CLI](https://supabase.com/docs/reference/javascript/typescript-support) and passed to `createMiddlewareSupabaseClient` to add type support to the Supabase client.
</TabPanel>
</Tabs>
> Any Server Component route that uses a Supabase client must be added to this middleware's `matcher` array. Without this, the Server Component may try to make a request to Supabase with an expired `access_token`.
We can now use our server-side Supabase client to fetch data in Server Components.
## Supabase Listener
```jsx
import 'server-only'
We need to set up a listener to fetch fresh data whenever our user logs in or out.
import { createClient } from '../../utils/supabase-server'
// do not cache this page
export const revalidate = 0
export default async function ServerComponent() {
const supabase = createClient()
const { data } = await supabase.from('posts').select('*')
return <pre>{JSON.stringify({ data }, null, 2)}</pre>
}
```
### Client-side
We still need a Supabase instance client-side for authentication and realtime subscriptions. It is important, when using Supabase client-side, to have a single instance of a client. We can share this singleton instance across our components using providers and React context.
<Tabs
scrollable
@@ -207,286 +197,291 @@ We need to set up a listener to fetch fresh data whenever our user logs in or ou
>
<TabPanel id="js" label="JavaScript">
Create a `/components/supabase-listener.jsx` file and add the following:
Create a new file at `/utils/supabase-browser.js` and populate with the following:
```js title="/utils/supabase-browser.js"
import { createBrowserSupabaseClient } from '@supabase/auth-helpers-nextjs'
export const createClient = () => createBrowserSupabaseClient()
```
</TabPanel>
<TabPanel id="ts" label="TypeScript">
Create a new file at `/utils/supabase-browser.ts` and populate with the following:
```ts
import { createBrowserSupabaseClient } from '@supabase/auth-helpers-nextjs'
import { Database } from '../lib/database.types'
export const createClient = () => createBrowserSupabaseClient<Database>()
```
> TypeScript types can be [generated with the Supabase CLI](https://supabase.com/docs/reference/javascript/typescript-support) and passed to `createBrowserSupabaseClient` to add type support to the Supabase client.
</TabPanel>
</Tabs>
<Tabs
scrollable
size="small"
type="underlined"
defaultActiveId="js"
>
<TabPanel id="js" label="JavaScript">
Next, we need to create a single instance of Supabase to use client-side. Let's create a new Provider for Supabase at `/components/supabase-provider.jsx` and populate with the following:
```jsx title=components/supabase-provider.jsx
'use client'
import { createContext, useContext, useState } from 'react'
import { createClient } from '../utils/supabase-browser'
const Context = createContext()
export default function SupabaseProvider({ children }) {
const [supabase] = useState(() => createClient())
useEffect(() => {
const { data: { subscription } } = supabase.auth.onAuthStateChange((event, session) => {
if (session?.access_token !== accessToken) {
router.refresh()
}
})
return () => subscription.unsubscribe()
}, [accessToken])
return (
<Context.Provider value={{ supabase }}>
<>{children}</>
</Context.Provider>
)
}
export const useSupabase = () => useContext(Context)
```
</TabPanel>
<TabPanel id="ts" label="TypeScript">
Next, we need to create a single instance of Supabase to use client-side. Let's create a new Provider for Supabase at `/components/supabase-provider.tsx` and populate with the following:
```tsx title=components/supabase-provider.tsx
'use client'
import { createContext, useContext, useState } from 'react'
import { createClient } from '../utils/supabase-browser'
import type { SupabaseClient } from '@supabase/auth-helpers-nextjs'
import type { Database } from '../lib/database.types'
type SupabaseContext = {
supabase: SupabaseClient<Database>
}
const Context = createContext<SupabaseContext>()
export default function SupabaseProvider({ children }: { children: React.ReactNode }) {
const [supabase] = useState(() => createClient())
return (
<Context.Provider value={{ supabase }}>
<>{children}</>
</Context.Provider>
)
}
export const useSupabase = () => useContext(Context)
```
> TypeScript types can be [generated with the Supabase CLI](https://supabase.com/docs/reference/javascript/typescript-support) and passed to `createBrowserSupabaseClient` to add type support to the Supabase client.
</TabPanel>
</Tabs>
We need to set up a listener to fetch fresh data whenever our user logs in or out. For this we need to check whether our client and server sessions match. Let's start by installing the `server-only` package.
```bash
npm install server-only
```
This will ensure that any component that imports this package will be a Server Component, and excluded from the browser bundle.
Next, let's modify our root layout to fetch the user's session, wrap our application in our Supabase Provider, and pass the server access token as a prop to the `<SupabaseListener />` component (we will create this next).
<Tabs
scrollable
size="small"
type="underlined"
defaultActiveId="js"
>
<TabPanel id="js" label="JavaScript">
```jsx title=app/layout.jsx
import 'server-only'
import SupabaseListener from '../components/supabase-listener'
import SupabaseProvider from '../components/supabase-provider'
import './globals.css'
import { createClient } from '../utils/supabase-server'
// do not cache this layout
export const revalidate = 0
export default async function RootLayout({ children }) {
const supabase = createClient()
const {
data: { session },
} = await supabase.auth.getSession()
return (
<html lang="en">
{/*
<head /> will contain the components returned by the nearest parent
head.tsx. Find out more at https://beta.nextjs.org/docs/api-reference/file-conventions/head
*/}
<head />
<body>
<SupabaseProvider>
<SupabaseListener serverAccessToken={session?.access_token} />
{children}
</SupabaseProvider>
</body>
</html>
)
}
```
</TabPanel>
<TabPanel id="ts" label="TypeScript">
```tsx title=app/layout.tsx
import 'server-only'
import SupabaseListener from '../components/supabase-listener'
import SupabaseProvider from '../components/supabase-provider'
import './globals.css'
import { createClient } from '../utils/supabase-server'
// do not cache this layout
export const revalidate = 0
export default async function RootLayout({ children }: { children: React.ReactNode }) {
const supabase = createClient()
const {
data: { session },
} = await supabase.auth.getSession()
return (
<html lang="en">
{/*
<head /> will contain the components returned by the nearest parent
head.tsx. Find out more at https://beta.nextjs.org/docs/api-reference/file-conventions/head
*/}
<head />
<body>
<SupabaseProvider>
<SupabaseListener serverAccessToken={session?.access_token} />
{children}
</SupabaseProvider>
</body>
</html>
)
}
```
</TabPanel>
</Tabs>
And now create our Supabase listener component that uses the singleton Supabase instance to listen for auth changes.
<Tabs
scrollable
size="small"
type="underlined"
defaultActiveId="js"
>
<TabPanel id="js" label="JavaScript">
```jsx title="/components/supabase-listener.jsx"
'use client'
import { useRouter } from 'next/navigation'
import { useEffect } from 'react'
import supabase from '../utils/supabase-browser'
import { useSupabase } from './supabase-provider'
export default function SupabaseListener({ accessToken }) {
export default function SupabaseListener({ serverAccessToken }) {
const { supabase } = useSupabase()
const router = useRouter()
useEffect(() => {
const { data: subscription } = supabase.auth.onAuthStateChange((event, session) => {
if (session?.access_token !== accessToken) {
const {
data: { subscription },
} = supabase.auth.onAuthStateChange((event, session) => {
if (session?.access_token !== serverAccessToken) {
router.refresh()
}
})
return () => subscription.unsubscribe()
}, [accessToken])
return () => {
subscription.unsubscribe()
}
}, [serverAccessToken, router, supabase])
return null
}
```
> `use client` tells Next.js that this is a [Client Component](https://beta.nextjs.org/docs/rendering/server-and-client-components#client-components). Only Client Components can use hooks like `useEffect` and `useRouter`.
The function we pass to `onAuthStateChange` is automatically called by Supabase whenever a user's session changes. This component takes an `accessToken` prop, which will be the server's state for our user (we'll set this up next). If the `accessToken` from the server and the new `access_token` do not match then the client and server are out of sync, therefore, we want to reload the active route.
Lastly, fetch the server-side session in the `RootLayout` and pass it to our new `<SupabaseListener />` component.
```jsx title="/app/layout.jsx"
import SupabaseListener from '../components/supabase-listener'
import createClient from '../utils/supabase-server'
export default async function RootLayout({ children }) {
const supabase = createClient()
const {
data: { session },
} = await supabase.auth.getSession()
return (
// html and head section omitted
<body>
<SupabaseListener accessToken={session?.access_token} />
{children}
</body>
)
}
```
We don't want Next.js to cache this `session` value, so we need to export a `revalidate` value of `0` in the layout file.
```jsx
export const revalidate = 0
```
We also want to tell Next.js to explicitly exclude this component's code from the client bundle by installing the `server-only` package:
```bash
npm install server-only
```
And importing it at the top of our component.
```jsx
import 'server-only'
```
The entire Layout component should look something like this:
```jsx title="/app/layout.jsx"
import 'server-only'
import SupabaseListener from '../components/supabase-listener'
import './globals.css'
import createClient from '../utils/supabase-server'
// do not cache this layout
export const revalidate = 0
export default async function RootLayout({ children }) {
const supabase = createClient()
const {
data: { session },
} = await supabase.auth.getSession()
return (
<html lang="en">
{/*
<head /> will contain the components returned by the nearest parent
head.jsx. Find out more at https://beta.nextjs.org/docs/api-reference/file-conventions/head
*/}
<head />
<body>
<SupabaseListener accessToken={session?.access_token} />
{children}
</body>
</html>
)
}
```
</TabPanel>
<TabPanel id="ts" label="TypeScript">
Create a `/components/supabase-listener.tsx` file and add the following:
```tsx title="/components/supabase-listener.tsx"
'use client'
import { useRouter } from 'next/navigation'
import { useEffect } from 'react'
import supabase from '../utils/supabase-browser'
import { useSupabase } from './supabase-provider'
export default function SupabaseListener({ accessToken }: { accessToken?: string }) {
export default function SupabaseListener({ serverAccessToken }: { serverAccessToken?: string }) {
const { supabase } = useSupabase()
const router = useRouter()
useEffect(() => {
supabase.auth.onAuthStateChange((event, session) => {
if (session?.access_token !== accessToken) {
const {
data: { subscription },
} = supabase.auth.onAuthStateChange((event, session) => {
if (session?.access_token !== serverAccessToken) {
router.refresh()
}
})
}, [accessToken])
return () => {
subscription.unsubscribe()
}
}, [serverAccessToken, router, supabase])
return null
}
```
</TabPanel>
</Tabs>
> `use client` tells Next.js that this is a [Client Component](https://beta.nextjs.org/docs/rendering/server-and-client-components#client-components). Only Client Components can use hooks like `useEffect` and `useRouter`.
The function we pass to `onAuthStateChange` is automatically called by Supabase whenever a user's session changes. This component takes an `accessToken` prop, which will be the server's state for our user (we'll set this up next). If the `accessToken` from the server and the new `access_token` do not match then the client and server are out of sync, therefore, we want to reload the active route.
The function we pass to `onAuthStateChange` is automatically called by Supabase whenever a user's session changes. This component takes an `serverAccessToken` prop, which is the server's state for our user. If the `serverAccessToken` and the new session's `access_token` do not match then the client and server are out of sync, therefore, we want to reload the active route.
Lastly, fetch the server-side session in the `RootLayout` and pass it to our new `<SupabaseListener />` component.
```tsx title="/app/layout.tsx"
import SupabaseListener from '../components/supabase-listener'
import createClient from '../utils/supabase-server'
export default async function RootLayout({ children }: { children: React.ReactNode }) {
const supabase = createClient()
const {
data: { session },
} = await supabase.auth.getSession()
return (
// html and head section omitted
<body>
<SupabaseListener accessToken={session?.access_token} />
{children}
</body>
)
}
```
> Again, TypeScript types can be [generated with the Supabase CLI](https://supabase.com/docs/reference/javascript/typescript-support) and passed to `createServerComponentSupabaseClient` to add type support to the Supabase client.
We don't want Next.js to cache this `session` value, so we need to export a `revalidate` value of `0`.
```tsx
export const revalidate = 0
```
We also want to tell Next.js to explicitly exclude this component's code from the client bundle by installing the `server-only` package:
```bash
npm install server-only
```
And importing it at the top of our component.
```tsx
import 'server-only'
```
The entire Layout component should look something like this:
```tsx title="/app/layout.tsx"
import 'server-only'
import SupabaseListener from '../components/supabase-listener'
import './globals.css'
import createClient from '../utils/supabase-server'
// do not cache this layout
export const revalidate = 0
export default async function RootLayout({ children }: { children: React.ReactNode }) {
const supabase = createClient()
const {
data: { session },
} = await supabase.auth.getSession()
return (
<html lang="en">
{/*
<head /> will contain the components returned by the nearest parent
head.tsx. Find out more at https://beta.nextjs.org/docs/api-reference/file-conventions/head
*/}
<head />
<body>
<SupabaseListener accessToken={session?.access_token} />
{children}
</body>
</html>
)
}
```
</TabPanel>
</Tabs>
Supabase can now be used in any Client or Server component.
## Server Components
Next.js recommends fetching data in Server Components whenever possible.
<Tabs
scrollable
size="small"
type="underlined"
defaultActiveId="js"
>
<TabPanel id="js" label="JavaScript">
```jsx
import 'server-only'
import createClient from '../../utils/supabase-server'
// do not cache this page
export const revalidate = 0
export default async function ServerComponent() {
const supabase = createClient()
const { data } = await supabase.from('posts').select('*')
return <pre>{JSON.stringify({ data }, null, 2)}</pre>
}
```
</TabPanel>
<TabPanel id="ts" label="TypeScript">
```tsx
import 'server-only'
import createClient from '../../utils/supabase-server'
// do not cache this page
export const revalidate = 0
export default async function ServerComponent() {
const supabase = createClient()
const { data } = await supabase.from('posts').select('*')
return <pre>{JSON.stringify({ data }, null, 2)}</pre>
}
```
</TabPanel>
</Tabs>
## Client Components
While Next.js recommend doing all data fetching in Server Components, we still need Supabase client-side for things like authentication and subscribing to realtime updates.
Now we can use our `useSupabase` hook throughout our client-side components.
### Authentication
We can call any of [Supabase's authentication methods](https://supabase.com/docs/reference/javascript/v1/auth-signin) - such as `supabase.auth.signInWithOAuth` - from a client component.
<Tabs
scrollable
size="small"
@@ -498,30 +493,33 @@ We can call any of [Supabase's authentication methods](https://supabase.com/docs
```jsx title="/components/login.jsx"
'use client'
import supabase from '../utils/supabase-browser'
import { useSupabase } from './supabase-provider'
// Supabase auth needs to be triggered client-side
export default function Login() {
const handleLogin = async () => {
const { error } = await supabase.auth.signInWithOAuth({
const { supabase, session } = useSupabase()
const handleEmailLogin = async () => {
await supabase.auth.signInWithPassword({
email: 'jon@supabase.com',
password: 'password',
})
}
const handleGitHubLogin = async () => {
await supabase.auth.signInWithOAuth({
provider: 'github',
})
if (error) {
console.log({ error })
}
}
const handleLogout = async () => {
const { error } = await supabase.auth.signOut()
if (error) {
console.log({ error })
}
await supabase.auth.signOut()
}
return (
<>
<button onClick={handleLogin}>Login</button>
<button onClick={handleEmailLogin}>Email Login</button>
<button onClick={handleGitHubLogin}>GitHub Login</button>
<button onClick={handleLogout}>Logout</button>
</>
)
@@ -535,30 +533,33 @@ export default function Login() {
```tsx title="/components/login.tsx"
'use client'
import supabase from '../utils/supabase-browser'
import { useSupabase } from './supabase-provider'
// Supabase auth needs to be triggered client-side
export default function Login() {
const handleLogin = async () => {
const { error } = await supabase.auth.signInWithOAuth({
const { supabase, session } = useSupabase()
const handleEmailLogin = async () => {
await supabase.auth.signInWithPassword({
email: 'jon@supabase.com',
password: 'password',
})
}
const handleGitHubLogin = async () => {
await supabase.auth.signInWithOAuth({
provider: 'github',
})
if (error) {
console.log({ error })
}
}
const handleLogout = async () => {
const { error } = await supabase.auth.signOut()
if (error) {
console.log({ error })
}
await supabase.auth.signOut()
}
return (
<>
<button onClick={handleLogin}>Login</button>
<button onClick={handleEmailLogin}>Email Login</button>
<button onClick={handleGitHubLogin}>GitHub Login</button>
<button onClick={handleLogout}>Logout</button>
</>
)
@@ -588,10 +589,11 @@ Create a new file at `/app/realtime/posts.jsx` and populate with the following:
'use client'
import { useEffect, useState } from 'react'
import supabase from '../../utils/supabase-browser'
import { useSupabase } from '../../components/supabase-provider'
export default function Posts({ serverPosts }) {
const [posts, setPosts] = useState(serverPosts)
const { supabase } = useSupabase()
useEffect(() => {
setPosts(serverPosts)
@@ -608,14 +610,12 @@ export default function Posts({ serverPosts }) {
return () => {
supabase.removeChannel(channel)
}
}, [serverPosts])
}, [supabase, setPosts, posts])
return <pre>{JSON.stringify(posts, null, 2)}</pre>
}
```
> The first `useEffect` is required to overwrite posts if the `serverPosts` prop changes.
This can now be used in a Server Component to subscribe to realtime updates.
Create a new file at `/app/realtime/page.jsx` and populate with the following:
@@ -623,7 +623,7 @@ Create a new file at `/app/realtime/page.jsx` and populate with the following:
```jsx title="/app/realtime/page.jsx"
import 'server-only'
import createClient from '../../utils/supabase-server'
import { createClient } from '../../utils/supabase-server'
import Posts from './posts'
// do not cache this page
@@ -647,13 +647,15 @@ Create a new file at `/app/realtime/posts.tsx` and populate with the following:
'use client'
import { useEffect, useState } from 'react'
import { Database } from '../lib/database.types'
import supabase from '../../utils/supabase-browser'
import { useSupabase } from '../../components/supabase-provider'
import type { Database } from '../../lib/database.types'
type Post = Database['public']['Tables']['posts']['Row']
export default function Posts({ serverPosts }: { serverPosts: Post[] }) {
const [posts, setPosts] = useState(serverPosts)
const { supabase } = useSupabase()
useEffect(() => {
setPosts(serverPosts)
@@ -670,13 +672,13 @@ export default function Posts({ serverPosts }: { serverPosts: Post[] }) {
return () => {
supabase.removeChannel(channel)
}
}, [serverPosts])
}, [supabase, setPosts, posts])
return <pre>{JSON.stringify(posts, null, 2)}</pre>
}
```
> The first `useEffect` is required to overwrite posts if the `serverPosts` prop changes.
> TypeScript types can be [generated with the Supabase CLI](https://supabase.com/docs/reference/javascript/typescript-support) and passed to `createServerSupabaseClient` to add type support to the Supabase client.
This can now be used in a Server Component to subscribe to realtime updates.
@@ -685,7 +687,7 @@ Create a new file at `/app/realtime/page.tsx` and populate with the following:
```tsx title="/app/realtime/page.tsx"
import 'server-only'
import createClient from '../../utils/supabase-server'
import { createClient } from '../../utils/supabase-server'
import Posts from './posts'
// do not cache this page
@@ -12,9 +12,7 @@ Even though Supabase provides an `auth.users` table, it can be helpful to create
## Creating user tables
When you create tables to store user data, it's helpful to reference the `auth.users` table in the primary key. This ensures data integrity.
For example, a `public.profiles` table might look like this:
When you create tables to store user data, it's helpful to reference the `auth.users` table in the primary key to ensure data integrity. Also specify the `on delete cascade` clause when referencing `auth.users`. Omitting it may cause problems when deleting users. For example, a `public.profiles` table might look like this:
```sql
create table public.profiles (
@@ -28,9 +26,11 @@ create table public.profiles (
alter table public.profiles enable row level security;
```
<Admonition type="note">
<Admonition type="caution">
Make sure to specify the `on delete cascade` clause when referencing `auth.users`. Omitting it may cause problems when deleting users.
Only use primary keys as [foreign key references](https://www.postgresql.org/docs/current/tutorial-fk.html) for schemas and tables like `auth.users` which are managed by Supabase. PostgreSQL lets you specify a foreign key reference for columns backed by a unique index (not necessarily primary keys).
Primary keys are **guaranteed not to change**. Columns, indices, constraints or other database objects managed by Supabase **may change at any time** and you should be careful when referencing them directly.
</Admonition>
@@ -8,6 +8,15 @@ export const meta = {
Postgres has built-in functions to handle `Full Text Search` queries. This is like a "search engine" within Postgres.
<div className="video-container">
<iframe
src="https://www.youtube-nocookie.com/embed/b-mgca_2Oe4"
frameBorder="1"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
></iframe>
</div>
## Preparation
For this guide we'll use the following example data:
+1 -1
View File
@@ -77,7 +77,7 @@ You must define the "data type" when you create a column.
### Data types
Every column is a predefined type. PostgreSQL provides many [default types](https://www.postgresql.org/docs/current/datatype.html), and you can even design your own (or use extensions)
if the default types don't fit your needs.
if the default types don't fit your needs. You can use any data type that Postgres supports via the SQL editor. We only support a subset of these in the Table Editor in an effort to keep the experience simple for people with less experience with databases.
<details>
<summary>Show/Hide default data types</summary>
@@ -0,0 +1,166 @@
import Layout from '~/layouts/DefaultGuideLayout'
import StepHikeCompact from '~/components/StepHikeCompact'
export const meta = {
title: 'Use Supabase with SvelteKit',
subtitle:
'Learn how to create a Supabase project, add some sample data to your database, and query the data from a SvelteKit app.',
breadcrumb: 'Framework Quickstarts',
}
<StepHikeCompact>
<StepHikeCompact.Step step={1}>
<StepHikeCompact.Details title="Set up a Supabase project with sample data">
[Create a new project](https://app.supabase.com) in the Supabase Dashboard.
After your project is ready, create a table in your Supabase database using the [SQL Editor](https://app.supabase.com/project/_/sql) in the Dashboard. Use the following SQL statement to create a `countries` table with some sample data.
</StepHikeCompact.Details>
<StepHikeCompact.Code>
```sql SQL_EDITOR
-- Create the table
CREATE TABLE countries (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL
);
-- Insert some sample data into the table
INSERT INTO countries (name) VALUES ('United States');
INSERT INTO countries (name) VALUES ('Canada');
INSERT INTO countries (name) VALUES ('Mexico');
````
</StepHikeCompact.Code>
</StepHikeCompact.Step>
<StepHikeCompact.Step step={2}>
<StepHikeCompact.Details title="Create a SvelteKit app">
Create a SvelteKit app using the `npm create` command.
</StepHikeCompact.Details>
<StepHikeCompact.Code>
```bash Terminal
npm create svelte@latest myapp
```
</StepHikeCompact.Code>
</StepHikeCompact.Step>
<StepHikeCompact.Step step={3}>
<StepHikeCompact.Details title="Install the Supabase client library">
The fastest way to get started is to use the `supabase-js` client library which provides a convenient interface for working with Supabase from a SvelteKit app.
Navigate to the SvelteKit app and install `supabase-js`.
</StepHikeCompact.Details>
<StepHikeCompact.Code>
```bash Terminal
cd my-app && npm install @supabase/supabase-js
```
</StepHikeCompact.Code>
</StepHikeCompact.Step>
<StepHikeCompact.Step step={4}>
<StepHikeCompact.Details title="Create the Supabase client">
Create a `/src/lib` directory of your SvelteKit app, create a file called `supabaseClient.js` and add the following code to initialize the Supabase client with your [project URL and public API (anon) key](https://app.supabase.com/project/_/settings/api).
</StepHikeCompact.Details>
<StepHikeCompact.Code>
```js src/lib/supabaseClient.js
import { createClient } from '@supabase/supabase-js'
export const supabase = createClient('https://<project>.supabase.co', '<your-anon-key>')
```
</StepHikeCompact.Code>
</StepHikeCompact.Step>
<StepHikeCompact.Step step={5}>
<StepHikeCompact.Details title="Query data from the app">
Use `load` method to fetch the data server-side and display the query results as a simple list.
Create `+page.server.js` file in the `routes` directory with the following code.
</StepHikeCompact.Details>
<StepHikeCompact.Code>
```js src/routes/+page.server.js
import { supabase } from "$lib/supabaseClient";
export async function load() {
const { data } = await supabase.from("countries").select();
return {
countries: data ?? [],
};
}
```
</StepHikeCompact.Code>
<StepHikeCompact.Details title="">
Replace the existing content in your `+page.svelte` file in the `routes` directory with the following code.
</StepHikeCompact.Details>
<StepHikeCompact.Code>
```svelte src/routes/+page.svelte
<script>
export let data;
let { countries } = data;
$: ({ countries } = data);
</script>
<ul>
{#each countries as country}
<li>{country.name}</li>
{/each}
</ul>
```
</StepHikeCompact.Code>
</StepHikeCompact.Step>
<StepHikeCompact.Step step={6}>
<StepHikeCompact.Details title="Start the app">
Start the app, go to http://localhost:5173 in a browser, and open the browser console and you should see the list of countries.
</StepHikeCompact.Details>
<StepHikeCompact.Code>
```bash Terminal
npm run dev
```
</StepHikeCompact.Code>
</StepHikeCompact.Step>
</StepHikeCompact>
export const Page = ({ children }) => <Layout meta={meta} children={children} hideToc={true} />
export default Page
+33 -26
View File
@@ -18,7 +18,7 @@ Once created, the pipeline backfills all your historical data from Firestore and
Before you begin, you'll need:
- An [Estuary account](https://go.estuary.dev/sign-up).
- An Estuary account. [Head to the web app to start for free](https://dashboard.estuary.dev).
- For your Firestore database:
@@ -31,36 +31,35 @@ Before you begin, you'll need:
You'll start by creating a **capture**, a task in Flow that connects to your data source system: in this case, Firestore. This process will create one or more data **collections**, backed by a real-time data lake.
1. Go to the Flow web application at [dashboard.estuary.dev](http://dashboard.estuary.dev) and sign in using the credentials provided by your Estuary account manager.
1. Go to the [**Captures** tab](https://dashboard.estuary.dev/captures) of the Flow web app and choose **New Capture**.
2. Click the **Captures** tab and choose **New Capture**.
3. Locate and select the **Google Firestore** card.
2. Locate and select the **Google Firestore** card.
A form appears with the properties required for a Firestore capture.
4. Set a name for your capture.
3. Set a name for your capture.
Click inside the **Name** field to generate a drop-down menu of available **prefixes** and select one (likely, this will be the name of your organization). Append a unique capture name after the `/` to create the full name, for example, `acmeCo/myFirestoreCapture`.
5. Fill out the required properties for Firestore.
4. Fill out the required properties for Firestore.
- **Database**: Flow can autodetect the database name, but you may optionally specify it here. This is helpful if the service account used has access to multiple Firebase projects. Your database name usually follows the format `projects/$PROJECTID/databases/(default)`.
- **Credentials**: The JSON service account key created per the prerequisites.
**Database**: Flow can autodetect the database name, but you may optionally specify it here. This is helpful if the service account used has access to multiple Firebase projects. Your database name usually follows the format `projects/$PROJECTID/databases/(default)`.
**Credentials**: The JSON service account key created per the prerequisites.
6. Click **Discover Endpoint**.
5. Click **Next**.
Flow uses the provided configuration to initiate a connection with Firestore. It generates a capture specification and details of the collections that it will create, once published.
Flow uses the provided configuration to initiate a connection with Firestore. It maps each collection in the Firestore database to a Flow collection.
7. Use the **Specification Editor** to view the [JSON schemas](https://docs.estuary.dev/concepts/schemas/) for each collection and make sure they are formatted correctly for your needs. If they're not, you can edit them.
6. Optionally, use the **Collection Selector** to remove any collections you don't need to migrate to Supabase.
8. Click **Save and publish**.
7. Click **Save and Publish**.
You'll see a notification when the capture publishes successfully.
You'll see a notification when the capture publishes successfully.
The data currently in your Firestore database has been captured, and future updates to it will be captured continuously.
The data currently in your Firestore database has been captured to Flow, and future updates to it will be captured continuously.
Click **Materialize Collections** to continue.
Click **Materialize Collections** to continue.
## Step 2: Materialize your collections to Postgres
@@ -72,23 +71,31 @@ A form appears with the properties required for a Postgres materialization.
2. Choose a unique name for your materialization like you did when naming your capture; for example, `acmeCo/mySupabaseMaterialization`.
3. Fill out the required properties for PostgreSQL. You can find most of these in Supabase by going to the **Settings** section and clicking **Database**.
3. Fill out the required properties for Postgres. You can find most of these in Supabase by going to the **Settings** section and clicking **Database**.
- **Address**: Format at `<host>:<port>`.
- **User**: Usually, this is `postgres`.
- **Password**: The password you set when you created your Supabase project.
**Address**: Format at `<host>:<port>`.
**User**: Usually, this is `postgres`.
**Password**: The password you set when you created your Supabase project.
4. Scroll down to view the **Collection Selector** and fill in the **Table** field for each collection.
4. Click **Next**.
The collections you just created have already been selected, but you must provide names for the tables to which they'll be materialized.
Flow initiates a connection with the database and the **Collection Selector** expands.
It's populated with your collections from Firestore, each mapped to a Postgres table.
5. Click **Discover Endpoint**.
5. For each collection, apply a stricter JSON schema.
This ensure that the less-structured Firestore data will be written to a Postgres table in the correct shape.
Flow uses the provided configuration to initiate a connection to your Supabase Postgres database and generate a specification.
In the Collection Selector, choose a collection and click its **Specification** tab.
Click **Schema Inference**. Flow scans the data in your collection and infers a new schema to use for materialization.
Review the new schema and click **Apply Inferred Schema**.
6. Click **Save and Publish**. You'll see a notification when the full materialization publishes successfully.
6. Click **Save and Publish**. You'll see a notification when the materialization publishes successfully.
Your Firestore collections are copied to tables in Supabase. As long as you leave the capture and materialation running, any changes to the Firestore data will be reflected in Supabase in milliseconds.
Your Firestore collections are copied to tables in Supabase. As long as you leave the capture and materialation running, any changes to the Firestore data will be reflected in Supabase in milliseconds.
## Resources
@@ -0,0 +1,107 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'illa',
title: 'ILLA',
description:
'Get started with Supabase and ILLA, a low-code platform for developers that enables the rapid development and deployment of internal tools.',
}
This tutorial outlines the process of creating an Admin Panel using ILLA Builder and Supabase in a few simple steps. ILLA is a low-code platform for developers that enables the rapid development and deployment of internal tools. It allows for creating pages by dragging and dropping UI components, connecting to any database or API, and writing JavaScript. To learn more about ILLA and give it a try, visit their website at [https://www.illacloud.com/](https://www.illacloud.com/). Let's begin!
### Step 1: Set up your Back end on Supabase
On the [Supabase dashboard](https://app.supabase.com/projects), click `New project` and set the name to adminPanel.
![Create Supabase Project for ILLA Admin Panel](/docs/img/guides/integrations/illa/supabase-illa-project.png)
Create a new table by clicking on the `Create a new table` .
Supabase offers a variety of options for populating tables with data, including writing queries, creating schemas through a user interface, and uploading CSV files.
![Create Supabase Table for ILLA Admin Panel](/docs/img/guides/integrations/illa/supabase-table-1.png)
![Config Supabase Table for ILLA Admin Panel](/docs/img/guides/integrations/illa/supabase-table-2.png)
Fill out the info in the table. The database is now set up.
### Step 2: Build UI on ILLA Builder
On [ILLA Builder](https://fast-try.illacloud.com/), click Create New to create a new application.
![Create new project on ILLA Builder](/docs/img/guides/integrations/illa/supabase-illa-create-project.png)
Drag components from the `Insert` panel to the canvas.
Select the components on the canvas and configure the property on the `Inspect` panel.
As seen in the below screenshot, we have built a simple admin panel.
![Build UI with ILLA Builder](/docs/img/guides/integrations/illa/supabase-illa-UI.png)
### Step 3: Connect to Supabase and config CRUD
Note down the database connection information under [Project Settings](https://app.supabase.com/project/hdcfnsxpwwgboqomdrhp/settings/database) in Supabase.
![Note information in supabase](/docs/img/guides/integrations/illa/supabase-information.png)
In the Action List, click `+ New` and select Supabase DB.
![Connect ILLA to Supabase](/docs/img/guides/integrations/illa/supabase-illa-connect.png)
Fill out the form to connect to your Supabase instance. Test connection and save resource.
![Config Supabase in ILLA](/docs/img/guides/integrations/illa/supabase-illa-connect-2.png)
Click `Create Action` to create an action with the Supabase resource and config your CRUD.
![Select Supabase resource in ILLA](/docs/img/guides/integrations/illa/supabase-illa-select.png)
Use `{{` to get the front-end input data. The following is an example of the User Management page in the Admin Panel.
Search for a user by the name inputted in input1
```
SELECT *
FROM user
WHERE name = "{{input1.value}}"
;
```
Update user data. Update user information when id matches
```
UPDATE user
SET name = "{{input3.value}}"
, email = "{{input4.value}}"
WHERE id="{{input2.value}}"
;
```
Insert user data
```
INSERT INTO user VALUES("{{input5.value}}","{{input6.value}}","{{input7.value}}");
```
Delete a user by id
```
DELETE FROM user WHERE id = "{{input2.value}}";
```
### Step 4: Show data on components
Configure the properties of components with `{{` . For example:
![Show Supabase data on ILLA components](/docs/img/guides/integrations/illa/supabase-illa-show-data.png)
## Resources
- [ILLA Cloud official website](https://www.illacloud.com/)
- [ILLA Cloud GitHub](https://github.com/illacloud/illa-builder)
- [ILLA Cloud documentation](https://www.illacloud.com/docs/about-illa)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
+17 -2
View File
@@ -23,7 +23,14 @@ Note your application key from the Keys and Credentials section - this will be u
# Web
[![Keyri-Supabase Web Integration Video](http://img.youtube.com/vi/jrjrcpc2PFQ/0.jpg)](http://www.youtube.com/watch?v=jrjrcpc2PFQ 'Supabase & Keyri | Web Integration Overview')
<div className="video-container">
<iframe
src="https://www.youtube-nocookie.com/embed/jrjrcpc2PFQ"
frameBorder="1"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
></iframe>
</div>
For your web app, first download KeyriQR.html (available [here](https://raw.githubusercontent.com/Keyri-Co/library-keyri-connect/main/KeyriQR.html)) and save it to a public directory.
@@ -64,7 +71,15 @@ That's it!
# Mobile
[![Keyri-Supabase Mobile Integration Video](http://img.youtube.com/vi/oGMsSKyh6tc/0.jpg)](http://www.youtube.com/watch?v=oGMsSKyh6tc 'Supabase & Keyri | Mobile Integration Overview')
<div className="video-container">
<iframe
src="https://www.youtube-nocookie.com/embed/oGMsSKyh6tc"
frameBorder="1"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
></iframe>
</div>
### Install Flutter
@@ -14,7 +14,18 @@ export const meta = {
- Emails
- In-app notifications
This guide steps you through how you can integrate OneSignal with Supabase to create a seamless cloud messaging experience for your users using Database webhooks and edge functions through a simple Next.js application.
Here is William giving us the overview of how OneSignal can work with Supabase to send notifications to your users.
<div className="video-container">
<iframe
src="https://www.youtube-nocookie.com/embed/mw0DLwItue4"
frameBorder="1"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
></iframe>
</div>
In this guide, we will build a similar app and steps you through how you can integrate OneSignal with Supabase to create a seamless cloud messaging experience for your users using Database webhooks and edge functions through a simple Next.js application.
![Entity Diagram](/docs/img/guides/integrations/onesignal/diagram.png)
@@ -73,6 +73,11 @@ After developing your project and deciding it's Production Ready, you should run
- Supabase provides CAPTCHA protection on the signup, sign-in and password reset endpoints. Please refer to [our guide](/docs/guides/auth/auth-captcha) on how to protect against abuse using this method.
### Email Link Validity
- When working with enterprise systems, email scanners may scan and make a `GET` request to the reset password link or sign up link in your email. Since links in Supabase Auth are single use, a user who opens an email post-scan to click on a link will receive an error. To get around this problem,
consider altering the email template to replace the original magic link with a link to a domain you control. The domain can present the user with a "Sign-in" button which redirect the user to the original magic link URL when clicked.
## Next steps
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).
@@ -0,0 +1,41 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'http-status-codes',
title: 'HTTP Status Codes',
description: 'HTTP Status Codes used by the Supabase platform',
}
The Supabase platform offers several HTTP APIs for each project. These APIs can use the status codes to indicate the state of the project, and the request being processed. The status codes returned for requests can be access via the [logs explorer](../platform/logs#logs-explorer).
## 2XX Success
2XX status codes indicate that the request was processed successfully.
## 3XX Redirects
3XX status codes indicate that the client must initiate another course of action to have the request processed successfully. The most popular usage of 3XX codes is to redirect the client to a different location.
## 4XX Client Errors
4XX status codes indicate an issue on the client's end with the request being made. These could include missing or invalid auth information, a malformed request, making too many requests in too short a time period ("rate limiting"), or a network issue on the client's end.
## 5XX Server or Project Errors
5XX status codes indicate that the project was unable to process the request successfully, but not because of an issue with the client's request.
5XX status codes can be the result of the project not having enough [compute](/docs/guides/platform/compute-add-ons) to process a complex request being made by a client or not being able to keep up with the volume of requests made against the project.
### 54X Project Errors
54X status codes are custom codes used by the Supabase platform to indicate the state of the project.
#### 540 Project Paused
The project the request was being made against has been paused. The project cannot process requests until it is un-paused by the owner.
Free-tier projects may be paused due to inactivity, on request by the owner, or in rare instances, due to abuse.
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -12,6 +12,8 @@ In some cases, access to new features require upgrading or migrating your Supaba
## Upgrade your project
<Admonition type="note">This is only available for projects on the Free plan.</Admonition>
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.
@@ -0,0 +1,57 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'troubeshooting',
title: 'Troubleshooting',
description: 'Diagnosing and fixing issues with your hosted Supabase project.',
}
Learn how to diagnose and fix common issues with your Supabase project.
## HTTP API Issues
Symptoms:
- HTTP timeouts
- 5xx response codes
- High response times
### Under-provisioned resources
The most common class of issues that causes HTTP timeouts and 5xx response codes is the under-provisioning of resources for your project. This can cause your project to be unable to service the traffic it is receiving.
Each Supabase project is provisioned with [segregated compute resources](../platform/compute-add-ons). This allows the project to serve unlimited requests, as long as they can be handled using the resources that have been provisioned. Complex queries, or queries that process larger amounts of data, will require higher amounts of resources. As such, the amount of resources that can handle a high volume of simple queries (or queries involving small amounts of data), will likely be unable to handle a similar volume of complex queries.
You can view the resource utilization of your Supabase Project using the [reports in the Dashboard](https://app.supabase.com/project/_/reports/database).
Some common solutions for this issue are:
- [Upgrading](https://app.supabase.com/project/_/settings/billing/subscription) to a [larger compute add-on](../platform/compute-add-ons) in order to serve higher volumes of traffic.
- [Optimizing the queries](../platform/performance#examining-query-performance) being executed.
- [Using fewer Postgres connections](../platform/performance#configuring-clients-to-use-fewer-connections) can reduce the amount of resources needed on the project.
- [Restarting](https://app.supabase.com/project/_/settings/general) the database. This only temporarily solves the issue by terminating any ongoing workloads that might be tying up your compute resources.
If your [Daily Disk IO budget](../platform/compute-add-ons#disk-io-bandwidth) has been drained, you will need to either wait for it to be replenished the next day, or upgrade to a larger compute add-on to increase the budget available to your project.
## Unable to connect to your Supabase Project
Symptom: You're unable to connect to your Postgres database directly, but can open the Project in the [Supabase Dashboard](https://app.supabase.com/project/_/).
### Too many open connections
Errors about too many open connections can be _temporarily_ resolved by [restarting the database](https://app.supabase.com/project/_/settings/general). However, this won't solve the underlying issue for a permanent solution.
- If you're receiving a `No more connections allowed (max_client_conn)` error:
- Configure your applications and services to [use fewer connections](../platform/performance#configuring-clients-to-use-fewer-connections).
- [Upgrade](https://app.supabase.com/project/_/settings/billing/subscription) to a [larger compute add-on](../platform/compute-add-ons) to increase the number of available connections.
- If you're receiving a `sorry, too many clients already` or `remaining connection slots are reserved for non-replication superuser connections` error message in addition to the above suggestions, switch to using the [connection pooler](/docs/guides/database/connecting-to-postgres#connection-pool) instead.
### Connection refused
If you receive a `connection refused` error after a few initial failed connection attempts, your client has likely been temporarily blocked in order to protect the database from brute-force attacks. You can wait 30 minutes before trying again with the correct password, or you can [contact support](https://app.supabase.com/support/new) with your client's IP address to manually unblock you.
If you're also unable to open the project using the [Supabase Dashboard](https://app.supabase.com/project/_/), review the solutions for [under-provisioned projects](#under-provisioned-resources).
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -121,7 +121,7 @@ channel.subscribe(async (status) => {
})
```
## Track and display which users are online
## Track and Display Which Users Are Online
[Presence](/docs/guides/realtime#presence) stores and synchronize shared state across clients. The `sync` event is triggered whenever the shared state changes. The `join` event is triggered when new clients join the channel and `leave` event is triggered when clients leave.
@@ -5,8 +5,6 @@ export const meta = {
title: 'Supabase CLI',
description:
'The Supabase CLI provides tools to develop your project locally and deploy to the Supabase Platform.',
sidebar_label: 'Overview',
toc_max_heading_level: 2,
}
The Supabase CLI provides tools to develop your project locally and deploy to the Supabase Platform.
@@ -215,8 +215,6 @@ jobs:
- uses: actions/checkout@v3
- uses: supabase/setup-cli@v1
with:
version: 1.0.0
- name: Start Supabase local development setup
run: supabase start
@@ -256,8 +254,6 @@ jobs:
- uses: actions/checkout@v3
- uses: supabase/setup-cli@v1
with:
version: 1.0.0
- run: |
supabase link --project-ref $STAGING_PROJECT_ID
@@ -289,8 +285,6 @@ jobs:
- uses: actions/checkout@v3
- uses: supabase/setup-cli@v1
with:
version: 1.0.0
- run: |
supabase link --project-ref $PRODUCTION_PROJECT_ID
@@ -95,6 +95,10 @@ POSTGRES_PORT=5432
Once you have done this, you can safely comment out the `db` section of the `docker-compose` file, and remove any instances where the services `depends_on` the `db` image.
Supabase services require your external database to be initialized with a specific schema. Refer to our [postgres/migrations](https://github.com/supabase/postgres/tree/develop/migrations) repository for instructions on running these migrations.
Note that you need superuser permission on the postgres role to perform the initial schema migration. Once completed, the postgres role will be demoted to non-superuser to prevent abuse.
### Setting database's `log_min_messages`
By default, `docker compose` sets the database's `log_min_messages` configuration to `fatal` to prevent redundant logs generated by Realtime.
@@ -14,6 +14,15 @@ Supabase Storage offers the functionality to transform and resize images dynamic
above](https://supabase.com/pricing).
</Admonition>
<div className="video-container">
<iframe
src="https://www.youtube-nocookie.com/embed/dLqSmxX3r7I"
frameBorder="1"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
></iframe>
</div>
## Get a public URL for a transformed image
You can pass a `transform` option to the functions you are currently using to interact with your objects. This returns the public URL that serves the resized image.
@@ -59,6 +68,32 @@ supabase.storage.from('bucket').download('image.jpg', {
})
```
## Automatic Image Optimisation (WebP)
When using the image transformation API we will automatically find the best format supported by the browser and return that to the client, without any code change.
For instance, if you use Chrome when viewing a jpeg image and using transformation options, you'll see that the content-type returned is `webp`.
As a result, this will lower the bandwidth that you send to your users and your application will load much faster.
<Admonition type="note">
We currently only support WebP. AVIF support will come in the near future
</Admonition>
**Disabling automatic optimisation:**
In case you'd like to return the original format of the image and **opt-out** from the automatic image optimization detection, you can pass the `format=origin` parameter when requesting a transformed image,
this is also supported in the JavaScript SDK starting from v2.2.0
```ts
await storage.from('bucket').download('image.jpeg', {
transform: {
width: 200,
height: 200,
format: 'origin',
},
})
```
## NextJS Loader
You can use Supabase Image Transformation to optimise your NextJS images using a custom [Loader](https://nextjs.org/docs/api-reference/next/image#loader-configuration).
@@ -69,7 +104,7 @@ To get started, create a `supabase-image-loader.js` file in your NextJS project
const projectId = '' // your supabase project id
export default function supabaseLoader({ src, width, height, quality }) {
return `https://${projectId}.supabase.co/storage/v1/${src}?width=${width}&height=${
return `https://${projectId}.supabase.co/storage/v1/render/image/public/${src}?width=${width}&height=${
height || 0
}&quality=${quality || 75}`
}
@@ -149,6 +184,34 @@ supabase.storage.from('bucket').download('image.jpg', {
| BMP | `bmp` | ☑️ | ☑️ |
| TIFF | `tiff` | ☑️ | ☑️ |
## Self Hosting
Our solution to image resizing and optimisation can be self-hosted as with any other Supabase product.
Under the hood we use the awesome [Imgproxy](https://imgproxy.net/)
#### Imgproxy Configuration:
Simply deploy an imgproxy container with the following configuration:
```yaml
imgproxy:
image: darthsim/imgproxy
environment:
- IMGPROXY_ENABLE_WEBP_DETECTION=true
- IMGPROXY_JPEG_PROGRESSIVE=true
```
Note: make sure that this service can only be reachable within an internal network and not exposed to the public internet
#### Storage API Configuration:
Once [Imgproxy](https://imgproxy.net/) is deployed we need to configure a couple of environment variables in your self-hosted [storage-api](https://github.com/supabase/storage-api) service as follows:
```shell
ENABLE_IMAGE_TRANSFORMATION=true
IMGPROXY_URL=yourinternalimgproxyurl.internal.com
```
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
+6 -1
View File
@@ -61,7 +61,7 @@ export const meta = {
<div className="hidden">
<Button size="small">Get started</Button>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-3 md:gap-6">
<div className="grid grid-cols-1 md:grid-cols-4 gap-3 md:gap-6">
<Link href={`/guides/getting-started/quickstarts/reactjs`} passHref>
<a className="no-underline">
<IconPanel title="ReactJS" icon="/docs/img/icons/react-icon" />
@@ -77,6 +77,11 @@ export const meta = {
<IconPanel title="Flutter" icon="/docs/img/icons/flutter-icon" />
</a>
</Link>
<Link href={`/guides/getting-started/quickstarts/sveltekit`} passHref>
<a className="no-underline">
<IconPanel title="SvelteKit" icon="/docs/img/icons/svelte-icon" />
</a>
</Link>
</div>
</div>
</div>
+1 -1
View File
@@ -4,7 +4,7 @@ import { Parameter } from '~/lib/refGenerator/refTypes'
import ReactMarkdown from 'react-markdown'
// Parameters are grouped on the page by tag
const TAGS = ['general', 'auth', 'api', 'database', 'dashboard', 'local']
const TAGS = ['general', 'auth', 'api', 'database', 'dashboard', 'local', 'edge-functions']
export default function Config() {
return (
@@ -0,0 +1,30 @@
import clientLibsCommonSections from '~/../../spec/common-client-libs-sections.json'
import typeSpec from '~/../../spec/enrichments/tsdoc_v2/combined.json'
// @ts-expect-error
import spec from '~/../../spec/supabase_py_v2.yml' assert { type: 'yml' }
import RefSectionHandler from '~/components/reference/RefSectionHandler'
import { flattenSections } from '~/lib/helpers'
import handleRefGetStaticPaths from '~/lib/mdx/handleRefStaticPaths'
import handleRefStaticProps from '~/lib/mdx/handleRefStaticProps'
const sections = flattenSections(clientLibsCommonSections)
export default function PyReference(props) {
return (
<RefSectionHandler
sections={sections}
spec={spec}
typeSpec={typeSpec}
pageProps={props}
type="client-lib"
/>
)
}
export async function getStaticProps() {
return handleRefStaticProps(sections, '/python')
}
export function getStaticPaths() {
return handleRefGetStaticPaths()
}
@@ -0,0 +1,44 @@
import clientLibsCommonSections from '~/../../spec/common-client-libs-sections.json'
import typeSpec from '~/../../spec/enrichments/tsdoc_v2/combined.json'
// @ts-expect-error
import spec from '~/../../spec/supabase_py_v2.yml' assert { type: 'yml' }
import RefSectionHandler from '~/components/reference/RefSectionHandler'
import { flattenSections } from '~/lib/helpers'
import handleRefGetStaticPaths from '~/lib/mdx/handleRefStaticPaths'
import handleRefStaticProps from '~/lib/mdx/handleRefStaticProps'
import { useRouter } from 'next/router'
import RefSEO from '~/components/reference/RefSEO'
const sections = flattenSections(clientLibsCommonSections)
export default function PyReference(props) {
const router = useRouter()
const slug = router.query.slug[0]
const filteredSection = sections.filter((section) => section.id === slug)
const pageTitle = filteredSection[0]?.title
? `${filteredSection[0]?.title} | Supabase`
: 'Supabase'
return (
<>
<RefSEO title={pageTitle} />
<RefSectionHandler
sections={filteredSection}
spec={spec}
typeSpec={typeSpec}
pageProps={props}
type="client-lib"
/>
</>
)
}
export async function getStaticProps() {
return handleRefStaticProps(sections, '/python')
}
export function getStaticPaths() {
return handleRefGetStaticPaths()
}
+3 -2
View File
@@ -15,11 +15,11 @@ Chase Granberry
Chris Copplestone
Danny Ng
Dave Wilson
David Thyresson
Div Arora
Egor Romanov
Fabrizio Fenoglio
Francisco Mazzoni
Haydn Maley
Hieu Pham
Inian P
Isaiah Hamilton
@@ -30,6 +30,7 @@ Jonny Summers-Muir
Joshen Lim
Kang Ming Tay
Karlo Ison
Kevin Brolly
Kevin Grüneberg
Lakshmipathi G
Laura C
@@ -47,7 +48,7 @@ Paul Copplestone
Pavel Borisov
Qiao Han
Ramiro Nuñez Dosio
Rodrigo Mansueli Nunes
Rodrigo Martins Mansueli
Rory Wilding
Stanislav M
Steve Chavez
Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

@@ -1,9 +1,9 @@
<svg width="15" height="18" viewBox="0 0 15 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.0347 0.420898L8.66508 0.426816L0.00683594 9.08506L2.67814 11.7597L5.02598 9.42498L14.0347 0.420898ZM8.81915 8.40844C8.74295 8.40492 8.6644 8.40129 8.61512 8.46638L3.99811 13.0827L6.6501 15.7185L6.64716 15.7214L8.54505 17.6209C8.55749 17.6319 8.56986 17.6443 8.58246 17.6569C8.6344 17.7088 8.69013 17.7645 8.77056 17.7465C9.6472 17.7445 10.5238 17.7448 11.4006 17.7452C12.2775 17.7455 13.1545 17.7458 14.0316 17.7439L9.36417 13.0721L14.0301 8.40918L8.91032 8.40787C8.88183 8.41133 8.85069 8.40989 8.81915 8.40844Z" fill="url(#paint0_linear_657_71721)"/>
<svg width="81" height="80" viewBox="0 0 81 80" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M62.8209 10.9141L44.7971 10.9339L15.7344 39.9967L24.701 48.9743L32.5819 41.1376L62.8209 10.9141ZM45.3142 37.7255C45.0585 37.7136 44.7948 37.7015 44.6294 37.92L29.1317 53.4154L38.0337 62.2629L38.0234 62.273L44.394 68.649C44.4358 68.6861 44.4773 68.7276 44.5196 68.7698C44.6939 68.944 44.881 69.1309 45.151 69.0705C48.0938 69.0639 51.0367 69.065 53.9798 69.0661C56.9231 69.0672 59.8666 69.0683 62.8106 69.0617L47.1433 53.3803L62.8055 37.728L45.6203 37.7235C45.5247 37.7352 45.4201 37.7303 45.3142 37.7255Z" fill="url(#paint0_linear_1785_15060)"/>
<defs>
<linearGradient id="paint0_linear_657_71721" x1="5.29108" y1="13.0823" x2="16.7808" y2="13.0823" gradientUnits="userSpaceOnUse">
<stop stop-color="#429B72"/>
<stop offset="1" stop-color="#D8FFEC" stop-opacity="0.6"/>
<linearGradient id="paint0_linear_1785_15060" x1="15.7344" y1="69.0821" x2="72.627" y2="23.028" gradientUnits="userSpaceOnUse">
<stop stop-color="#171717" stop-opacity="0.5"/>
<stop offset="1"/>
</linearGradient>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 959 B

After

Width:  |  Height:  |  Size: 925 B

@@ -1,9 +1,9 @@
<svg width="15" height="18" viewBox="0 0 15 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.0347 0.420898L8.66508 0.426816L0.00683594 9.08506L2.67814 11.7597L5.02598 9.42498L14.0347 0.420898ZM8.81915 8.40844C8.74295 8.40492 8.6644 8.40129 8.61512 8.46638L3.99811 13.0827L6.6501 15.7185L6.64716 15.7214L8.54505 17.6209C8.55749 17.6319 8.56986 17.6443 8.58246 17.6569C8.6344 17.7088 8.69013 17.7645 8.77056 17.7465C9.6472 17.7445 10.5238 17.7448 11.4006 17.7452C12.2775 17.7455 13.1545 17.7458 14.0316 17.7439L9.36417 13.0721L14.0301 8.40918L8.91032 8.40787C8.88183 8.41133 8.85069 8.40989 8.81915 8.40844Z" fill="url(#paint0_linear_657_71721)"/>
<svg width="81" height="80" viewBox="0 0 81 80" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M62.8209 10.9141L44.7971 10.9339L15.7344 39.9967L24.701 48.9743L32.5819 41.1376L62.8209 10.9141ZM45.3142 37.7255C45.0585 37.7136 44.7948 37.7015 44.6294 37.92L29.1317 53.4154L38.0337 62.2629L38.0234 62.273L44.394 68.649C44.4358 68.6861 44.4773 68.7276 44.5196 68.7698C44.6939 68.944 44.881 69.1309 45.151 69.0705C48.0938 69.0639 51.0367 69.065 53.9798 69.0661C56.9231 69.0672 59.8666 69.0683 62.8106 69.0617L47.1433 53.3803L62.8055 37.728L45.6203 37.7235C45.5247 37.7352 45.4201 37.7303 45.3142 37.7255Z" fill="url(#paint0_linear_1785_15060)"/>
<defs>
<linearGradient id="paint0_linear_657_71721" x1="5.29108" y1="13.0823" x2="16.7808" y2="13.0823" gradientUnits="userSpaceOnUse">
<stop stop-color="#F4FFFA"/>
<stop offset="1" stop-color="#D8FFEC" stop-opacity="0.6"/>
<linearGradient id="paint0_linear_1785_15060" x1="15.7344" y1="69.0821" x2="72.627" y2="23.028" gradientUnits="userSpaceOnUse">
<stop stop-color="#EDEDED" stop-opacity="0.5"/>
<stop offset="1" stop-color="white"/>
</linearGradient>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 959 B

After

Width:  |  Height:  |  Size: 944 B

@@ -1,9 +1,9 @@
<svg width="54" height="54" viewBox="0 0 54 54" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.230469 0.339844H53.7695V53.8789H0.230469V0.339844ZM41.1578 45.2235C38.6763 45.2235 37.2732 43.9292 36.1948 42.1683L32.1063 44.5436C33.5832 47.4619 36.6018 49.6885 41.2742 49.6885C46.0527 49.6885 49.611 47.207 49.611 42.6774C49.611 38.4758 47.1975 36.6071 42.9228 34.774L41.6651 34.2352C39.5065 33.3004 38.5717 32.6894 38.5717 31.1801C38.5717 29.9589 39.5057 29.0241 40.9793 29.0241C42.424 29.0241 43.3545 29.6334 44.2171 31.1801L48.1348 28.6646C46.4777 25.7497 44.178 24.6364 40.9793 24.6364C36.4863 24.6364 33.6113 27.5088 33.6113 31.2821C33.6113 35.3782 36.0231 37.3158 39.6536 38.8625L40.9113 39.4022C43.2058 40.4058 44.5741 41.0168 44.5741 42.742C44.5741 44.1816 43.2424 45.2235 41.1578 45.2235ZM21.6602 45.192C19.9316 45.192 19.2127 44.0065 18.4223 42.6043L14.327 45.0837C15.5134 47.5945 17.8462 49.6791 21.8743 49.6791C26.3325 49.6791 29.3868 47.3081 29.3868 42.0987V24.9237H24.3558V42.0307C24.3558 44.5453 23.3131 45.192 21.6602 45.192Z" fill="url(#paint0_linear_1029_62398)"/>
<svg width="81" height="80" viewBox="0 0 81 80" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.7305 13.2305H67.2695V66.7695H13.7305V13.2305ZM54.6588 58.1144C52.1773 58.1144 50.7743 56.8201 49.6959 55.0593L45.6073 57.4345C47.0843 60.3528 50.1029 62.5794 54.7753 62.5794C59.5538 62.5794 63.1121 60.0979 63.1121 55.5683C63.1121 51.3668 60.6986 49.498 56.4239 47.6649L55.1662 47.1261C53.0076 46.1913 52.0728 45.5803 52.0728 44.071C52.0728 42.8498 53.0068 41.915 54.4804 41.915C55.9251 41.915 56.8556 42.5243 57.7182 44.071L61.6359 41.5555C59.9788 38.6406 57.6791 37.5273 54.4804 37.5273C49.9873 37.5273 47.1124 40.3998 47.1124 44.173C47.1124 48.2691 49.5242 50.2068 53.1546 51.7534L54.4124 52.2931C56.7069 53.2967 58.0751 53.9077 58.0751 55.6329C58.0751 57.0725 56.7435 58.1144 54.6588 58.1144ZM35.1613 58.0829C33.4327 58.0829 32.7138 56.8974 31.9234 55.4952L27.8281 57.9746C29.0145 60.4854 31.3473 62.57 35.3754 62.57C39.8336 62.57 42.8879 60.199 42.8879 54.9896V37.8146H37.8569V54.9216C37.8569 57.4362 36.8142 58.0829 35.1613 58.0829Z" fill="url(#paint0_linear_1785_15055)"/>
<defs>
<linearGradient id="paint0_linear_1029_62398" x1="-9.88098" y1="0.339844" x2="73.784" y2="0.33984" gradientUnits="userSpaceOnUse">
<stop stop-color="#429B72"/>
<stop offset="1" stop-color="#D8FFEC" stop-opacity="0.6"/>
<linearGradient id="paint0_linear_1785_15055" x1="13.7305" y1="66.7695" x2="67.2695" y2="13.2305" gradientUnits="userSpaceOnUse">
<stop stop-color="#171717" stop-opacity="0.5"/>
<stop offset="1"/>
</linearGradient>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

@@ -1,9 +1,9 @@
<svg width="54" height="54" viewBox="0 0 54 54" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.230469 0.339844H53.7695V53.8789H0.230469V0.339844ZM41.1578 45.2235C38.6763 45.2235 37.2732 43.9292 36.1948 42.1683L32.1063 44.5436C33.5832 47.4619 36.6018 49.6885 41.2742 49.6885C46.0527 49.6885 49.611 47.207 49.611 42.6774C49.611 38.4758 47.1975 36.6071 42.9228 34.774L41.6651 34.2352C39.5065 33.3004 38.5717 32.6894 38.5717 31.1801C38.5717 29.9589 39.5057 29.0241 40.9793 29.0241C42.424 29.0241 43.3545 29.6334 44.2171 31.1801L48.1348 28.6646C46.4777 25.7497 44.178 24.6364 40.9793 24.6364C36.4863 24.6364 33.6113 27.5088 33.6113 31.2821C33.6113 35.3782 36.0231 37.3158 39.6536 38.8625L40.9113 39.4022C43.2058 40.4058 44.5741 41.0168 44.5741 42.742C44.5741 44.1816 43.2424 45.2235 41.1578 45.2235ZM21.6602 45.192C19.9316 45.192 19.2127 44.0065 18.4223 42.6043L14.327 45.0837C15.5134 47.5945 17.8462 49.6791 21.8743 49.6791C26.3325 49.6791 29.3868 47.3081 29.3868 42.0987V24.9237H24.3558V42.0307C24.3558 44.5453 23.3131 45.192 21.6602 45.192Z" fill="url(#paint0_linear_1029_62398)"/>
<svg width="81" height="80" viewBox="0 0 81 80" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.7305 13.2305H67.2695V66.7695H13.7305V13.2305ZM54.6588 58.1144C52.1773 58.1144 50.7743 56.8201 49.6959 55.0593L45.6073 57.4345C47.0843 60.3528 50.1029 62.5794 54.7753 62.5794C59.5538 62.5794 63.1121 60.0979 63.1121 55.5683C63.1121 51.3668 60.6986 49.498 56.4239 47.6649L55.1662 47.1261C53.0076 46.1913 52.0728 45.5803 52.0728 44.071C52.0728 42.8498 53.0068 41.915 54.4804 41.915C55.9251 41.915 56.8556 42.5243 57.7182 44.071L61.6359 41.5555C59.9788 38.6406 57.6791 37.5273 54.4804 37.5273C49.9873 37.5273 47.1124 40.3998 47.1124 44.173C47.1124 48.2691 49.5242 50.2068 53.1546 51.7534L54.4124 52.2931C56.7069 53.2967 58.0751 53.9077 58.0751 55.6329C58.0751 57.0725 56.7435 58.1144 54.6588 58.1144ZM35.1613 58.0829C33.4327 58.0829 32.7138 56.8974 31.9234 55.4952L27.8281 57.9746C29.0145 60.4854 31.3473 62.57 35.3754 62.57C39.8336 62.57 42.8879 60.199 42.8879 54.9896V37.8146H37.8569V54.9216C37.8569 57.4362 36.8142 58.0829 35.1613 58.0829Z" fill="url(#paint0_linear_1785_15055)"/>
<defs>
<linearGradient id="paint0_linear_1029_62398" x1="-9.88098" y1="0.339844" x2="73.784" y2="0.33984" gradientUnits="userSpaceOnUse">
<stop stop-color="#F4FFFA"/>
<stop offset="1" stop-color="#D8FFEC" stop-opacity="0.6"/>
<linearGradient id="paint0_linear_1785_15055" x1="13.7305" y1="66.7695" x2="67.2695" y2="13.2305" gradientUnits="userSpaceOnUse">
<stop stop-color="#EDEDED" stop-opacity="0.5"/>
<stop offset="1" stop-color="white"/>
</linearGradient>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

@@ -0,0 +1,9 @@
<svg width="80" height="80" viewBox="0 0 80 80" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M32.7668 10.4216C34.8343 10.0558 37.1841 9.84591 39.6447 9.83448C42.1052 9.82305 44.6707 10.01 47.1433 10.4216C51.0493 11.0726 54.3399 14.0028 54.3399 17.9034V31.6089C54.3399 35.628 51.1452 38.923 47.1433 38.923H32.7668C27.8856 38.923 23.7752 43.1136 23.7752 47.8643V54.4403H18.8264C14.6433 54.4403 12.2 51.4041 11.1768 47.143C9.79667 41.4184 9.85529 37.9973 11.1768 32.5148C12.3225 27.7318 15.9853 25.2175 20.1684 25.2175H25.5701H39.9634V23.389H25.5701V17.9034C25.5701 13.7499 26.676 11.4977 32.7668 10.4216ZM34.5617 16.9976C34.5617 15.4804 33.3476 14.2464 31.8609 14.2464C30.3688 14.2464 29.1601 15.4804 29.1601 16.9976C29.1601 18.5094 30.3688 19.732 31.8609 19.732C33.3476 19.732 34.5617 18.5094 34.5617 16.9976ZM56.1348 31.6092V25.2178H61.5365C65.7249 25.2178 67.6994 28.3508 68.7332 32.5151C70.1719 38.2988 70.2359 42.6346 68.7332 47.1432C67.2784 51.5227 65.7196 54.4405 61.5365 54.4405H54.3399H39.9634V56.269H54.3399V61.7546C54.3399 65.9081 50.7668 68.0195 47.1432 69.0687C41.6919 70.6505 37.3228 70.4083 32.7667 69.0687C28.962 67.9496 25.5701 65.6553 25.5701 61.7546V48.0491C25.5701 44.1054 28.8287 40.735 32.7667 40.735H47.1432C51.9338 40.735 56.1348 36.5644 56.1348 31.6092ZM50.7499 62.6605C50.7499 61.1486 49.5412 59.9261 48.0491 59.9261C46.5624 59.9261 45.3483 61.1486 45.3483 62.6605C45.3483 64.1777 46.5624 65.4116 48.0491 65.4116C49.5412 65.4116 50.7499 64.1777 50.7499 62.6605Z" fill="url(#paint0_linear_1785_15271)"/>
<defs>
<linearGradient id="paint0_linear_1785_15271" x1="10.1636" y1="70.1661" x2="70.492" y2="10.4969" gradientUnits="userSpaceOnUse">
<stop stop-color="#171717" stop-opacity="0.5"/>
<stop offset="1"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

@@ -0,0 +1,9 @@
<svg width="80" height="80" viewBox="0 0 80 80" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M32.7668 10.4216C34.8343 10.0558 37.1841 9.84591 39.6447 9.83448C42.1052 9.82305 44.6707 10.01 47.1433 10.4216C51.0493 11.0726 54.3399 14.0028 54.3399 17.9034V31.6089C54.3399 35.628 51.1452 38.923 47.1433 38.923H32.7668C27.8856 38.923 23.7752 43.1136 23.7752 47.8643V54.4403H18.8264C14.6433 54.4403 12.2 51.4041 11.1768 47.143C9.79667 41.4184 9.85529 37.9973 11.1768 32.5148C12.3225 27.7318 15.9853 25.2175 20.1684 25.2175H25.5701H39.9634V23.389H25.5701V17.9034C25.5701 13.7499 26.676 11.4977 32.7668 10.4216ZM34.5617 16.9976C34.5617 15.4804 33.3476 14.2464 31.8609 14.2464C30.3688 14.2464 29.1601 15.4804 29.1601 16.9976C29.1601 18.5094 30.3688 19.732 31.8609 19.732C33.3476 19.732 34.5617 18.5094 34.5617 16.9976ZM56.1348 31.6092V25.2178H61.5365C65.7249 25.2178 67.6994 28.3508 68.7332 32.5151C70.1719 38.2988 70.2359 42.6346 68.7332 47.1432C67.2784 51.5227 65.7196 54.4405 61.5365 54.4405H54.3399H39.9634V56.269H54.3399V61.7546C54.3399 65.9081 50.7668 68.0195 47.1432 69.0687C41.6919 70.6505 37.3228 70.4083 32.7667 69.0687C28.962 67.9496 25.5701 65.6553 25.5701 61.7546V48.0491C25.5701 44.1054 28.8287 40.735 32.7667 40.735H47.1432C51.9338 40.735 56.1348 36.5644 56.1348 31.6092ZM50.7499 62.6605C50.7499 61.1486 49.5412 59.9261 48.0491 59.9261C46.5624 59.9261 45.3483 61.1486 45.3483 62.6605C45.3483 64.1777 46.5624 65.4116 48.0491 65.4116C49.5412 65.4116 50.7499 64.1777 50.7499 62.6605Z" fill="url(#paint0_linear_1785_15271)"/>
<defs>
<linearGradient id="paint0_linear_1785_15271" x1="10.1636" y1="70.1661" x2="70.492" y2="10.4969" gradientUnits="userSpaceOnUse">
<stop stop-color="#EDEDED" stop-opacity="0.5"/>
<stop offset="1" stop-color="white"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

+2 -2
View File
@@ -13,7 +13,7 @@ date: '2022-12-16'
toc_depth: 3
---
Today we're announcing the 1.0 release of pg_graphql and its general availability on our platform. pg_graphql is a PostgreSQL extension that allows you to query your database using GraphQL. It is the foundation of GraphQL support in the Supabase stack.
Today we're announcing the 1.0 release of [`pg_graphql`](https://github.com/supabase/pg_graphql) and its general availability on our platform. pg_graphql is a PostgreSQL extension that allows you to query your database using GraphQL. It is the foundation of GraphQL support in the Supabase stack.
Since our first platform release, v0.2.1, the feature set of pg_graphql has steadily grown and stabilized. Despite being a pre-1.0, we've been extremely cautious with each new feature and have yet to introduce a backwards-incompatible change. With the 1.0 release we're formalizing that guarantee, subject to the SemVer spec.
@@ -21,7 +21,7 @@ Since our first platform release, v0.2.1, the feature set of pg_graphql has stea
pg_graphql was created to satisfy an extreme set of constraints. Mainly, [Supabase free tier](https://supabase.com/pricing) projects run on servers with 1 GB of memory. On those servers, we squeeze tuned versions of [PostgreSQL](https://github.com/supabase/postgres), [PostgREST](https://postgrest.org/en/stable/), and [GoTrue](https://github.com/supabase/gotrue). Every megabyte consumed by something that isn't PostgreSQL is another chance for an index to fall out of memory, or a large query to fail.
Our philosophy when adding to the stack is to use existing open source tools wherever possible. We surveyed the available GraphQL → SQL options and found some excellent candidates in [Hasura](https://hasura.io/) and [Graphile](https://www.graphile.org/). Both support the set of features we're interested in, but consume significantly more memory than we could sacrifice on the free tier. Realizing that, we searched for an architecture that could meet our runtime constraints and performance requirements.
Our philosophy when adding to the stack is to use existing open source tools wherever possible. We surveyed the available GraphQL → SQL options and found some excellent candidates in Hasura and Graphile. Both support the set of features we're interested in, but consume significantly more memory than we could sacrifice on the free tier. Realizing that, we searched for an architecture that could meet our runtime constraints and performance requirements.
## Architecture
-21
View File
@@ -234,19 +234,6 @@ const Nav = () => {
Blog
</a>
</Link>
<div className="xl:block hidden">
<Link href="/launch-week">
<a
className={`
text-brand-900 hover:text-brand-900 hover:border-brand-1000 dark:text-dark-100 dark:hover:border-dark-100 inline-flex items-center
border-b-2 border-transparent p-5 px-1
text-sm font-medium
`}
>
Launch Week 6
</a>
</Link>
</div>
</div>
</div>
<div className="flex items-center gap-2">
@@ -376,14 +363,6 @@ const Nav = () => {
Blog
</a>
</Link>
<Link href="/launch-week">
<a
target="_blank"
className="text-scale-900 dark:hover:bg-scale-600 block rounded-md py-2 pl-3 pr-4 text-base font-medium hover:border-gray-300 hover:bg-gray-50 dark:text-white"
>
Launch Week 6
</a>
</Link>
</div>
<div className="p-3">
<p className="text-scale-900 mb-6 text-sm">Products available:</p>
+2 -2
View File
@@ -27,8 +27,8 @@
"url": "/pricing"
},
{
"text": "Beta",
"url": "/beta"
"text": "Launch Week 6",
"url": "/launch-week"
}
]
},
+16 -18
View File
@@ -22,6 +22,7 @@
},
{
"title": "Database space",
"tooltip": "Billing is based on the average database size in GB throughout the billing period.",
"tiers": {
"free": "Up to 500 MB",
"pro": "8 GB included, then $0.125 per GB",
@@ -33,15 +34,16 @@
"tooltip": "Backups are entire copies of your database that can be restored in the future.",
"tiers": {
"free": false,
"pro": "7 days of backup* (* if > 1TB, contact enterprise)",
"pro": "7 days of backup (if > 1TB, contact enterprise)",
"enterprise": "Custom"
}
},
{
"title": "Point in time recovery",
"tooltip": "PITR cannot be applied retroactively, projects can only be rolled back to the point from which PITR has been applied.",
"tiers": {
"free": false,
"pro": false,
"pro": "$100 per 7 day rollback period (if > 28 days, contact enterprise)",
"enterprise": true
}
},
@@ -56,7 +58,7 @@
},
{
"title": "Database egress",
"tooltip": "Good news, only download operations are counted towards the limit",
"tooltip": "Billing is based on the total sum of outgoing traffic of your database in GB throughout your billing period.",
"tiers": {
"free": "Up to 2GB",
"pro": "50GB included per month, then $0.09 per GB",
@@ -80,7 +82,7 @@
},
{
"title": "MAUs",
"tooltip": "Monthly Active Users; a user that has made an API request in the last month.",
"tooltip": "Distinct users requesting your API throughout the billing period.",
"tiers": {
"free": "Up to 50,000 MAUs",
"pro": "100,000 included, then $0.00325 per MAU",
@@ -127,14 +129,6 @@
"enterprise": true
}
},
{
"title": "Custom SMTP",
"tiers": {
"free": true,
"pro": true,
"enterprise": true
}
},
{
"title": "Supabase Auth emails",
"tooltip": "Rate limits do not apply to Custom SMTP",
@@ -160,6 +154,7 @@
"features": [
{
"title": "Storage",
"tooltip": "The sum of all objects' size in your storage buckets.",
"tiers": {
"free": "Up to 1 GB",
"pro": "100 GB included, then $0.021 per GB",
@@ -168,7 +163,7 @@
},
{
"title": "Storage egress",
"tooltip": "Good news, only download operations are counted towards the limit",
"tooltip": "Total sum of outgoing traffic (egress) from your storage buckets, only download operations are counted.",
"tiers": {
"free": "Up to 2 GB",
"pro": "200 GB included, then $0.09 per GB",
@@ -194,10 +189,10 @@
},
{
"title": "Asset transformations",
"tooltip": "Origin images are only relevant for image transformations. Images in the bucket which are not transformed do not count towards this limit.",
"tooltip": "Distinct count of all images that were transformed in the billing period, ignoring any transformations.",
"tiers": {
"free": false,
"pro": "Unlimited transformations for 100 origin images, then 5$ per 1000 origin images",
"pro": "Unlimited transformations for 100 origin images, then $5 per 1000 origin images",
"enterprise": "Unlimited"
}
}
@@ -209,6 +204,7 @@
"features": [
{
"title": "Invocations",
"tooltip": "Billing is based on the sum of all invocations, independent of response status, throughout your billing period.",
"tiers": {
"free": "500K per month",
"pro": "2 Million per month, then $2 per 1 million invocations",
@@ -225,6 +221,7 @@
},
{
"title": "Number of functions",
"tooltip": "Billing is based on the maximum amount of functions at any point in time throughout your billing period.",
"tiers": {
"free": "10",
"pro": "100, then $10 per additional 100 functions",
@@ -247,6 +244,7 @@
},
{
"title": "Concurrent Peak Connections",
"tooltip": "We only charge for successful connections - not connection attempts!",
"tiers": {
"free": "Up to 200",
"pro": "Up to 500, then $10 per 1000",
@@ -254,10 +252,11 @@
}
},
{
"title": "Max Messages Per Month",
"title": "Messages Per Month",
"tooltip": "Billing is based on the total sum of Realtime messages throughout your billing period.",
"tiers": {
"free": "2 Million (including database)",
"pro": "5 Million",
"pro": "5 Million, then $2.50 per million",
"enterprise": "Volume discounts on messages"
}
},
@@ -379,7 +378,6 @@
},
{
"title": "Custom Domains",
"tooltip": "Contact us to access this feature",
"tiers": {
"free": false,
"pro": "$10 per domain per month per project add on",
+5
View File
@@ -1,5 +1,10 @@
Pricing Changelog
### 18 Jan 2023
- Clarified over-usage of Realtime messages per month
- On the Pro tier, $2.50 per million after included 5 million messages
### 09 Jan 2023
----
+28 -1
View File
@@ -11,6 +11,11 @@ services:
container_name: supabase-studio
image: supabase/studio:20221214-4eecc99
restart: unless-stopped
healthcheck:
test: [ "CMD", "node", "-e", "require('http').get('http://localhost:3000/api/profile', (r) => {if (r.statusCode !== 200) throw new Error(r.statusCode)})" ]
timeout: 5s
interval: 5s
retries: 3
ports:
- ${STUDIO_PORT}:3000/tcp
environment:
@@ -51,6 +56,11 @@ services:
depends_on:
db: # Disable this if you are using an external Postgres database
condition: service_healthy
healthcheck:
test: [ "CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:9999/health" ]
timeout: 5s
interval: 5s
retries: 3
restart: unless-stopped
environment:
GOTRUE_API_HOST: 0.0.0.0
@@ -109,6 +119,11 @@ services:
depends_on:
db: # Disable this if you are using an external Postgres database
condition: service_healthy
healthcheck:
test: [ "CMD", "bash", "-c", "printf \\0 > /dev/tcp/localhost/4000" ]
timeout: 5s
interval: 5s
retries: 3
restart: unless-stopped
environment:
PORT: 4000
@@ -137,6 +152,13 @@ services:
condition: service_healthy
rest:
condition: service_started
imgproxy:
condition: service_started
healthcheck:
test: [ "CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:5000/status" ]
timeout: 5s
interval: 5s
retries: 3
restart: unless-stopped
environment:
ANON_KEY: ${ANON_KEY}
@@ -159,6 +181,11 @@ services:
imgproxy:
container_name: supabase-imgproxy
image: darthsim/imgproxy:v3.11
healthcheck:
test: [ "CMD", "imgproxy", "health" ]
timeout: 5s
interval: 5s
retries: 3
environment:
IMGPROXY_BIND: ":5001"
IMGPROXY_LOCAL_FILESYSTEM_ROOT: /
@@ -168,7 +195,7 @@ services:
meta:
container_name: supabase-meta
image: supabase/postgres-meta:v0.52.1
image: supabase/postgres-meta:v0.59.0
depends_on:
db: # Disable this if you are using an external Postgres database
condition: service_healthy
@@ -0,0 +1,5 @@
# these values can be found in your project's API settings
# https://app.supabase.com/project/_/settings/api
NEXT_PUBLIC_SUPABASE_URL=your-supabase-url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-supabase-anon-key
@@ -0,0 +1,3 @@
{
"extends": "next/core-web-vitals"
}
@@ -0,0 +1,36 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# local env files
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
@@ -0,0 +1,3 @@
This is an example repo to show how to fetch and cache Supabase data with Next.js Server Components.
Check out [the article](https://supabase.com/blog/fetching-and-caching-supabase-data-in-next-js-server-components) to learn more.
@@ -0,0 +1,21 @@
'use client'
import { useEffect, useState } from 'react'
import supabase from '../../utils/supabase'
export default function ClientPosts() {
const [isLoading, setIsLoading] = useState(true)
const [posts, setPosts] = useState<any>([])
useEffect(() => {
const fetchPosts = async () => {
const { data } = await supabase.from('posts').select()
setPosts(data)
setIsLoading(false)
}
fetchPosts()
}, [])
return isLoading ? <p>Loading</p> : <pre>{JSON.stringify(posts, null, 2)}</pre>
}
@@ -0,0 +1,26 @@
html,
body {
padding: 0;
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell,
Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
}
a {
color: inherit;
text-decoration: none;
}
* {
box-sizing: border-box;
}
@media (prefers-color-scheme: dark) {
html {
color-scheme: dark;
}
body {
color: white;
background: black;
}
}
@@ -0,0 +1,10 @@
export default function Head() {
return (
<>
<title>Create Next App</title>
<meta content="width=device-width, initial-scale=1" name="viewport" />
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/favicon.ico" />
</>
)
}
@@ -0,0 +1,14 @@
import './globals.css'
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
{/*
<head /> will contain the components returned by the nearest parent
head.tsx. Find out more at https://beta.nextjs.org/docs/api-reference/file-conventions/head
*/}
<head />
<body>{children}</body>
</html>
)
}
@@ -0,0 +1,146 @@
.container {
padding: 0 2rem;
}
.main {
min-height: 100vh;
padding: 4rem 0;
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.footer {
display: flex;
flex: 1;
padding: 2rem 0;
border-top: 1px solid #eaeaea;
justify-content: center;
align-items: center;
}
.footer a {
display: flex;
justify-content: center;
align-items: center;
flex-grow: 1;
}
.title {
margin: 0;
line-height: 1.15;
font-size: 4rem;
font-style: normal;
font-weight: 800;
letter-spacing: -0.025em;
}
.title a {
text-decoration: none;
color: #0070f3;
}
.title a:hover,
.title a:focus,
.title a:active {
text-decoration: underline;
}
.title,
.description {
text-align: center;
}
.description {
margin: 4rem 0;
line-height: 1.5;
font-size: 1.5rem;
}
.code {
background: #fafafa;
border-radius: 5px;
padding: 0.75rem;
font-size: 1.1rem;
font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,
Bitstream Vera Sans Mono, Courier New, monospace;
}
.grid {
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
max-width: 1200px;
}
.card {
margin: 1rem;
padding: 1.5rem;
text-align: left;
color: inherit;
text-decoration: none;
border: 1px solid #eaeaea;
border-radius: 10px;
transition: color 0.15s ease, border-color 0.15s ease;
max-width: 300px;
}
.card:hover,
.card:focus,
.card:active {
color: #0070f3;
border-color: #0070f3;
}
.card h2 {
margin: 0 0 1rem 0;
font-size: 1.5rem;
}
.card p {
margin: 0;
font-size: 1.25rem;
line-height: 1.5;
}
.logo {
height: 1em;
margin-left: 0.5rem;
}
@media (max-width: 600px) {
.grid {
width: 100%;
flex-direction: column;
}
}
@media (prefers-color-scheme: dark) {
.title {
background: linear-gradient(180deg, #ffffff 0%, #aaaaaa 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-fill-color: transparent;
}
.title a {
background: linear-gradient(180deg, #0070f3 0%, #0153af 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-fill-color: transparent;
}
.card,
.footer {
border-color: #222;
}
.code {
background: #111;
}
.logo img {
filter: invert(1);
}
}
@@ -0,0 +1,53 @@
import Image from 'next/image'
import styles from './page.module.css'
export default function Home() {
return (
<div className={styles.container}>
<main className={styles.main}>
<h1 className={styles.title}>
Welcome to <a href="https://nextjs.org">Next.js 13!</a>
</h1>
<p className={styles.description}>
Get started by editing <code className={styles.code}>app/page.tsx</code>
</p>
<div className={styles.grid}>
<a href="https://beta.nextjs.org/docs" className={styles.card}>
<h2>Documentation &rarr;</h2>
<p>Find in-depth information about Next.js 13</p>
</a>
<a href="https://github.com/vercel/next.js/tree/canary/examples" className={styles.card}>
<h2>Examples &rarr;</h2>
<p>Explore the Next.js 13 playground.</p>
</a>
<a
href="https://vercel.com/templates/next.js/app-directory?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
className={styles.card}
>
<h2>Deploy &rarr;</h2>
<p>Deploy your Next.js site to a public URL with Vercel.</p>
</a>
</div>
</main>
<footer className={styles.footer}>
<a
href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Powered by{' '}
<span className={styles.logo}>
<Image src="/vercel.svg" alt="Vercel Logo" width={72} height={16} />
</span>
</a>
</footer>
</div>
)
}
@@ -0,0 +1,16 @@
import supabase from '../../utils/supabase'
import RealtimePosts from './realtime-posts'
// do not cache this page
export const revalidate = 0
// this component fetches the current posts server-side
// and subscribes to new posts client-side
export default async function Realtime() {
const { data } = await supabase.from('posts').select('*')
// data can be passed from server components to client components
// this allows us to fetch the initial posts before rendering the page
// our <RealtimePosts /> component will then subscribe to new posts client-side
return <RealtimePosts serverPosts={data} />
}
@@ -0,0 +1,34 @@
'use client'
import { useEffect, useState } from 'react'
import supabase from '../../utils/supabase'
// realtime subscriptions need to be set up client-side
// this component takes initial posts as props and automatically
// updates when new posts are inserted into Supabase's `posts` table
export default function RealtimePosts({ serverPosts }: { serverPosts: any }) {
const [posts, setPosts] = useState(serverPosts)
useEffect(() => {
// this overwrites `posts` any time the `serverPosts` prop changes
// this happens when the parent Server Component is re-rendered
setPosts(serverPosts)
}, [serverPosts])
useEffect(() => {
// ensure you have enabled replication on the `posts` table
// https://app.supabase.com/project/_/database/replication
const channel = supabase
.channel('*')
.on('postgres_changes', { event: 'INSERT', schema: 'public', table: 'posts' }, (payload) =>
setPosts((posts: any) => [...posts, payload.new])
)
.subscribe()
return () => {
supabase.removeChannel(channel)
}
}, [serverPosts])
return <pre>{JSON.stringify(posts, null, 2)}</pre>
}
@@ -0,0 +1,23 @@
import supabase from '../../../utils/supabase'
import { notFound } from 'next/navigation'
// do not cache this page
export const revalidate = 0
export async function generateStaticParams() {
const { data: posts } = await supabase.from('posts').select('id')
return posts?.map(({ id }) => ({
id,
}))
}
export default async function Post({ params: { id } }: { params: { id: string } }) {
const { data: post } = await supabase.from('posts').select().match({ id }).single()
if (!post) {
notFound()
}
return <pre>{JSON.stringify(post, null, 2)}</pre>
}
@@ -0,0 +1,19 @@
import Link from 'next/link'
import supabase from '../../utils/supabase'
// do not cache this page
export const revalidate = 0
export default async function Posts() {
const { data: posts } = await supabase.from('posts').select('id, title')
if (!posts) {
return <p>No posts found.</p>
}
return posts.map((post) => (
<p key={post.id}>
<Link href={`/static/${post.id}`}>{post.title}</Link>
</p>
))
}
@@ -0,0 +1,23 @@
import supabase from '../../../utils/supabase'
import { notFound } from 'next/navigation'
// cache this page for 1 minute
export const revalidate = 60
export async function generateStaticParams() {
const { data: posts } = await supabase.from('posts').select('id')
return posts?.map(({ id }) => ({
id,
}))
}
export default async function Post({ params: { id } }: { params: { id: string } }) {
const { data: post } = await supabase.from('posts').select().match({ id }).single()
if (!post) {
notFound()
}
return <pre>{JSON.stringify(post, null, 2)}</pre>
}
@@ -0,0 +1,19 @@
import Link from 'next/link'
import supabase from '../../utils/supabase'
// cache this page for 1 minute
export const revalidate = 60
export default async function Posts() {
const { data: posts } = await supabase.from('posts').select('id, title')
if (!posts) {
return <p>No posts found.</p>
}
return posts.map((post) => (
<p key={post.id}>
<Link href={`/static/${post.id}`}>{post.title}</Link>
</p>
))
}
@@ -0,0 +1,20 @@
import supabase from '../../../utils/supabase'
import { notFound } from 'next/navigation'
export async function generateStaticParams() {
const { data: posts } = await supabase.from('posts').select('id')
return posts?.map(({ id }) => ({
id,
}))
}
export default async function Post({ params: { id } }: { params: { id: string } }) {
const { data: post } = await supabase.from('posts').select().match({ id }).single()
if (!post) {
notFound()
}
return <pre>{JSON.stringify(post, null, 2)}</pre>
}
@@ -0,0 +1,16 @@
import Link from 'next/link'
import supabase from '../../utils/supabase'
export default async function Posts() {
const { data: posts } = await supabase.from('posts').select('id, title')
if (!posts) {
return <p>No posts found.</p>
}
return posts.map((post) => (
<p key={post.id}>
<Link href={`/static/${post.id}`}>{post.title}</Link>
</p>
))
}
@@ -0,0 +1,8 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
appDir: true,
},
}
module.exports = nextConfig
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,23 @@
{
"name": "next13",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@supabase/supabase-js": "^2.1.0",
"@types/node": "18.11.9",
"@types/react": "18.0.25",
"@types/react-dom": "18.0.9",
"eslint": "8.27.0",
"eslint-config-next": "13.0.3",
"next": "13.0.3",
"react": "18.2.0",
"react-dom": "18.2.0",
"typescript": "4.8.4"
}
}
@@ -0,0 +1,10 @@
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import type { NextApiRequest, NextApiResponse } from 'next'
type Data = {
name: string
}
export default function handler(req: NextApiRequest, res: NextApiResponse<Data>) {
res.status(200).json({ name: 'John Doe' })
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

@@ -0,0 +1,4 @@
<svg width="283" height="64" viewBox="0 0 283 64" fill="none"
xmlns="http://www.w3.org/2000/svg">
<path d="M141.04 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.46 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM248.72 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.45 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM200.24 34c0 6 3.92 10 10 10 4.12 0 7.21-1.87 8.8-4.92l7.68 4.43c-3.18 5.3-9.14 8.49-16.48 8.49-11.05 0-19-7.2-19-18s7.96-18 19-18c7.34 0 13.29 3.19 16.48 8.49l-7.68 4.43c-1.59-3.05-4.68-4.92-8.8-4.92-6.07 0-10 4-10 10zm82.48-29v46h-9V5h9zM36.95 0L73.9 64H0L36.95 0zm92.38 5l-27.71 48L73.91 5H84.3l17.32 30 17.32-30h10.39zm58.91 12v9.69c-1-.29-2.06-.49-3.2-.49-5.81 0-10 4-10 10V51h-9V17h9v9.2c0-5.08 5.91-9.2 13.2-9.2z" fill="#000"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -0,0 +1,25 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
]
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}
@@ -0,0 +1,6 @@
import { createClient } from '@supabase/supabase-js'
export default createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
)
@@ -0,0 +1,13 @@
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import type { NextApiRequest, NextApiResponse } from 'next'
type Data = {
name: string
}
export default function handler(
req: NextApiRequest,
res: NextApiResponse<Data>
) {
res.status(200).json({ name: 'John Doe' })
}
@@ -9,7 +9,7 @@ class SplashPage extends StatefulWidget {
}
class _SplashPageState extends State<SplashPage> {
bool _redicrectCalled = false;
bool _redirectCalled = false;
@override
void didChangeDependencies() {
super.didChangeDependencies();
@@ -18,11 +18,11 @@ class _SplashPageState extends State<SplashPage> {
Future<void> _redirect() async {
await Future.delayed(Duration.zero);
if (_redicrectCalled || !mounted) {
if (_redirectCalled || !mounted) {
return;
}
_redicrectCalled = true;
_redirectCalled = true;
final session = supabase.auth.currentSession;
if (session != null) {
Navigator.of(context).pushReplacementNamed('/account');
+1 -1
View File
@@ -27,7 +27,7 @@
За пълната документация посетете [supabase.com/docs](https://supabase.com/docs)
За да видите как да допринасяте, посетете [Getting Started](./DEVELOPERS.md)
За да видите как да допринасяте, посетете [Getting Started](../DEVELOPERS.md)
## Общност & поддръжка
+1 -1
View File
@@ -27,7 +27,7 @@
Katso täydelliset dokumentaatiot osoitteesta [supabase.com/docs](https://supabase.com/docs)
Katso ohjeet osallistumiseen vierailemalla [Aloitus](./DEVELOPERS.md)
Katso ohjeet osallistumiseen vierailemalla [Aloitus](../DEVELOPERS.md)
## Yhteisö ja tuki
+1 -1
View File
@@ -23,7 +23,7 @@
Pour une documentation complète, visitez [supabase.com/docs](https://supabase.com/docs)
Pour voir comment Contribuer, visitez [Getting Started](./DEVELOPERS.md)
Pour voir comment Contribuer, visitez [Getting Started](../DEVELOPERS.md)
## Communauté et Support
+1 -1
View File
@@ -27,7 +27,7 @@
詳しいドキュメントは[supabase.com/docs](https://supabase.com/docs)をご覧ください。
コントリビュート方法は[Getting Started](./DEVELOPERS.md)をご覧ください。
コントリビュート方法は[Getting Started](../DEVELOPERS.md)をご覧ください。
## コミュニティとサポート
+6 -6
View File
@@ -5,9 +5,9 @@
---
# सुपबेस
# सुपाबेस
[सुपबेस](https://supabase.com) एक खुल्ला स्रोत फायरबेस विकल्प हो। हामीले यसमा उद्यम-ग्रेडका खुल्ला स्रोत उपकरणहरू प्रयोग गरेर फायरबेसका सुविधाहरू निर्माण गर्दैछौं।
[सुपाबेस](https://supabase.com) एक खुल्ला स्रोत फायरबेस विकल्प हो। हामीले यसमा उद्यम-ग्रेडका खुल्ला स्रोत उपकरणहरू प्रयोग गरेर फायरबेसका सुविधाहरू निर्माण गर्दैछौं।
- [x] होस्ट गरिएको Postgres डाटाबेस
- [x] रियलटाइम सदस्यताहरू
@@ -31,7 +31,7 @@
## स्थिति
- [x] अल्फा: हामी ग्राहकहरु को एक सानो समूह संग सुपबेस परीक्षण गर्दैछौं।
- [x] अल्फा: हामी ग्राहकहरु को एक सानो समूह संग सुपाबेस परीक्षण गर्दैछौं।
- [x] सार्वजनिक अल्फा: सबैले [app.supabase.com](https://app.supabase.com) मा साइनअप गर्न सक्दछन्। तर तपाईं केहि अवरोधहरू प्राप्त गर्न सक्नुहुन्छ।
- [x] सार्वजनिक बीटा: अधिकतर गैर-उद्यम प्रयोगका मामलाहरुका लागि पर्याप्त स्थिर।
- [ ] सार्वजनिक: उत्पादन प्रयोगको लागि तयार।
@@ -44,16 +44,16 @@
## यो कसरी काम गर्दछ
सुपबेस एक खुल्ला स्रोत उपकरणहरूको संयोजन हो। हामी उद्यम-ग्रेड, खुल्ला स्रोत उत्पादनहरू प्रयोग गरेर फायरबेसको सुविधाहरू निर्माण गर्दैछौं। यदि उपकरण र समुदायहरू एमआईटी, अपाचे २ वा बराबरका खुल्ला लाइसेन्सको साथ अवस्थित छन् भने हामी त्यसको प्रयोग र समर्थन गर्दछौं। यदि उपकरण अवस्थित छैन भने, हामी त्यसलाई आफैं सिर्जना गर्छौं र स्रोत खुल्ला गर्छौ । सुपबेस फायरबेसको १ देखि १ म्यापि होईन। हाम्रो उद्देश्य भनेको विकासकर्ताहरूलाई खुल्ला-स्रोत उपकरणहरू प्रयोग गरेर फायरबेस जस्तो अनुभव दिने हो।
सुपाबेस एक खुल्ला स्रोत उपकरणहरूको संयोजन हो। हामी उद्यम-ग्रेड, खुल्ला स्रोत उत्पादनहरू प्रयोग गरेर फायरबेसको सुविधाहरू निर्माण गर्दैछौं। यदि उपकरण र समुदायहरू एमआईटी, अपाचे २ वा बराबरका खुल्ला लाइसेन्सको साथ अवस्थित छन् भने हामी त्यसको प्रयोग र समर्थन गर्दछौं। यदि उपकरण अवस्थित छैन भने, हामी त्यसलाई आफैं सिर्जना गर्छौं र स्रोत खुल्ला गर्छौ । सुपाबेस फायरबेसको १ देखि १ म्यापि होईन। हाम्रो उद्देश्य भनेको विकासकर्ताहरूलाई खुल्ला-स्रोत उपकरणहरू प्रयोग गरेर फायरबेस जस्तो अनुभव दिने हो।
**वर्तमान योजना**
सुपबेस एक [होस्ट गरिएको प्लेटफर्म](https://app.supabase.com) हो। तपाई आफै साइन अप गर्न सक्नुहुन्छ र कुनै स्थापना बिना सुपबेस प्रयोग गर्न सक्नुहुन्छ। हामी अझै स्थानीय विकासको अनुभव सिर्जना गर्दैछौं। यो प्लेटफर्म स्थिरताको साथ हाम्रो मूल फोकस हो।
सुपाबेस एक [होस्ट गरिएको प्लेटफर्म](https://app.supabase.com) हो। तपाई आफै साइन अप गर्न सक्नुहुन्छ र कुनै स्थापना बिना सुपाबेस प्रयोग गर्न सक्नुहुन्छ। हामी अझै स्थानीय विकासको अनुभव सिर्जना गर्दैछौं। यो प्लेटफर्म स्थिरताको साथ हाम्रो मूल फोकस हो।
![आर्किटेक्चर](https://user-images.githubusercontent.com/70828596/187547862-ffa9d058-0c3a-4851-a3e7-92ccfca4b596.png)
- [PostgreSQL](https://www.postgresql.org/) एक वस्तु-रिलेशनल डाटाबेस प्रणाली हो जसले ३० बर्ष भन्दा बढी सक्रिय विकासको साथ विश्वसनीयता, सुविधा मजबूती र प्रदर्शनको बलियो प्रतिष्ठा कमाएको छ।
- [Realtime](https://github.com/supabase/realtime) एक Elixer सर्भर हो जसले तपाइँलाई वेबसकेटहरू प्रयोग गरेर PostgreSQL इन्सर्टहरू, अपडेटहरु, र डिलीटहरु सुन्न अनुमति दिन्छ। सुपबेसले पोष्टग्रेसको भित्र निर्मित प्रतिकृति कार्यक्षमता सुन्दछ, प्रतिकृति बाइट स्ट्रिमलाई JSON मा रूपान्तरण गर्दछ, र त्यसपछि JSON लाई वेबसकेटमा प्रसारण गर्दछ।।
- [Realtime](https://github.com/supabase/realtime) एक Elixer सर्भर हो जसले तपाइँलाई वेबसकेटहरू प्रयोग गरेर PostgreSQL इन्सर्टहरू, अपडेटहरु, र डिलीटहरु सुन्न अनुमति दिन्छ। सुपाबेसले पोष्टग्रेसको भित्र निर्मित प्रतिकृति कार्यक्षमता सुन्दछ, प्रतिकृति बाइट स्ट्रिमलाई JSON मा रूपान्तरण गर्दछ, र त्यसपछि JSON लाई वेबसकेटमा प्रसारण गर्दछ।।
- [PostgREST](http://postgrest.org/) एक वेब सर्वर हो जसले तपाईको PostgreSQL डाटाबेसलाई सीधा एक RESTful एपीआई मा बदल्छ।
- [Storage](https://github.com/supabase/storage-api) ले अनुमतिहरू प्रबन्ध गर्न पोष्टग्रेस प्रयोग गरेर S3 मा भण्डारित फाइलहरू प्रबन्ध गर्नका लागि RESTful ईन्टरफेस प्रदान गर्दछ।
- [postgres-meta](https://github.com/supabase/postgres-meta) तपाईंको Postgres प्रबन्धको लागि RESTful एपीआई हो जसले तपाईंलाई टेबुलहरू प्राप्त गर्न, भूमिकाहरू थप्न र प्रश्नहरू चलाउन र अधिकको लागि अनुमति दिन्छ।
+2 -2
View File
@@ -27,7 +27,7 @@
Para documentação completa, visite [supabase.com/docs](https://supabase.com/docs)
Para ver como Contribuir, visite [Primeiros Passos](./DEVELOPERS.md)
Para ver como Contribuir, visite [Primeiros Passos](../DEVELOPERS.md)
## Comunidade & Suporte
@@ -142,7 +142,7 @@ Nossa biblioteca de cliente é modular. Cada sub-biblioteca é uma implementaç
<td>-</td>
<td><a href="https://github.com/supabase-community/gotrue-java" target="_blank" rel="noopener noreferrer">gotrue-java</a></td>
<td>-</td>
<td>-</td>
<td><a href="https://github.com/supabase-community/storage-java" target="_blank" rel="noopener noreferrer">storage-java</a></td>
<td>-</td>
</tr>
<tr>
+1 -1
View File
@@ -27,7 +27,7 @@
Za punu dokumentaciju, posetite [supabase.com/docs](https://supabase.com/docs)
Da vidite kako možete doprineti razvoju, posetite [Getting Started](./DEVELOPERS.md)
Da vidite kako možete doprineti razvoju, posetite [Getting Started](../DEVELOPERS.md)
## Zajednica & Podrška
+1 -1
View File
@@ -27,7 +27,7 @@
Để có tài liệu đầy đủ, hãy truy cập [supabase.com/docs](https://supabase.com/docs)
Để biết cách Đóng góp, hãy truy cập [Bắt đầu](./DEVELOPERS.md)
Để biết cách Đóng góp, hãy truy cập [Bắt đầu](../DEVELOPERS.md)
## Hỗ trợ & Cộng đồng
+1
View File
@@ -159,6 +159,7 @@ module.exports = ui({
position: 'relative',
transition: 'color 0.3s ease-in-out',
paddingBottom: '2px',
fontWeight: '400',
color: 'var(--colors-scale12)',
textDecorationLine: 'underline',
textDecorationColor: 'var(--colors-brand7)',
+2 -2
View File
@@ -441,9 +441,9 @@ export default {
container: 'inline-flex font-medium',
type: {
primary: `
bg-brand-fixed-800 hover:bg-brand-fixed-900
bg-brand-fixed-1100 hover:bg-brand-fixed-1000
text-white
bordershadow-brand-fixed-800 hover:bordershadow-brand-fixed-1000
bordershadow-brand-fixed-1000 hover:bordershadow-brand-fixed-900 dark:bordershadow-brand-fixed-1000 dark:hover:bordershadow-brand-fixed-1000
focus-visible:outline-brand-600
`,
secondary: `
Loaded 100 of 147 files, more files were not shown because too many files have changed in this diff. Show more