Since December 2023, I have been building my very first software business, Screenly. It is a saas product with the mission of making candidate screening easier and faster for businesses.
Whilst building Screenly I come across certain technical issues that I wish more people had written in more depth about. I am writing this series (#buildingScreenly) as I come across some common problems in the hope that it would be helpful to someone else in the future.
Prerequisities
My tech stack of choice is: Prisma, Remix and Supabase. I will also be deploying on Cloudflare Pages. You don’t need to know any of these in a lot of details, if you have used Nextjs before then you should be comfortable with Remix.
Set up your remix application
Use the create-cloudflare to create your new remix project
npm create cloudflare@latest my-remix-app -- --framework=remix
Set up Supabase Authenthication
//lib/supabase.server.ts
import { createServerClient, parse, serialize } from "@supabase/ssr";
import type { AppLoadContext } from '@remix-run/cloudflare';
export const createSupabaseServerClient = (request: Request, context: AppLoadContext) => {
const cookies = parse(request.headers.get('Cookie') ?? '')
const headers = new Headers()
const supabaseClient = createServerClient(
context.cloudflare.env.SUPABASE_URL!,
context.cloudflare.env.SUPABASE_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 { supabaseClient, headers }
}
//.dev.vars
SUPABASE_URL=*********************
SUPABASE_KEY=*********************
Create up /register page
// routes/register.tsx
import { Form, Link, useActionData, useNavigation } from "@remix-run/react";
export default function RegisterPage(){
return (
<div>
<Form>
<Input
type="text"
id="first_name"
name="first_name"
placeholder="Max"
autoCapitalize="none"
autoComplete="given-name"
autoCorrect="off"
/>
<Input
type="text"
id="last_name"
name="last_name"
placeholder="Max"
autoCapitalize="none"
autoComplete="family-name"
autoCorrect="off"
/>
<Input
type="text"
id="email"
name="email"
placeholder="[email protected]"
autoCapitalize="none"
autoComplete="email"
autoCorrect="off"
/>
<Input
type="password"
id="password"
name="password"
placeholder="********"
/>
<button>
Sign up
</button>
</Form>
</div>
)
}
export const action = async ({ request, context }: ActionFunctionArgs<GetLoadContext>) => {
const formData = await request.formData();
const first_name = formData.get("first_name") as string;
const last_name = formData.get("last_name") as string;
const email = formData.get("email") as string;
const password = formData.get("password") as string;
const { supabaseClient, headers } = createSupabaseServerClient(request, context)
const { data, error } = await supabaseClient.auth.signUp({
email,
password,
options: {
data: {
first_name,
last_name,
}
}
})
if (error) {
return json({ message: error.message }, { status: 400 });
}
// if you want to expand this tutorial you can return a specific authnethicated page like /on-boarding or /dashboard instead
return redirect("/");
}