Merge remote-tracking branch 'origin/master' into studio

This commit is contained in:
Inian committed 2021-11-29 20:38:54 +05:30
commit 35df9ff9e5
95 files changed
+2320 -1225

No files matched your search

-23
View File
@@ -1,23 +0,0 @@
name: Deploy studio app to production on vercel
on:
push:
branches:
- studio
paths:
- 'studio/**'
jobs:
deploy:
runs-on: ubuntu-18.04
timeout-minutes: 10
steps:
- uses: actions/checkout@v2
- uses: amondnet/vercel-action@v19
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }} # Required
github-comment: false
vercel-args: '--prod -A .vercel/production.json'
vercel-org-id: team_E6KJ1W561hMTjon1QSwOh0WO #Required
vercel-project-id: Qmd7Mw8yFW8SbtvTWFV9uyRzw9P1rJLmgtHjSezywhtYec #Required
working-directory: ./studio #Your Working Directory, Optional
vercel-project-name: app
@@ -1,23 +0,0 @@
name: Deploy studio app to staging on vercel
on:
push:
branches:
- master
paths:
- 'studio/**'
jobs:
deploy:
runs-on: ubuntu-18.04
timeout-minutes: 10
steps:
- uses: actions/checkout@v2
- uses: amondnet/vercel-action@v19
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }} # Required
github-comment: false
vercel-args: '--prod -A .vercel/staging.json'
vercel-org-id: team_E6KJ1W561hMTjon1QSwOh0WO #Required
vercel-project-id: QmcmhbiAtCMFTAHCuGgQscNbke4TzgWULECctNcKmxWCoT #Required
working-directory: ./studio #Your Working Directory, Optional
vercel-project-name: staging-app
+7 -1
View File
@@ -1,7 +1,10 @@
# Secrets
# Secrets
# YOU MUST CHANGE THESE BEFORE GOING INTO PRODUCTION
POSTGRES_PASSWORD=your-super-secret-and-long-postgres-password
JWT_SECRET=your-super-secret-jwt-token-with-at-least-32-characters-long
ANON_KEY=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlIjoiYW5vbiIsImlhdCI6MTYyNzIwODU0MCwiZXhwIjoxOTc0MzYzNzQwfQ.zcaQfHd3VA7XgJmdGfmV86OLVJT9s2MTmSy-e69BpUY
SERVICE_ROLE_KEY=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlIjoic2VydmljZV9yb2xlIiwiaWF0IjoxNjI3MjA4NTQwLCJleHAiOjE5NzQzNjM3NDB9.pkT3PNpO4DtO45Ac5HK_TKCx8sGLgNtV__pr_ZrRSAU
# Auth
@@ -27,6 +30,9 @@ ENABLE_PHONE_AUTOCONFIRM=false
# Ports
## Studio port
STUDIO_PORT=3000
## API endpoint ports
KONG_HTTP_PORT=8000
KONG_HTTPS_PORT=8443
+13 -2
View File
@@ -8,6 +8,17 @@
version: "3.8"
services:
studio:
container_name: supabase-studio
image: supabase/studio:latest
restart: unless-stopped
ports:
- ${STUDIO_PORT}:3000/tcp
environment:
SUPABASE_URL: http://kong:8000
STUDIO_PG_META_URL: http://meta:8080
SUPABASE_SERVICE_KEY: ${SERVICE_ROLE_KEY}
kong:
container_name: supabase-kong
image: kong:2.1
@@ -99,8 +110,8 @@ services:
- rest
restart: unless-stopped
environment:
ANON_KEY: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlIjoiYW5vbiIsImlhdCI6MTYyNzIwODU0MCwiZXhwIjoxOTc0MzYzNzQwfQ.zcaQfHd3VA7XgJmdGfmV86OLVJT9s2MTmSy-e69BpUY
SERVICE_KEY: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlIjoic2VydmljZV9yb2xlIiwiaWF0IjoxNjI3MjA4NTQwLCJleHAiOjE5NzQzNjM3NDB9.pkT3PNpO4DtO45Ac5HK_TKCx8sGLgNtV__pr_ZrRSAU
ANON_KEY: ${ANON_KEY}
SERVICE_KEY: ${SERVICE_ROLE_KEY}
POSTGREST_URL: http://rest:3000
PGRST_JWT_SECRET: ${JWT_SECRET}
DATABASE_URL: postgres://postgres:${POSTGRES_PASSWORD}@db:5432/postgres
+7
View File
@@ -0,0 +1,7 @@
node_modules/
.vercel
.next
.env.local
.env.production
.env.dev
.env.*
+22 -17
View File
@@ -1,24 +1,29 @@
# Extermely simple setup - should be optimized before sending
# to docker hub
## USAGE:
# docker build -t next .
# docker run -p 3010:3000 next
# Build: docker build --target production -t supabase/studio:latest .
# Run: docker run -p 3000:3000 supabase/studio
# Deploy: docker push supabase/studio:latest
# Clean build:
# docker build --target production --no-cache -t supabase/studio:latest .
# docker builder prune
FROM node:16
RUN mkdir -p /home/app/ && chown -R node:node /home/app
WORKDIR /home/app
COPY --chown=node:node . .
FROM node:14 as base
WORKDIR /usr/src/app
COPY package*.json ./
# Sometimes need this for the pg-parser
# RUN apk add --no-cache python build-base gcc g++ libpq
USER node
# Can change to "npm ci" eventually
RUN npm install
FROM base as build
ENV NODE_ENV=build
RUN npm install
COPY . .
RUN npm run build
FROM build as dev
ENV NODE_ENV=dev
EXPOSE 8082
CMD [ "npm", "start" ]
CMD [ "npm", "run", "dev" ]
FROM build as production
ENV NODE_ENV=production
COPY . .
EXPOSE 3000
CMD ["npm", "start"]
@@ -37,7 +37,7 @@ const ProjectUsage: FC<Props> = ({ project }) => {
return (
<div className="mx-6 space-y-6">
<Typography.Title level={4}>Statistics for the past 7 days</Typography.Title>
<Typography.Title level={4}>Statistics for past 24 hours</Typography.Title>
<div className="">
{startDate && endDate && (
<div className="grid lg:grid-cols-4 lg:gap-8">
+9 -14
View File
@@ -1,6 +1,6 @@
import { useProfile, useStore } from 'hooks'
import { ComponentType, useEffect, useState } from 'react'
import { flatten } from 'lodash'
import { flatten, isUndefined } from 'lodash'
import { NextRouter, useRouter } from 'next/router'
import { IS_PLATFORM } from 'lib/constants'
import Connecting from 'components/ui/Loading'
@@ -15,9 +15,6 @@ export function withAuth(
redirectIfFound?: boolean
}
) {
const redirectTo = options?.redirectTo ?? '/'
const redirectIfFound = options?.redirectIfFound
return (props: any) => {
const router = useRouter()
const rootStore = useStore()
@@ -25,21 +22,19 @@ export function withAuth(
const { ref, slug } = router.query
const { app, ui } = rootStore
const page = router.pathname.split('/')[3]
const redirectTo = options?.redirectTo ?? (!isUndefined(ref) ? `/project/${ref}` : '/')
const redirectIfFound = options?.redirectIfFound
const returning =
app.projects.isInitialized && app.organizations.isInitialized ? 'minimal' : undefined
const { profile, isLoading } = useProfile(returning)
const isRedirecting = checkRedirectTo(isLoading, router, profile, redirectTo, redirectIfFound)
// We might probably be able to bring this into a _middleware with next 12
if (!IS_PLATFORM) {
// Hide routes for self-hosted version until features are functionally ready
const page = router.pathname.split('/')[3]
if (PLATFORM_ONLY_PAGES.includes(page)) {
router.push(`/project/${ref}`)
}
}
const isAccessingBlockedPage = !IS_PLATFORM && PLATFORM_ONLY_PAGES.includes(page)
const isRedirecting =
isAccessingBlockedPage ||
checkRedirectTo(isLoading, router, profile, redirectTo, redirectIfFound)
useEffect(() => {
// this should run before redirecting
+3 -1
View File
@@ -55,4 +55,6 @@ const sentryWebpackPluginOptions = {
// Make sure adding Sentry options is the last code to run before exporting, to
// ensure that your source maps include changes from all other Webpack plugins
module.exports = withSentryConfig(moduleExports, sentryWebpackPluginOptions)
module.exports = process.env.NEXT_PUBLIC_IS_PLATFORM === 'true'
? withSentryConfig(moduleExports, sentryWebpackPluginOptions)
: nextConfig
+630 -1132
View File
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -12,6 +12,7 @@
"@auth0/nextjs-auth0": "^1.6.1",
"@headlessui/react": "^1.4.1",
"@monaco-editor/react": "^4.3.1",
"@next/bundle-analyzer": "^12.0.4",
"@radix-ui/react-portal": "^0.1.1",
"@sentry/browser": "^6.15.0",
"@sentry/nextjs": "^6.15.0",
@@ -59,7 +60,6 @@
"zxcvbn": "^4.4.2"
},
"devDependencies": {
"@next/bundle-analyzer": "^12.0.4",
"@types/crypto-js": "^4.0.2",
"@types/lodash": "^4.14.172",
"@types/markdown-table": "^3.0.0",
+1 -1
View File
@@ -93,7 +93,7 @@ const SqlEditor = observer(() => {
if (monaco) {
// Enable pgsql format
const formatprovider = monaco.languages.registerDocumentFormattingEditProvider('pgsql', {
async provideDocumentFormattingEdits(model) {
async provideDocumentFormattingEdits(model: any) {
const value = model.getValue()
const formatted = await formatPgsql(value)
return [
+458
View File
@@ -0,0 +1,458 @@
---
id: auth0
title: 'Auth0'
description: 'Swap out Supabase authentication with Auth0. Let Auth0 handle tokens and signing users in and out, while Supabase enforces authorization policies with Row Level Security (RLS).'
---
This guide steps through building a Next.js application with Auth0 and Supabase. We configure Auth0 to handle authenticating users and managing tokens, while writing our authorization logic in Supabase - using Row Level Security policies.
> Note: This guide is heavily inspired by the [Using Next.js and Auth0 with Supabase](https://auth0.com/blog/using-nextjs-and-auth0-with-supabase/) article on [Auth0's blog](https://auth0.com/blog/). Check it out for a practical step-by-step guide on integrating Auth0 and Supabase.
The full code example for this guide can be found [here](https://github.com/dijonmusters/supabase-auth0-example).
[Auth0](https://auth0.com/) is an authentication and authorization platform, offering numerous strategies to authenticate and manage users. It provides fine-grain control over how users sign in to your application, the token that is generated, and what data is stored about your users.
[Next.js](https://nextjs.org/) is a web application framework built on top of React. We will be using it for this example, as it allows us to write server-side logic within our application. Auth0 have also written a [very well integrated authentication library](https://www.npmjs.com/package/@auth0/nextjs-auth0) specifically for Next.js.
> Note: API routes (serverless functions) in Next.js closely resemble the structure of Node server frameworks - such as Express, Koa and Fastify. The server-side logic in this guide could easily be refactored in one of these frameworks and managed as a separate application to the front-end.
If you don’t have an Auth0 account, create one [here](https://auth0.com/signup).
You will also need a Supabase account, which can be created by signing in [here](https://app.supabase.io/).
## Step 1: Creating an Auth0 tenant
From the Auth0 dashboard, click the menu to the right of the Auth0 logo, and select `Create tenant`.
![Create tenant from Auth0 dashboard](/img/guides/integrations/auth0/IYzHxeW.png)
Enter a `Domain` for your tenant - this will need to be unique.
Select a `Region` - this should be geographically close to the majority of your users.
Select `Development` for `Environment Tag` - this should be production when you're ready to go live.
![Auth0 tenant settings](/img/guides/integrations/auth0/iSA3E0J.png)
## Step 2: Setting up an Auth0 application
From the sidebar menu, select `Applications` > `Applications` and click `Create Application`.
Give your application a name, select the `Regular Web Applications` option and click `Create`.
![Auth0 application settings](/img/guides/integrations/auth0/ANU4Wez.png)
Select `Settings` and navigate to the `Application URIs` section, and update the following:
`Allowed Callback URLs`: `http://localhost:3000/api/auth/callback`
`Allowed Logout URLs`: `http://localhost:3000`
Scroll to the bottom of the `Settings` section and reveal the `Advanced Settings`.
Select `OAuth` and set `JSON Web Token Signature` to `RS256`.
Confirm `OIDC Conformant` is `Enabled`.
Click `Save` to update the settings.
## Step 3: Creating a Supabase project
From your [Supabase dashboard](https://app.supabase.io/), click `New project`.
Enter a `Name` for your Supabase project.
Enter a secure `Database Password`.
Select the same `Region` you selected for your Auth0 tenant.
Click `Create new project`.
![New Supabase project settings](/img/guides/integrations/auth0/qnmJEU7.png)
## Step 4: Creating data in Supabase
From the sidebar menu in the [Supabase dashboard](https://app.supabase.io/), click `Table editor`, then `New table`.
Enter `todo` as the `Name` field.
Select `Enable Row Level Security (RLS)`.
Create two new columns:
- `title` as `text`
- `user_id` as `text`
- `is_complete` as `bool` with the default value `false`
Click `Save` to create the new table.
![Todo table](/img/guides/integrations/auth0/33kqP4K.png)
From the `Table editor` view, select the `todo` table and click `Insert row`.
Fill out the `title` field and click `Save`.
![New row settings](/img/guides/integrations/auth0/mEhHAWC.png)
Click `Insert row` and add a couple of extra todos.
![List of todos](/img/guides/integrations/auth0/dLOvhdq.png)
## Step 5: Building a Next.js app
Create a new Next.js project:
```bash
npx create-next-app <name-of-project>
```
Create a `.env.local` file and enter the following values:
```
AUTH0_SECRET=any-secure-value
AUTH0_BASE_URL=http://localhost:3000
AUTH0_ISSUER_BASE_URL=https://<name-of-your-tenant>.<region-you-selected>.auth0.com
AUTH0_CLIENT_ID=get-from-auth0-dashboard
AUTH0_CLIENT_SECRET=get-from-auth0-dashboard
NEXT_PUBLIC_SUPABASE_URL=get-from-supabase-dashboard
NEXT_PUBLIC_SUPABASE_KEY=get-from-supabase-dashboard
SUPABASE_SIGNING_SECRET=get-from-supabase-dashboard
```
> Note: Auth0 values can be found under `Settings > Basic Information` for your application.
![Auth0 settings](/img/guides/integrations/auth0/o07FaoV.png)
> Note: Supabase values can be found under `Settings > API` for your project.
![Supabase settings](/img/guides/integrations/auth0/r1GAfLo.png)
Restart your Next.js development server to read in the new values from `.env.local`.
```bash
npm run dev
```
## Step 5: Install Auth0 Next.js library
Install the `@auth0/nextjs-auth0` library.
```bash
npm i @auth0/nextjs-auth0
```
Create a new file `pages/api/auth/[...auth0].js` and add:
```jsx
// pages/api/auth/[...auth0].js
import { handleAuth } from '@auth0/nextjs-auth0'
export default handleAuth()
```
> Note: This will create a few API routes for us. The main ones we will use are `/api/auth/login` and `/api/auth/logout` to handle signing users in and out.
Open `pages/_app.js` and wrap our `Component` with the `UserProvider` from Auth0:
```jsx
// pages/_app.js
import React from 'react'
import { UserProvider } from '@auth0/nextjs-auth0'
const App = ({ Component, pageProps }) => {
return (
<UserProvider>
<Component {...pageProps} />
</UserProvider>
)
}
export default App
```
Update `pages/index.js` to ensure the user is logged in to view the landing page.
```jsx
// pages/index.js
import styles from '../styles/Home.module.css'
import { withPageAuthRequired } from '@auth0/nextjs-auth0'
import Link from 'next/link'
const Index = ({ user }) => {
return (
<div className={styles.container}>
<p>
Welcome {user.name}!{' '}
<Link href="/api/auth/logout">
<a>Logout</a>
</Link>
</p>
</div>
)
}
export const getServerSideProps = withPageAuthRequired()
export default Index
```
> Note: `withPageAuthRequired` will automatically redirect the user to `/api/auth/login` if they are not currently logged in.
Test this is working by navigating to `http://localhost:3000` which should redirect you to an Auth0 sign in screen.
![Auth0 sign in screen](/img/guides/integrations/auth0/xLRL7S7.png)
Either `Sign up` for a new account, or click `Continue with Google` to sign in.
You should now be able to view the landing page.
![Landing page](/img/guides/integrations/auth0/YdBKRy6.png)
## Step 6: Sign Auth0 token for Supabase
Currently, neither Supabase or Auth0 allow for a custom signing secret to be set for their JWT. They also use different [signing algorithms](https://auth0.com/docs/configure/applications/signing-algorithms).
Therefore, we need to extract the bits we need from Auth0's JWT, and sign our own to send to Supabase.
We can do that using Auth0's `afterCallback` function, which gets called anytime the user authenticates.
Install the `jsonwebtoken` library.
```bash
npm i jsonwebtoken
```
Update `pages/api/auth/[...auth0].js` with the following:
```jsx
// pages/api/auth/[...auth0].js
import { handleAuth, handleCallback } from '@auth0/nextjs-auth0'
import jwt from 'jsonwebtoken'
const afterCallback = async (req, res, session) => {
const payload = {
userId: session.user.sub,
exp: Math.floor(Date.now() / 1000) + 60 * 60,
}
session.user.accessToken = jwt.sign(payload, process.env.SUPABASE_SIGNING_SECRET)
return session
}
export default handleAuth({
async callback(req, res) {
try {
await handleCallback(req, res, { afterCallback })
} catch (error) {
res.status(error.status || 500).end(error.message)
}
},
})
```
Our `payload` for the JWT will contain our user's unique identifier from Auth0 - `session.user.sub` and an expiry of 1 hour.
We are signing this JWT using Supabase's signing secret, so Supabase will be able to validate it is authentic and hasn't been tampered with in transit.
> Note: We need to sign the user out and back in again to run the `afterCallback` function, and create our new token.
Now we just need to send the token along with the request to Supabase.
## Step 7: Requesting data from Supabase
Create a new file called `utils/supabase.js` and add the following:
```jsx
// utils/supabase.js
import { createClient } from '@supabase/supabase-js'
const getSupabase = (access_token) => {
const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL,
process.env.NEXT_PUBLIC_SUPABASE_KEY
)
if (access_token) {
supabase.auth.session = () => ({
access_token,
})
}
return supabase
}
export { getSupabase }
```
This will be our client for talking to Supabase. We can pass it an `access_token` and it will be attached to our request.
Let's load our `todos` from Supabase in our landing page!
```jsx
// pages/index.js
import styles from '../styles/Home.module.css'
import { withPageAuthRequired } from '@auth0/nextjs-auth0'
import { getSupabase } from '../utils/supabase'
import Link from 'next/link'
import { useEffect } from 'react'
const Index = ({ user }) => {
const [todos, setTodos] = useState([])
const supabase = getSupabase(user.accessToken)
useEffect(() => {
const fetchTodos = async () => {
const { data } = await supabase.from('todo').select('*')
setTodos(data)
}
fetchTodos()
}, [])
return (
<div className={styles.container}>
<p>
Welcome {user.name}!{' '}
<Link href="/api/auth/logout">
<a>Logout</a>
</Link>
</p>
{todos?.length > 0 ? (
todos.map((todo) => <p key={todo.id}>{todo.content}</p>)
) : (
<p>You have completed all todos!</p>
)}
</div>
)
}
export const getServerSideProps = withPageAuthRequired()
export default Index
```
Alternatively, we could fetch todos on the server using the `getServerSideProps` function.
```jsx
// pages/index.js
import styles from '../styles/Home.module.css'
import { withPageAuthRequired, getSession } from '@auth0/nextjs-auth0'
import { getSupabase } from '../utils/supabase'
import Link from 'next/link'
const Index = ({ user, todos }) => {
return (
<div className={styles.container}>
<p>
Welcome {user.name}!{' '}
<Link href="/api/auth/logout">
<a>Logout</a>
</Link>
</p>
{todos?.length > 0 ? (
todos.map((todo) => <p key={todo.id}>{todo.content}</p>)
) : (
<p>You have completed all todos!</p>
)}
</div>
)
}
export const getServerSideProps = withPageAuthRequired({
async getServerSideProps({ req, res }) {
const {
user: { accessToken },
} = await getSession(req, res)
const supabase = getSupabase(accessToken)
const { data: todos } = await supabase.from('todo').select('*')
return {
props: { todos },
}
},
})
export default Index
```
Either way, when we reload our application, we are still getting the empty state for todos.
![Empty todo list](/img/guides/integrations/auth0/XgEMwnN.png)
This is because we enabled Row Level Security, which blocks all requests by default. To enable our user to select their `todos` we need to write a policy.
## Step 8: Write a policy to allow select
Our policy will need to know who our currently logged in user is to determine whether or not they should have access. Let's create a PostgreSQL function to extract the current user from our new JWT.
Navigate back to the Supabase dashboard, select `SQL` from the sidebar menu, and click `New query`. This will create a new query called `new sql snippet`, which will allow us to run any SQL against our Postgres database.
Write the following and click `Run`.
```sql
create or replace function auth.user_id() returns text as $$
select nullif(current_setting('request.jwt.claim.userId', true), '')::text;
$$ language sql stable;
```
This will create a function called `auth.user_id()`, which will inspect the `userId` field of our JWT payload.
> Note: To learn more about PostgreSQL functions, check out [our deep dive video](https://www.youtube.com/watch?v=MJZCCpCYEqk).
Let's create a policy that checks whether this user is the owner of the todo.
Select `Authentication` from the Supabase sidebar menu, click `Policies`, and then `New Policy` on the `todo` table.
![Create new policy](/img/guides/integrations/auth0/M7XyhHe.png)
From the modal, select `Create a policy from scratch` and add the following.
![Policy settings for SELECT](/img/guides/integrations/auth0/wuWz3am.png)
This policy is calling the function we just created to get the currently logged in user's ID `auth.user_id()` and checking whether this matches the `user_id` column for the current `todo`. If it does, then it will allow the user to select it, otherwise it will continue to deny.
Click `Review` and then `Save policy`.
> Note: To learn more about RLS and policies, check out [our deep dive video](https://www.youtube.com/watch?v=Ow_Uzedfohk).
The last thing we need to do is update the `user_id` columns for our existing `todos`.
Head back to the Supabase dashboard, and select `Table editor` from the sidebar.
![User ID null in Supabase Table Editor](/img/guides/integrations/auth0/dLOvhdq.png)
Each of our `user_id` columns are set to `NULL`!
To get the ID for our Auth0 user, head over to the Auth0 dashboard, select `User Management` from the sidebar, click `Users` and select your test user.
![List of users in Auth0 dashboard](/img/guides/integrations/auth0/GdXS013.png)
Copy their `user_id`.
![User ID in Auth0 dashboard](/img/guides/integrations/auth0/tbvd0Uj.png)
Update each row in Supabase.
![User ID set to Auth0 user](/img/guides/integrations/auth0/tPu4Tt8.png)
Now when we refresh our application, we should finally see our list of `todos`!
> Note: Check out [the repo](https://github.com/dijonmusters/supabase-auth0-example/blob/main/pages/index.js) for an example of writing new `todos` to Supabase.
## Resources
- [Auth0](https://auth0.com/) official website.
- [Auth0 blog](https://auth0.com/blog/).
- [Using Next.js and Auth0 with Supabase article](https://auth0.com/blog/using-nextjs-and-auth0-with-supabase/).
- [Auth0 community](https://community.auth0.com/).
- [Auth0 documentation](https://auth0.com/docs/).
+231
View File
@@ -0,0 +1,231 @@
---
id: draftbit
title: 'Draftbit'
description: 'Connect your Supabase postgres database to your Low-code mobile app.'
---
This guide explains how to connect a Supabase back-end to a Draftbit front-end and then configure all CRUD operations necessary to build a simple mobile app.
[Draftbit](https://draftb.it/3Fkbask) is a "pro-code" low-code mobile app building platform. Draftbit exports React Native source code that is 100% run on open-source languages and libraries.
Draftbit is back-end agnostic and connects to Supabase via REST API.
> Note: For the demonstration purpose of this guide, we are using a pre-populated database in Supabase. We are calling `Groceries`. To follow along, rename it any way you prefer.
![Prepopulated Database](/img/guides/integrations/draftbit/prepopulated-database.png)
If you don’t have a Draftbit account, create one [here](https://draftb.it/3Fkbask). Once you’ve got your account set up, Create a New App. You can select `Start From a Blank App` for this demo and proceed to the Builder interface.
## Step 1: Get the RESTful endpoint and Project API key
To connect the REST API in the Draftbit app, the following fields are required:
- Base URL of the REST API, which is in the format: `https://<your-domain>.supabase.co/rest/v1` where the `<your-domain` is a unique domain name generated by Supabase.
- The `supabase-key` is the secret key.
You can find these unique values in the API settings of your Supabase account.
- Click the Settings button from the top menu bar.
- In Settings, select **API**.
- In the Project API Keys section, select and copy the API key under `anon`. It is required for every request made to the Supabase database.
- Also, under `Config`, select and copy the URL. It is the Base URL of your Supabase REST API. It will be requirest to make a connection to the Draftbit app.
![Get Supabase connection string](/img/guides/integrations/draftbit/endpoint.png)
## Step 2: Save Supabase API key as Authorization Header in Draftbit
To authorize your Draftbit app with Supabse, in the builder interface:
- Open the **Settings** tab from the top menu bar.
- In Project Settings, navigate to App Variables.
- Enter a name to access the API Key such as `Authorization_Header`. When making the service connection in the next section, it will be passed as the value for the header `Authorization`.
- The value of this key requires you to enter an authorization token that starts with syntax `Bearer <your-api-key>` (the space between `Bearer` and `<your-api-key>` is required). Click **Add** after adding the value.
- Enter another key name to access the API Key such as `Api_Key_Header`. When making the service connection in the next section, it will be passed as the header `apiKey` value.
- The value of this key requires you to enter an authorization token that starts with syntax is `<your-api-key>`. Click **Add** after adding the value.
- Click **Save** to save these keys and close the modal.
![Add header values in Draftbit](/img/guides/integrations/draftbit/authheader.png)
## Step 3: Add Supabase RESTful endpoint in Draftbit
In your Draftbit builder interface:
- Open the **API & Cloud Services** modal from the top menu bar.
- From the **Connect a service** menu, click on **Rest API**.
- In Step 1: Enter a name for your REST API. Then, paste your `Base URL` (from the first section) into the Base URL field.
- In Step 2: Under **Key** add `Authorization` and `apikey`. Then, under **Value**, select the global variables (from the previous section) to add the actual values for both keys.
- Click Save.
![Create a API service in Draftbit](/img/guides/integrations/draftbit/service.png)
## Making API requests with Supabase & Draftbit
### GET request to Fetch all records
In this section, let's populate a Fetch component with all the data from a simple Supabase and then display the data fetched from the Supabase data table in a List component.
For a reference, here is a how the Components tree looks like for this screen:
![Components tree](/img/guides/integrations/draftbit/ctree.png)
The next step is to create an endpoint. Let's try fetching all the data using a `GET` HTTP request. Select the Supabase service in the **API & Cloud Services** modal, and then:
- Click **Add endpoint**.
- In Step 1: enter the name for the endpoint. Make sure the **Method** select is `GET`.
- In Step 2: add the base name path: `/groceries/select=*`, where `groceries` is the table name in Supabase.
- In Step 4: click the **Test** button next to the Endpoint input to verify the response coming from the Supabase.
- Click Save.
![Creating a GET request endpoint](/img/guides/integrations/draftbit/get-request.gif)
In the Builder, on the app screen:
- Select the Fetch component in the Components tree and go to the [Data tab from Properties Panel](doc:introduction-to-the-builder#properties-panel).
- For **Service**, select the name of the Supabase Service.
- For **Endpoint**, select the endpoint you want to fetch the data from.
- Select the List component in the Components and go to the [Data tab from Properties Panel](doc:introduction-to-the-builder#properties-panel). In Data, select `Top-Level Response` from the dropdown menu.
- Then, select the Text component in the Components and then go to the Data tab from the Properties Panel.
- Add a `{{varName}}` value (inside the curly braces) to represent a column field from the Supabase. For example, add `{{title}}` to represent the column name from the Supabase Base.
- Under **Variables**, you will see the variable name defined in the previous step. From the dropdown menu, select the appropriate field that represents the data field.
![Fetching data on app screen](/img/guides/integrations/draftbit/fetchall.gif)
### GET request to fetch single row
Open the **API & Cloud services** modal from the top menu, select the Supabase service, and then:
- Click **Add endpoint**.
- In Step 1: enter a name for the endpoint.
- In Step 2: add the `/groceries/column-name=eq.{{column-name}}` variable. Then, add a Test value for the `{{column-name}}`. For example, it can be the `title` or the `id`.
- In Step 4: click the **Test** button next to the Endpoint input to verify the response coming from the Supabase.
- Click Save.
![Creating endpoint to fetch a single row](/img/guides/integrations/draftbit/getsingle.gif)
On app screen:
- Select the Fetch component in the Components tree and go to the Data tab from Properties Panel
- For **Service**, select the name of the Supabase Service.
- For **endpoint**, select the endpoint you want to fetch the data from.
- Set the value for the `id` in the Configuration > URL Structure section to Navigation > id.
- Select the List component in the Components and go to the Data tab from Properties Panel. In Data, select `Top-Level Response` from the dropdown menu.
- Then, select the Text component in the Components and then go to the Data tab from the Properties Panel.
- Add a `{{varName}}` value (inside the curly braces) to represent the column field from the Supabase. For example, add `{{title}}` to represent the field and value from the Supabase data table.
- Under **Variables**, you will see the variable name defined in the previous step. From the dropdown menu, select the appropriate field that represents the data field.
![Displaying data from single row](/img/guides/integrations/draftbit/fetchsingle.png)
### POST request to submit a new row
Submitting new Data from the Draftbit app to Supabase's REST API requires the request to be sent using the HTTP `POST` method.
For this section, you need to use at least one component that accepts user input and has a Field Name prop to POST data using Supabase REST API.
You can use one of the following components in Draftbit:
- Text Input
- Text Area/Text Field
- Checkbox
- Slider
- Radio Button Group
- Radio Button
In addition, you need a Touchable component like a Button to attach the POST action. After you have created these components, we will create the `POST` endpoint:
- Click **Add endpoint**.
- In Step 1: enter a name for the endpoint and select the Method to `POST`.
- In Step 2: enter the base name as path: `/groceries`.
- In Step 3: add a valid Body structure to submit a POST request. Add one or many `{{variable}}` for test values. Click Body Preview to validate the structure of the Body in the request. For the example, let's create a variable called `{{inputValue}}`.
- In Step 4: to see the new row added to the Supabase data table as JSON response inside the Builder, you have to pass a new header called `Prefer` with its value as `return=representation`.
- In Step 5: click the **Test** button next to the Endpoint input to verify the response coming from the Supabase and click Save.
![Make a POST request to add new data to Supabase database](/img/guides/integrations/draftbit/postrequest.gif)
Once you follow the above steps, you should get a 200 OK response with exactly the new record as a JSON you have entered for your schema.
An example of how Body in a request will look like:
```json
{
"title": {{inputValue}}
}
```
Where `title` is the column name in your Supabase database table.
In Draftbit, using a Touchable or a Button component, you can trigger the action **API Request** to submit the data to the endpoint.
Now, there is a working `POST` request in Draftbit. Map its response to the components on your screen in Draftbit.
First, for each input component, make sure you have set the Field Names (found in the Configs tab, second from the left) to unique values. For example, in the screen below, there is one TextInput field component with the the value of the `Field Name` prop of `textInputValue`.
![Field Name prop on a TextInput component](/img/guides/integrations/draftbit/textinput.png)
Next, on your Button component, go to the Interactions tab in the Properties panel located on the far-right-hand side. Select an Action called `API request`.
In the API request action:
- In **Service**, select the name to Supabase API Service.
- In **Endpoint**, select the name of the Endpoint.
- Then add the configuration for the body request to be sent by selecting the values for `{{inputValue}}`.
![Setting up the API Request to send a POST request](/img/guides/integrations/draftbit/postapirequest.png)
After completing the above steps, you can trigger the API request to submit new data to the Supabase database.
### PATCH request to Update a new record
Updating an existing record from the Draftbit app to Supabase's REST API requires the request to be sent using the HTTP `PATCH` method.
After you have created your screen components in the Draftbit builder, open the Supabase service and make the `PATCH` endpoint:
- Click **Add endpoint**.
- In Step 1: enter a name for the endpoint and select the Method to `PATCH`.
- In Step 2: enter the base name as path: `/groceries?id=eq.{{id}}`, where `id` is the value of an existing record in the database.
- In Step 3: add a valid Body structure to submit a PATCH request. Add one or many `{{variable}}` for test values depending on the structure of your app. Click Body Preview to validate the structure of the Body in the request. For the example, let's create a variable called `{{inputValue}}`.
- In Step 5: click the **Test** button next to the Endpoint input to verify the response coming from the Supabase and click Save.
![Creating an endpoint for PATCH request](/img/guides/integrations/draftbit/patch.gif)
Next, on your Button component, go to the Interactions tab in the Properties panel located on the far-right-hand side. Select an Action called `API request`.
In the API request action:
- In **Service**, select the name to Supabase API Service.
- In **Endpoint**, select the name of the Endpoint.
- Then add the configuration for the query param, and the body request to be sent by selecting the values for `{{inputValue}}`.
![Setting up the API Request to send a PATCH request](/img/guides/integrations/draftbit/patchapirequest.png)
After completing the above steps, you can trigger the API request to update existing data in the Supabase database.
### DELETE request to remove an existing record
The `DELETE` request is to the Supabase with an item's `column-name` to remove that particular record from the table. You can use a [filter from Supabase](https://supabase.io/docs/reference/javascript/using-filters) to filter the value of a specific `column-name`.
After you have created your screen components in the Draftbit builder, open the Supabase service and create the `DELETE` endpoint:
- Click **Add endpoint**.
- In Step 1: enter a name for the endpoint and select the Method to `DELETE`.
- In Step 2: add `/groceries/columnName=eq.{{columnName}}`. Then, add a Test value for the `{{columnName}}`. For example, the `{{columnName}}` here can be `id` of the record.
- In Step 4: click the **Test** button next to the Endpoint input to verify the response from the Supabase.
- Click Save.
![Creating an endpoint for DELETE request](/img/guides/integrations/draftbit/delete.gif)
Next, on your Button component, go to the Interactions tab in the Properties panel located on the far-right-hand side. Select an Action called `API request`.
In the API Request action:
- In **Service**, select the name to Supabase API Service.
- In **Endpoint**, select the name of the Endpoint.
- Then, add the configuration for the query request to be sent by selecting a value. For example, in this case it will be the `id` of the record coming from the Navigation parameter.
![Setting up the API Request to send a DELETE request](/img/guides/integrations/draftbit/deleteapirequest.gif)
## Resources
- [Draftbit](https://draftb.it/3Fkbask) official website.
- [Draftbit Community](https://community.draftbit.com/home).
- [Draftbit](https://docs.draftbit.com/) documentation.
+146
View File
@@ -0,0 +1,146 @@
---
id: prisma
title: 'Prisma'
description: 'Connect your Supabase postgres database to your Prisma project.'
---
This guide explains how to quickly connect the Postgres database provided by Supabase to a Prisma project.
[Prisma](https://prisma.io) is an [open source](https://github.com/prisma/prisma) next-generation ORM. It consists of the following parts:
- **Prisma Client**: Auto-generated and type-safe query builder for Node.js & TypeScript.
- **Prisma Migrate**: Migration system.
- **Prisma Studio**: GUI to view and edit data in your database.
## Step 1: Get the connection string from Supabase project settings
Go to the settings page from the sidebar and navigate to the **Database** tab. You’ll find the database’s connection string with a placeholder for the password you provided when you created the project.
![Getting the connection string](/img/guides/integrations/prisma/zntcsh3ic91gf1gy8j73.png)
## Step 2: Testing the connection
To make sure that everything works correctly, let’s try the connection string in a Prisma project.
If you already have one, all you need to do is set the `DATABASE_URL` to the connection string (including the password) in your `.env` file, and you’re good to go.
In case you don’t have a Prisma project or this is your first time working with Prisma, you’re going to use with the repo from the [quickstart](https://www.prisma.io/docs/getting-started/quickstart) guide.
### Cloning the starter project
Navigate into a directory of your choice and run the following command in your terminal if you’re on a Windows machine:
```bash
curl https://pris.ly/quickstart -L -o quickstart-master.tar.gz && tar -zxvf quickstart-master.tar.gz quickstart-master/typescript/starter && move quickstart-master\typescript\starter starter && rmdir /S /Q quickstart-master && del /Q quickstart-master.tar.gz
```
And if you’re using Mac OS or Linux, run the following command:
```bash
curl -L https://pris.ly/quickstart | tar -xz --strip=2 quickstart-master/typescript/starter
```
You can now navigate into the directory and install the project’s dependencies:
```bash
cd starter && npm install
```
### A look at the project’s structure
This project comes with TypeScript configured and has the following structure.
- A `prisma` directory which contains:
- A `dev.db` file: This is a SQLite database.
- A `schema.prisma` file: Where we define the different database models and relations between them.
- A `.env` file: Contains the `DATABASE_URL` variable, which Prisma will use.
- A `script.ts` file: where we will run some queries using Prisma Client.
This starter also comes with the following packages installed:
- [`@prisma/client`](https://www.npmjs.com/package/prisma): An auto-generated and type-safe query builder that’s _tailored_ to your data.
- [`prisma`](https://www.npmjs.com/package/@prisma/client): Prisma’s command-line interface (CLI). It allows you to initialize new project assets, generate Prisma Client, and analyze existing database structures through introspection to automatically create your application models.
> Note: Prisma works with both JavaScript and TypeScript. However, to get the best possible development experience, using TypeScript is highly recommended.
### Configuring the project to use PostgreSQL
Go ahead and delete the `prisma/dev.db` file because we will be switching to PostgreSQL.
Next, inside the `prisma/.env` file, update the value of the `DATABASE_URL` variable to the connection string you got in **step 3**. The URL might look as follows:
```env
# prisma/.env
postgres://postgres:[YOUR-PASSWORD]@db.vdbnhqozmlzdsaejdxwr.supabase.co:5432/postgres
```
Finally, inside your `schema.prisma` file, change the `provider` from “sqlite” to `“postgresql”`.
This is what your `schema.prisma` file should look like:
```go
datasource db {
provider = “postgresql”
url = env(“DATABASE_URL”)
}
generator client {
provider = “prisma-client-js”
}
model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
author User? @relation(fields: [authorId], references: [id])
authorId Int?
}
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
posts Post[]
}
```
To test that everything works correctly, run the following command to create a migration:
```bash
prisma migrate dev --name init
```
You can optionally give your migration a name, depending on the changes you made. Since this is the project’s first migration, you’re setting the `--name` flag to “init”.
If everything works correctly, you should get the following message in your terminal:
```text
Your database is now in sync with your schema.
:heavy_check_mark: Generated Prisma Client (2.x.x) to ./node_modules/@prisma/client in 111ms
```
This will create a `prisma/migrations` folder inside your `prisma` directory and synchronize your Prisma schema with your database schema.
> Note: if you want to skip the process of creating a migration history, you can use the [`db push`](https://www.prisma.io/docs/concepts/components/prisma-migrate/db-push) command instead of `migrate dev`.
> If you go to your Supabase project, in the table editor, you should see that two tables have been created, a `Post` and a `User` table.
> ![tables created in the UI](/img/guides/integrations/prisma/7y4qq4wwvfrheti6r09u.png)
> That’s it! You have now successfully connected a Prisma project to a PostgreSQL database hosted on Supabase and ran your first migration.
## Connection pooling with Supabase
If you’re working in a serverless environment (for example Node.js functions hosted on AWS Lambda, Vercel or Netlify Functions), you need to set up [connection pooling](https://www.prisma.io/docs/guides/performance-and-optimization/connection-management#serverless-environments-faas) using a tool like [PgBouncer](https://www.pgbouncer.org/). That’s because every function invocation may result in a [new connection to the database](https://www.prisma.io/docs/guides/performance-and-optimization/connection-management#the-serverless-challenge). Supabase [support connection management using PgBouncer](https://supabase.io/blog/2021/04/02/supabase-pgbouncer#what-is-connection-pooling) and are enabled by default.
Go to the **Database** page from the sidebar in the Supabase dashboard and navigate to **connection pool** settings
![Connection pool settings](/img/guides/integrations/prisma/w0oowg8vq435ob5c3gf0.png)
When running migrations you need to use the non pooled connection URL (like the one we used in **step 4**). However, when deploying your app, you’ll use the pooled connection URL. and add the `?pgbouncer=true` flag to the PostgreSQL connection URL. The URL might look as follows:
```env
# prisma/.env
postgres://postgres:[YOUR-PASSWORD]@db.vdbnhqozmlzdsaejdxwr.supabase.co:6543/postgres?pgbouncer=true
```
Prisma Migrate uses database transactions to check out the current state of the database and the migrations table. However, the Migration Engine is designed to use a single connection to the database, and does not support connection pooling with PgBouncer. If you attempt to run Prisma Migrate commands in any environment that uses PgBouncer for connection pooling, you might see the following error:
```bash
Error: undefined: Database error
Error querying the database: db error: ERROR: prepared statement “s0” already exists
```
This is a known issue and it is being worked on, you can follow the progress on this [GitHub issue](https://github.com/prisma/prisma/issues/6485).
If you want to learn more about Prisma, check out the [docs](https://www.prisma.io/docs). Also in case you have any questions or run into any issue, feel free to start a discussion in the repo’s [discussions section](https://github.com/prisma/prisma/discussions).
## Resources
- [Prisma](https://prisma.io) official website.
- [Prisma GitHub](https://github.com/prisma/prisma).
- [Prisma](https://www.prisma.io/docs/) documentation.
+241
View File
@@ -0,0 +1,241 @@
---
id: vercel
title: 'Vercel'
description: The fastest way to get up and running with an application that uses Supabase is with Vercel's Next.js starter and Supabase integration.
---
This guide steps through using Vercel's dashboard to create a Next.js project integrated with Supabase. To further streamline the process, we will be using the Next.js starter template, which can be automatically forked to a new GitHub repo, without leaving the dashboard!
If you don’t have a Vercel account, create one [here](https://vercel.com/signup).
## Step 1: Create a Supabase project
This guide could use an existing Supabase project, but to create the `todo` demo from scratch, navigate to [Supabase](https://app.supabase.io/), click `Sign In` and authenticate with GitHub to login or register a new account.
From the Supabase dashboard, click `New project` and select an organization.
> Note: You may need to create an organization first.
Give your project a `name`, `password`, select a `region` close to your potential users and click `Create new project`.
![Create a Supabase project](/img/guides/integrations/vercel/create-supabase-project.png)
Supabase will take a couple of minutes to configure the infrastructure.
Once this is finished, navigate to `SQL Editor` from the sidebar menu and click `New query`.
This will create a new SQL snippet called "New Query". Copy and paste the following and click `Run`.
```sql
create table todos (
id bigint generated by default as identity primary key,
title text,
is_complete boolean default false,
created_at timestamp with time zone default timezone('utc'::text, now()) not null
);
alter table todos enable row level security;
create policy "Anyone can view todos" on todos for
select using (true);
create policy "Anyone can add new todos" on todos for
insert with check (true);
insert into todos(title)
values
('Create Supabase project'),
('Create Vercel project'),
('Install Supabase integration');
```
This will create a new todos table, enable row level security, add policies for selecting and inserting data, and add some example rows.
> Note: To simplify this example, we are allowing anyone to `select` and `insert` rows on the `todos` table. Usually, these actions would be locked down to only allow logged in users to perform them. Check out [this video](https://www.youtube.com/watch?v=Ow_Uzedfohk) to learn more about Row Level Security and policies.
## Step 2: Create Vercel project
From your [Vercel dashboard](https://vercel.com/dashboard), click `New Project`.
![Create new Vercel project](/img/guides/integrations/vercel/create-vercel-project.png)
Under the `Clone Template` menu, click `Next.js`.
![Clone Next.js template](/img/guides/integrations/vercel/clone-next-js-template.png)
In the `Create Git Repository` section, click `GitHub`, select your username under `GIT SCOPE`, enter a name for your project, choose whether you want your repo `private` or `public`, and click `Create`.
![New GitHub repo settings](/img/guides/integrations/vercel/repo-settings.png)
This will create a new GitHub repository, clone and commit the Next.js starter project, then build and deploy your new project to Vercel.
Once you have been redirected to the `Congratulations` screen, click `Go to Dashboard`.
Navigate to `Settings`, `Integrations`, then click `Browse Marketplace`.
Search for `Supabase` and click the Supabase integration.
![Supabase integration](/img/guides/integrations/vercel/supabase-integration.png)
Click `Add Integration`. Select your account from the `Vercel Scope` dropdown, and click `CONTINUE`.
![Choose scope](/img/guides/integrations/vercel/choose-scope.png)
Choose `Specific Projects` and select your new Vercel project from the dropdown, and click `Add Integration`.
![Choose project](/img/guides/integrations/vercel/choose-project.png)
From the Supabase popup, select your new Vercel Project and Supabase project from the dropdowns.
![Supabase integration](/img/guides/integrations/vercel/link-vercel-to-supabase.png)
## Step 4: Clone GitHub repo
The fastest way to get this project running locally is to clone the repo that Vercel created for us.
Navigate back to the Vercel project `Overview` page, and click `View Git Repository`.
![Vercel Project Dashboard](/img/guides/integrations/vercel/vercel-project-dashboard.png)
This will open the GitHub repo. From here, click the arrow next to `Code` and copy the url from the dropdown.
![GitHub repo url](/img/guides/integrations/vercel/github-project-url.png)
Open a terminal window or CLI and run the following command to clone the GitHub repo.
```bash
git clone your-repo-url.git
```
Open the project in your code editor of choice, and update the contents of `pages/index.js` to the following:
```jsx
import styles from '../styles/Home.module.css'
export default function Home() {
return <div className={styles.container}>working</div>
}
```
Run a local development server.
```bash
npm run dev
```
Navigate to `http://localhost:3000` to confirm the project is "working".
## Step 4: Pull environment variables from Vercel
First, we need to login to Vercel using their CLI tool.
```bash
npx vercel login
```
This will ask if we are happy to install `vercel`. Type `y` and hit `Enter`.
We will the need to authenticate Vercel by selecting `Continue with GitHub`.
This will open a browser window where you need to authenticate with your GitHub account.
Next, we need to link our Vercel project.
```bash
npx vercel link
```
Step through the prompts to link the Vercel project.
![Link project from Vercel](/img/guides/integrations/vercel/vercel-link.png)
Copy the environment variables from our Vercel project.
```bash
npx vercel env pull
```
This will create a `.env` file containing our Supabase environment variables. Rename this file to `.env.local` to automatically ignore it from git.
## Step 5: Install Supabase.js
Install the `supabase-js` library.
```bash
npm i @supabase/supabase-js
```
Create a new file called `/utils/supabase.js` and add the following.
```jsx
import { createClient } from '@supabase/supabase-js'
export default createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY
)
```
Create a new file called `/components/NewTodo.js` and add the following.
```jsx
import { useState } from 'react'
import supabase from '../utils/supabase'
export default ({ reload }) => {
const [title, setTitle] = useState('')
const addTodo = async (e) => {
e.preventDefault()
await supabase.from('todos').insert({ title })
reload()
setTitle('')
}
return (
<form onSubmit={addTodo}>
<input value={title} onChange={(e) => setTitle(e.target.value)} />
</form>
)
}
```
This component will be responsible for writing a new `todo` to Supabase.
Let's import our new component in `pages/index.js` and display a list of todos.
```jsx
import { useState, useEffect } from 'react'
import styles from '../styles/Home.module.css'
import supabase from '../utils/supabase'
import NewTodo from '../components/NewTodo'
export default function Home() {
const [todos, setTodos] = useState([])
const fetchTodos = async () => {
const { data } = await supabase.from('todos').select('*')
setTodos(data)
}
useEffect(() => {
fetchTodos()
}, [])
return (
<div className={styles.container}>
<NewTodo reload={fetchTodos} />
{todos.map((todo) => (
<p key={todo.id}>{todo.title}</p>
))}
</div>
)
}
```
## Resources
- [Vercel official website](https://vercel.com).
- [Vercel blog](https://vercel.com/blog).
- [Vercel docs](https://vercel.com/docs).
- [Vercel Integration docs](https://vercel.com/docs/integrations)
+11
View File
@@ -182,6 +182,17 @@ module.exports = {
},
],
},
{
type: 'category',
label: 'Integrations',
collapsed: false,
items: [
'guides/integrations/auth0',
'guides/integrations/draftbit',
'guides/integrations/prisma',
'guides/integrations/vercel',
],
},
// {
// type: 'category',
// label: 'Storage',
+12
View File
@@ -425,6 +425,18 @@ pages:
''')
.execute();
```
- name: Filtering with inner joins
description: |
If you want to filter a table based on a child table's values you can use the `!inner()` function. For example, if you wanted
to select all rows in a `message` table which belong to a user with the `username` "Jane":
js: |
```js
final res = await supabase
.from('messages')
.select('*, users!inner(*)')
.eq('users.username', 'Jane')
.execute();
```
- name: Querying with count option
description: |
You can get the number of rows by using the count option.
+12 -1
View File
@@ -283,7 +283,7 @@ pages:
import { createClient } from '@supabase/supabase-js'
const supabase = createClient('https://xyzcompany.supabase.co', 'public-anon-key', {
fetch: fetch
fetch: fetch.bind(globalThis)
})
```
@@ -856,6 +856,17 @@ pages:
purchaser:purchaser_id ( name )
`)
```
- name: Filtering with inner joins
description: |
If you want to filter a table based on a child table's values you can use the `!inner()` function. For example, if you wanted
to select all rows in a `message` table which belong to a user with the `username` "Jane":
js: |
```js
const { data, error } = await supabase
.from('messages')
.select('*, users!inner(*)')
.eq('users.username', 'Jane')
```
- name: Querying with count option
description: |
You can get the number of rows by using the count option.
Binary file not shown.

After

Width:  |  Height:  |  Size: 519 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 340 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 721 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 623 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 464 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 411 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 298 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 868 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 512 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 828 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 756 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 690 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 514 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 407 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 864 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 769 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 437 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 332 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 419 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 434 KiB

@@ -149,7 +149,7 @@ http_post
After making a request, the extension will return an ID. You can use this ID to collect a response.
```sql noCopy
```sql hideCopy
-- Collect the response from a request
select
*
@@ -207,7 +207,7 @@ To build asynchronous behavior, we use a PostgreSQL [background worker](https://
Shout out to [Paul Ramsey](https://github.com/pramsey), who gave us the implementation idea in [pgsql-http](https://github.com/pramsey/pgsql-http/#to-do). While we originally hoped to add background workers to his extension, the implementation became too cumbersome and we decided to start with a clean slate. The advantage of being async can be seen by making some requests with both extensions:
```sql noCopy
```sql hideCopy
\timing on
-- using pgsql-http to fetch from "supabase.io" 10 times
@@ -5,8 +5,8 @@ author: paul_copplestone
author_url: https://github.com/kiwicopple
author_image_url: https://github.com/kiwicopple.png
authorURL: https://github.com/kiwicopple
image: launch-week-3/launch-week-3-og.jpg
thumb: launch-week-3/launch-week-3-thumb.jpg
image: launch-week-three/launch-week-three-og.jpg
thumb: launch-week-three/launch-week-three-thumb.jpg
tags:
- launch-week
date: '2021-11-26'
@@ -37,7 +37,9 @@ Last Launch Week we introduced the idea of Community Day - a day where we shine
### Where?
Blog post coming soon. Follow our [Twitter](https://twitter.com/supabase) to get notified immediately.
Read the [blog post](https://supabase.com/blog/2021/11/29/community-day).
Tune in on [Twitter Spaces](https://twitter.com/i/spaces/1YqKDqYXQnLGV) at 7am PT!
## Tuesday: Dashboard Surprise
+109
View File
@@ -0,0 +1,109 @@
---
title: 'PostgREST 9'
description: New features and updates in PostgREST version 9.
author: steve_chavez
author_url: https://github.com/steve-chavez
author_image_url: https://github.com/steve-chavez.png
authorURL: https://github.com/steve-chavez
image: launch-week-three/community-day/supabase-oss.jpg
thumb: launch-week-three/community-day/supabase-oss.jpg
tags:
- launch-week
- release-notes
- tech
- community
date: '2021-11-27'
toc_depth: 2
---
PostgREST turns your PostgreSQL database automatically into a RESTful API. Today, PostgREST 9 was [released](https://postgrest.org/en/v9.0/releases/v9.0.0.html). Let's take a look at some of the new features.
## Inner Joins
PostgREST 9 brings a [much-requested feature](https://github.com/supabase/postgrest-js/issues/197): the ability to do `inner joins`.
Here's an example with `supabase-js`:
```js hideCopy
const { data, error } = await supabase
.from('messages')
.select('*, users!inner(*)')
.eq('users.username', 'Jane')
```
With the new `!inner` keyword, you can now filter rows of the top-level table (`messages`) based on a filter (`eq`) of the embedded table (`users`).
This works across all Supabase client libraries and you can use it with any of the available operators (`gt`, `in`, etc.)
[Read more](https://postgrest.org/en/v9.0/releases/v9.0.0.html#resource-embedding-with-top-level-filtering).
## Functions with unnamed parameters
You can now make `POST` requests to functions with a single unnamed parameter. This is particularly useful for webhooks that send JSON payloads.
For example, imagine you were using Postmark as an email provider and you wanted to save email bounces using their [bounce webhook](https://postmarkapp.com/developer/webhooks/bounce-webhook).
Previously this wouldn't be possible with PostgREST, as every function required a named parameter.
As of PostgREST 9, this is possible.
Simply create a function inside your PostgreSQL database to receive the raw JSON:
```sql hideCopy
create function store_bounces(json)
returns json
language sql
as $$
insert into bounces (webhook_id, email)
values (
($1->>'ID')::bigint,
($1->>'Email')::text
);
select '{ "status": 200 }'::json;
$$;
```
And the webhook can send data directly to your database via an `rpc` call:
```bash hideCopy
POST https://<PROJECT_REF>.supabase.co/rest/v1/rpc/store_bounces HTTP/1.1
Content-Type: application/json
{
"RecordType": "Bounce",
"MessageStream": "outbound",
"ID": 4323372036854775807,
"Type": "HardBounce",
"MessageID": "883953f4-6105-42a2-a16a-77a8eac79483",
"Description": "The server was unable to deliver your message (ex: unknown user, mailbox not found).",
"Details": "Test bounce details",
"Email": "john@example.com",
"From": "sender@example.com",
"BouncedAt": "2019-11-05T16:33:54.9070259Z"
}
```
[Read more](https://postgrest.org/en/v9.0/api.html#s-proc-single-unnamed).
## PostgreSQL 14 compatibility
If you've ever done your own custom `auth` functions using PostgREST [HTTP Context](https://postgrest.org/en/v8.0/api.html#accessing-request-headers-cookies-and-jwt-claims),
note that a breaking change was necessary for PostgreSQL 14 Compatibility. You'll need to update them:
| `From` | `To` |
| --- | --- |
| `current_setting('request.jwt.claim.custom-claim', true)` | `current_setting('request.jwt.claims', true)::json->>'custom-claim'` |
| `current_setting('request.header.custom-header', true)` | `current_setting('request.headers', true)::json->>'custom-header'` |
If you only use Supabase default `auth` functions(`auth.email()`, `auth.uid()`, `auth.role()`), then no action is required because we have updated the functions to handle these changes transparently.
[Read more](https://postgrest.org/en/v9.0/releases/v9.0.0.html#postgresql-14-compatibility).
## Release notes
There are a lot more improvements released in PostgREST 9, including [support for Partitioned Tables](https://postgrest.org/en/v9.0/releases/v9.0.0.html#partitioned-tables),
[improved doc](https://postgrest.org/en/v9.0/releases/v9.0.0.html#documentation-improvements), and [bug fixes](https://postgrest.org/en/v9.0/releases/v9.0.0.html#bug-fixes).
You can see the full updates on the [PostgREST 9 release notes](https://postgrest.org/en/v9.0/releases/v9.0.0.html).
@@ -0,0 +1,166 @@
---
title: 'New in PostgreSQL 14: What every developer should know'
description: 'A quick look at some new features and functionality in PostgreSQL 14.'
author: gurjeet
author_url: https://github.com/gurjeet
author_image_url: https://github.com/gurjeet.png
authorURL: https://github.com/gurjeet
image: whats-new-in-postgres-14/whats-new-in-postgres-14-og.png
thumb: whats-new-in-postgres-14/whats-new-in-postgres-14-thumb.png
tags:
- tech
date: '2021-11-28'
toc_depth: 3
---
Every web developer knows the importance of choosing a suitable database for building modern apps.
Even though NoSQL, NewSQL, and other types of databases have received a great deal of buzz in the last few years,
relational database management systems (or RDBMS) are still relevant for several critical business use cases and will likely do so in the foreseeable future.
Among the many open-source relational databases available, [PostgreSQL](https://supabase.com/docs/guides/database/introduction) is a popular choice among developers.
It was named [DBMS of the year in 2020](https://db-engines.com/en/blog_post/85) by
DBEnginges, and with every release of PostgreSQL new features are available making it easy for developers and administrators to run their apps.
This blog will highlight some key features of the newly available PostgreSQL 14, which every developer can benefit from. So, let's dive right in.
## Complex data type support
### JSON subscripting
PostgreSQL continues to add innovations for complex data types, making JSON data more accessible. In postgreSQL 14, developers can use subscripts to iterate through JSON key pairs and fetch corresponding values. For example, using this capability, nested JSON fields can be quickly traversed to retrieve values to reconstruct application objects.
```sql hideCopy
select (
'{ "PostgreSQL": { "release": 14 }}'::jsonb
)['PostgreSQL']['release'];
jsonb
-------
14
```
### Working with disjointed data ranges
In the real world, data ranges are pretty common. For example, if you have sensor readings going into a PostgreSQL database, you may want to define a range for
low, medium, and high metrics. Before version 14, you could set data ranges for integers, numerics, timestamps, and other data types.
However, these ranges had to be contiguous, and it required multiple insert records across the different ranges.
In PostgreSQL 14, a single insert statement can be used to map data across multiple noncontiguous ranges. Let's check it out with an example.
First, create an enum data type to capture low, medium, and high sensor reading levels.
```sql hideCopy
create type valid_levels as enum (
'low', 'medium', 'high'
);
```
To store sensor reading metric data, create a table consisting of the sensor description, the level and the range of timestamps when the sensor was at that level.
```sql hideCopy
create table sensor_range (
reading_id serial primary key,
metric_desc varchar(100),
metric_level valid_levels,
metric_ts tsmultirange
);
```
Now, insert some data into the table. Note that, the insert statement below provides two time ranges that are not contiguous.
```sql hideCopy
insert into sensor_range (
metric_desc,
metric_level,
metric_ts
)
values (
'Temperature',
'high',
'{[2021-11-01 6:00, 2021-11-01 10:00],[2021-11-05 14:00, 2021-11-05 20:00]}'
);
insert into sensor_range (
metric_desc,
metric_level,
metric_ts
)
values (
'Temperature',
'low',
'{[2021-11-01 10:00, 2021-11-01 12:00],[2021-11-05 21:00, 2021-11-05 22:00]}'
);
```
Query the data in the table to see the inserted data rows
```sql hideCopy
select *
from sensor_range;
```
``` hideCopy
| `reading_id` | `metric_desc` | `metric_level` | `metric_ts` |
|--------------|---------------|----------------|-----------------------------------------------------------------------------|
| 1 | Temperature | high | {[2021-11-01 6:00, 2021-11-01 10:00],[2021-11-05 14:00, 2021-11-05 20:00]} |
| 2 | Temperature | low | {[2021-11-01 10:00, 2021-11-01 12:00],[2021-11-05 21:00, 2021-11-05 22:00]} |
```
## Improved performance and monitoring
### Query pipelining
If you're a developer accessing PostgreSQL using a high-level programming language, the database driver you're using is likely based on a C library called libpq. For example, suppose your application performs several write operations within a short period, or your network has a higher latency. In that case, you may want to fire off several queries over the same network connection simultaneously, so you don't have to wait for a reply after each query. Under the hood, libpq can send multiple queries to PostgreSQL while maintaining a queue of queries that are waiting for results. This queue consumes additional memory on both the client and server. Developers can now take advantage of PostgreSQL 14's client-side query pipeline mode using clients built on top of the latest libpq library to accelerate app performance.
Aside from query pipelining, the latest PostgreSQL 14 release brings several other enhancements to query parallelism, including improved performance of parallel sequential scans, parallel query execution capabilities for Foreign Data Wrappers, and the ability to run parallel queries when refreshing materialized views. With this, your PostgreSQL server can now do more work for you at a lower cost.
### Monitoring and troubleshooting queries
Ask any database troubleshooting expert, and they will tell you that without a way to correlate data across different database components, investigating and monitoring query-related issues can quickly become a nightmare. When it comes to PostgreSQL, the SQL statement’s query_id can be used for this purpose, allowing database experts an easy way to identify workload patterns quickly, detect rogue run-away or slow queries, and common query-related issues. Before PostgreSQL 14, this information was only available using the pg_stat_statements [extension](https://supabase.com/docs/guides/database/extensions), which shows aggregate statistics about queries that have finished executing, but now this information is additionally available for live running queries with pg_stat_activity, in the EXPLAIN VERBOSE statement, and log files.
An example of using query_id IN EXPLAIN (VERBOSE) statement
Start by altering the system to enable the query_id
```sql hideCopy
alter system set compute_query_id = 'on';
```
Restart the PostgreSQL database for the alter system statement to take effect.
Run the following query that selects schema and table names from the catalog tables
```sql hideCopy
explain (verbose, costs off)
select schemaname, tablename
from pg_tables, pg_sleep(5)
where schemaname <> 'pg_catalog';
```
View the result of the EXPLAIN statement
![View the result of the EXPLAIN statement](/images/blog/whats-new-in-postgres-14/explain-statement.png)
Notice that the query identifier is in the output:<br />
`Query Identifier: 4856400161806292488`
## Enhanced security
Security remains top of mind for every organization, and PostgreSQL 14 brings some critical enhancements in this area.
Have you ever wanted to [manage database user access](https://supabase.com/docs/guides/auth/managing-user-data) so that only specific individuals have read-only access? For example, consider a situation where you would like to host some sample data online for a demo and not allow demo users to modify or delete data. With the latest PostgreSQL version 14, you can easily set that up using a single GRANT statement.
```sql hideCopy
grant pg_read_all_data to bobfries;
```
From an authentication perspective, if you're still managing passwords in the database without [third-party login providers](https://supabase.com/docs/guides/auth#third-party-logins),
you may want to read this. Since version 10, PostgreSQL supports several different hashing mechanisms such as MD5 and SCRAM-SHA-256 to store user passwords on disk.
However, to keep up with the [latest security weaknesses around MD5](https://en.wikipedia.org/wiki/MD5#Security) and meet regulatory compliance requirements,
PostgreSQL 14 has made SCRAM-SHA-256 the default password management mechanism. So, if you are using old PostgreSQL client software,
you may want to update it, to be able to connect to the newer versions of the database server.
## It's only the tip of the iceberg
Indeed, there's a lot more to explore in the latest PostgreSQL, and we've barely scratched the surface of what PostgreSQL 14 has to offer.
There's no better way to experience some of these features than by trying them out for yourself.
You can check out these new features within your Supabase project without having to perform a local installation.
Explore the [Supabase dashboard now](https://app.supabase.io) and get started with your new project.
+136
View File
@@ -0,0 +1,136 @@
---
title: 'Community Day'
description: Kicking off launch week by highlighting the communities around Supabase.
author: thor_schaeff
author_url: https://github.com/thorwebdev
author_image_url: https://github.com/thorwebdev.png
authorURL: https://github.com/thorwebdev
image: launch-week-three/community-day/supabase-oss.jpg
thumb: launch-week-three/community-day/supabase-oss.jpg
tags:
- launch-week
- community
date: '2021-11-29'
toc_depth: 2
---
Supabase combines existing open-source tools with our own open-source contributions to provide a delightful experience for developers. As part of this goal we hope to build a community of communities, bringing together developers from many different tools, as well as new developers looking to get involved with open source.
To kick off launch week we want to showcase some of the communities that make up the Supabase community, highlight some of their updates, and celebrate everyone who contributes their time to the Supabase mission.
So let's get cracking, we've got a lot to cover. 🚀
## Open Source Spotlight: Kong API Gateway
Supabase provides a whole bunch of features that are made up of a [collection of open-source tools](/docs/architecture). To orchestrate these different services and functionalities we use the [Kong API Gateway](https://github.com/Kong/kong).
As a user of our hosted offering you don't really need to know all this, but we love showcasing the amazing open-source tools that we work with, and so we've invited [Vik Gamov](https://twitter.com/gAmUssA) to give you a little intro to [Kong](https://github.com/Kong/kong):
<iframe
className="w-full"
width="700"
height="350"
src="https://www.youtube-nocookie.com/embed/6ptUrE4QRPQ"
frameBorder="0"
allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen
></iframe>
## PostgreSQL 14 updates
![Postgres-14](/images/blog/whats-new-in-postgres-14/whats-new-in-postgres-14-og.png)
Every web developer knows the importance of choosing a suitable database for building modern apps.
Even though NoSQL, NewSQL, and other types of databases have received a great deal of buzz in the last few years,
relational database management systems (or RDBMS) are still relevant for several critical business use cases and will likely do so in the foreseeable future.
Among the many open-source relational databases available, [PostgreSQL](/docs/guides/database/introduction) is a popular choice among developers.
It was named [DBMS of the year in 2020](https://db-engines.com/en/blog_post/85) by
DBEnginges, and with every release of PostgreSQL new features are available making it easy for developers and administrators to run their apps.
[Gurjeet](https://twitter.com/0xGurjeet), one of our Postgres engineers, has written up a [blogpost](/blog/2021/11/28/whats-new-in-postgres-14) with the most important updates the every dev should know.
## PostgREST 9 updates
![PostgREST-9](/images/blog/launch-week-three/community-day/postgrest-9-og.png)
We've had a lot of feedback on our Postgres APIs, and we've been hard at work with the [PostgREST team](https://github.com/orgs/PostgREST/people) to improve an already-incredible product.
Some new features include
- Inner Joins
- Functions with unnamed parameters
- PostgreSQL 14 compatibility
From tomorrow, every Supabase project will be on PostgREST 9 (including existing projects).
Read about some of the new features in PostgREST 9 [here](/blog/2021/11/28/postgrest-9).
## New community partner: GitGuardian
![gitguardian-supabase-partnership.jpeg](/images/blog/launch-week-three/community-day/gitguardian-supabase-partnership.jpeg)
[As you (hopefully) know](/docs/learn/auth-deep-dive/auth-deep-dive-jwts#jwts-in-supabase), keeping your service role key secret is a crucial part of securing your database. But we also know that it can be difficult to make sure it stays secret as your team grows and you don't accidentally leak it. To help you with this, [we've partnered with GitGuardian](https://blog.gitguardian.com/the-detector-of-the-month-november-2021/).
GitGuardian helps developers keep 250+ types of secrets out of source code. Their automated secrets detection and remediation solution secures every step of the development life cycle, helping you monitor your code for sensitive data. Read [their blog post](https://blog.gitguardian.com/the-detector-of-the-month-november-2021/) to learn more.
## Auth updates
![new-oauth-providers.jpg](/images/blog/launch-week-three/community-day/new-oauth-providers.jpg)
The beauty of building in the open is that the community can get involved in adding new OAuth providers, which is exactly what [@TheHarryET](https://twitter.com/TheHarryET) and [MonsterDeveloper](https://github.com/MonsterDeveloper) have been doing. Thanks to them you can now provide sign-in with [Slack](/docs/guides/auth/auth-slack) as well as [Spotify](/docs/guides/auth/auth-spotify) to your users, as well as use [MessageBird](/docs/guides/auth/auth-messagebird) for phone auth as an alternative to the existing [Twilio](/docs/guides/auth/auth-twilio) integration.
## SupaSquad updates
The [SupaSquad](/docs/handbook/supasquad) is an official Supabase advocate program where community members help build and manage the Supabase community.
Among many other awesome things, the SupaSquad has been absolutely on fire building [client libraries](/docs/reference/javascript/supabase-client) for all flavors of backends and environments.
![client-libs.jpg](/images/blog/launch-week-three/community-day/client-libs.jpg)
### Python client library updates
- New maintainers [`@Dreinon`](https://github.com/dreinon), [`@anand2312`](https://github.com/anand2312), and [`@leynier`](https://github.com/leynier). 💚
- `gotrue-py` rewritten and now at feature parity with `gotrue-js`.
- Postgrest-py now support synchronous operations as well instead of just synchronous operations in the past. Also at feature-parity with postgrest-js.
- Supabase-py is now Supabase (e.g. you do `pip3 install supabase` instead of `pip3 install supabase-py`).
- Storage is working for the py client library but it's yet to be extracted out as a standalone lib.
## New tutorials and integration guides
Our own [Jon Meyers](https://twitter.com/_dijonmusters) and the broader community have been busy creating awesome tutorials and integration guides to help everyone build even more amazing things with Supabase:
- Jon has added official guides for working with [Auth0](/docs/guides/integrations/auth0) and [Vercel](/docs/guides/integrations/vercel),
- [Jonny Summers-Muir](https://twitter.com/JSummersMuir) has been busy paving the way for [Prisma](/docs/guides/integrations/prisma), and
- [Aman Mittal](https://github.com/amandeepmittal) has hooked us up with a guide for [Draftbit](/docs/guides/integrations/draftbit)!
And below are some of the community one's we've come across recently. If you've created one yourself, please notify us by tagging us in a Tweet!
- [Supabase & Sveltekit - Build Twitter in 75 minutes](https://youtu.be/mPQyckogDYc)
- [Supabase Auth With Next.Js 12 Middleware](https://jitsu.com/blog/supabase-nextjs-middleware#what-were-going-to-build)
- [Vue 3 & Supabase | Workout Tracker App](https://www.youtube.com/watch?v=3tF0fGkd4ho)
- [Use Supabase Auth with Vue.js 3](https://vueschool.io/articles/vuejs-tutorials/use-supabase-auth-with-vue-js-3/)
- [Dynamic Jamstack with Stencil and Supabase](https://ionicframework.com/blog/dynamic-jamstack-with-stencil-and-supabase)
## Developer stories
Anytime we scroll through [madewithsupabase.com](https://www.madewithsupabase.com/) we're absolutely blown away by the awesome things y'all are building with Supabase.
So we reached out to some of our users, requesting a short video to learn more and we were overwhelmed with your gracious submissions. We've started a user stories playlist and we are so excited to see many more of these stories in the future. 💚
<iframe
className="w-full"
width="700"
height="350"
src="https://www.youtube-nocookie.com/embed/videoseries?list=PL5S4mPUpp4OuzQN-a_FY3OZQuYo4NmXvb"
frameBorder="0"
allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen
></iframe>
And with that, we officially declare Launch Week as open 🥳 Check back [here](/blog) every day this week to see what new things we are shipping. We can't wait to share them with you 🚀
## Want even more Supabase in your life?
![tiktok.png](/images/blog/launch-week-three/community-day/tiktok.png)
We've got some stellar swag drops and behind the scenes footage for you on our new [TikTok page](https://www.tiktok.com/@supabase.com). Make sure to follow us there as we'll be giving away some swag randomly to 10 peeps who follow us during launch week!
+7
View File
@@ -82,5 +82,12 @@
"position": "Engineering",
"username": "darora",
"author_image_url": "https://github.com/darora.png"
},
"gurjeet": {
"author": "Gurjeet Singh",
"author_url": "https://github.com/gurjeet",
"position": "Engineering",
"username": "gurjeet",
"author_image_url": "https://github.com/gurjeet.png"
}
}
+7
View File
@@ -119,6 +119,13 @@ function AuthPage() {
<img src={`${basePath}/images/product/auth/gitlab-icon.svg`} width={21} />
<img src={`${basePath}/images/product/auth/bitbucket-icon.svg`} width={21} />
<img src={`${basePath}/images/product/auth/twitter-icon.svg`} width={21} />
<img src={`${basePath}/images/product/auth/apple-icon.svg`} width={21} />
<img src={`${basePath}/images/product/auth/discord-icon.svg`} width={21} />
<img src={`${basePath}/images/product/auth/microsoft-icon.svg`} width={21} />
<img src={`${basePath}/images/product/auth/messagebird-icon.svg`} width={21} />
<img src={`${basePath}/images/product/auth/twilio-icon.svg`} width={21} />
<img src={`${basePath}/images/product/auth/twitch-icon.svg`} width={21} />
<img src={`${basePath}/images/product/auth/spotify-icon.svg`} width={21} />
</Space>
</p>
<Typography.Title level={4}>All the social providers</Typography.Title>
Binary file not shown.

After

Width:  |  Height:  |  Size: 800 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 290 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 737 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="842.32007"
viewBox="0 0 842.32007 1000.0001"
version="1.1"
height="1000.0001"
id="svg2"
inkscape:version="0.92.3 (2405546, 2018-03-11)"
sodipodi:docname="Apple_logo_black.svg">
<metadata
id="metadata10">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs8" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1366"
inkscape:window-height="705"
id="namedview6"
showgrid="false"
inkscape:zoom="0.1767767"
inkscape:cx="-621.03745"
inkscape:cy="964.94676"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="svg2" />
<path
d="m 824.66636,779.30363 c -15.12299,34.93724 -33.02368,67.09674 -53.7638,96.66374 -28.27076,40.3074 -51.4182,68.2078 -69.25717,83.7012 -27.65347,25.4313 -57.2822,38.4556 -89.00964,39.1963 -22.77708,0 -50.24539,-6.4813 -82.21973,-19.629 -32.07926,-13.0861 -61.55985,-19.5673 -88.51583,-19.5673 -28.27075,0 -58.59083,6.4812 -91.02193,19.5673 -32.48053,13.1477 -58.64639,19.9994 -78.65196,20.6784 -30.42501,1.29623 -60.75123,-12.0985 -91.02193,-40.2457 -19.32039,-16.8514 -43.48632,-45.7394 -72.43607,-86.6641 C 77.707522,829.30207 52.171259,778.62464 32.165691,720.84861 10.740416,658.44309 0,598.01283 0,539.50845 0,472.49197 14.481044,414.69125 43.486336,366.25444 66.28194,327.34823 96.60818,296.6578 134.5638,274.1276 c 37.95566,-22.53016 78.96676,-34.01129 123.1321,-34.74585 24.16591,0 55.85633,7.47508 95.23784,22.166 39.27042,14.74029 64.48571,22.21538 75.54091,22.21538 8.26518,0 36.27668,-8.7405 83.7629,-26.16587 44.90607,-16.16001 82.80614,-22.85118 113.85458,-20.21546 84.13326,6.78992 147.34122,39.95559 189.37699,99.70686 -75.24463,45.59122 -112.46573,109.4473 -111.72502,191.36456 0.67899,63.8067 23.82643,116.90384 69.31888,159.06309 20.61664,19.56727 43.64066,34.69027 69.2571,45.4307 -5.55531,16.11062 -11.41933,31.54225 -17.65372,46.35662 z M 631.70926,20.0057 c 0,50.01141 -18.27108,96.70693 -54.6897,139.92782 -43.94932,51.38118 -97.10817,81.07162 -154.75459,76.38659 -0.73454,-5.99983 -1.16045,-12.31444 -1.16045,-18.95003 0,-48.01091 20.9006,-99.39207 58.01678,-141.40314 C 497.65157,54.696 521.21876,37.0095 549.79815,22.90064 578.3158,9.00229 605.2903,1.31621 630.65988,0 c 0.74076,6.68575 1.04938,13.37191 1.04938,20.00505 z"
id="path4"
inkscape:connector-curvature="0"
style="stroke-width:0.84231985;fill:#b3b3b3;fill-opacity:1" />
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

@@ -0,0 +1,10 @@
<svg width="71" height="55" viewBox="0 0 71 55" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0)">
<path d="M60.1045 4.8978C55.5792 2.8214 50.7265 1.2916 45.6527 0.41542C45.5603 0.39851 45.468 0.440769 45.4204 0.525289C44.7963 1.6353 44.105 3.0834 43.6209 4.2216C38.1637 3.4046 32.7345 3.4046 27.3892 4.2216C26.905 3.0581 26.1886 1.6353 25.5617 0.525289C25.5141 0.443589 25.4218 0.40133 25.3294 0.41542C20.2584 1.2888 15.4057 2.8186 10.8776 4.8978C10.8384 4.9147 10.8048 4.9429 10.7825 4.9795C1.57795 18.7309 -0.943561 32.1443 0.293408 45.3914C0.299005 45.4562 0.335386 45.5182 0.385761 45.5576C6.45866 50.0174 12.3413 52.7249 18.1147 54.5195C18.2071 54.5477 18.305 54.5139 18.3638 54.4378C19.7295 52.5728 20.9469 50.6063 21.9907 48.5383C22.0523 48.4172 21.9935 48.2735 21.8676 48.2256C19.9366 47.4931 18.0979 46.6 16.3292 45.5858C16.1893 45.5041 16.1781 45.304 16.3068 45.2082C16.679 44.9293 17.0513 44.6391 17.4067 44.3461C17.471 44.2926 17.5606 44.2813 17.6362 44.3151C29.2558 49.6202 41.8354 49.6202 53.3179 44.3151C53.3935 44.2785 53.4831 44.2898 53.5502 44.3433C53.9057 44.6363 54.2779 44.9293 54.6529 45.2082C54.7816 45.304 54.7732 45.5041 54.6333 45.5858C52.8646 46.6197 51.0259 47.4931 49.0921 48.2228C48.9662 48.2707 48.9102 48.4172 48.9718 48.5383C50.038 50.6034 51.2554 52.5699 52.5959 54.435C52.6519 54.5139 52.7526 54.5477 52.845 54.5195C58.6464 52.7249 64.529 50.0174 70.6019 45.5576C70.6551 45.5182 70.6887 45.459 70.6943 45.3942C72.1747 30.0791 68.2147 16.7757 60.1968 4.9823C60.1772 4.9429 60.1437 4.9147 60.1045 4.8978ZM23.7259 37.3253C20.2276 37.3253 17.3451 34.1136 17.3451 30.1693C17.3451 26.225 20.1717 23.0133 23.7259 23.0133C27.308 23.0133 30.1626 26.2532 30.1066 30.1693C30.1066 34.1136 27.28 37.3253 23.7259 37.3253ZM47.3178 37.3253C43.8196 37.3253 40.9371 34.1136 40.9371 30.1693C40.9371 26.225 43.7636 23.0133 47.3178 23.0133C50.9 23.0133 53.7545 26.2532 53.6986 30.1693C53.6986 34.1136 50.9 37.3253 47.3178 37.3253Z" fill="#5865F2"/>
</g>
<defs>
<clipPath id="clip0">
<rect width="71" height="55" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64"><path d="M57.425 14.726c-2.568 0-4.863 1.284-6.264 3.23l-9.454 13.228a1.9 1.9 0 0 1-1.556.817 1.91 1.91 0 0 1-1.906-1.906c0-.39.117-.778.31-1.05L46.53 17.1c.817-1.206 1.284-2.684 1.284-4.28 0-4.24-3.424-7.664-7.664-7.664H0v7.664h34.393c0 2.1-1.712 3.852-3.852 3.852H0c0 2.723.584 5.33 1.595 7.664H26.69c0 2.1-1.712 3.852-3.852 3.852H3.852a19.12 19.12 0 0 0 15.368 7.664h9.454a1.91 1.91 0 0 1 1.906 1.906 1.91 1.91 0 0 1-1.906 1.906H19.18l-12.84 19.18h23.227c10.62 0 19.647-6.925 22.8-16.496l4.32-13.11c1.4-4.24 3.968-7.976 7.314-10.816-1.323-2.218-3.774-3.696-6.575-3.696zm0 5.252c-.778 0-1.44-.66-1.44-1.44s.66-1.44 1.44-1.44 1.44.66 1.44 1.44c0 .817-.66 1.44-1.44 1.44z" fill="#2481d7"/></svg>

After

Width:  |  Height:  |  Size: 758 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 23 23"><path fill="#f3f3f3" d="M0 0h23v23H0z"/><path fill="#f35325" d="M1 1h10v10H1z"/><path fill="#81bc06" d="M12 1h10v10H12z"/><path fill="#05a6f0" d="M1 12h10v10H1z"/><path fill="#ffba08" d="M12 12h10v10H12z"/></svg>

After

Width:  |  Height:  |  Size: 272 B

@@ -0,0 +1 @@
<svg enable-background="new 0 0 2447.6 2452.5" viewBox="0 0 2447.6 2452.5" xmlns="http://www.w3.org/2000/svg"><g clip-rule="evenodd" fill-rule="evenodd"><path d="m897.4 0c-135.3.1-244.8 109.9-244.7 245.2-.1 135.3 109.5 245.1 244.8 245.2h244.8v-245.1c.1-135.3-109.5-245.1-244.9-245.3.1 0 .1 0 0 0m0 654h-652.6c-135.3.1-244.9 109.9-244.8 245.2-.2 135.3 109.4 245.1 244.7 245.3h652.7c135.3-.1 244.9-109.9 244.8-245.2.1-135.4-109.5-245.2-244.8-245.3z" fill="#36c5f0"/><path d="m2447.6 899.2c.1-135.3-109.5-245.1-244.8-245.2-135.3.1-244.9 109.9-244.8 245.2v245.3h244.8c135.3-.1 244.9-109.9 244.8-245.3zm-652.7 0v-654c.1-135.2-109.4-245-244.7-245.2-135.3.1-244.9 109.9-244.8 245.2v654c-.2 135.3 109.4 245.1 244.7 245.3 135.3-.1 244.9-109.9 244.8-245.3z" fill="#2eb67d"/><path d="m1550.1 2452.5c135.3-.1 244.9-109.9 244.8-245.2.1-135.3-109.5-245.1-244.8-245.2h-244.8v245.2c-.1 135.2 109.5 245 244.8 245.2zm0-654.1h652.7c135.3-.1 244.9-109.9 244.8-245.2.2-135.3-109.4-245.1-244.7-245.3h-652.7c-135.3.1-244.9 109.9-244.8 245.2-.1 135.4 109.4 245.2 244.7 245.3z" fill="#ecb22e"/><path d="m0 1553.2c-.1 135.3 109.5 245.1 244.8 245.2 135.3-.1 244.9-109.9 244.8-245.2v-245.2h-244.8c-135.3.1-244.9 109.9-244.8 245.2zm652.7 0v654c-.2 135.3 109.4 245.1 244.7 245.3 135.3-.1 244.9-109.9 244.8-245.2v-653.9c.2-135.3-109.4-245.1-244.7-245.3-135.4 0-244.9 109.8-244.8 245.1 0 0 0 .1 0 0" fill="#e01e5a"/></g></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1333.33 1333.3" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" image-rendering="optimizeQuality" fill-rule="evenodd" clip-rule="evenodd"><path d="M666.66 0C298.48 0 0 298.47 0 666.65c0 368.19 298.48 666.65 666.66 666.65 368.22 0 666.67-298.45 666.67-666.65C1333.33 298.49 1034.88.03 666.65.03l.01-.04zm305.73 961.51c-11.94 19.58-37.57 25.8-57.16 13.77-156.52-95.61-353.57-117.26-585.63-64.24-22.36 5.09-44.65-8.92-49.75-31.29-5.12-22.37 8.84-44.66 31.26-49.75 253.95-58.02 471.78-33.04 647.51 74.35 19.59 12.02 25.8 37.57 13.77 57.16zm81.6-181.52c-15.05 24.45-47.05 32.17-71.49 17.13-179.2-110.15-452.35-142.05-664.31-77.7-27.49 8.3-56.52-7.19-64.86-34.63-8.28-27.49 7.22-56.46 34.66-64.82 242.11-73.46 543.1-37.88 748.89 88.58 24.44 15.05 32.16 47.05 17.12 71.46V780zm7.01-189.02c-214.87-127.62-569.36-139.35-774.5-77.09-32.94 9.99-67.78-8.6-77.76-41.55-9.98-32.96 8.6-67.77 41.56-77.78 235.49-71.49 626.96-57.68 874.34 89.18 29.69 17.59 39.41 55.85 21.81 85.44-17.52 29.63-55.89 39.4-85.42 21.8h-.03z" fill="#1ed760" fill-rule="nonzero"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -0,0 +1 @@
<svg fill="#F22F46" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30"><path d="M15 0C6.7 0 0 6.7 0 15s6.7 15 15 15 15-6.7 15-15S23.3 0 15 0zm0 26C8.9 26 4 21.1 4 15S8.9 4 15 4s11 4.9 11 11-4.9 11-11 11zm6.8-14.7c0 1.7-1.4 3.1-3.1 3.1s-3.1-1.4-3.1-3.1 1.4-3.1 3.1-3.1 3.1 1.4 3.1 3.1zm0 7.4c0 1.7-1.4 3.1-3.1 3.1s-3.1-1.4-3.1-3.1c0-1.7 1.4-3.1 3.1-3.1s3.1 1.4 3.1 3.1zm-7.4 0c0 1.7-1.4 3.1-3.1 3.1s-3.1-1.4-3.1-3.1c0-1.7 1.4-3.1 3.1-3.1s3.1 1.4 3.1 3.1zm0-7.4c0 1.7-1.4 3.1-3.1 3.1S8.2 13 8.2 11.3s1.4-3.1 3.1-3.1 3.1 1.4 3.1 3.1z"/></svg>

After

Width:  |  Height:  |  Size: 544 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64"><path d="M5.7 0L1.4 10.985V55.88h15.284V64h8.597l8.12-8.12h12.418l16.716-16.716V0H5.7zm51.104 36.3L47.25 45.85H31.967l-8.12 8.12v-8.12H10.952V5.73h45.85V36.3zM47.25 16.716v16.716h-5.73V16.716h5.73zm-15.284 0v16.716h-5.73V16.716h5.73z" fill="#6441a4" fill-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 340 B

+11 -3
View File
@@ -5,12 +5,20 @@
<link>https://supabase.com</link>
<description>Latest news from Supabase</description>
<language>en</language>
<lastBuildDate>Thu, 25 Nov 2021 16:00:00 GMT</lastBuildDate>
<lastBuildDate>Sat, 27 Nov 2021 16:00:00 GMT</lastBuildDate>
<atom:link href="https://supabase.com/blog/rss.xml" rel="self" type="application/rss+xml"/>
<item>
<guid>https://supabase.com/blog/2021/11/28/whats-new-in-postgres-14</guid>
<title>New in PostgreSQL 14: What every developer should know</title>
<link>https://supabase.com/blog/2021/11/28/whats-new-in-postgres-14</link>
<description>A quick look at some new features and functionality in PostgreSQL 14.</description>
<pubDate>Sat, 27 Nov 2021 16:00:00 GMT</pubDate>
</item>
<item>
<guid>https://supabase.com/blog/2021/11/26/supabase-launch-week-the-trilogy</guid>
<title>Supabase Launch Week III: Holiday Special.</title>
<title>Supabase Launch Week III: Holiday Special</title>
<link>https://supabase.com/blog/2021/11/26/supabase-launch-week-the-trilogy</link>
<description>Tis the season to be shipping.</description>
<pubDate>Thu, 25 Nov 2021 16:00:00 GMT</pubDate>
@@ -18,7 +26,7 @@
<item>
<guid>https://supabase.com/blog/2021/11/26/supabase-how-we-launch</guid>
<title>How we launch at Supabase.</title>
<title>How we launch at Supabase</title>
<link>https://supabase.com/blog/2021/11/26/supabase-how-we-launch</link>
<description>The history and methodology of Supabase Launch Week.</description>
<pubDate>Thu, 25 Nov 2021 16:00:00 GMT</pubDate>