Skip to content

Commit

Permalink
supabase course init
Browse files Browse the repository at this point in the history
  • Loading branch information
codediodeio committed Nov 23, 2022
1 parent 029a790 commit a4282a5
Show file tree
Hide file tree
Showing 43 changed files with 1,844 additions and 5 deletions.
2 changes: 1 addition & 1 deletion bin/img.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { readdirSync, rmSync } from 'fs';
import { exec } from 'child_process';

// npx @squoosh/cli --webp auto ./4.jpg
const dir = './content/courses/js/img/prizes/';
const dir = './content/courses/supabase/img/prizes/';
const files = readdirSync(dir)
files.forEach(file => {
if (!file.includes('.webp')) {
Expand Down
42 changes: 42 additions & 0 deletions content/courses/supabase/_index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
---
lastmod: 2022-11-11T10:23:30-09:00
title: React Supabase Course
description: Build a production-ready web app with Supabase and React
weight: 0
type: courses
author: Zack De Rose
authorname: Zack DeRose
authorbio: Senior Engineering Manager at Nrwl
vimeo: 773378423
tags:
- react
- supabase
- pro

stack:
- supabase
- react
- postgres
---

**The Supabase Course** is a intermediate-level project-based course that builds a Reddit-clone from scratch with Supabase, Postgres, and React.


## 🦄 What will I learn?

The goal of this course is to provide you with a solid foundation for building fullstack Supabase apps on the web.

- 👶 The Basics of Supabase
- Data modeling in a relational SQL database
- Advanced Postgres Techniques like Triggers and Functions


## 🤔 Is this Course Right for Me?

<div class="box box-blue">
This course is intermediate level 🟦 and expects some experience with React, JavaScript, and general web development. It is fast-paced and similar to my style on YouTube, but far more in-depth and with hands on projects and quizzes.
</div>

### When was the course last updated?

<span class="tag tag-sm tag-pro">Updated Nov 23rd, 2022</span>
36 changes: 36 additions & 0 deletions content/courses/supabase/app-client.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
title: Connect to Supabase
description: How to connect to the Supabase client
weight: 21
lastmod: 2022-11-20T10:23:30-09:00
draft: false
vimeo: 773633635
emoji: 🚆
video_length: 1:33
quiz: true
---

Command to install Supabase React Auth helper:

```bash
yarn add @supabase/auth-ui-react
```

Command to install Supabase JS Client:

```bash
yarn add @supabase/supabase-js
```

supa-client.ts

```ts
import { createClient } from "@supabase/supabase-js";

const supabaseUrl = (import.meta as any).env.VITE_SUPABASE_API_URL;
const supabaseKey = (import.meta as any).env.VITE_SUPABASE_ANON_KEY;

export const supaClient = createClient(supabaseUrl, supabaseKey);
```

Supabase JS Client Reference Docs: https://supabase.com/docs/reference/javascript
37 changes: 37 additions & 0 deletions content/courses/supabase/app-rls-enable.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
title: Enable RLS
description: How to enable Row Level Security
weight: 23
lastmod: 2022-11-20T10:23:30-09:00
draft: false
vimeo: 773628507
emoji: 🚔
video_length: 1:53
quiz: true
---

Turning on RLS via SQL:

```sql
alter table <table name> enable row level security
```

RLS Policies for the user_profile table:

```sql
CREATE POLICY "all can see" ON "public"."user_profiles"
AS PERMISSIVE FOR SELECT
TO public
USING (true);

CREATE POLICY "users can insert" ON "public"."user_profiles"
AS PERMISSIVE FOR INSERT
TO public
WITH CHECK (auth.uid() = user_id);

CREATE POLICY "owners can update" ON "public"."user_profiles"
AS PERMISSIVE FOR UPDATE
TO public
USING (auth.uid()=user_id)
WITH CHECK (auth.uid()=user_id);
```
13 changes: 13 additions & 0 deletions content/courses/supabase/app-rls.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
title: Row Level Security
description: What is Row Level Security in Supabase?
weight: 22
lastmod: 2022-11-20T10:23:30-09:00
draft: false
vimeo: 773628343
emoji: 👮‍♂️
video_length: 1:25
quiz: true
---

Supabase docs on RLS: https://supabase.com/docs/guides/auth/row-level-security
83 changes: 83 additions & 0 deletions content/courses/supabase/app-routing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
---
title: Routing and Layout
description: Design the layout and routing structure with React Router
weight: 20
lastmod: 2022-11-20T10:23:30-09:00
draft: false
vimeo: 773633416
emoji: 🚆
video_length: 1:33
quiz: true
chapter_start: App Architecture
---

React Router docs: https://reactrouter.com/en/main

Command to install react router:

```bash
yarn add react-router-dom
```

Routes for our app:

```ts
const router = createBrowserRouter([
{
path: "/",
element: <Layout />,
children: [
{
path: "",
element: <MessageBoard />,
children: [
{
path: ":pageNumber",
element: <AllPosts />,
},
{
path: "post/:postId",
element: <PostView />,
},
],
},
{
path: "welcome",
element: <Welcome />,
loader: welcomeLoader,
},
],
},
]);
```

Our starting NavBar component:

```tsx
import { Link } from "react-router-dom";

export default function NavBar() {
return (
<>
<nav className="nav-bar">
<Link className="nav-logo-link" to="/">
<img
id="logo"
className="nav-logo"
src="https://supaship.io/supaship_logo_with_text.svg"
alt="logo"
/>
</Link>

<ul className="nav-right-list">
<li className="nav-message-board-list-item">
<Link to="/1" className="nav-message-board-link">
message board
</Link>
</li>
</ul>
</nav>
</>
);
}
```
30 changes: 30 additions & 0 deletions content/courses/supabase/app-tables.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
title: Database Tables
description: Add a database table for usernames
weight: 21
lastmod: 2022-11-20T10:23:30-09:00
draft: false
vimeo: 773628200
emoji: 🍱
video_length: 2:17
quiz: true
---

## Resources

Command to create a migration file from your current local Supabase state:

```bash
npx supabase db diff --use-migra --file=<name of the migration>
```

SQL to create our user profile table:

```sql
create table user_profiles (
user_id uuid primary key references auth.users (id) not null,
username text unique not null
CONSTRAINT proper_username CHECK (username ~* '^[a-zA-Z0-9_]+$')
CONSTRAINT username_length CHECK (char_length(username) > 3 and char_length(username) < 15)
);
```
17 changes: 17 additions & 0 deletions content/courses/supabase/app-typescript.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
title: Generate Types
description: How to generate TypeScript types from your Supabase database
weight: 24
lastmod: 2022-11-20T10:23:30-09:00
draft: false
vimeo: 773628675
emoji: 💪
video_length: 0:44
quiz: true
---

Command to generate typscript types:

```bash
npx supabase gen types typescript --local > ./src/database.types.ts
```
11 changes: 11 additions & 0 deletions content/courses/supabase/auth-context.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
title: User Context
description: Use React context to access user data
weight: 32
lastmod: 2022-11-20T10:23:30-09:00
draft: false
vimeo: 773629204
emoji: 🌲
video_length: 1:19
quiz: true
---
89 changes: 89 additions & 0 deletions content/courses/supabase/auth-hook.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
---
title: Session Hook
description: Create a custom React hook for session management
weight: 31
lastmod: 2022-11-20T10:23:30-09:00
draft: false
vimeo: 773629010
emoji: 🎣
video_length: 2:43
quiz: true
---


use-session.ts:

```ts
import { RealtimeChannel, Session } from "@supabase/supabase-js";
import { useEffect, useState } from "react";
import { supaClient } from "./supa-client";

export interface UserProfile {
username: string;
avatarUrl?: string;
}

export interface SupashipUserInfo {
session: Session | null;
profile: UserProfile | null;
}

export function useSession(): SupashipUserInfo {
const [userInfo, setUserInfo] = useState<SupashipUserInfo>({
profile: null,
session: null,
});
const [channel, setChannel] = useState<RealtimeChannel | null>(null);
useEffect(() => {
supaClient.auth.getSession().then(({ data: { session } }) => {
setUserInfo({ ...userInfo, session });
supaClient.auth.onAuthStateChange((_event, session) => {
setUserInfo({ session, profile: null });
});
});
}, []);

useEffect(() => {
if (userInfo.session?.user && !userInfo.profile) {
listenToUserProfileChanges(userInfo.session.user.id).then(
(newChannel) => {
if (channel) {
channel.unsubscribe();
}
setChannel(newChannel);
}
);
} else if (!userInfo.session?.user) {
channel?.unsubscribe();
setChannel(null);
}
}, [userInfo.session]);

async function listenToUserProfileChanges(userId: string) {
const { data } = await supaClient
.from("user_profiles")
.select("*")
.filter("user_id", "eq", userId);
if (data?.[0]) {
setUserInfo({ ...userInfo, profile: data?.[0] });
}
return supaClient
.channel(`public:user_profiles`)
.on(
"postgres_changes",
{
event: "*",
schema: "public",
table: "user_profiles",
filter: `user_id=eq.${userId}`,
},
(payload) => {
setUserInfo({ ...userInfo, profile: payload.new as UserProfile });
}
)
.subscribe();
}

return userInfo;
}
```
Loading

0 comments on commit a4282a5

Please sign in to comment.