qwik
qwik copied to clipboard
[📖] Supabase Integration does not work.
Suggestion
When trying to incorporate the Supabase authentication using the code suggested in the docs:
export const useDBTest = routeLoader$(async (requestEv) => {
const supabaseClient = createServerClient(
import.meta.env.PUBLIC_SUPABASE_URL,
import.meta.env.PUBLIC_SUPABASE_ANON_KEY,
requestEv
);
const { data } = await supabaseClient.from('test').select('*')
return { data };
});
I get the following TS error:
Argument of type 'RequestEventLoader<QwikCityPlatform>' is not assignable to parameter of type 'RequestEvent<QwikCityPlatform>'.
Type 'RequestEventLoader<QwikCityPlatform>' is missing the following properties from type 'RequestEvent<QwikCityPlatform>': headersSent, exited, getWritableStream, nextts(2345)
The requestEv does not have all the attributes needed for the supabase server-client.
where is createServerClient coming from?
This is from qwik supabase auth lib.
import { createServerClient } from 'supabase-auth-helpers-qwik';
https://qwik.builder.io/docs/integrations/supabase/
@y471n Please note that if you are using routeLoader$ outside of the layout.tsx or index.tsx files located in the routes folder, it may not function as intended. Have you checked whether you are using routeLoader$ in any of these files?
I'm using it inside index.tsx itself. The issue was with createServerClient. I used createClient from Supabase lib itself as a workaround to make it work.
@y471n then, clear the vite cache for dev mode. Go to package.json file and find the start script. Then add --force before the --open flag.
Hi @y471n 👋 did @diecodev 's reply helped with this issue? thanks for helping out @diecodev 🙏
Have you figured how to set it up ? I am trying to use supabase-cli but I kinda get stuck because we don't have the same auth-helpers as other framework... Is this because Qwik uses serverClient only ?
@PaVeilleux Can you please show any repository where you have problems with the package?
Well I am quite confused on the process to authenticate user server-side. From what I understand, the flow would be somewhat similar to this :
- Create client from "@supabase/supabase-js"
- Wrap the app with a context provider (root layout)
- Use createServerClient from "supabase-auth-helpers-qwik" to store cookies
- Use createBrowserClient from "supabase-auth-helpers-qwik" for components
Does wraping the app in the layout acts as createMiddlewareSupabaseClient from @supabase/auth-helpers-nextjs ? Am I supposed to use the createServerClient or the client from @supabase/supabase-js for the context?
To use supabase with qwik you don't need to create a context provider or start the supabase client from the @supabase/supabase-js package, you just need to follow the example in the qwik documentation. Previously there was a problem with supabase.auth but it has been fixed recently. In case you don't understand how to start or create the supabase client, here is a step by step guide:
Guide to use Supabase with Qwik:
- First, install the packages:
I recommend you to install the packages as devDependencies and the exact version (to avoid versioning problems in the future).
npm install --exact --save-dev @supabase/supabase-js supabase-auth-helpers-qwik
- Create a routeLoader$ (this utility is provided by qwik-city) or if you want to use the supabaseClient from a Form (qwik-city component) you can use a routeAction$ or globalAction$ (both utilities are also provided by qwik-city).
In this example, I'm using a globalAction$ to handle a login form, so I can use the globalAction$ from the loginForm.tsx file.
//loginForm.tsx
import { Form, globalAction$, zod$, z } from "@builder.io/qwik-city";
export const useLoginForm = globalAction$(
async (user, event) => {
const supabaseUrl = event.env.get("SUPABASE_URL")!;
const supabaseKey = event.env.get("SUPABASE_KEY")!;
const supabase = createServerClient(supabaseUrl, supabaseKey, event);
const { data, error } = await supabase.auth.signInWithPassword({
email: user.email,
password: user.password,
});
return {
success: true,
data: data,
};
},
zod$({
email: z.string().email("Please enter a valid email."),
password: z
.string()
.min(8, "Password must be at least 8 characters long.")
.max(20, "The password must be less than 20 characters long."),
})
);
- Now, use the globalAction$ (or the routeLoader$/routeAction$) in whatever file you need to use it in.
// loginForm.tsx
export const LoginForm = component$(() => {
const action = useLoginForm();
if (action.value?.success) {
location.reload();
}
return (
<>
<h1 class="my-10 text-center text-2xl font-bold">
Please Sign in to continue
</h1>
<Form
action={action}
class="mx-auto mt-20 flex max-w-md flex-col items-stretch gap-6 p-4"
>
<input
type="email"
placeholder="[email protected]"
name="email"
class="rounded-md border border-gray-300 px-3 py-1 outline-none transition-colors focus-within:border-black"
/>
<input
type="password"
placeholder="********"
name="password"
minLength={8}
maxLength={20}
class="rounded-md border border-gray-300 px-3 py-1 outline-none transition-colors focus-within:border-black"
/>
<button
type="submit"
class="rounded-md bg-black px-3 py-1 font-medium text-white"
>
Ingresar
</button>
</Form>
</>
);
});
@zanettin I think we can close this issue in order to do not have a backlog of issues that may already be resolved.
hi @diecodev 👋 sure. feel free to reopen when needed 🙏