├── .eslintrc.json ├── .gitignore ├── README.md ├── netlify.toml ├── next.config.js ├── package-lock.json ├── package.json ├── pages ├── [id].js ├── _app.js ├── api │ └── hello.js ├── index.js └── login.js ├── public ├── favicon.ico └── vercel.svg ├── styles ├── Home.module.css └── globals.css └── utils └── supabase.js /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | 33 | # vercel 34 | .vercel 35 | 36 | # Local Netlify folder 37 | .netlify 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | Learn With Jason 4 | 5 |

6 |

7 | Build an App With Supabase and Next.js (with Jon Meyers) 8 |

9 |

10 | This app was built live on Learn With Jason and it was super fun and I’m sad you weren’t there. 11 |

12 |

13 | But don’t worry! You can still: 14 | watch the video · 15 | see the demo · 16 | deploy this project · 17 | see upcoming episodes 18 |

19 | 20 |   21 | 22 | Supabase combines database storage and authentication into a powerful workflow for web devs. In this episode, Jon Meyers will teach us how to build a Next.js app with Supabase and deploy it to Netlify! 23 | 24 |   25 | 26 | ## More Information 27 | 28 | - [Watch this app get built live + see links and additional resources][episode] 29 | - [Follow _Learn With Jason_ on Twitch][twitch] to watch future episodes live 30 | - [Add the _Learn With Jason_ schedule to your Google Calendar][cal] 31 | 32 |   33 | 34 |

35 | 36 | Deploy this project to Netlify 37 | 38 |

39 | 40 | [episode]: https://www.learnwithjason.dev/build-an-app-with-supabase-and-nextjs 41 | [twitch]: https://jason.af/twitch 42 | [cal]: https://lwj.dev/cal 43 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | # example netlify.toml 2 | [build] 3 | command = "next build" 4 | functions = "netlify/functions" 5 | publish = ".next" 6 | 7 | ## Uncomment to use this redirect for Single Page Applications like create-react-app. 8 | ## Not needed for static site generators. 9 | #[[redirects]] 10 | # from = "/*" 11 | # to = "/index.html" 12 | # status = 200 13 | 14 | ## (optional) Settings for Netlify Dev 15 | ## https://github.com/netlify/cli/blob/main/docs/netlify-dev.md#project-detection 16 | #[dev] 17 | # command = "yarn start" # Command to start your dev server 18 | # port = 3000 # Port that the dev server will be listening on 19 | # publish = "dist" # Folder with the static content for _redirect file 20 | 21 | ## more info on configuring this file: https://www.netlify.com/docs/netlify-toml-reference/ 22 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | reactStrictMode: true, 3 | } 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "supabase-blog", 3 | "private": true, 4 | "scripts": { 5 | "dev": "next dev", 6 | "build": "next build", 7 | "start": "next start", 8 | "lint": "next lint" 9 | }, 10 | "dependencies": { 11 | "@supabase/supabase-js": "^1.29.1", 12 | "next": "12.0.7", 13 | "react": "17.0.2", 14 | "react-dom": "17.0.2" 15 | }, 16 | "devDependencies": { 17 | "eslint": "8.6.0", 18 | "eslint-config-next": "12.0.7" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /pages/[id].js: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react'; 2 | import supabase from '../utils/supabase'; 3 | 4 | export async function getServerSideProps({ params }) { 5 | const { data: post, error } = await supabase 6 | .from('posts') 7 | .select('*, comments(*)') 8 | .eq('id', params.id) 9 | .single(); 10 | 11 | if (error) { 12 | console.log(error.message); 13 | } 14 | 15 | return { 16 | props: { 17 | post, 18 | }, 19 | }; 20 | } 21 | 22 | export default function PostPage({ post = {} }) { 23 | useEffect(() => { 24 | const subscription = supabase 25 | .from('comments') 26 | .on('INSERT', (payload) => { 27 | console.log(payload); 28 | }) 29 | .subscribe(); 30 | 31 | return () => supabase.removeSubscription(subscription); 32 | }, []); 33 | 34 | return ( 35 |
36 |

{post.title}

37 |

{post.content}

38 |
{JSON.stringify(post, null, 2)}
39 |
40 | ); 41 | } 42 | -------------------------------------------------------------------------------- /pages/_app.js: -------------------------------------------------------------------------------- 1 | import '../styles/globals.css' 2 | 3 | function MyApp({ Component, pageProps }) { 4 | return 5 | } 6 | 7 | export default MyApp 8 | -------------------------------------------------------------------------------- /pages/api/hello.js: -------------------------------------------------------------------------------- 1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction 2 | 3 | export default function handler(req, res) { 4 | res.status(200).json({ name: 'John Doe' }) 5 | } 6 | -------------------------------------------------------------------------------- /pages/index.js: -------------------------------------------------------------------------------- 1 | import styles from '../styles/Home.module.css'; 2 | import supabase from '../utils/supabase'; 3 | 4 | export async function getStaticProps() { 5 | const { data: posts, error } = await supabase.from('posts').select('*'); 6 | 7 | if (error) { 8 | throw new Error(error); 9 | } 10 | 11 | return { 12 | props: { 13 | posts, 14 | }, 15 | }; 16 | } 17 | 18 | export default function Home({ posts }) { 19 | console.log(supabase.auth.user()); 20 | 21 | return ( 22 |
23 |

Hello chat!

24 |
{JSON.stringify(posts, null, 2)}
25 |
26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /pages/login.js: -------------------------------------------------------------------------------- 1 | import supabase from '../utils/supabase'; 2 | 3 | export default function LoginPage() { 4 | async function handleSubmit(event) { 5 | event.preventDefault(); 6 | 7 | const email = event.target.email.value; 8 | 9 | await supabase.auth.signIn({ email }); 10 | } 11 | 12 | return ( 13 |
14 |

Log In

15 |
16 | 17 | 18 | 19 |
20 |
21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/learnwithjason/supabase-blog/8c4ccf35aff97767b394d2168c766d9b6eb90793/public/favicon.ico -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /styles/Home.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | padding: 0 2rem; 3 | } 4 | 5 | .main { 6 | min-height: 100vh; 7 | padding: 4rem 0; 8 | flex: 1; 9 | display: flex; 10 | flex-direction: column; 11 | justify-content: center; 12 | align-items: center; 13 | } 14 | 15 | .footer { 16 | display: flex; 17 | flex: 1; 18 | padding: 2rem 0; 19 | border-top: 1px solid #eaeaea; 20 | justify-content: center; 21 | align-items: center; 22 | } 23 | 24 | .footer a { 25 | display: flex; 26 | justify-content: center; 27 | align-items: center; 28 | flex-grow: 1; 29 | } 30 | 31 | .title a { 32 | color: #0070f3; 33 | text-decoration: none; 34 | } 35 | 36 | .title a:hover, 37 | .title a:focus, 38 | .title a:active { 39 | text-decoration: underline; 40 | } 41 | 42 | .title { 43 | margin: 0; 44 | line-height: 1.15; 45 | font-size: 4rem; 46 | } 47 | 48 | .title, 49 | .description { 50 | text-align: center; 51 | } 52 | 53 | .description { 54 | margin: 4rem 0; 55 | line-height: 1.5; 56 | font-size: 1.5rem; 57 | } 58 | 59 | .code { 60 | background: #fafafa; 61 | border-radius: 5px; 62 | padding: 0.75rem; 63 | font-size: 1.1rem; 64 | font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, 65 | Bitstream Vera Sans Mono, Courier New, monospace; 66 | } 67 | 68 | .grid { 69 | display: flex; 70 | align-items: center; 71 | justify-content: center; 72 | flex-wrap: wrap; 73 | max-width: 800px; 74 | } 75 | 76 | .card { 77 | margin: 1rem; 78 | padding: 1.5rem; 79 | text-align: left; 80 | color: inherit; 81 | text-decoration: none; 82 | border: 1px solid #eaeaea; 83 | border-radius: 10px; 84 | transition: color 0.15s ease, border-color 0.15s ease; 85 | max-width: 300px; 86 | } 87 | 88 | .card:hover, 89 | .card:focus, 90 | .card:active { 91 | color: #0070f3; 92 | border-color: #0070f3; 93 | } 94 | 95 | .card h2 { 96 | margin: 0 0 1rem 0; 97 | font-size: 1.5rem; 98 | } 99 | 100 | .card p { 101 | margin: 0; 102 | font-size: 1.25rem; 103 | line-height: 1.5; 104 | } 105 | 106 | .logo { 107 | height: 1em; 108 | margin-left: 0.5rem; 109 | } 110 | 111 | @media (max-width: 600px) { 112 | .grid { 113 | width: 100%; 114 | flex-direction: column; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /styles/globals.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | padding: 0; 4 | margin: 0; 5 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, 6 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; 7 | } 8 | 9 | a { 10 | color: inherit; 11 | text-decoration: none; 12 | } 13 | 14 | * { 15 | box-sizing: border-box; 16 | } 17 | -------------------------------------------------------------------------------- /utils/supabase.js: -------------------------------------------------------------------------------- 1 | import { createClient } from '@supabase/supabase-js'; 2 | 3 | export default createClient( 4 | process.env.NEXT_PUBLIC_SUPABASE_URL, 5 | process.env.NEXT_PUBLIC_SUPABASE_API_KEY, 6 | ); 7 | --------------------------------------------------------------------------------