├── .gitignore ├── 01-create-a-supabase-project └── README.md ├── 02-create-a-table-in-supabase └── README.md ├── 03-create-a-next-js-app-with-tailwind-css ├── README.md ├── package-lock.json ├── package.json ├── pages │ ├── _app.js │ ├── api │ │ └── hello.js │ └── index.js ├── postcss.config.js ├── public │ ├── favicon.ico │ └── vercel.svg └── tailwind.config.js ├── 04-query-data-from-supabase-using-next-js ├── README.md ├── package-lock.json ├── package.json ├── pages │ ├── _app.js │ ├── api │ │ └── hello.js │ └── index.js ├── postcss.config.js ├── public │ ├── favicon.ico │ └── vercel.svg ├── tailwind.config.js └── utils │ └── supabase.js ├── 05-use-next-js-to-query-a-single-record-from-supabase ├── README.md ├── package-lock.json ├── package.json ├── pages │ ├── [id].js │ ├── _app.js │ ├── api │ │ └── hello.js │ └── index.js ├── postcss.config.js ├── public │ ├── favicon.ico │ └── vercel.svg ├── tailwind.config.js └── utils │ └── supabase.js ├── 06-implement-third-party-authentication-with-github-in-next-js-using-supabase ├── README.md ├── package-lock.json ├── package.json ├── pages │ ├── [id].js │ ├── _app.js │ ├── api │ │ └── hello.js │ ├── index.js │ ├── login.js │ └── logout.js ├── postcss.config.js ├── public │ ├── favicon.ico │ └── vercel.svg ├── tailwind.config.js └── utils │ └── supabase.js ├── 07-add-relationships-between-tables-in-supabase-using-foreign-keys ├── README.md ├── package-lock.json ├── package.json ├── pages │ ├── [id].js │ ├── _app.js │ ├── api │ │ └── hello.js │ ├── index.js │ ├── login.js │ └── logout.js ├── postcss.config.js ├── public │ ├── favicon.ico │ └── vercel.svg ├── tailwind.config.js └── utils │ └── supabase.js ├── 08-use-postgres-functions-to-implement-database-logic-with-supabase ├── README.md ├── package-lock.json ├── package.json ├── pages │ ├── [id].js │ ├── _app.js │ ├── api │ │ └── hello.js │ ├── index.js │ ├── login.js │ └── logout.js ├── postcss.config.js ├── public │ ├── favicon.ico │ └── vercel.svg ├── tailwind.config.js └── utils │ └── supabase.js ├── 09-use-supabase-to-subscribe-to-database-events-with-postgres-triggers ├── README.md ├── package-lock.json ├── package.json ├── pages │ ├── [id].js │ ├── _app.js │ ├── api │ │ └── hello.js │ ├── index.js │ ├── login.js │ └── logout.js ├── postcss.config.js ├── public │ ├── favicon.ico │ └── vercel.svg ├── tailwind.config.js └── utils │ └── supabase.js ├── 10-create-a-stripe-customer-with-next-js-api-routes ├── README.md ├── package-lock.json ├── package.json ├── pages │ ├── [id].js │ ├── _app.js │ ├── api │ │ ├── create-stripe-customer.js │ │ └── hello.js │ ├── index.js │ ├── login.js │ └── logout.js ├── postcss.config.js ├── public │ ├── favicon.ico │ └── vercel.svg ├── tailwind.config.js └── utils │ └── supabase.js ├── 11-generate-a-custom-api-key-to-secure-an-api-route-in-next-js ├── README.md ├── package-lock.json ├── package.json ├── pages │ ├── [id].js │ ├── _app.js │ ├── api │ │ ├── create-stripe-customer.js │ │ └── hello.js │ ├── index.js │ ├── login.js │ └── logout.js ├── postcss.config.js ├── public │ ├── favicon.ico │ └── vercel.svg ├── tailwind.config.js └── utils │ └── supabase.js ├── 12-automatically-create-a-stripe-customer-for-each-user-with-supabase-function-hooks ├── README.md ├── package-lock.json ├── package.json ├── pages │ ├── [id].js │ ├── _app.js │ ├── api │ │ ├── create-stripe-customer.js │ │ └── hello.js │ ├── index.js │ ├── login.js │ └── logout.js ├── postcss.config.js ├── public │ ├── favicon.ico │ └── vercel.svg ├── tailwind.config.js └── utils │ └── supabase.js ├── 13-make-user-state-globally-accessible-in-next-js-with-react-context-and-providers ├── README.md ├── context │ └── user.js ├── package-lock.json ├── package.json ├── pages │ ├── [id].js │ ├── _app.js │ ├── api │ │ ├── create-stripe-customer.js │ │ └── hello.js │ ├── index.js │ ├── login.js │ └── logout.js ├── postcss.config.js ├── public │ ├── favicon.ico │ └── vercel.svg ├── tailwind.config.js └── utils │ └── supabase.js ├── 14-implement-authorization-using-row-level-security-and-policies ├── README.md ├── context │ └── user.js ├── package-lock.json ├── package.json ├── pages │ ├── [id].js │ ├── _app.js │ ├── api │ │ ├── create-stripe-customer.js │ │ └── hello.js │ ├── index.js │ ├── login.js │ └── logout.js ├── postcss.config.js ├── public │ ├── favicon.ico │ └── vercel.svg ├── tailwind.config.js └── utils │ └── supabase.js ├── 15-implement-gated-content-using-row-level-security-with-supabase ├── README.md ├── context │ └── user.js ├── package-lock.json ├── package.json ├── pages │ ├── [id].js │ ├── _app.js │ ├── api │ │ ├── create-stripe-customer.js │ │ └── hello.js │ ├── index.js │ ├── login.js │ └── logout.js ├── postcss.config.js ├── public │ ├── favicon.ico │ └── vercel.svg ├── tailwind.config.js └── utils │ └── supabase.js ├── 16-use-stripe-js-to-query-product-data-and-pre-render-with-next-js ├── README.md ├── context │ └── user.js ├── package-lock.json ├── package.json ├── pages │ ├── [id].js │ ├── _app.js │ ├── api │ │ ├── create-stripe-customer.js │ │ └── hello.js │ ├── index.js │ ├── login.js │ ├── logout.js │ └── pricing.js ├── postcss.config.js ├── public │ ├── favicon.ico │ └── vercel.svg ├── tailwind.config.js └── utils │ └── supabase.js ├── 17-create-shared-nav-bar-in-next-js-with-_app-js ├── README.md ├── components │ └── nav.js ├── context │ └── user.js ├── package-lock.json ├── package.json ├── pages │ ├── [id].js │ ├── _app.js │ ├── api │ │ ├── create-stripe-customer.js │ │ └── hello.js │ ├── index.js │ ├── login.js │ ├── logout.js │ └── pricing.js ├── postcss.config.js ├── public │ ├── favicon.ico │ └── vercel.svg ├── tailwind.config.js └── utils │ └── supabase.js ├── 18-query-dynamic-supabase-data-in-static-pages-using-next-js ├── README.md ├── components │ └── nav.js ├── context │ └── user.js ├── package-lock.json ├── package.json ├── pages │ ├── [id].js │ ├── _app.js │ ├── api │ │ ├── create-stripe-customer.js │ │ └── hello.js │ ├── index.js │ ├── login.js │ ├── logout.js │ └── pricing.js ├── postcss.config.js ├── public │ ├── favicon.ico │ └── vercel.svg ├── tailwind.config.js └── utils │ └── supabase.js ├── 19-pass-supabase-session-cookie-to-api-route-to-identify-user ├── README.md ├── components │ └── nav.js ├── context │ └── user.js ├── package-lock.json ├── package.json ├── pages │ ├── [id].js │ ├── _app.js │ ├── api │ │ ├── create-stripe-customer.js │ │ ├── hello.js │ │ ├── set-supabase-cookie.js │ │ └── subscription │ │ │ └── [priceId].js │ ├── index.js │ ├── login.js │ ├── logout.js │ └── pricing.js ├── postcss.config.js ├── public │ ├── favicon.ico │ └── vercel.svg ├── tailwind.config.js └── utils │ └── supabase.js ├── 20-charge-customer-for-stripe-subscription-in-next-js ├── README.md ├── components │ └── nav.js ├── context │ └── user.js ├── package-lock.json ├── package.json ├── pages │ ├── [id].js │ ├── _app.js │ ├── api │ │ ├── create-stripe-customer.js │ │ ├── hello.js │ │ ├── set-supabase-cookie.js │ │ └── subscription │ │ │ └── [priceId].js │ ├── index.js │ ├── login.js │ ├── logout.js │ ├── payment │ │ ├── cancelled.js │ │ └── success.js │ └── pricing.js ├── postcss.config.js ├── public │ ├── favicon.ico │ └── vercel.svg ├── tailwind.config.js └── utils │ └── supabase.js ├── 21-subscribe-to-stripe-webhooks-using-next-js-api-routes ├── README.md ├── components │ └── nav.js ├── context │ └── user.js ├── package-lock.json ├── package.json ├── pages │ ├── [id].js │ ├── _app.js │ ├── api │ │ ├── create-stripe-customer.js │ │ ├── hello.js │ │ ├── set-supabase-cookie.js │ │ ├── stripe-hooks.js │ │ └── subscription │ │ │ └── [priceId].js │ ├── index.js │ ├── login.js │ ├── logout.js │ ├── payment │ │ ├── cancelled.js │ │ └── success.js │ └── pricing.js ├── postcss.config.js ├── public │ ├── favicon.ico │ └── vercel.svg ├── tailwind.config.js └── utils │ └── supabase.js ├── 22-use-the-supabase-service-key-to-bypass-row-level-security ├── README.md ├── components │ └── nav.js ├── context │ └── user.js ├── package-lock.json ├── package.json ├── pages │ ├── [id].js │ ├── _app.js │ ├── api │ │ ├── create-stripe-customer.js │ │ ├── hello.js │ │ ├── set-supabase-cookie.js │ │ ├── stripe-hooks.js │ │ └── subscription │ │ │ └── [priceId].js │ ├── index.js │ ├── login.js │ ├── logout.js │ ├── payment │ │ ├── cancelled.js │ │ └── success.js │ └── pricing.js ├── postcss.config.js ├── public │ ├── favicon.ico │ └── vercel.svg ├── tailwind.config.js └── utils │ └── supabase.js ├── 23-create-a-client-page-that-requires-authentication-in-next-js-using-getserversideprops ├── README.md ├── components │ └── nav.js ├── context │ └── user.js ├── package-lock.json ├── package.json ├── pages │ ├── [id].js │ ├── _app.js │ ├── api │ │ ├── create-stripe-customer.js │ │ ├── hello.js │ │ ├── set-supabase-cookie.js │ │ ├── stripe-hooks.js │ │ └── subscription │ │ │ └── [priceId].js │ ├── dashboard.js │ ├── index.js │ ├── login.js │ ├── logout.js │ ├── payment │ │ ├── cancelled.js │ │ └── success.js │ └── pricing.js ├── postcss.config.js ├── public │ ├── favicon.ico │ └── vercel.svg ├── tailwind.config.js └── utils │ └── supabase.js ├── 24-allow-customer-to-manage-their-subscription-with-stripe-customer-portal ├── README.md ├── components │ └── nav.js ├── context │ └── user.js ├── package-lock.json ├── package.json ├── pages │ ├── [id].js │ ├── _app.js │ ├── api │ │ ├── create-stripe-customer.js │ │ ├── hello.js │ │ ├── portal.js │ │ ├── set-supabase-cookie.js │ │ ├── stripe-hooks.js │ │ └── subscription │ │ │ └── [priceId].js │ ├── dashboard.js │ ├── index.js │ ├── login.js │ ├── logout.js │ ├── payment │ │ ├── cancelled.js │ │ └── success.js │ ├── pricing.js │ ├── privacy.js │ └── terms.js ├── postcss.config.js ├── public │ ├── favicon.ico │ └── vercel.svg ├── tailwind.config.js └── utils │ └── supabase.js ├── 25-subscribe-the-ui-to-database-changes-with-supabase-real-time ├── README.md ├── components │ └── nav.js ├── context │ └── user.js ├── package-lock.json ├── package.json ├── pages │ ├── [id].js │ ├── _app.js │ ├── api │ │ ├── create-stripe-customer.js │ │ ├── hello.js │ │ ├── portal.js │ │ ├── set-supabase-cookie.js │ │ ├── stripe-hooks.js │ │ └── subscription │ │ │ └── [priceId].js │ ├── dashboard.js │ ├── index.js │ ├── login.js │ ├── logout.js │ ├── payment │ │ ├── cancelled.js │ │ └── success.js │ ├── pricing.js │ ├── privacy.js │ └── terms.js ├── postcss.config.js ├── public │ ├── favicon.ico │ └── vercel.svg ├── tailwind.config.js └── utils │ └── supabase.js ├── 26-configure-stripe-for-production-and-deploy-next-js-application-with-vercel ├── README.md ├── components │ └── nav.js ├── context │ └── user.js ├── package-lock.json ├── package.json ├── pages │ ├── [id].js │ ├── _app.js │ ├── api │ │ ├── create-stripe-customer.js │ │ ├── hello.js │ │ ├── portal.js │ │ ├── set-supabase-cookie.js │ │ ├── stripe-hooks.js │ │ └── subscription │ │ │ └── [priceId].js │ ├── dashboard.js │ ├── index.js │ ├── login.js │ ├── logout.js │ ├── payment │ │ ├── cancelled.js │ │ └── success.js │ ├── pricing.js │ ├── privacy.js │ └── terms.js ├── postcss.config.js ├── public │ ├── favicon.ico │ └── vercel.svg ├── tailwind.config.js └── utils │ └── supabase.js └── README.md /.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 | # temp 37 | /example 38 | -------------------------------------------------------------------------------- /01-create-a-supabase-project/README.md: -------------------------------------------------------------------------------- 1 | # Create a Supabase Project 2 | 3 | **[📹 Video](https://egghead.io/lessons/supabase-create-a-supabase-project)** 4 | 5 | Supabase is a backend-as-a-service (BaaS) that makes it super simple to create databases, tables, columns and input data. 6 | 7 | Every project contains the infrastructure to handle database storage, authentication, authorization, file storage and real-time. 8 | 9 | In this video, we look at setting up a Supabase project from scratch - creating an account, organisation and a project - with a geographically close region. 10 | 11 | [👉 Next lesson](/02-create-a-table-in-supabase) 12 | 13 | --- 14 | 15 | Enjoyed the course? Follow me on [Twitter](https://twitter.com/jonmeyers_io) and subscribe to my [YouTube channel](https://www.youtube.com/channel/UCPitAIwktfCfcMR4kDWebDQ). 16 | -------------------------------------------------------------------------------- /02-create-a-table-in-supabase/README.md: -------------------------------------------------------------------------------- 1 | # Create a Table in Supabase 2 | 3 | **[📹 Video](https://egghead.io/lessons/supabase-create-a-table-in-supabase)** 4 | 5 | Supabase makes it super easy to create new tables, and change the structure of your database over time! 6 | 7 | In this video, we look at creating a lesson table, which will store our publicly available data for each lesson. We add a column for title and description, and input some basic test data. 8 | 9 | [👉 Next lesson](/03-create-a-next-js-app-with-tailwind-css) 10 | 11 | --- 12 | 13 | Enjoyed the course? Follow me on [Twitter](https://twitter.com/jonmeyers_io) and subscribe to my [YouTube channel](https://www.youtube.com/channel/UCPitAIwktfCfcMR4kDWebDQ). 14 | -------------------------------------------------------------------------------- /03-create-a-next-js-app-with-tailwind-css/README.md: -------------------------------------------------------------------------------- 1 | # Create a Next.js App with Tailwind CSS 2 | 3 | **[📹 Video](https://egghead.io/lessons/next-js-create-a-next-js-app-with-tailwind-css)** 4 | 5 | Next.js is a super efficient web development framework, built on React. It handles both client-side and server-side code, making it a great tool for most web apps. 6 | 7 | In this video, we use the create-next-app package to create a new Next.js application, and use the with-tailwind argument to configure TailwindCSS. 8 | 9 | Additionally, we remove the boilerplate markup, and run our application in development mode with npm run dev. 10 | 11 | [👉 Next lesson](/04-query-data-from-supabase-using-next-js) 12 | 13 | --- 14 | 15 | Enjoyed the course? Follow me on [Twitter](https://twitter.com/jonmeyers_io) and subscribe to my [YouTube channel](https://www.youtube.com/channel/UCPitAIwktfCfcMR4kDWebDQ). 16 | -------------------------------------------------------------------------------- /03-create-a-next-js-app-with-tailwind-css/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "dev": "next dev", 5 | "build": "next build", 6 | "start": "next start" 7 | }, 8 | "dependencies": { 9 | "next": "latest", 10 | "react": "^17.0.2", 11 | "react-dom": "^17.0.2" 12 | }, 13 | "devDependencies": { 14 | "autoprefixer": "^10.2.6", 15 | "postcss": "^8.3.5", 16 | "tailwindcss": "^2.2.4" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /03-create-a-next-js-app-with-tailwind-css/pages/_app.js: -------------------------------------------------------------------------------- 1 | import 'tailwindcss/tailwind.css' 2 | 3 | function MyApp({ Component, pageProps }) { 4 | return 5 | } 6 | 7 | export default MyApp 8 | -------------------------------------------------------------------------------- /03-create-a-next-js-app-with-tailwind-css/pages/api/hello.js: -------------------------------------------------------------------------------- 1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction 2 | 3 | export default function helloAPI(req, res) { 4 | res.status(200).json({ name: 'John Doe' }) 5 | } 6 | -------------------------------------------------------------------------------- /03-create-a-next-js-app-with-tailwind-css/pages/index.js: -------------------------------------------------------------------------------- 1 | export default function Home() { 2 | return ( 3 |
4 | working 5 |
6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /03-create-a-next-js-app-with-tailwind-css/postcss.config.js: -------------------------------------------------------------------------------- 1 | // If you want to use other PostCSS plugins, see the following: 2 | // https://tailwindcss.com/docs/using-with-preprocessors 3 | module.exports = { 4 | plugins: { 5 | tailwindcss: {}, 6 | autoprefixer: {}, 7 | }, 8 | } 9 | -------------------------------------------------------------------------------- /03-create-a-next-js-app-with-tailwind-css/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dijonmusters/build-a-saas-with-next-js-supabase-and-stripe/578c4681065d34fbd46d988005787e6033f6436f/03-create-a-next-js-app-with-tailwind-css/public/favicon.ico -------------------------------------------------------------------------------- /03-create-a-next-js-app-with-tailwind-css/public/vercel.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /03-create-a-next-js-app-with-tailwind-css/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | mode: 'jit', 3 | purge: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'], 4 | darkMode: false, // or 'media' or 'class' 5 | theme: { 6 | extend: {}, 7 | }, 8 | variants: { 9 | extend: {}, 10 | }, 11 | plugins: [], 12 | } 13 | -------------------------------------------------------------------------------- /04-query-data-from-supabase-using-next-js/README.md: -------------------------------------------------------------------------------- 1 | # Query Data From Supabase Using Next.js 2 | 3 | **[📹 Video](https://egghead.io/lessons/supabase-query-data-from-supabase-using-next-js)** 4 | 5 | supabase-js is a JavaScript library that makes interacting with your Supabase database simple! 6 | 7 | In this video, we install the package with npm using npm i @supabase/supabase.js. We then create a reusable Supabase client that can be used throughout our application. 8 | 9 | Additionally, we create environment variables for the Supabase URL and key, which are available in our Next.js client. Exposing these values to the client is not a security risk, as we will be enabling Row Level Security in a future lesson. 10 | 11 | Lastly, we tell Next.js that we would like to pre-render our landing page at build time, by exporting out a getStaticProps function. This ensures that our lesson data is only fetched once - when we rebuild our application - rather than each time to user visits the page. 12 | 13 | [👉 Next lesson](/05-use-next-js-to-query-a-single-record-from-supabase) 14 | 15 | --- 16 | 17 | Enjoyed the course? Follow me on [Twitter](https://twitter.com/jonmeyers_io) and subscribe to my [YouTube channel](https://www.youtube.com/channel/UCPitAIwktfCfcMR4kDWebDQ). 18 | -------------------------------------------------------------------------------- /04-query-data-from-supabase-using-next-js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "dev": "next dev", 5 | "build": "next build", 6 | "start": "next start" 7 | }, 8 | "dependencies": { 9 | "@supabase/supabase-js": "^1.28.2", 10 | "next": "latest", 11 | "react": "^17.0.2", 12 | "react-dom": "^17.0.2" 13 | }, 14 | "devDependencies": { 15 | "autoprefixer": "^10.2.6", 16 | "postcss": "^8.3.5", 17 | "tailwindcss": "^2.2.4" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /04-query-data-from-supabase-using-next-js/pages/_app.js: -------------------------------------------------------------------------------- 1 | import 'tailwindcss/tailwind.css' 2 | 3 | function MyApp({ Component, pageProps }) { 4 | return 5 | } 6 | 7 | export default MyApp 8 | -------------------------------------------------------------------------------- /04-query-data-from-supabase-using-next-js/pages/api/hello.js: -------------------------------------------------------------------------------- 1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction 2 | 3 | export default function helloAPI(req, res) { 4 | res.status(200).json({ name: 'John Doe' }) 5 | } 6 | -------------------------------------------------------------------------------- /04-query-data-from-supabase-using-next-js/pages/index.js: -------------------------------------------------------------------------------- 1 | import { supabase } from "../utils/supabase"; 2 | 3 | export default function Home({ lessons }) { 4 | console.log({ lessons }); 5 | return ( 6 |
7 | {lessons.map((lesson) => ( 8 |

{lesson.title}

9 | ))} 10 |
11 | ); 12 | } 13 | 14 | export const getStaticProps = async () => { 15 | const { data: lessons } = await supabase.from("lesson").select("*"); 16 | 17 | return { 18 | props: { 19 | lessons, 20 | }, 21 | }; 22 | }; 23 | -------------------------------------------------------------------------------- /04-query-data-from-supabase-using-next-js/postcss.config.js: -------------------------------------------------------------------------------- 1 | // If you want to use other PostCSS plugins, see the following: 2 | // https://tailwindcss.com/docs/using-with-preprocessors 3 | module.exports = { 4 | plugins: { 5 | tailwindcss: {}, 6 | autoprefixer: {}, 7 | }, 8 | } 9 | -------------------------------------------------------------------------------- /04-query-data-from-supabase-using-next-js/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dijonmusters/build-a-saas-with-next-js-supabase-and-stripe/578c4681065d34fbd46d988005787e6033f6436f/04-query-data-from-supabase-using-next-js/public/favicon.ico -------------------------------------------------------------------------------- /04-query-data-from-supabase-using-next-js/public/vercel.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /04-query-data-from-supabase-using-next-js/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | mode: 'jit', 3 | purge: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'], 4 | darkMode: false, // or 'media' or 'class' 5 | theme: { 6 | extend: {}, 7 | }, 8 | variants: { 9 | extend: {}, 10 | }, 11 | plugins: [], 12 | } 13 | -------------------------------------------------------------------------------- /04-query-data-from-supabase-using-next-js/utils/supabase.js: -------------------------------------------------------------------------------- 1 | import { createClient } from "@supabase/supabase-js"; 2 | 3 | export const supabase = createClient( 4 | process.env.NEXT_PUBLIC_SUPABASE_URL, 5 | process.env.NEXT_PUBLIC_SUPABASE_KEY 6 | ); 7 | -------------------------------------------------------------------------------- /05-use-next-js-to-query-a-single-record-from-supabase/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "dev": "next dev", 5 | "build": "next build", 6 | "start": "next start" 7 | }, 8 | "dependencies": { 9 | "@supabase/supabase-js": "^1.28.2", 10 | "next": "latest", 11 | "react": "^17.0.2", 12 | "react-dom": "^17.0.2" 13 | }, 14 | "devDependencies": { 15 | "autoprefixer": "^10.2.6", 16 | "postcss": "^8.3.5", 17 | "tailwindcss": "^2.2.4" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /05-use-next-js-to-query-a-single-record-from-supabase/pages/[id].js: -------------------------------------------------------------------------------- 1 | import { supabase } from "../utils/supabase"; 2 | 3 | const LessonDetails = ({ lesson }) => { 4 | console.log({ lesson }); 5 | return ( 6 |
7 |

{lesson.title}

8 |

{lesson.description}

9 |
10 | ); 11 | }; 12 | 13 | export const getStaticPaths = async () => { 14 | const { data: lessons } = await supabase.from("lesson").select("id"); 15 | 16 | const paths = lessons.map(({ id }) => ({ 17 | params: { 18 | id: id.toString(), 19 | }, 20 | })); 21 | 22 | return { 23 | paths, 24 | fallback: false, 25 | }; 26 | }; 27 | 28 | export const getStaticProps = async ({ params: { id } }) => { 29 | const { data: lesson } = await supabase 30 | .from("lesson") 31 | .select("*") 32 | .eq("id", id) 33 | .single(); 34 | 35 | return { 36 | props: { 37 | lesson, 38 | }, 39 | }; 40 | }; 41 | 42 | export default LessonDetails; 43 | -------------------------------------------------------------------------------- /05-use-next-js-to-query-a-single-record-from-supabase/pages/_app.js: -------------------------------------------------------------------------------- 1 | import 'tailwindcss/tailwind.css' 2 | 3 | function MyApp({ Component, pageProps }) { 4 | return 5 | } 6 | 7 | export default MyApp 8 | -------------------------------------------------------------------------------- /05-use-next-js-to-query-a-single-record-from-supabase/pages/api/hello.js: -------------------------------------------------------------------------------- 1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction 2 | 3 | export default function helloAPI(req, res) { 4 | res.status(200).json({ name: 'John Doe' }) 5 | } 6 | -------------------------------------------------------------------------------- /05-use-next-js-to-query-a-single-record-from-supabase/pages/index.js: -------------------------------------------------------------------------------- 1 | import { supabase } from "../utils/supabase"; 2 | import Link from "next/link"; 3 | 4 | export default function Home({ lessons }) { 5 | return ( 6 |
7 | {lessons.map((lesson) => ( 8 | 9 | 10 | {lesson.title} 11 | 12 | 13 | ))} 14 |
15 | ); 16 | } 17 | 18 | export const getStaticProps = async () => { 19 | const { data: lessons } = await supabase.from("lesson").select("*"); 20 | 21 | return { 22 | props: { 23 | lessons, 24 | }, 25 | }; 26 | }; 27 | -------------------------------------------------------------------------------- /05-use-next-js-to-query-a-single-record-from-supabase/postcss.config.js: -------------------------------------------------------------------------------- 1 | // If you want to use other PostCSS plugins, see the following: 2 | // https://tailwindcss.com/docs/using-with-preprocessors 3 | module.exports = { 4 | plugins: { 5 | tailwindcss: {}, 6 | autoprefixer: {}, 7 | }, 8 | } 9 | -------------------------------------------------------------------------------- /05-use-next-js-to-query-a-single-record-from-supabase/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dijonmusters/build-a-saas-with-next-js-supabase-and-stripe/578c4681065d34fbd46d988005787e6033f6436f/05-use-next-js-to-query-a-single-record-from-supabase/public/favicon.ico -------------------------------------------------------------------------------- /05-use-next-js-to-query-a-single-record-from-supabase/public/vercel.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /05-use-next-js-to-query-a-single-record-from-supabase/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | mode: 'jit', 3 | purge: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'], 4 | darkMode: false, // or 'media' or 'class' 5 | theme: { 6 | extend: {}, 7 | }, 8 | variants: { 9 | extend: {}, 10 | }, 11 | plugins: [], 12 | } 13 | -------------------------------------------------------------------------------- /05-use-next-js-to-query-a-single-record-from-supabase/utils/supabase.js: -------------------------------------------------------------------------------- 1 | import { createClient } from "@supabase/supabase-js"; 2 | 3 | export const supabase = createClient( 4 | process.env.NEXT_PUBLIC_SUPABASE_URL, 5 | process.env.NEXT_PUBLIC_SUPABASE_KEY 6 | ); 7 | -------------------------------------------------------------------------------- /06-implement-third-party-authentication-with-github-in-next-js-using-supabase/README.md: -------------------------------------------------------------------------------- 1 | # Implement Third Party Authentication with GitHub in Next.js Using Supabase 2 | 3 | **[📹 Video](https://egghead.io/lessons/supabase-implement-third-party-authentication-with-github-in-next-js-using-supabase)** 4 | 5 | Supabase offers a number of authentication options out of the box. In this video, we look at implementing third party auth using GitHub as a provider. 6 | 7 | In order to do this, we need to register a new OAuth application in GitHub, and configure it to communicate with our Supabase project. Supabase automatically handles receiving tokens at the /auth/v1/callback endpoint. 8 | 9 | Additionally, we implement /login and /logout pages to handle authentication in our Next.js application. We can use the supabase-js library to detect whether or not a user is signed in using the supabase.auth.user() function. 10 | 11 | [👉 Next lesson](/07-add-relationships-between-tables-in-supabase-using-foreign-keys) 12 | 13 | --- 14 | 15 | Enjoyed the course? Follow me on [Twitter](https://twitter.com/jonmeyers_io) and subscribe to my [YouTube channel](https://www.youtube.com/channel/UCPitAIwktfCfcMR4kDWebDQ). 16 | -------------------------------------------------------------------------------- /06-implement-third-party-authentication-with-github-in-next-js-using-supabase/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "dev": "next dev", 5 | "build": "next build", 6 | "start": "next start" 7 | }, 8 | "dependencies": { 9 | "@supabase/supabase-js": "^1.28.2", 10 | "next": "latest", 11 | "react": "^17.0.2", 12 | "react-dom": "^17.0.2" 13 | }, 14 | "devDependencies": { 15 | "autoprefixer": "^10.2.6", 16 | "postcss": "^8.3.5", 17 | "tailwindcss": "^2.2.4" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /06-implement-third-party-authentication-with-github-in-next-js-using-supabase/pages/[id].js: -------------------------------------------------------------------------------- 1 | import { supabase } from "../utils/supabase"; 2 | 3 | const LessonDetails = ({ lesson }) => { 4 | console.log({ lesson }); 5 | return ( 6 |
7 |

{lesson.title}

8 |

{lesson.description}

9 |
10 | ); 11 | }; 12 | 13 | export const getStaticPaths = async () => { 14 | const { data: lessons } = await supabase.from("lesson").select("id"); 15 | 16 | const paths = lessons.map(({ id }) => ({ 17 | params: { 18 | id: id.toString(), 19 | }, 20 | })); 21 | 22 | return { 23 | paths, 24 | fallback: false, 25 | }; 26 | }; 27 | 28 | export const getStaticProps = async ({ params: { id } }) => { 29 | const { data: lesson } = await supabase 30 | .from("lesson") 31 | .select("*") 32 | .eq("id", id) 33 | .single(); 34 | 35 | return { 36 | props: { 37 | lesson, 38 | }, 39 | }; 40 | }; 41 | 42 | export default LessonDetails; 43 | -------------------------------------------------------------------------------- /06-implement-third-party-authentication-with-github-in-next-js-using-supabase/pages/_app.js: -------------------------------------------------------------------------------- 1 | import 'tailwindcss/tailwind.css' 2 | 3 | function MyApp({ Component, pageProps }) { 4 | return 5 | } 6 | 7 | export default MyApp 8 | -------------------------------------------------------------------------------- /06-implement-third-party-authentication-with-github-in-next-js-using-supabase/pages/api/hello.js: -------------------------------------------------------------------------------- 1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction 2 | 3 | export default function helloAPI(req, res) { 4 | res.status(200).json({ name: 'John Doe' }) 5 | } 6 | -------------------------------------------------------------------------------- /06-implement-third-party-authentication-with-github-in-next-js-using-supabase/pages/index.js: -------------------------------------------------------------------------------- 1 | import { supabase } from "../utils/supabase"; 2 | import Link from "next/link"; 3 | 4 | export default function Home({ lessons }) { 5 | console.log(supabase.auth.user()); 6 | return ( 7 |
8 | {lessons.map((lesson) => ( 9 | 10 | 11 | {lesson.title} 12 | 13 | 14 | ))} 15 |
16 | ); 17 | } 18 | 19 | export const getStaticProps = async () => { 20 | const { data: lessons } = await supabase.from("lesson").select("*"); 21 | 22 | return { 23 | props: { 24 | lessons, 25 | }, 26 | }; 27 | }; 28 | -------------------------------------------------------------------------------- /06-implement-third-party-authentication-with-github-in-next-js-using-supabase/pages/login.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { supabase } from "../utils/supabase"; 3 | 4 | const Login = () => { 5 | useEffect(() => { 6 | supabase.auth.signIn({ 7 | provider: "github", 8 | }); 9 | }, []); 10 | 11 | return

Logging in

; 12 | }; 13 | 14 | export default Login; 15 | -------------------------------------------------------------------------------- /06-implement-third-party-authentication-with-github-in-next-js-using-supabase/pages/logout.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { supabase } from "../utils/supabase"; 3 | import { useRouter } from "next/router"; 4 | 5 | const Logout = () => { 6 | const router = useRouter(); 7 | 8 | useEffect(() => { 9 | const logout = async () => { 10 | supabase.auth.signOut(); 11 | router.push("/"); 12 | }; 13 | logout(); 14 | }, []); 15 | 16 | return

Logging out

; 17 | }; 18 | 19 | export default Logout; 20 | -------------------------------------------------------------------------------- /06-implement-third-party-authentication-with-github-in-next-js-using-supabase/postcss.config.js: -------------------------------------------------------------------------------- 1 | // If you want to use other PostCSS plugins, see the following: 2 | // https://tailwindcss.com/docs/using-with-preprocessors 3 | module.exports = { 4 | plugins: { 5 | tailwindcss: {}, 6 | autoprefixer: {}, 7 | }, 8 | } 9 | -------------------------------------------------------------------------------- /06-implement-third-party-authentication-with-github-in-next-js-using-supabase/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dijonmusters/build-a-saas-with-next-js-supabase-and-stripe/578c4681065d34fbd46d988005787e6033f6436f/06-implement-third-party-authentication-with-github-in-next-js-using-supabase/public/favicon.ico -------------------------------------------------------------------------------- /06-implement-third-party-authentication-with-github-in-next-js-using-supabase/public/vercel.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /06-implement-third-party-authentication-with-github-in-next-js-using-supabase/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | mode: 'jit', 3 | purge: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'], 4 | darkMode: false, // or 'media' or 'class' 5 | theme: { 6 | extend: {}, 7 | }, 8 | variants: { 9 | extend: {}, 10 | }, 11 | plugins: [], 12 | } 13 | -------------------------------------------------------------------------------- /06-implement-third-party-authentication-with-github-in-next-js-using-supabase/utils/supabase.js: -------------------------------------------------------------------------------- 1 | import { createClient } from "@supabase/supabase-js"; 2 | 3 | export const supabase = createClient( 4 | process.env.NEXT_PUBLIC_SUPABASE_URL, 5 | process.env.NEXT_PUBLIC_SUPABASE_KEY 6 | ); 7 | -------------------------------------------------------------------------------- /07-add-relationships-between-tables-in-supabase-using-foreign-keys/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "dev": "next dev", 5 | "build": "next build", 6 | "start": "next start" 7 | }, 8 | "dependencies": { 9 | "@supabase/supabase-js": "^1.28.2", 10 | "next": "latest", 11 | "react": "^17.0.2", 12 | "react-dom": "^17.0.2" 13 | }, 14 | "devDependencies": { 15 | "autoprefixer": "^10.2.6", 16 | "postcss": "^8.3.5", 17 | "tailwindcss": "^2.2.4" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /07-add-relationships-between-tables-in-supabase-using-foreign-keys/pages/[id].js: -------------------------------------------------------------------------------- 1 | import { supabase } from "../utils/supabase"; 2 | 3 | const LessonDetails = ({ lesson }) => { 4 | console.log({ lesson }); 5 | return ( 6 |
7 |

{lesson.title}

8 |

{lesson.description}

9 |
10 | ); 11 | }; 12 | 13 | export const getStaticPaths = async () => { 14 | const { data: lessons } = await supabase.from("lesson").select("id"); 15 | 16 | const paths = lessons.map(({ id }) => ({ 17 | params: { 18 | id: id.toString(), 19 | }, 20 | })); 21 | 22 | return { 23 | paths, 24 | fallback: false, 25 | }; 26 | }; 27 | 28 | export const getStaticProps = async ({ params: { id } }) => { 29 | const { data: lesson } = await supabase 30 | .from("lesson") 31 | .select("*") 32 | .eq("id", id) 33 | .single(); 34 | 35 | return { 36 | props: { 37 | lesson, 38 | }, 39 | }; 40 | }; 41 | 42 | export default LessonDetails; 43 | -------------------------------------------------------------------------------- /07-add-relationships-between-tables-in-supabase-using-foreign-keys/pages/_app.js: -------------------------------------------------------------------------------- 1 | import 'tailwindcss/tailwind.css' 2 | 3 | function MyApp({ Component, pageProps }) { 4 | return 5 | } 6 | 7 | export default MyApp 8 | -------------------------------------------------------------------------------- /07-add-relationships-between-tables-in-supabase-using-foreign-keys/pages/api/hello.js: -------------------------------------------------------------------------------- 1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction 2 | 3 | export default function helloAPI(req, res) { 4 | res.status(200).json({ name: 'John Doe' }) 5 | } 6 | -------------------------------------------------------------------------------- /07-add-relationships-between-tables-in-supabase-using-foreign-keys/pages/index.js: -------------------------------------------------------------------------------- 1 | import { supabase } from "../utils/supabase"; 2 | import Link from "next/link"; 3 | 4 | export default function Home({ lessons }) { 5 | console.log(supabase.auth.user()); 6 | return ( 7 |
8 | {lessons.map((lesson) => ( 9 | 10 | 11 | {lesson.title} 12 | 13 | 14 | ))} 15 |
16 | ); 17 | } 18 | 19 | export const getStaticProps = async () => { 20 | const { data: lessons } = await supabase.from("lesson").select("*"); 21 | 22 | return { 23 | props: { 24 | lessons, 25 | }, 26 | }; 27 | }; 28 | -------------------------------------------------------------------------------- /07-add-relationships-between-tables-in-supabase-using-foreign-keys/pages/login.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { supabase } from "../utils/supabase"; 3 | 4 | const Login = () => { 5 | useEffect(() => { 6 | supabase.auth.signIn({ 7 | provider: "github", 8 | }); 9 | }, []); 10 | 11 | return

Logging in

; 12 | }; 13 | 14 | export default Login; 15 | -------------------------------------------------------------------------------- /07-add-relationships-between-tables-in-supabase-using-foreign-keys/pages/logout.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { supabase } from "../utils/supabase"; 3 | import { useRouter } from "next/router"; 4 | 5 | const Logout = () => { 6 | const router = useRouter(); 7 | 8 | useEffect(() => { 9 | const logout = async () => { 10 | supabase.auth.signOut(); 11 | router.push("/"); 12 | }; 13 | logout(); 14 | }, []); 15 | 16 | return

Logging out

; 17 | }; 18 | 19 | export default Logout; 20 | -------------------------------------------------------------------------------- /07-add-relationships-between-tables-in-supabase-using-foreign-keys/postcss.config.js: -------------------------------------------------------------------------------- 1 | // If you want to use other PostCSS plugins, see the following: 2 | // https://tailwindcss.com/docs/using-with-preprocessors 3 | module.exports = { 4 | plugins: { 5 | tailwindcss: {}, 6 | autoprefixer: {}, 7 | }, 8 | } 9 | -------------------------------------------------------------------------------- /07-add-relationships-between-tables-in-supabase-using-foreign-keys/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dijonmusters/build-a-saas-with-next-js-supabase-and-stripe/578c4681065d34fbd46d988005787e6033f6436f/07-add-relationships-between-tables-in-supabase-using-foreign-keys/public/favicon.ico -------------------------------------------------------------------------------- /07-add-relationships-between-tables-in-supabase-using-foreign-keys/public/vercel.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /07-add-relationships-between-tables-in-supabase-using-foreign-keys/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | mode: 'jit', 3 | purge: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'], 4 | darkMode: false, // or 'media' or 'class' 5 | theme: { 6 | extend: {}, 7 | }, 8 | variants: { 9 | extend: {}, 10 | }, 11 | plugins: [], 12 | } 13 | -------------------------------------------------------------------------------- /07-add-relationships-between-tables-in-supabase-using-foreign-keys/utils/supabase.js: -------------------------------------------------------------------------------- 1 | import { createClient } from "@supabase/supabase-js"; 2 | 3 | export const supabase = createClient( 4 | process.env.NEXT_PUBLIC_SUPABASE_URL, 5 | process.env.NEXT_PUBLIC_SUPABASE_KEY 6 | ); 7 | -------------------------------------------------------------------------------- /08-use-postgres-functions-to-implement-database-logic-with-supabase/README.md: -------------------------------------------------------------------------------- 1 | # Use Postgres Functions to Implement Database Logic with Supabase 2 | 3 | **[📹 Video](https://egghead.io/lessons/supabase-use-postgres-functions-to-implement-database-logic-with-supabase)** 4 | 5 | Each time a new user signs in, we want to create an associated profile. In PostgreSQL, we can use triggers to listen to events - such as INSERT - on specific tables - such as auth.users - and execute a Postgres function. 6 | 7 | In this video, we use the Supabase UI to create a Postgres function that returns a trigger. This is a special kind of function that can be invoked when particular events occur in the database - covered in the next lesson. This function will be responsible for creating a new row in the profile table, using the ID from our auth.users record. 8 | 9 | [👉 Next lesson](/09-use-supabase-to-subscribe-to-database-events-with-postgres-triggers) 10 | 11 | --- 12 | 13 | Enjoyed the course? Follow me on [Twitter](https://twitter.com/jonmeyers_io) and subscribe to my [YouTube channel](https://www.youtube.com/channel/UCPitAIwktfCfcMR4kDWebDQ). 14 | -------------------------------------------------------------------------------- /08-use-postgres-functions-to-implement-database-logic-with-supabase/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "dev": "next dev", 5 | "build": "next build", 6 | "start": "next start" 7 | }, 8 | "dependencies": { 9 | "@supabase/supabase-js": "^1.28.2", 10 | "next": "latest", 11 | "react": "^17.0.2", 12 | "react-dom": "^17.0.2" 13 | }, 14 | "devDependencies": { 15 | "autoprefixer": "^10.2.6", 16 | "postcss": "^8.3.5", 17 | "tailwindcss": "^2.2.4" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /08-use-postgres-functions-to-implement-database-logic-with-supabase/pages/[id].js: -------------------------------------------------------------------------------- 1 | import { supabase } from "../utils/supabase"; 2 | 3 | const LessonDetails = ({ lesson }) => { 4 | console.log({ lesson }); 5 | return ( 6 |
7 |

{lesson.title}

8 |

{lesson.description}

9 |
10 | ); 11 | }; 12 | 13 | export const getStaticPaths = async () => { 14 | const { data: lessons } = await supabase.from("lesson").select("id"); 15 | 16 | const paths = lessons.map(({ id }) => ({ 17 | params: { 18 | id: id.toString(), 19 | }, 20 | })); 21 | 22 | return { 23 | paths, 24 | fallback: false, 25 | }; 26 | }; 27 | 28 | export const getStaticProps = async ({ params: { id } }) => { 29 | const { data: lesson } = await supabase 30 | .from("lesson") 31 | .select("*") 32 | .eq("id", id) 33 | .single(); 34 | 35 | return { 36 | props: { 37 | lesson, 38 | }, 39 | }; 40 | }; 41 | 42 | export default LessonDetails; 43 | -------------------------------------------------------------------------------- /08-use-postgres-functions-to-implement-database-logic-with-supabase/pages/_app.js: -------------------------------------------------------------------------------- 1 | import 'tailwindcss/tailwind.css' 2 | 3 | function MyApp({ Component, pageProps }) { 4 | return 5 | } 6 | 7 | export default MyApp 8 | -------------------------------------------------------------------------------- /08-use-postgres-functions-to-implement-database-logic-with-supabase/pages/api/hello.js: -------------------------------------------------------------------------------- 1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction 2 | 3 | export default function helloAPI(req, res) { 4 | res.status(200).json({ name: 'John Doe' }) 5 | } 6 | -------------------------------------------------------------------------------- /08-use-postgres-functions-to-implement-database-logic-with-supabase/pages/index.js: -------------------------------------------------------------------------------- 1 | import { supabase } from "../utils/supabase"; 2 | import Link from "next/link"; 3 | 4 | export default function Home({ lessons }) { 5 | console.log(supabase.auth.user()); 6 | return ( 7 |
8 | {lessons.map((lesson) => ( 9 | 10 | 11 | {lesson.title} 12 | 13 | 14 | ))} 15 |
16 | ); 17 | } 18 | 19 | export const getStaticProps = async () => { 20 | const { data: lessons } = await supabase.from("lesson").select("*"); 21 | 22 | return { 23 | props: { 24 | lessons, 25 | }, 26 | }; 27 | }; 28 | -------------------------------------------------------------------------------- /08-use-postgres-functions-to-implement-database-logic-with-supabase/pages/login.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { supabase } from "../utils/supabase"; 3 | 4 | const Login = () => { 5 | useEffect(() => { 6 | supabase.auth.signIn({ 7 | provider: "github", 8 | }); 9 | }, []); 10 | 11 | return

Logging in

; 12 | }; 13 | 14 | export default Login; 15 | -------------------------------------------------------------------------------- /08-use-postgres-functions-to-implement-database-logic-with-supabase/pages/logout.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { supabase } from "../utils/supabase"; 3 | import { useRouter } from "next/router"; 4 | 5 | const Logout = () => { 6 | const router = useRouter(); 7 | 8 | useEffect(() => { 9 | const logout = async () => { 10 | supabase.auth.signOut(); 11 | router.push("/"); 12 | }; 13 | logout(); 14 | }, []); 15 | 16 | return

Logging out

; 17 | }; 18 | 19 | export default Logout; 20 | -------------------------------------------------------------------------------- /08-use-postgres-functions-to-implement-database-logic-with-supabase/postcss.config.js: -------------------------------------------------------------------------------- 1 | // If you want to use other PostCSS plugins, see the following: 2 | // https://tailwindcss.com/docs/using-with-preprocessors 3 | module.exports = { 4 | plugins: { 5 | tailwindcss: {}, 6 | autoprefixer: {}, 7 | }, 8 | } 9 | -------------------------------------------------------------------------------- /08-use-postgres-functions-to-implement-database-logic-with-supabase/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dijonmusters/build-a-saas-with-next-js-supabase-and-stripe/578c4681065d34fbd46d988005787e6033f6436f/08-use-postgres-functions-to-implement-database-logic-with-supabase/public/favicon.ico -------------------------------------------------------------------------------- /08-use-postgres-functions-to-implement-database-logic-with-supabase/public/vercel.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /08-use-postgres-functions-to-implement-database-logic-with-supabase/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | mode: 'jit', 3 | purge: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'], 4 | darkMode: false, // or 'media' or 'class' 5 | theme: { 6 | extend: {}, 7 | }, 8 | variants: { 9 | extend: {}, 10 | }, 11 | plugins: [], 12 | } 13 | -------------------------------------------------------------------------------- /08-use-postgres-functions-to-implement-database-logic-with-supabase/utils/supabase.js: -------------------------------------------------------------------------------- 1 | import { createClient } from "@supabase/supabase-js"; 2 | 3 | export const supabase = createClient( 4 | process.env.NEXT_PUBLIC_SUPABASE_URL, 5 | process.env.NEXT_PUBLIC_SUPABASE_KEY 6 | ); 7 | -------------------------------------------------------------------------------- /09-use-supabase-to-subscribe-to-database-events-with-postgres-triggers/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "dev": "next dev", 5 | "build": "next build", 6 | "start": "next start" 7 | }, 8 | "dependencies": { 9 | "@supabase/supabase-js": "^1.28.2", 10 | "next": "latest", 11 | "react": "^17.0.2", 12 | "react-dom": "^17.0.2" 13 | }, 14 | "devDependencies": { 15 | "autoprefixer": "^10.2.6", 16 | "postcss": "^8.3.5", 17 | "tailwindcss": "^2.2.4" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /09-use-supabase-to-subscribe-to-database-events-with-postgres-triggers/pages/[id].js: -------------------------------------------------------------------------------- 1 | import { supabase } from "../utils/supabase"; 2 | 3 | const LessonDetails = ({ lesson }) => { 4 | console.log({ lesson }); 5 | return ( 6 |
7 |

{lesson.title}

8 |

{lesson.description}

9 |
10 | ); 11 | }; 12 | 13 | export const getStaticPaths = async () => { 14 | const { data: lessons } = await supabase.from("lesson").select("id"); 15 | 16 | const paths = lessons.map(({ id }) => ({ 17 | params: { 18 | id: id.toString(), 19 | }, 20 | })); 21 | 22 | return { 23 | paths, 24 | fallback: false, 25 | }; 26 | }; 27 | 28 | export const getStaticProps = async ({ params: { id } }) => { 29 | const { data: lesson } = await supabase 30 | .from("lesson") 31 | .select("*") 32 | .eq("id", id) 33 | .single(); 34 | 35 | return { 36 | props: { 37 | lesson, 38 | }, 39 | }; 40 | }; 41 | 42 | export default LessonDetails; 43 | -------------------------------------------------------------------------------- /09-use-supabase-to-subscribe-to-database-events-with-postgres-triggers/pages/_app.js: -------------------------------------------------------------------------------- 1 | import 'tailwindcss/tailwind.css' 2 | 3 | function MyApp({ Component, pageProps }) { 4 | return 5 | } 6 | 7 | export default MyApp 8 | -------------------------------------------------------------------------------- /09-use-supabase-to-subscribe-to-database-events-with-postgres-triggers/pages/api/hello.js: -------------------------------------------------------------------------------- 1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction 2 | 3 | export default function helloAPI(req, res) { 4 | res.status(200).json({ name: 'John Doe' }) 5 | } 6 | -------------------------------------------------------------------------------- /09-use-supabase-to-subscribe-to-database-events-with-postgres-triggers/pages/index.js: -------------------------------------------------------------------------------- 1 | import { supabase } from "../utils/supabase"; 2 | import Link from "next/link"; 3 | 4 | export default function Home({ lessons }) { 5 | console.log(supabase.auth.user()); 6 | return ( 7 |
8 | {lessons.map((lesson) => ( 9 | 10 | 11 | {lesson.title} 12 | 13 | 14 | ))} 15 |
16 | ); 17 | } 18 | 19 | export const getStaticProps = async () => { 20 | const { data: lessons } = await supabase.from("lesson").select("*"); 21 | 22 | return { 23 | props: { 24 | lessons, 25 | }, 26 | }; 27 | }; 28 | -------------------------------------------------------------------------------- /09-use-supabase-to-subscribe-to-database-events-with-postgres-triggers/pages/login.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { supabase } from "../utils/supabase"; 3 | 4 | const Login = () => { 5 | useEffect(() => { 6 | supabase.auth.signIn({ 7 | provider: "github", 8 | }); 9 | }, []); 10 | 11 | return

Logging in

; 12 | }; 13 | 14 | export default Login; 15 | -------------------------------------------------------------------------------- /09-use-supabase-to-subscribe-to-database-events-with-postgres-triggers/pages/logout.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { supabase } from "../utils/supabase"; 3 | import { useRouter } from "next/router"; 4 | 5 | const Logout = () => { 6 | const router = useRouter(); 7 | 8 | useEffect(() => { 9 | const logout = async () => { 10 | supabase.auth.signOut(); 11 | router.push("/"); 12 | }; 13 | logout(); 14 | }, []); 15 | 16 | return

Logging out

; 17 | }; 18 | 19 | export default Logout; 20 | -------------------------------------------------------------------------------- /09-use-supabase-to-subscribe-to-database-events-with-postgres-triggers/postcss.config.js: -------------------------------------------------------------------------------- 1 | // If you want to use other PostCSS plugins, see the following: 2 | // https://tailwindcss.com/docs/using-with-preprocessors 3 | module.exports = { 4 | plugins: { 5 | tailwindcss: {}, 6 | autoprefixer: {}, 7 | }, 8 | } 9 | -------------------------------------------------------------------------------- /09-use-supabase-to-subscribe-to-database-events-with-postgres-triggers/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dijonmusters/build-a-saas-with-next-js-supabase-and-stripe/578c4681065d34fbd46d988005787e6033f6436f/09-use-supabase-to-subscribe-to-database-events-with-postgres-triggers/public/favicon.ico -------------------------------------------------------------------------------- /09-use-supabase-to-subscribe-to-database-events-with-postgres-triggers/public/vercel.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /09-use-supabase-to-subscribe-to-database-events-with-postgres-triggers/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | mode: 'jit', 3 | purge: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'], 4 | darkMode: false, // or 'media' or 'class' 5 | theme: { 6 | extend: {}, 7 | }, 8 | variants: { 9 | extend: {}, 10 | }, 11 | plugins: [], 12 | } 13 | -------------------------------------------------------------------------------- /09-use-supabase-to-subscribe-to-database-events-with-postgres-triggers/utils/supabase.js: -------------------------------------------------------------------------------- 1 | import { createClient } from "@supabase/supabase-js"; 2 | 3 | export const supabase = createClient( 4 | process.env.NEXT_PUBLIC_SUPABASE_URL, 5 | process.env.NEXT_PUBLIC_SUPABASE_KEY 6 | ); 7 | -------------------------------------------------------------------------------- /10-create-a-stripe-customer-with-next-js-api-routes/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "dev": "next dev", 5 | "build": "next build", 6 | "start": "next start" 7 | }, 8 | "dependencies": { 9 | "@supabase/supabase-js": "^1.28.2", 10 | "next": "latest", 11 | "react": "^17.0.2", 12 | "react-dom": "^17.0.2", 13 | "stripe": "^8.191.0" 14 | }, 15 | "devDependencies": { 16 | "autoprefixer": "^10.2.6", 17 | "postcss": "^8.3.5", 18 | "tailwindcss": "^2.2.4" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /10-create-a-stripe-customer-with-next-js-api-routes/pages/[id].js: -------------------------------------------------------------------------------- 1 | import { supabase } from "../utils/supabase"; 2 | 3 | const LessonDetails = ({ lesson }) => { 4 | console.log({ lesson }); 5 | return ( 6 |
7 |

{lesson.title}

8 |

{lesson.description}

9 |
10 | ); 11 | }; 12 | 13 | export const getStaticPaths = async () => { 14 | const { data: lessons } = await supabase.from("lesson").select("id"); 15 | 16 | const paths = lessons.map(({ id }) => ({ 17 | params: { 18 | id: id.toString(), 19 | }, 20 | })); 21 | 22 | return { 23 | paths, 24 | fallback: false, 25 | }; 26 | }; 27 | 28 | export const getStaticProps = async ({ params: { id } }) => { 29 | const { data: lesson } = await supabase 30 | .from("lesson") 31 | .select("*") 32 | .eq("id", id) 33 | .single(); 34 | 35 | return { 36 | props: { 37 | lesson, 38 | }, 39 | }; 40 | }; 41 | 42 | export default LessonDetails; 43 | -------------------------------------------------------------------------------- /10-create-a-stripe-customer-with-next-js-api-routes/pages/_app.js: -------------------------------------------------------------------------------- 1 | import 'tailwindcss/tailwind.css' 2 | 3 | function MyApp({ Component, pageProps }) { 4 | return 5 | } 6 | 7 | export default MyApp 8 | -------------------------------------------------------------------------------- /10-create-a-stripe-customer-with-next-js-api-routes/pages/api/create-stripe-customer.js: -------------------------------------------------------------------------------- 1 | import initStripe from "stripe"; 2 | import { supabase } from "../../utils/supabase"; 3 | 4 | const handler = async (req, res) => { 5 | const stripe = initStripe(process.env.STRIPE_SECRET_KEY); 6 | 7 | const customer = await stripe.customers.create({ 8 | email: req.body.record.email, 9 | }); 10 | 11 | await supabase 12 | .from("profile") 13 | .update({ 14 | stripe_customer: customer.id, 15 | }) 16 | .eq("id", req.body.record.id); 17 | 18 | res.send({ message: `stripe customer created: ${customer.id}` }); 19 | }; 20 | 21 | export default handler; 22 | -------------------------------------------------------------------------------- /10-create-a-stripe-customer-with-next-js-api-routes/pages/api/hello.js: -------------------------------------------------------------------------------- 1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction 2 | 3 | export default function helloAPI(req, res) { 4 | res.status(200).json({ name: 'John Doe' }) 5 | } 6 | -------------------------------------------------------------------------------- /10-create-a-stripe-customer-with-next-js-api-routes/pages/index.js: -------------------------------------------------------------------------------- 1 | import { supabase } from "../utils/supabase"; 2 | import Link from "next/link"; 3 | 4 | export default function Home({ lessons }) { 5 | console.log(supabase.auth.user()); 6 | return ( 7 |
8 | {lessons.map((lesson) => ( 9 | 10 | 11 | {lesson.title} 12 | 13 | 14 | ))} 15 |
16 | ); 17 | } 18 | 19 | export const getStaticProps = async () => { 20 | const { data: lessons } = await supabase.from("lesson").select("*"); 21 | 22 | return { 23 | props: { 24 | lessons, 25 | }, 26 | }; 27 | }; 28 | -------------------------------------------------------------------------------- /10-create-a-stripe-customer-with-next-js-api-routes/pages/login.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { supabase } from "../utils/supabase"; 3 | 4 | const Login = () => { 5 | useEffect(() => { 6 | supabase.auth.signIn({ 7 | provider: "github", 8 | }); 9 | }, []); 10 | 11 | return

Logging in

; 12 | }; 13 | 14 | export default Login; 15 | -------------------------------------------------------------------------------- /10-create-a-stripe-customer-with-next-js-api-routes/pages/logout.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { supabase } from "../utils/supabase"; 3 | import { useRouter } from "next/router"; 4 | 5 | const Logout = () => { 6 | const router = useRouter(); 7 | 8 | useEffect(() => { 9 | const logout = async () => { 10 | supabase.auth.signOut(); 11 | router.push("/"); 12 | }; 13 | logout(); 14 | }, []); 15 | 16 | return

Logging out

; 17 | }; 18 | 19 | export default Logout; 20 | -------------------------------------------------------------------------------- /10-create-a-stripe-customer-with-next-js-api-routes/postcss.config.js: -------------------------------------------------------------------------------- 1 | // If you want to use other PostCSS plugins, see the following: 2 | // https://tailwindcss.com/docs/using-with-preprocessors 3 | module.exports = { 4 | plugins: { 5 | tailwindcss: {}, 6 | autoprefixer: {}, 7 | }, 8 | } 9 | -------------------------------------------------------------------------------- /10-create-a-stripe-customer-with-next-js-api-routes/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dijonmusters/build-a-saas-with-next-js-supabase-and-stripe/578c4681065d34fbd46d988005787e6033f6436f/10-create-a-stripe-customer-with-next-js-api-routes/public/favicon.ico -------------------------------------------------------------------------------- /10-create-a-stripe-customer-with-next-js-api-routes/public/vercel.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /10-create-a-stripe-customer-with-next-js-api-routes/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | mode: 'jit', 3 | purge: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'], 4 | darkMode: false, // or 'media' or 'class' 5 | theme: { 6 | extend: {}, 7 | }, 8 | variants: { 9 | extend: {}, 10 | }, 11 | plugins: [], 12 | } 13 | -------------------------------------------------------------------------------- /10-create-a-stripe-customer-with-next-js-api-routes/utils/supabase.js: -------------------------------------------------------------------------------- 1 | import { createClient } from "@supabase/supabase-js"; 2 | 3 | export const supabase = createClient( 4 | process.env.NEXT_PUBLIC_SUPABASE_URL, 5 | process.env.NEXT_PUBLIC_SUPABASE_KEY 6 | ); 7 | -------------------------------------------------------------------------------- /11-generate-a-custom-api-key-to-secure-an-api-route-in-next-js/README.md: -------------------------------------------------------------------------------- 1 | # Generate a Custom API Key to Secure an API Route in Next.js 2 | 3 | **[📹 Video](https://egghead.io/lessons/next-js-generate-a-custom-api-key-to-secure-an-api-route-in-next-js)** 4 | 5 | Currently, our API route can be used by anyone navigating to its endpoint. To ensure that this function can only be executed by our application, we need to create a special secret value. 6 | 7 | In this video, we generate a special key to secure our API route. This value must be provided as a query parameter on the request to our endpoint, or an error response will be sent back. 8 | 9 | To confirm this is working correctly, we use the Thunder Client extension. When the API_ROUTE_SECRET is not provided we receive a 401 response. When the correct value is attached our serverless function executes, creating a stripe customer and updating our profile table in Supabase. 10 | 11 | [👉 Next lesson](/12-automatically-create-a-stripe-customer-for-each-user-with-supabase-function-hooks) 12 | 13 | --- 14 | 15 | Enjoyed the course? Follow me on [Twitter](https://twitter.com/jonmeyers_io) and subscribe to my [YouTube channel](https://www.youtube.com/channel/UCPitAIwktfCfcMR4kDWebDQ). 16 | -------------------------------------------------------------------------------- /11-generate-a-custom-api-key-to-secure-an-api-route-in-next-js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "dev": "next dev", 5 | "build": "next build", 6 | "start": "next start" 7 | }, 8 | "dependencies": { 9 | "@supabase/supabase-js": "^1.28.2", 10 | "next": "latest", 11 | "react": "^17.0.2", 12 | "react-dom": "^17.0.2", 13 | "stripe": "^8.191.0" 14 | }, 15 | "devDependencies": { 16 | "autoprefixer": "^10.2.6", 17 | "postcss": "^8.3.5", 18 | "tailwindcss": "^2.2.4" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /11-generate-a-custom-api-key-to-secure-an-api-route-in-next-js/pages/[id].js: -------------------------------------------------------------------------------- 1 | import { supabase } from "../utils/supabase"; 2 | 3 | const LessonDetails = ({ lesson }) => { 4 | console.log({ lesson }); 5 | return ( 6 |
7 |

{lesson.title}

8 |

{lesson.description}

9 |
10 | ); 11 | }; 12 | 13 | export const getStaticPaths = async () => { 14 | const { data: lessons } = await supabase.from("lesson").select("id"); 15 | 16 | const paths = lessons.map(({ id }) => ({ 17 | params: { 18 | id: id.toString(), 19 | }, 20 | })); 21 | 22 | return { 23 | paths, 24 | fallback: false, 25 | }; 26 | }; 27 | 28 | export const getStaticProps = async ({ params: { id } }) => { 29 | const { data: lesson } = await supabase 30 | .from("lesson") 31 | .select("*") 32 | .eq("id", id) 33 | .single(); 34 | 35 | return { 36 | props: { 37 | lesson, 38 | }, 39 | }; 40 | }; 41 | 42 | export default LessonDetails; 43 | -------------------------------------------------------------------------------- /11-generate-a-custom-api-key-to-secure-an-api-route-in-next-js/pages/_app.js: -------------------------------------------------------------------------------- 1 | import 'tailwindcss/tailwind.css' 2 | 3 | function MyApp({ Component, pageProps }) { 4 | return 5 | } 6 | 7 | export default MyApp 8 | -------------------------------------------------------------------------------- /11-generate-a-custom-api-key-to-secure-an-api-route-in-next-js/pages/api/create-stripe-customer.js: -------------------------------------------------------------------------------- 1 | import initStripe from "stripe"; 2 | import { supabase } from "../../utils/supabase"; 3 | 4 | const handler = async (req, res) => { 5 | if (req.query.API_ROUTE_SECRET !== process.env.API_ROUTE_SECRET) { 6 | return res.status(401).send("You are not authorized to call this API"); 7 | } 8 | const stripe = initStripe(process.env.STRIPE_SECRET_KEY); 9 | 10 | const customer = await stripe.customers.create({ 11 | email: req.body.record.email, 12 | }); 13 | 14 | await supabase 15 | .from("profile") 16 | .update({ 17 | stripe_customer: customer.id, 18 | }) 19 | .eq("id", req.body.record.id); 20 | 21 | res.send({ message: `stripe customer created: ${customer.id}` }); 22 | }; 23 | 24 | export default handler; 25 | -------------------------------------------------------------------------------- /11-generate-a-custom-api-key-to-secure-an-api-route-in-next-js/pages/api/hello.js: -------------------------------------------------------------------------------- 1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction 2 | 3 | export default function helloAPI(req, res) { 4 | res.status(200).json({ name: 'John Doe' }) 5 | } 6 | -------------------------------------------------------------------------------- /11-generate-a-custom-api-key-to-secure-an-api-route-in-next-js/pages/index.js: -------------------------------------------------------------------------------- 1 | import { supabase } from "../utils/supabase"; 2 | import Link from "next/link"; 3 | 4 | export default function Home({ lessons }) { 5 | console.log(supabase.auth.user()); 6 | return ( 7 |
8 | {lessons.map((lesson) => ( 9 | 10 | 11 | {lesson.title} 12 | 13 | 14 | ))} 15 |
16 | ); 17 | } 18 | 19 | export const getStaticProps = async () => { 20 | const { data: lessons } = await supabase.from("lesson").select("*"); 21 | 22 | return { 23 | props: { 24 | lessons, 25 | }, 26 | }; 27 | }; 28 | -------------------------------------------------------------------------------- /11-generate-a-custom-api-key-to-secure-an-api-route-in-next-js/pages/login.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { supabase } from "../utils/supabase"; 3 | 4 | const Login = () => { 5 | useEffect(() => { 6 | supabase.auth.signIn({ 7 | provider: "github", 8 | }); 9 | }, []); 10 | 11 | return

Logging in

; 12 | }; 13 | 14 | export default Login; 15 | -------------------------------------------------------------------------------- /11-generate-a-custom-api-key-to-secure-an-api-route-in-next-js/pages/logout.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { supabase } from "../utils/supabase"; 3 | import { useRouter } from "next/router"; 4 | 5 | const Logout = () => { 6 | const router = useRouter(); 7 | 8 | useEffect(() => { 9 | const logout = async () => { 10 | supabase.auth.signOut(); 11 | router.push("/"); 12 | }; 13 | logout(); 14 | }, []); 15 | 16 | return

Logging out

; 17 | }; 18 | 19 | export default Logout; 20 | -------------------------------------------------------------------------------- /11-generate-a-custom-api-key-to-secure-an-api-route-in-next-js/postcss.config.js: -------------------------------------------------------------------------------- 1 | // If you want to use other PostCSS plugins, see the following: 2 | // https://tailwindcss.com/docs/using-with-preprocessors 3 | module.exports = { 4 | plugins: { 5 | tailwindcss: {}, 6 | autoprefixer: {}, 7 | }, 8 | } 9 | -------------------------------------------------------------------------------- /11-generate-a-custom-api-key-to-secure-an-api-route-in-next-js/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dijonmusters/build-a-saas-with-next-js-supabase-and-stripe/578c4681065d34fbd46d988005787e6033f6436f/11-generate-a-custom-api-key-to-secure-an-api-route-in-next-js/public/favicon.ico -------------------------------------------------------------------------------- /11-generate-a-custom-api-key-to-secure-an-api-route-in-next-js/public/vercel.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /11-generate-a-custom-api-key-to-secure-an-api-route-in-next-js/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | mode: 'jit', 3 | purge: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'], 4 | darkMode: false, // or 'media' or 'class' 5 | theme: { 6 | extend: {}, 7 | }, 8 | variants: { 9 | extend: {}, 10 | }, 11 | plugins: [], 12 | } 13 | -------------------------------------------------------------------------------- /11-generate-a-custom-api-key-to-secure-an-api-route-in-next-js/utils/supabase.js: -------------------------------------------------------------------------------- 1 | import { createClient } from "@supabase/supabase-js"; 2 | 3 | export const supabase = createClient( 4 | process.env.NEXT_PUBLIC_SUPABASE_URL, 5 | process.env.NEXT_PUBLIC_SUPABASE_KEY 6 | ); 7 | -------------------------------------------------------------------------------- /12-automatically-create-a-stripe-customer-for-each-user-with-supabase-function-hooks/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "dev": "next dev", 5 | "build": "next build", 6 | "start": "next start" 7 | }, 8 | "dependencies": { 9 | "@supabase/supabase-js": "^1.28.2", 10 | "next": "latest", 11 | "react": "^17.0.2", 12 | "react-dom": "^17.0.2", 13 | "stripe": "^8.191.0" 14 | }, 15 | "devDependencies": { 16 | "autoprefixer": "^10.2.6", 17 | "postcss": "^8.3.5", 18 | "tailwindcss": "^2.2.4" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /12-automatically-create-a-stripe-customer-for-each-user-with-supabase-function-hooks/pages/[id].js: -------------------------------------------------------------------------------- 1 | import { supabase } from "../utils/supabase"; 2 | 3 | const LessonDetails = ({ lesson }) => { 4 | console.log({ lesson }); 5 | return ( 6 |
7 |

{lesson.title}

8 |

{lesson.description}

9 |
10 | ); 11 | }; 12 | 13 | export const getStaticPaths = async () => { 14 | const { data: lessons } = await supabase.from("lesson").select("id"); 15 | 16 | const paths = lessons.map(({ id }) => ({ 17 | params: { 18 | id: id.toString(), 19 | }, 20 | })); 21 | 22 | return { 23 | paths, 24 | fallback: false, 25 | }; 26 | }; 27 | 28 | export const getStaticProps = async ({ params: { id } }) => { 29 | const { data: lesson } = await supabase 30 | .from("lesson") 31 | .select("*") 32 | .eq("id", id) 33 | .single(); 34 | 35 | return { 36 | props: { 37 | lesson, 38 | }, 39 | }; 40 | }; 41 | 42 | export default LessonDetails; 43 | -------------------------------------------------------------------------------- /12-automatically-create-a-stripe-customer-for-each-user-with-supabase-function-hooks/pages/_app.js: -------------------------------------------------------------------------------- 1 | import 'tailwindcss/tailwind.css' 2 | 3 | function MyApp({ Component, pageProps }) { 4 | return 5 | } 6 | 7 | export default MyApp 8 | -------------------------------------------------------------------------------- /12-automatically-create-a-stripe-customer-for-each-user-with-supabase-function-hooks/pages/api/create-stripe-customer.js: -------------------------------------------------------------------------------- 1 | import initStripe from "stripe"; 2 | import { supabase } from "../../utils/supabase"; 3 | 4 | const handler = async (req, res) => { 5 | if (req.query.API_ROUTE_SECRET !== process.env.API_ROUTE_SECRET) { 6 | return res.status(401).send("You are not authorized to call this API"); 7 | } 8 | const stripe = initStripe(process.env.STRIPE_SECRET_KEY); 9 | 10 | const customer = await stripe.customers.create({ 11 | email: req.body.record.email, 12 | }); 13 | 14 | await supabase 15 | .from("profile") 16 | .update({ 17 | stripe_customer: customer.id, 18 | }) 19 | .eq("id", req.body.record.id); 20 | 21 | res.send({ message: `stripe customer created: ${customer.id}` }); 22 | }; 23 | 24 | export default handler; 25 | -------------------------------------------------------------------------------- /12-automatically-create-a-stripe-customer-for-each-user-with-supabase-function-hooks/pages/api/hello.js: -------------------------------------------------------------------------------- 1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction 2 | 3 | export default function helloAPI(req, res) { 4 | res.status(200).json({ name: 'John Doe' }) 5 | } 6 | -------------------------------------------------------------------------------- /12-automatically-create-a-stripe-customer-for-each-user-with-supabase-function-hooks/pages/index.js: -------------------------------------------------------------------------------- 1 | import { supabase } from "../utils/supabase"; 2 | import Link from "next/link"; 3 | 4 | export default function Home({ lessons }) { 5 | console.log(supabase.auth.user()); 6 | return ( 7 |
8 | {lessons.map((lesson) => ( 9 | 10 | 11 | {lesson.title} 12 | 13 | 14 | ))} 15 |
16 | ); 17 | } 18 | 19 | export const getStaticProps = async () => { 20 | const { data: lessons } = await supabase.from("lesson").select("*"); 21 | 22 | return { 23 | props: { 24 | lessons, 25 | }, 26 | }; 27 | }; 28 | -------------------------------------------------------------------------------- /12-automatically-create-a-stripe-customer-for-each-user-with-supabase-function-hooks/pages/login.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { supabase } from "../utils/supabase"; 3 | 4 | const Login = () => { 5 | useEffect(() => { 6 | supabase.auth.signIn({ 7 | provider: "github", 8 | }); 9 | }, []); 10 | 11 | return

Logging in

; 12 | }; 13 | 14 | export default Login; 15 | -------------------------------------------------------------------------------- /12-automatically-create-a-stripe-customer-for-each-user-with-supabase-function-hooks/pages/logout.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { supabase } from "../utils/supabase"; 3 | import { useRouter } from "next/router"; 4 | 5 | const Logout = () => { 6 | const router = useRouter(); 7 | 8 | useEffect(() => { 9 | const logout = async () => { 10 | supabase.auth.signOut(); 11 | router.push("/"); 12 | }; 13 | logout(); 14 | }, []); 15 | 16 | return

Logging out

; 17 | }; 18 | 19 | export default Logout; 20 | -------------------------------------------------------------------------------- /12-automatically-create-a-stripe-customer-for-each-user-with-supabase-function-hooks/postcss.config.js: -------------------------------------------------------------------------------- 1 | // If you want to use other PostCSS plugins, see the following: 2 | // https://tailwindcss.com/docs/using-with-preprocessors 3 | module.exports = { 4 | plugins: { 5 | tailwindcss: {}, 6 | autoprefixer: {}, 7 | }, 8 | } 9 | -------------------------------------------------------------------------------- /12-automatically-create-a-stripe-customer-for-each-user-with-supabase-function-hooks/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dijonmusters/build-a-saas-with-next-js-supabase-and-stripe/578c4681065d34fbd46d988005787e6033f6436f/12-automatically-create-a-stripe-customer-for-each-user-with-supabase-function-hooks/public/favicon.ico -------------------------------------------------------------------------------- /12-automatically-create-a-stripe-customer-for-each-user-with-supabase-function-hooks/public/vercel.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /12-automatically-create-a-stripe-customer-for-each-user-with-supabase-function-hooks/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | mode: 'jit', 3 | purge: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'], 4 | darkMode: false, // or 'media' or 'class' 5 | theme: { 6 | extend: {}, 7 | }, 8 | variants: { 9 | extend: {}, 10 | }, 11 | plugins: [], 12 | } 13 | -------------------------------------------------------------------------------- /12-automatically-create-a-stripe-customer-for-each-user-with-supabase-function-hooks/utils/supabase.js: -------------------------------------------------------------------------------- 1 | import { createClient } from "@supabase/supabase-js"; 2 | 3 | export const supabase = createClient( 4 | process.env.NEXT_PUBLIC_SUPABASE_URL, 5 | process.env.NEXT_PUBLIC_SUPABASE_KEY 6 | ); 7 | -------------------------------------------------------------------------------- /13-make-user-state-globally-accessible-in-next-js-with-react-context-and-providers/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "dev": "next dev", 5 | "build": "next build", 6 | "start": "next start" 7 | }, 8 | "dependencies": { 9 | "@supabase/supabase-js": "^1.28.2", 10 | "next": "latest", 11 | "react": "^17.0.2", 12 | "react-dom": "^17.0.2", 13 | "stripe": "^8.191.0" 14 | }, 15 | "devDependencies": { 16 | "autoprefixer": "^10.2.6", 17 | "postcss": "^8.3.5", 18 | "tailwindcss": "^2.2.4" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /13-make-user-state-globally-accessible-in-next-js-with-react-context-and-providers/pages/[id].js: -------------------------------------------------------------------------------- 1 | import { supabase } from "../utils/supabase"; 2 | 3 | const LessonDetails = ({ lesson }) => { 4 | console.log({ lesson }); 5 | return ( 6 |
7 |

{lesson.title}

8 |

{lesson.description}

9 |
10 | ); 11 | }; 12 | 13 | export const getStaticPaths = async () => { 14 | const { data: lessons } = await supabase.from("lesson").select("id"); 15 | 16 | const paths = lessons.map(({ id }) => ({ 17 | params: { 18 | id: id.toString(), 19 | }, 20 | })); 21 | 22 | return { 23 | paths, 24 | fallback: false, 25 | }; 26 | }; 27 | 28 | export const getStaticProps = async ({ params: { id } }) => { 29 | const { data: lesson } = await supabase 30 | .from("lesson") 31 | .select("*") 32 | .eq("id", id) 33 | .single(); 34 | 35 | return { 36 | props: { 37 | lesson, 38 | }, 39 | }; 40 | }; 41 | 42 | export default LessonDetails; 43 | -------------------------------------------------------------------------------- /13-make-user-state-globally-accessible-in-next-js-with-react-context-and-providers/pages/_app.js: -------------------------------------------------------------------------------- 1 | import "tailwindcss/tailwind.css"; 2 | import UserProvider from "../context/user"; 3 | 4 | function MyApp({ Component, pageProps }) { 5 | return ( 6 | 7 | 8 | 9 | ); 10 | } 11 | 12 | export default MyApp; 13 | -------------------------------------------------------------------------------- /13-make-user-state-globally-accessible-in-next-js-with-react-context-and-providers/pages/api/create-stripe-customer.js: -------------------------------------------------------------------------------- 1 | import initStripe from "stripe"; 2 | import { supabase } from "../../utils/supabase"; 3 | 4 | const handler = async (req, res) => { 5 | if (req.query.API_ROUTE_SECRET !== process.env.API_ROUTE_SECRET) { 6 | return res.status(401).send("You are not authorized to call this API"); 7 | } 8 | const stripe = initStripe(process.env.STRIPE_SECRET_KEY); 9 | 10 | const customer = await stripe.customers.create({ 11 | email: req.body.record.email, 12 | }); 13 | 14 | await supabase 15 | .from("profile") 16 | .update({ 17 | stripe_customer: customer.id, 18 | }) 19 | .eq("id", req.body.record.id); 20 | 21 | res.send({ message: `stripe customer created: ${customer.id}` }); 22 | }; 23 | 24 | export default handler; 25 | -------------------------------------------------------------------------------- /13-make-user-state-globally-accessible-in-next-js-with-react-context-and-providers/pages/api/hello.js: -------------------------------------------------------------------------------- 1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction 2 | 3 | export default function helloAPI(req, res) { 4 | res.status(200).json({ name: 'John Doe' }) 5 | } 6 | -------------------------------------------------------------------------------- /13-make-user-state-globally-accessible-in-next-js-with-react-context-and-providers/pages/index.js: -------------------------------------------------------------------------------- 1 | import { supabase } from "../utils/supabase"; 2 | import Link from "next/link"; 3 | import { useUser } from "../context/user"; 4 | 5 | export default function Home({ lessons }) { 6 | const { user } = useUser(); 7 | console.log({ user }); 8 | return ( 9 |
10 | {lessons.map((lesson) => ( 11 | 12 | 13 | {lesson.title} 14 | 15 | 16 | ))} 17 |
18 | ); 19 | } 20 | 21 | export const getStaticProps = async () => { 22 | const { data: lessons } = await supabase.from("lesson").select("*"); 23 | 24 | return { 25 | props: { 26 | lessons, 27 | }, 28 | }; 29 | }; 30 | -------------------------------------------------------------------------------- /13-make-user-state-globally-accessible-in-next-js-with-react-context-and-providers/pages/login.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { useUser } from "../context/user"; 3 | 4 | const Login = () => { 5 | const { login } = useUser(); 6 | 7 | useEffect(login, []); 8 | 9 | return

Logging in

; 10 | }; 11 | 12 | export default Login; 13 | -------------------------------------------------------------------------------- /13-make-user-state-globally-accessible-in-next-js-with-react-context-and-providers/pages/logout.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { useUser } from "../context/user"; 3 | 4 | const Logout = () => { 5 | const { logout } = useUser(); 6 | 7 | useEffect(logout, []); 8 | 9 | return

Logging out

; 10 | }; 11 | 12 | export default Logout; 13 | -------------------------------------------------------------------------------- /13-make-user-state-globally-accessible-in-next-js-with-react-context-and-providers/postcss.config.js: -------------------------------------------------------------------------------- 1 | // If you want to use other PostCSS plugins, see the following: 2 | // https://tailwindcss.com/docs/using-with-preprocessors 3 | module.exports = { 4 | plugins: { 5 | tailwindcss: {}, 6 | autoprefixer: {}, 7 | }, 8 | } 9 | -------------------------------------------------------------------------------- /13-make-user-state-globally-accessible-in-next-js-with-react-context-and-providers/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dijonmusters/build-a-saas-with-next-js-supabase-and-stripe/578c4681065d34fbd46d988005787e6033f6436f/13-make-user-state-globally-accessible-in-next-js-with-react-context-and-providers/public/favicon.ico -------------------------------------------------------------------------------- /13-make-user-state-globally-accessible-in-next-js-with-react-context-and-providers/public/vercel.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /13-make-user-state-globally-accessible-in-next-js-with-react-context-and-providers/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | mode: 'jit', 3 | purge: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'], 4 | darkMode: false, // or 'media' or 'class' 5 | theme: { 6 | extend: {}, 7 | }, 8 | variants: { 9 | extend: {}, 10 | }, 11 | plugins: [], 12 | } 13 | -------------------------------------------------------------------------------- /13-make-user-state-globally-accessible-in-next-js-with-react-context-and-providers/utils/supabase.js: -------------------------------------------------------------------------------- 1 | import { createClient } from "@supabase/supabase-js"; 2 | 3 | export const supabase = createClient( 4 | process.env.NEXT_PUBLIC_SUPABASE_URL, 5 | process.env.NEXT_PUBLIC_SUPABASE_KEY 6 | ); 7 | -------------------------------------------------------------------------------- /14-implement-authorization-using-row-level-security-and-policies/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "dev": "next dev", 5 | "build": "next build", 6 | "start": "next start" 7 | }, 8 | "dependencies": { 9 | "@supabase/supabase-js": "^1.28.2", 10 | "next": "latest", 11 | "react": "^17.0.2", 12 | "react-dom": "^17.0.2", 13 | "stripe": "^8.191.0" 14 | }, 15 | "devDependencies": { 16 | "autoprefixer": "^10.2.6", 17 | "postcss": "^8.3.5", 18 | "tailwindcss": "^2.2.4" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /14-implement-authorization-using-row-level-security-and-policies/pages/[id].js: -------------------------------------------------------------------------------- 1 | import { supabase } from "../utils/supabase"; 2 | 3 | const LessonDetails = ({ lesson }) => { 4 | console.log({ lesson }); 5 | return ( 6 |
7 |

{lesson.title}

8 |

{lesson.description}

9 |
10 | ); 11 | }; 12 | 13 | export const getStaticPaths = async () => { 14 | const { data: lessons } = await supabase.from("lesson").select("id"); 15 | 16 | const paths = lessons.map(({ id }) => ({ 17 | params: { 18 | id: id.toString(), 19 | }, 20 | })); 21 | 22 | return { 23 | paths, 24 | fallback: false, 25 | }; 26 | }; 27 | 28 | export const getStaticProps = async ({ params: { id } }) => { 29 | const { data: lesson } = await supabase 30 | .from("lesson") 31 | .select("*") 32 | .eq("id", id) 33 | .single(); 34 | 35 | return { 36 | props: { 37 | lesson, 38 | }, 39 | }; 40 | }; 41 | 42 | export default LessonDetails; 43 | -------------------------------------------------------------------------------- /14-implement-authorization-using-row-level-security-and-policies/pages/_app.js: -------------------------------------------------------------------------------- 1 | import "tailwindcss/tailwind.css"; 2 | import UserProvider from "../context/user"; 3 | 4 | function MyApp({ Component, pageProps }) { 5 | return ( 6 | 7 | 8 | 9 | ); 10 | } 11 | 12 | export default MyApp; 13 | -------------------------------------------------------------------------------- /14-implement-authorization-using-row-level-security-and-policies/pages/api/create-stripe-customer.js: -------------------------------------------------------------------------------- 1 | import initStripe from "stripe"; 2 | import { supabase } from "../../utils/supabase"; 3 | 4 | const handler = async (req, res) => { 5 | if (req.query.API_ROUTE_SECRET !== process.env.API_ROUTE_SECRET) { 6 | return res.status(401).send("You are not authorized to call this API"); 7 | } 8 | const stripe = initStripe(process.env.STRIPE_SECRET_KEY); 9 | 10 | const customer = await stripe.customers.create({ 11 | email: req.body.record.email, 12 | }); 13 | 14 | await supabase 15 | .from("profile") 16 | .update({ 17 | stripe_customer: customer.id, 18 | }) 19 | .eq("id", req.body.record.id); 20 | 21 | res.send({ message: `stripe customer created: ${customer.id}` }); 22 | }; 23 | 24 | export default handler; 25 | -------------------------------------------------------------------------------- /14-implement-authorization-using-row-level-security-and-policies/pages/api/hello.js: -------------------------------------------------------------------------------- 1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction 2 | 3 | export default function helloAPI(req, res) { 4 | res.status(200).json({ name: 'John Doe' }) 5 | } 6 | -------------------------------------------------------------------------------- /14-implement-authorization-using-row-level-security-and-policies/pages/index.js: -------------------------------------------------------------------------------- 1 | import { supabase } from "../utils/supabase"; 2 | import Link from "next/link"; 3 | import { useUser } from "../context/user"; 4 | 5 | export default function Home({ lessons }) { 6 | const { user } = useUser(); 7 | console.log({ user }); 8 | return ( 9 |
10 | {lessons.map((lesson) => ( 11 | 12 | 13 | {lesson.title} 14 | 15 | 16 | ))} 17 |
18 | ); 19 | } 20 | 21 | export const getStaticProps = async () => { 22 | const { data: lessons } = await supabase.from("lesson").select("*"); 23 | 24 | return { 25 | props: { 26 | lessons, 27 | }, 28 | }; 29 | }; 30 | -------------------------------------------------------------------------------- /14-implement-authorization-using-row-level-security-and-policies/pages/login.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { useUser } from "../context/user"; 3 | 4 | const Login = () => { 5 | const { login } = useUser(); 6 | 7 | useEffect(login, []); 8 | 9 | return

Logging in

; 10 | }; 11 | 12 | export default Login; 13 | -------------------------------------------------------------------------------- /14-implement-authorization-using-row-level-security-and-policies/pages/logout.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { useUser } from "../context/user"; 3 | 4 | const Logout = () => { 5 | const { logout } = useUser(); 6 | 7 | useEffect(logout, []); 8 | 9 | return

Logging out

; 10 | }; 11 | 12 | export default Logout; 13 | -------------------------------------------------------------------------------- /14-implement-authorization-using-row-level-security-and-policies/postcss.config.js: -------------------------------------------------------------------------------- 1 | // If you want to use other PostCSS plugins, see the following: 2 | // https://tailwindcss.com/docs/using-with-preprocessors 3 | module.exports = { 4 | plugins: { 5 | tailwindcss: {}, 6 | autoprefixer: {}, 7 | }, 8 | } 9 | -------------------------------------------------------------------------------- /14-implement-authorization-using-row-level-security-and-policies/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dijonmusters/build-a-saas-with-next-js-supabase-and-stripe/578c4681065d34fbd46d988005787e6033f6436f/14-implement-authorization-using-row-level-security-and-policies/public/favicon.ico -------------------------------------------------------------------------------- /14-implement-authorization-using-row-level-security-and-policies/public/vercel.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /14-implement-authorization-using-row-level-security-and-policies/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | mode: 'jit', 3 | purge: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'], 4 | darkMode: false, // or 'media' or 'class' 5 | theme: { 6 | extend: {}, 7 | }, 8 | variants: { 9 | extend: {}, 10 | }, 11 | plugins: [], 12 | } 13 | -------------------------------------------------------------------------------- /14-implement-authorization-using-row-level-security-and-policies/utils/supabase.js: -------------------------------------------------------------------------------- 1 | import { createClient } from "@supabase/supabase-js"; 2 | 3 | export const supabase = createClient( 4 | process.env.NEXT_PUBLIC_SUPABASE_URL, 5 | process.env.NEXT_PUBLIC_SUPABASE_KEY 6 | ); 7 | -------------------------------------------------------------------------------- /15-implement-gated-content-using-row-level-security-with-supabase/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "dev": "next dev", 5 | "build": "next build", 6 | "start": "next start" 7 | }, 8 | "dependencies": { 9 | "@supabase/supabase-js": "^1.28.2", 10 | "next": "latest", 11 | "react": "^17.0.2", 12 | "react-dom": "^17.0.2", 13 | "react-player": "^2.9.0", 14 | "stripe": "^8.191.0" 15 | }, 16 | "devDependencies": { 17 | "autoprefixer": "^10.2.6", 18 | "postcss": "^8.3.5", 19 | "tailwindcss": "^2.2.4" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /15-implement-gated-content-using-row-level-security-with-supabase/pages/_app.js: -------------------------------------------------------------------------------- 1 | import "tailwindcss/tailwind.css"; 2 | import UserProvider from "../context/user"; 3 | 4 | function MyApp({ Component, pageProps }) { 5 | return ( 6 | 7 | 8 | 9 | ); 10 | } 11 | 12 | export default MyApp; 13 | -------------------------------------------------------------------------------- /15-implement-gated-content-using-row-level-security-with-supabase/pages/api/create-stripe-customer.js: -------------------------------------------------------------------------------- 1 | import initStripe from "stripe"; 2 | import { supabase } from "../../utils/supabase"; 3 | 4 | const handler = async (req, res) => { 5 | if (req.query.API_ROUTE_SECRET !== process.env.API_ROUTE_SECRET) { 6 | return res.status(401).send("You are not authorized to call this API"); 7 | } 8 | const stripe = initStripe(process.env.STRIPE_SECRET_KEY); 9 | 10 | const customer = await stripe.customers.create({ 11 | email: req.body.record.email, 12 | }); 13 | 14 | await supabase 15 | .from("profile") 16 | .update({ 17 | stripe_customer: customer.id, 18 | }) 19 | .eq("id", req.body.record.id); 20 | 21 | res.send({ message: `stripe customer created: ${customer.id}` }); 22 | }; 23 | 24 | export default handler; 25 | -------------------------------------------------------------------------------- /15-implement-gated-content-using-row-level-security-with-supabase/pages/api/hello.js: -------------------------------------------------------------------------------- 1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction 2 | 3 | export default function helloAPI(req, res) { 4 | res.status(200).json({ name: 'John Doe' }) 5 | } 6 | -------------------------------------------------------------------------------- /15-implement-gated-content-using-row-level-security-with-supabase/pages/index.js: -------------------------------------------------------------------------------- 1 | import { supabase } from "../utils/supabase"; 2 | import Link from "next/link"; 3 | import { useUser } from "../context/user"; 4 | 5 | export default function Home({ lessons }) { 6 | const { user } = useUser(); 7 | console.log({ user }); 8 | return ( 9 |
10 | {lessons.map((lesson) => ( 11 | 12 | 13 | {lesson.title} 14 | 15 | 16 | ))} 17 |
18 | ); 19 | } 20 | 21 | export const getStaticProps = async () => { 22 | const { data: lessons } = await supabase.from("lesson").select("*"); 23 | 24 | return { 25 | props: { 26 | lessons, 27 | }, 28 | }; 29 | }; 30 | -------------------------------------------------------------------------------- /15-implement-gated-content-using-row-level-security-with-supabase/pages/login.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { useUser } from "../context/user"; 3 | 4 | const Login = () => { 5 | const { login } = useUser(); 6 | 7 | useEffect(login, []); 8 | 9 | return

Logging in

; 10 | }; 11 | 12 | export default Login; 13 | -------------------------------------------------------------------------------- /15-implement-gated-content-using-row-level-security-with-supabase/pages/logout.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { useUser } from "../context/user"; 3 | 4 | const Logout = () => { 5 | const { logout } = useUser(); 6 | 7 | useEffect(logout, []); 8 | 9 | return

Logging out

; 10 | }; 11 | 12 | export default Logout; 13 | -------------------------------------------------------------------------------- /15-implement-gated-content-using-row-level-security-with-supabase/postcss.config.js: -------------------------------------------------------------------------------- 1 | // If you want to use other PostCSS plugins, see the following: 2 | // https://tailwindcss.com/docs/using-with-preprocessors 3 | module.exports = { 4 | plugins: { 5 | tailwindcss: {}, 6 | autoprefixer: {}, 7 | }, 8 | } 9 | -------------------------------------------------------------------------------- /15-implement-gated-content-using-row-level-security-with-supabase/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dijonmusters/build-a-saas-with-next-js-supabase-and-stripe/578c4681065d34fbd46d988005787e6033f6436f/15-implement-gated-content-using-row-level-security-with-supabase/public/favicon.ico -------------------------------------------------------------------------------- /15-implement-gated-content-using-row-level-security-with-supabase/public/vercel.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /15-implement-gated-content-using-row-level-security-with-supabase/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | mode: 'jit', 3 | purge: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'], 4 | darkMode: false, // or 'media' or 'class' 5 | theme: { 6 | extend: {}, 7 | }, 8 | variants: { 9 | extend: {}, 10 | }, 11 | plugins: [], 12 | } 13 | -------------------------------------------------------------------------------- /15-implement-gated-content-using-row-level-security-with-supabase/utils/supabase.js: -------------------------------------------------------------------------------- 1 | import { createClient } from "@supabase/supabase-js"; 2 | 3 | export const supabase = createClient( 4 | process.env.NEXT_PUBLIC_SUPABASE_URL, 5 | process.env.NEXT_PUBLIC_SUPABASE_KEY 6 | ); 7 | -------------------------------------------------------------------------------- /16-use-stripe-js-to-query-product-data-and-pre-render-with-next-js/README.md: -------------------------------------------------------------------------------- 1 | # Use Stripe.js to Query Product Data and Pre-Render with Next.js 2 | 3 | **[📹 Video](https://egghead.io/lessons/next-js-use-stripe-js-to-query-product-data-and-pre-render-with-next-js)** 4 | 5 | Each of our subscription options will be a product in Stripe. In this video, we create a /pricing page to display our new products. We use the Stripe package to fetch these options at build time and add some simple styling to make it look nice! 6 | 7 | Annoyingly, the prices list is what we need to request for pricing information, but does not contain product information - such as name. This means we need to make an additional request for the product information for each price. Good thing we are only doing this once at build time, as the request time could add up pretty quickly, if we needed to do this on every request! 8 | 9 | Lastly, we sort the list of plans from Stripe, to ensure that the prices go up in ascending order. Since each plan has a price, we can use a simple native JS sorting technique. 10 | 11 | [👉 Next lesson](/17-create-shared-nav-bar-in-next-js-with-_app-js) 12 | 13 | --- 14 | 15 | Enjoyed the course? Follow me on [Twitter](https://twitter.com/jonmeyers_io) and subscribe to my [YouTube channel](https://www.youtube.com/channel/UCPitAIwktfCfcMR4kDWebDQ). 16 | -------------------------------------------------------------------------------- /16-use-stripe-js-to-query-product-data-and-pre-render-with-next-js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "dev": "next dev", 5 | "build": "next build", 6 | "start": "next start" 7 | }, 8 | "dependencies": { 9 | "@supabase/supabase-js": "^1.28.2", 10 | "next": "latest", 11 | "react": "^17.0.2", 12 | "react-dom": "^17.0.2", 13 | "react-player": "^2.9.0", 14 | "stripe": "^8.191.0" 15 | }, 16 | "devDependencies": { 17 | "autoprefixer": "^10.2.6", 18 | "postcss": "^8.3.5", 19 | "tailwindcss": "^2.2.4" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /16-use-stripe-js-to-query-product-data-and-pre-render-with-next-js/pages/_app.js: -------------------------------------------------------------------------------- 1 | import "tailwindcss/tailwind.css"; 2 | import UserProvider from "../context/user"; 3 | 4 | function MyApp({ Component, pageProps }) { 5 | return ( 6 | 7 | 8 | 9 | ); 10 | } 11 | 12 | export default MyApp; 13 | -------------------------------------------------------------------------------- /16-use-stripe-js-to-query-product-data-and-pre-render-with-next-js/pages/api/create-stripe-customer.js: -------------------------------------------------------------------------------- 1 | import initStripe from "stripe"; 2 | import { supabase } from "../../utils/supabase"; 3 | 4 | const handler = async (req, res) => { 5 | if (req.query.API_ROUTE_SECRET !== process.env.API_ROUTE_SECRET) { 6 | return res.status(401).send("You are not authorized to call this API"); 7 | } 8 | const stripe = initStripe(process.env.STRIPE_SECRET_KEY); 9 | 10 | const customer = await stripe.customers.create({ 11 | email: req.body.record.email, 12 | }); 13 | 14 | await supabase 15 | .from("profile") 16 | .update({ 17 | stripe_customer: customer.id, 18 | }) 19 | .eq("id", req.body.record.id); 20 | 21 | res.send({ message: `stripe customer created: ${customer.id}` }); 22 | }; 23 | 24 | export default handler; 25 | -------------------------------------------------------------------------------- /16-use-stripe-js-to-query-product-data-and-pre-render-with-next-js/pages/api/hello.js: -------------------------------------------------------------------------------- 1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction 2 | 3 | export default function helloAPI(req, res) { 4 | res.status(200).json({ name: 'John Doe' }) 5 | } 6 | -------------------------------------------------------------------------------- /16-use-stripe-js-to-query-product-data-and-pre-render-with-next-js/pages/index.js: -------------------------------------------------------------------------------- 1 | import { supabase } from "../utils/supabase"; 2 | import Link from "next/link"; 3 | import { useUser } from "../context/user"; 4 | 5 | export default function Home({ lessons }) { 6 | const { user } = useUser(); 7 | console.log({ user }); 8 | return ( 9 |
10 | {lessons.map((lesson) => ( 11 | 12 | 13 | {lesson.title} 14 | 15 | 16 | ))} 17 |
18 | ); 19 | } 20 | 21 | export const getStaticProps = async () => { 22 | const { data: lessons } = await supabase.from("lesson").select("*"); 23 | 24 | return { 25 | props: { 26 | lessons, 27 | }, 28 | }; 29 | }; 30 | -------------------------------------------------------------------------------- /16-use-stripe-js-to-query-product-data-and-pre-render-with-next-js/pages/login.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { useUser } from "../context/user"; 3 | 4 | const Login = () => { 5 | const { login } = useUser(); 6 | 7 | useEffect(login, []); 8 | 9 | return

Logging in

; 10 | }; 11 | 12 | export default Login; 13 | -------------------------------------------------------------------------------- /16-use-stripe-js-to-query-product-data-and-pre-render-with-next-js/pages/logout.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { useUser } from "../context/user"; 3 | 4 | const Logout = () => { 5 | const { logout } = useUser(); 6 | 7 | useEffect(logout, []); 8 | 9 | return

Logging out

; 10 | }; 11 | 12 | export default Logout; 13 | -------------------------------------------------------------------------------- /16-use-stripe-js-to-query-product-data-and-pre-render-with-next-js/pages/pricing.js: -------------------------------------------------------------------------------- 1 | import initStripe from "stripe"; 2 | 3 | const Pricing = ({ plans }) => { 4 | return ( 5 |
6 | {plans.map((plan) => ( 7 |
8 |

{plan.name}

9 |

10 | ${plan.price / 100} / {plan.interval} 11 |

12 |
13 | ))} 14 |
15 | ); 16 | }; 17 | 18 | export const getStaticProps = async () => { 19 | const stripe = initStripe(process.env.STRIPE_SECRET_KEY); 20 | 21 | const { data: prices } = await stripe.prices.list(); 22 | 23 | const plans = await Promise.all( 24 | prices.map(async (price) => { 25 | const product = await stripe.products.retrieve(price.product); 26 | return { 27 | id: price.id, 28 | name: product.name, 29 | price: price.unit_amount, 30 | interval: price.recurring.interval, 31 | currency: price.currency, 32 | }; 33 | }) 34 | ); 35 | 36 | const sortedPlans = plans.sort((a, b) => a.price - b.price); 37 | 38 | return { 39 | props: { 40 | plans: sortedPlans, 41 | }, 42 | }; 43 | }; 44 | 45 | export default Pricing; 46 | -------------------------------------------------------------------------------- /16-use-stripe-js-to-query-product-data-and-pre-render-with-next-js/postcss.config.js: -------------------------------------------------------------------------------- 1 | // If you want to use other PostCSS plugins, see the following: 2 | // https://tailwindcss.com/docs/using-with-preprocessors 3 | module.exports = { 4 | plugins: { 5 | tailwindcss: {}, 6 | autoprefixer: {}, 7 | }, 8 | } 9 | -------------------------------------------------------------------------------- /16-use-stripe-js-to-query-product-data-and-pre-render-with-next-js/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dijonmusters/build-a-saas-with-next-js-supabase-and-stripe/578c4681065d34fbd46d988005787e6033f6436f/16-use-stripe-js-to-query-product-data-and-pre-render-with-next-js/public/favicon.ico -------------------------------------------------------------------------------- /16-use-stripe-js-to-query-product-data-and-pre-render-with-next-js/public/vercel.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /16-use-stripe-js-to-query-product-data-and-pre-render-with-next-js/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | mode: 'jit', 3 | purge: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'], 4 | darkMode: false, // or 'media' or 'class' 5 | theme: { 6 | extend: {}, 7 | }, 8 | variants: { 9 | extend: {}, 10 | }, 11 | plugins: [], 12 | } 13 | -------------------------------------------------------------------------------- /16-use-stripe-js-to-query-product-data-and-pre-render-with-next-js/utils/supabase.js: -------------------------------------------------------------------------------- 1 | import { createClient } from "@supabase/supabase-js"; 2 | 3 | export const supabase = createClient( 4 | process.env.NEXT_PUBLIC_SUPABASE_URL, 5 | process.env.NEXT_PUBLIC_SUPABASE_KEY 6 | ); 7 | -------------------------------------------------------------------------------- /17-create-shared-nav-bar-in-next-js-with-_app-js/README.md: -------------------------------------------------------------------------------- 1 | # Create Shared Nav Bar in Next.js with \_app.js 2 | 3 | **[📹 Video](https://egghead.io/lessons/next-js-create-shared-nav-bar-in-next-js-with-_app-js)** 4 | 5 | Manually typing the URL to navigate between our different pages is becoming cumbersome. Let's create a shared navigation bar that will display on every page in our application. 6 | 7 | In this video, we create a new component to display our Nav options - / and /pricing. Each of our routes uses the Next.js component to make transitions between pages client-side. By rendering our