diff --git a/src/content/blog/supabase-auth-with-remix-and-vite.md b/src/content/blog/supabase-auth-with-remix-and-vite.md new file mode 100644 index 0000000..33a2ee5 --- /dev/null +++ b/src/content/blog/supabase-auth-with-remix-and-vite.md @@ -0,0 +1,255 @@ +--- +title: Supabase Auth with Remix + Vite +description: Get up and running with Remix, Vite and the new Supabase SSR package +year: 2024 +published_at: 2024-03-08 +--- + +With Supabase now fully supporting doing authentication completely server-side, it has never been easier to take advantage of all of the features that it offers. We'll go through how to quickly spin up a Remix project and add supabase with authentication. + +## Spin up a new Remix project + +This guide should also work for non-vite Remix projects but my preference is to use vite so we'll use a clean vite template. + +```bash +npx create-remix@latest --template remix-run/remix/templates/vite +``` + +Run this command in your terminal and follow the prompts, then open your new project in your editor of choice. You'll also need to [create a new project in supabase](https://supabase.com/) so that we can get access to the environment variables for this example. + +Once you've created a supabase project, in the root of your project create a `.env` file and paste in the values for your `SUPABASE_URL` and `SUPABASE_ANON_KEY`. You can find these values by going into your supabase project dashboard, clicking on the on the connect button and selecting Remix from the frameworks list. + + + + + +Lastly, install the `@supabase/ssr` package. + +```bash +npm install @supabase/ssr +``` + +## Setting up authentication + +There are a bunch of ways you can set up auth in supabase. E-mail and password combo, E-mail magic link, OAuth and so on. For the sake of this blog post we'll set up E-mail and password since it's the simplest one. Though that should be enough to explore the other methods as well if you wish. + +### Create the necessary files + +In here we are just creating a utility function so that we can re-use this function across all instances where we need to access supabase resources. + +```ts +// app/utils/supabase.server.ts + +import { createServerClient, serialize, parse } from "@supabase/ssr"; + +export function createClient(request: Request) { + const cookies = parse(request.headers.get("Cookie") ?? ""); + const headers = new Headers(); + + const supabase = createServerClient( + process.env.SUPABASE_URL!, + process.env.SUPABASE_ANON_KEY!, + { + cookies: { + get(key) { + return cookies[key]; + }, + set(key, value, options) { + headers.append("Set-Cookie", serialize(key, value, options)); + }, + remove(key, options) { + headers.append("Set-Cookie", serialize(key, "", options)); + }, + }, + } + ); + + return { + supabase, + headers, + }; +} +``` + +This file is for when we are signing up for the first time and receive a confirmation e-mail. Clicking the link with log you in automatically but going forward you will log in through the log in page. + +```ts +// app/routes/auth.callback.tsx + +import { redirect, type LoaderFunctionArgs } from "@remix-run/node"; +import { createClient } from "~/utils/supabase.server"; + +export async function loader({ request }: LoaderFunctionArgs) { + const requestUrl = new URL(request.url); + const code = requestUrl.searchParams.get("code"); + const next = requestUrl.searchParams.get("next") || "/"; + const { supabase, headers } = createClient(request); + + if (code) { + const { error } = await supabase.auth.exchangeCodeForSession(code); + if (!error) { + return redirect(next, { headers }); + } + } + + return redirect("/auth/auth-error-page", { headers }); +} +``` + +Sign up page, you will recieve a confirmation e-mail for the first time that you sign up. After you click the link in your e-mail you will be signed in. + +```ts +// app/routes/signup.tsx + +import { ActionFunctionArgs, json } from "@remix-run/node"; +import { Form } from "@remix-run/react"; +import { createClient } from "~/utils/supabase.server"; + +export async function action({ request }: ActionFunctionArgs) { + const formData = await request.formData(); + const email = formData.get("email") as string; + const password = formData.get("password") as string; + const { supabase } = createClient(request); + + const { error } = await supabase.auth.signUp({ + email, + password, + }); + + if (error) { + return json({ message: error.message }, { status: 400 }); + } + return null; +} + +export default function Component() { + return ( +
{JSON.stringify(user, null, 2)}
+ {JSON.stringify(user, null, 2)}
++
+