Skip to content

Commit

Permalink
examples: improve with-supabase example (#51442)
Browse files Browse the repository at this point in the history
  • Loading branch information
dijonmusters committed Jun 21, 2023
1 parent 77ef61e commit 0674d95
Show file tree
Hide file tree
Showing 17 changed files with 273 additions and 81 deletions.
32 changes: 15 additions & 17 deletions examples/with-supabase/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,32 +11,30 @@ The Vercel deployment will guide you through creating a Supabase account and pro
## How to use

1. Create a [new Supabase project](https://database.new)
1. Run `npx create-next-app -e with-supabase myapp` to create a Next.js app using the Supabase Starter template
1. Run `cd myapp` to change into the app's directory
1. Run `npx create-next-app -e with-supabase` to create a Next.js app using the Supabase Starter template
1. Use `cd` to change into the app's directory
1. Run `npm install` to install dependencies
1. Rename `.env.local.example` to `.env.local` and update the values for `NEXT_PUBLIC_SUPABASE_URL` and `NEXT_PUBLIC_SUPABASE_ANON_KEY` from [your Supabase project's API settings](https://app.supabase.com/project/_/settings/api)
1. Run `npm run dev` to start the local development server

> Check out [the docs for Local Development](https://supabase.com/docs/guides/getting-started/local-development) to also run Supabase locally.
### Create Table and seed with data (optional)
### Create a Supabase client

Navigate to [your project's SQL Editor](https://app.supabase.com/project/_/sql), click `New query`, paste the following SQL 👇 and click `RUN`.
Check out the [`/app/_examples`](./app/_examples/) folder for an example of creating a Supabase client in:

```sql
create table if not exists todos (
id uuid default gen_random_uuid() primary key,
created_at timestamp with time zone default timezone('utc'::text, now()) not null,
title text,
is_complete boolean default false
);
- [Client Components](./app/_examples/client-component/page.tsx)
- [Server Components](./app/_examples/server-component/page.tsx)
- [Route Handlers](./app/_examples/route-handler/route.ts)
- [Server Actions](./app/_examples/server-action/page.tsx)

insert into todos(title)
values
('Create Supabase project'),
('Create Next.js app from Supabase Starter template'),
('Keeping building cool stuff!');
```
### Create `todo` table and seed with data (optional)

Navigate to [your project's SQL Editor](https://app.supabase.com/project/_/sql), click `New query`, paste the contents of the [init.sql](./supabase/migrations/20230618024722_init.sql) file and click `RUN`.

This will create a basic `todos` table, enable Row Level Security (RLS), and write RLS policies enabling `select` and `insert` actions for `authenticated` users.

To seed your `todos` table with some dummy data, run the contents of the [seed.sql](./supabase/seed.sql) file.

## Feedback and issues

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
'use client'

// TODO: Duplicate or move this file outside the `_examples` folder to make it a route

import { createClientComponentClient } from '@supabase/auth-helpers-nextjs'
import { useEffect, useState } from 'react'

Expand Down
81 changes: 81 additions & 0 deletions examples/with-supabase/app/_examples/protected-route/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// TODO: Duplicate or move this file outside the `_examples` folder to make it a route

import {
createServerActionClient,
createServerComponentClient,
} from '@supabase/auth-helpers-nextjs'
import { cookies } from 'next/headers'
import Image from 'next/image'
import { redirect } from 'next/navigation'

export default async function ProtectedRoute() {
const supabase = createServerComponentClient({ cookies })

const {
data: { user },
} = await supabase.auth.getUser()

if (!user) {
// This route can only be accessed by authenticated users.
// Unauthenticated users will be redirected to the `/login` route.
redirect('/login')
}

const signOut = async () => {
'use server'
const supabase = createServerActionClient({ cookies })
await supabase.auth.signOut()
redirect('/login')
}

return (
<div className="flex-1 flex flex-col max-w-3xl mt-24">
<h1 className="text-2xl mb-2 flex justify-between">
<span className="sr-only">Supabase and Next.js Starter Template</span>
</h1>

<div className="flex border-b py-3 text-sm text-neutral-100">
<div className="flex items-center justify-between w-full">
<code className="bg-neutral-700 px-3 py-1 rounded-lg text-sm">
Protected page
</code>
<span className="flex gap-4">
Hey, {user.email}! <span className="border-r"></span>{' '}
<form action={signOut}>
<button className="text-neutral-100">Logout</button>
</form>
</span>
</div>
</div>

<div className="flex gap-8 justify-center mt-12">
<Image
src="/supabase.svg"
alt="Supabase Logo"
width={225}
height={45}
priority
/>
<div className="border-l rotate-45 h-10"></div>
<Image
src="/next.svg"
alt="Vercel Logo"
width={150}
height={36}
priority
/>
</div>

<p className="text-3xl mx-auto max-w-2xl text-center mt-8 text-white">
The fastest way to get started building apps with{' '}
<strong>Supabase</strong> and <strong>Next.js</strong>
</p>

<div className="flex justify-center mt-12">
<span className="bg-neutral-100 py-3 px-6 rounded-lg font-mono text-sm text-neutral-900">
Get started by editing <strong>app/page.tsx</strong>
</span>
</div>
</div>
)
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// TODO: Duplicate or move this file outside the `_examples` folder to make it a route

import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs'
import { cookies } from 'next/headers'
import { NextResponse } from 'next/server'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// TODO: Duplicate or move this file outside the `_examples` folder to make it a route

import { createServerActionClient } from '@supabase/auth-helpers-nextjs'
import { revalidatePath } from 'next/cache'
import { cookies } from 'next/headers'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// TODO: Duplicate or move this file outside the `_examples` folder to make it a route

import { createServerComponentClient } from '@supabase/auth-helpers-nextjs'
import { cookies } from 'next/headers'

Expand Down
2 changes: 1 addition & 1 deletion examples/with-supabase/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export default function RootLayout({
return (
<html lang="en">
<body>
<main className="min-h-screen bg-gray-900 flex flex-col items-center">
<main className="min-h-screen bg-neutral-900 flex flex-col items-center">
{children}
</main>
</body>
Expand Down
21 changes: 10 additions & 11 deletions examples/with-supabase/app/login/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,17 @@ export default function Login() {

const handleSignIn = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
const { data, error } = await supabase.auth.signInWithPassword({
await supabase.auth.signInWithPassword({
email,
password,
})
console.log({ data, error })
router.push('/')
}

return (
<div className="flex-1 flex flex-col w-full max-w-sm justify-center gap-2">
{view === 'check-email' ? (
<p className="text-center text-gray-400">
<p className="text-center text-neutral-400">
Check <span className="font-bold text-white">{email}</span> to
continue signing up
</p>
Expand All @@ -45,21 +44,21 @@ export default function Login() {
className="flex-1 flex flex-col w-full max-w-sm justify-center gap-2"
onSubmit={view === 'sign-in' ? handleSignIn : handleSignUp}
>
<label className="text-md text-gray-400" htmlFor="email">
<label className="text-md text-neutral-400" htmlFor="email">
Email
</label>
<input
className="rounded-md px-4 py-2 bg-inherit border mb-6"
className="rounded-md px-4 py-2 bg-inherit border mb-6 text-neutral-100"
name="email"
onChange={(e) => setEmail(e.target.value)}
value={email}
placeholder="you@example.com"
/>
<label className="text-md text-gray-400" htmlFor="password">
<label className="text-md text-neutral-400" htmlFor="password">
Password
</label>
<input
className="rounded-md px-4 py-2 bg-inherit border mb-6"
className="rounded-md px-4 py-2 bg-inherit border mb-6 text-neutral-100"
type="password"
name="password"
onChange={(e) => setPassword(e.target.value)}
Expand All @@ -68,10 +67,10 @@ export default function Login() {
/>
{view === 'sign-in' ? (
<>
<button className="bg-green-700 rounded px-4 py-2 text-gray-200 mb-6">
<button className="bg-green-700 rounded px-4 py-2 text-neutral-200 mb-6">
Sign In
</button>
<p className="text-sm text-gray-500 text-center">
<p className="text-sm text-neutral-500 text-center">
Don't have an account?
<button
className="ml-1 text-white underline"
Expand All @@ -84,10 +83,10 @@ export default function Login() {
) : null}
{view === 'sign-up' ? (
<>
<button className="bg-green-700 rounded px-4 py-2 text-gray-200 mb-6">
<button className="bg-green-700 rounded px-4 py-2 text-neutral-200 mb-6">
Sign Up
</button>
<p className="text-sm text-gray-500 text-center">
<p className="text-sm text-neutral-500 text-center">
Already have an account?
<button
className="ml-1 text-white underline"
Expand Down
22 changes: 22 additions & 0 deletions examples/with-supabase/app/logout-button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
'use client'

import { createClientComponentClient } from '@supabase/auth-helpers-nextjs'
import { useRouter } from 'next/navigation'

export default function LogoutButton() {
const router = useRouter()

// Create a Supabase client configured to use cookies
const supabase = createClientComponentClient()

const signOut = async () => {
await supabase.auth.signOut()
router.push('/login')
}

return (
<button className="hover:underline" onClick={signOut}>
Logout
</button>
)
}

0 comments on commit 0674d95

Please sign in to comment.