auth-helpers icon indicating copy to clipboard operation
auth-helpers copied to clipboard

Usage example with Next.js edge functions

Open ilyabo opened this issue 3 years ago • 19 comments

Improve documentation

Describe the problem

There is no documentation on the use of auth helpers in Next.js edge functions to be used on Vercel or Cloudflare. The Edge Functions API is somewhat different, so it seems that createServerSupabaseClient from @supabase/auth-helpers-nextjs cannot be used directly. I found an example using a third-party library. But it would be great if we could perform Supabase authentication in edge functions using the official library from Supabase.

ilyabo avatar Nov 12 '22 09:11 ilyabo

@ilyabo you should be able to use the createMiddlewareSupabaseClient: https://supabase.com/docs/guides/auth/auth-helpers/nextjs#auth-with-nextjs-middleware

As middleware is always run on the edge: https://nextjs.org/docs/api-reference/edge-runtime

Which brings up a good point that we should likely rename createMiddlewareSupabaseClient to createEdgeSupabaseClient.

Thanks for flagging this!

thorwebdev avatar Nov 14 '22 05:11 thorwebdev

@ilyabo you should be able to use the createMiddlewareSupabaseClient: https://supabase.com/docs/guides/auth/auth-helpers/nextjs#auth-with-nextjs-middleware

As middleware is always run on the edge: https://nextjs.org/docs/api-reference/edge-runtime

Which brings up a good point that we should likely rename createMiddlewareSupabaseClient to createEdgeSupabaseClient.

Thanks for flagging this!

Do I need to pass Authorization header with fetch API to use nextjs edge functions?

I found out that, calling nextjs edge function from server component with fetch API, middleware doesn't receives req.cookies. Without cookies I can't create supabase client because it will always return session null.

Server component code in app directory:

const getUser = async () => {
    const res = await fetch('http://localhost:3000/api/user');

    if (!res?.ok) { return null; }

    return await res.json();
}

const Header = async () => {
    const user = await getUser();

    return(
        <div>
            {JSON.stringify(user)}
        </div>
    )
}

export default Header

rodzerjohn avatar Nov 28 '22 19:11 rodzerjohn

This solution did not work for me. I am still receiving the same error

Code: EDGE_FUNCTION_INVOCATION_FAILED```

DeveloperHIT avatar Dec 19 '22 20:12 DeveloperHIT

Is it possible to send a JSON via an edge API with createMiddlewareSupabaseClient?

In the code below, res is not setting a cookie unless being returned return res

Benefit would be sending session data upon login avoiding an additional getSession()

import { createMiddlewareSupabaseClient } from "@supabase/auth-helpers-nextjs";
import { NextRequest, NextResponse } from "next/server";

export const config = {
  runtime: "edge",
};

export default async function handler(req: NextRequest) {
  const res = NextResponse.next();
  const { email, password } = (await req.json()) as {
    email: string;
    password: string;
  };
  const supabase = createMiddlewareSupabaseClient({ req, res });
  const { data, error } = await supabase.auth.signInWithPassword({
    email: email,
    password: password,
  });

  if (data.session) {
   // return res // correctly sets cookie but unable to attach a JSON
    res; // not setting cookie but JSON returned
    return new Response(
      JSON.stringify({
        session: data.session,
      }),
      {
        status: 200,
        statusText: "Successfully logged in",
        headers: {
          "content-type": "application/json",
        },
      }
    );
  } 
}

hjaber avatar Feb 04 '23 03:02 hjaber

@ilyabo you should be able to use the createMiddlewareSupabaseClient: https://supabase.com/docs/guides/auth/auth-helpers/nextjs#auth-with-nextjs-middleware

As middleware is always run on the edge: https://nextjs.org/docs/api-reference/edge-runtime

Which brings up a good point that we should likely rename createMiddlewareSupabaseClient to createEdgeSupabaseClient.

Thanks for flagging this!

Ok, how do I get req and res in the server components? Those are required to call createMiddlewareSupabaseClient

KajSzy avatar Mar 06 '23 08:03 KajSzy

Here's the workaround:

Suppose you have a route handler for a nodejs runtime like this:

// https://supabase.com/docs/guides/auth/auth-helpers/nextjs#route-handlers
import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs'
import { NextResponse } from 'next/server'
import { cookies } from 'next/headers'

import type { Database } from '@/lib/database.types'

export async function POST(request: Request) {
  const { title } = await request.json()
  const supabase = createRouteHandlerClient<Database>({ cookies })
  const { data } = await supabase.from('todos').insert({ title }).select()
  return NextResponse.json(data)
}

You can change the runtime to edge by adding export const runtime = "edge" to the file. But this changes the APIs available for execution. Notably, cookies() from Vercel's next/headers no longer works. So you have to swap that out with the RequestCookies object from @edge-runtime/cookies (also from Vercel). The new one is not a callable function, its an object so you have to wrap it in a callback for the createFOOClient functions. The edge version would look like this:

  import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs'
  import { NextResponse } from 'next/server'
+ import { RequestCookies } from "@edge-runtime/cookies";
- import { cookies } from 'next/headers'

  import type { Database } from '@/lib/database.types'

  export async function POST(request: Request) {
    const { title } = await request.json()
+   const cookies = new RequestCookies(request.headers) as any;
+   const supabase = createRouteHandlerClient<Database>({ cookies: () => cookies })
-   const supabase = createRouteHandlerClient<Database>({ cookies })
    const { data } = await supabase.from('todos').insert({ title }).select()
    return NextResponse.json(data)
  }

notjustinshaw avatar Jun 21 '23 21:06 notjustinshaw

Here's the workaround:

Suppose you have a route handler for a nodejs runtime like this:

// https://supabase.com/docs/guides/auth/auth-helpers/nextjs#route-handlers
import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs'
import { NextResponse } from 'next/server'
import { cookies } from 'next/headers'

import type { Database } from '@/lib/database.types'

export async function POST(request: Request) {
  const { title } = await request.json()
  const supabase = createRouteHandlerClient<Database>({ cookies })
  const { data } = await supabase.from('todos').insert({ title }).select()
  return NextResponse.json(data)
}

You can change the runtime to edge by adding export const runtime = "edge" to the file. But this changes the APIs available for execution. Notably, cookies() from Vercel's next/headers no longer works. So you have to swap that out with the RequestCookies object from @edge-runtime/cookies (also from Vercel). The new one is not a callable function, its an object so you have to wrap it in a callback for the createFOOClient functions. The edge version would look like this:

  import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs'
  import { NextResponse } from 'next/server'
+ import { RequestCookies } from "@edge-runtime/cookies";
- import { cookies } from 'next/headers'

  import type { Database } from '@/lib/database.types'

  export async function POST(request: Request) {
    const { title } = await request.json()
+   const cookies = new RequestCookies(request.headers) as any;
+   const supabase = createRouteHandlerClient<Database>({ cookies: () => cookies })
-   const supabase = createRouteHandlerClient<Database>({ cookies })
    const { data } = await supabase.from('todos').insert({ title }).select()
    return NextResponse.json(data)
  }

So far the cleanest approach. Thank you.

Necmttn avatar Jun 29 '23 16:06 Necmttn

I am seeing this error

TypeError: requestHeaders.get is not a function at new RequestCookies (/node_modules/@edge-runtime/cookies/dist/index.js:102:35)

Any idea whats causing this?

viraj071 avatar Jul 06 '23 17:07 viraj071

Here's the workaround:

Suppose you have a route handler for a nodejs runtime like this:

// https://supabase.com/docs/guides/auth/auth-helpers/nextjs#route-handlers
import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs'
import { NextResponse } from 'next/server'
import { cookies } from 'next/headers'

import type { Database } from '@/lib/database.types'

export async function POST(request: Request) {
  const { title } = await request.json()
  const supabase = createRouteHandlerClient<Database>({ cookies })
  const { data } = await supabase.from('todos').insert({ title }).select()
  return NextResponse.json(data)
}

You can change the runtime to edge by adding export const runtime = "edge" to the file. But this changes the APIs available for execution. Notably, cookies() from Vercel's next/headers no longer works. So you have to swap that out with the RequestCookies object from @edge-runtime/cookies (also from Vercel). The new one is not a callable function, its an object so you have to wrap it in a callback for the createFOOClient functions. The edge version would look like this:

  import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs'
  import { NextResponse } from 'next/server'
+ import { RequestCookies } from "@edge-runtime/cookies";
- import { cookies } from 'next/headers'

  import type { Database } from '@/lib/database.types'

  export async function POST(request: Request) {
    const { title } = await request.json()
+   const cookies = new RequestCookies(request.headers) as any;
+   const supabase = createRouteHandlerClient<Database>({ cookies: () => cookies })
-   const supabase = createRouteHandlerClient<Database>({ cookies })
    const { data } = await supabase.from('todos').insert({ title }).select()
    return NextResponse.json(data)
  }

This should be added to the supabase documentation @kiwicopple

brycedbjork avatar Jul 14 '23 16:07 brycedbjork

Not sure how I found this but thank goodness I did.

RyelBanfield avatar Jul 16 '23 02:07 RyelBanfield

I think there is an issue with next/headers: https://github.com/vercel/next.js/issues/45371 . We're currently checking with the Vercel team.

thorwebdev avatar Jul 17 '23 06:07 thorwebdev

But what about the OP – is it possible to run createServerSupabaseClient inside an edge function?

I don't see any way how the cookie from RequestCookies could be passed into it.

Context: I'm trying to use Supabase Auth inside a TRPC API callback.

alexthewilde avatar Aug 25 '23 14:08 alexthewilde

I have verified that it works. You don’t pass the cookie in, you create a new object and pass on an anonymous function that returns that object (see above).

I’d be happy to share more details if the @supabase team would like or if people are still getting stuck here. On Aug 25, 2023 at 11:37 AM -0300, Alex Wilde @.***>, wrote:

But what about the OP – is it possible to run createServerSupabaseClient inside an edge function? I don't see any way how the cookie from RequestCookies could be passed into it. — Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you commented.Message ID: @.***>

notjustinshaw avatar Aug 30 '23 13:08 notjustinshaw

I have verified that it works. You don’t pass the cookie in, you create a new object and pass on an anonymous function that returns that object (see above). I’d be happy to share more details if the @supabase team would like or if people are still getting stuck here. On Aug 25, 2023 at 11:37 AM -0300, Alex Wilde @.>, wrote: But what about the OP – is it possible to run createServerSupabaseClient inside an edge function? I don't see any way how the cookie from RequestCookies could be passed into it. — Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you commented.Message ID: @.>

A complete example would help.

viraj-lead avatar Aug 30 '23 14:08 viraj-lead

The Next.js team has stated that they have fixed this issue in their latest canary version. https://github.com/vercel/next.js/issues/45371#issuecomment-1719071552. I'm leaving this issue open until I get around to testing it out or someone having this issue report it has been fixed upstream. Do note that this isn't an auth-helpers issue, it's a Next.js issue.

silentworks avatar Sep 17 '23 12:09 silentworks

The Next.js team has stated that they have fixed this issue in their latest canary version. vercel/next.js#45371 (comment). I'm leaving this issue open until I get around to testing it out or someone having this issue report it has been fixed upstream. Do note that this isn't an auth-helpers issue, it's a Next.js issue.

There's still a couple of issues open around using cookes in next13 for those who use edge runtime when deployed or windows when developing locally: https://github.com/vercel/next.js/issues/53331 https://github.com/vercel/next.js/issues/52176

DevOfManyThings avatar Sep 17 '23 12:09 DevOfManyThings

The Next.js team has stated that they have fixed this issue in their latest canary version. vercel/next.js#45371 (comment). I'm leaving this issue open until I get around to testing it out or someone having this issue report it has been fixed upstream. Do note that this isn't an auth-helpers issue, it's a Next.js issue.

There's still a couple of issues open around using cookes in next13 for those who use edge runtime when deployed or windows when developing locally: vercel/next.js#53331 vercel/next.js#52176

@DevOfManyThings please read my comment carefully, this is not a auth-helpers issue. The issues you linked to are all old and none have tested the canary release which was mentioned in the issues I linked to only a few days ago. You should be testing this out with Next.js canary release and report back here if its not working.

silentworks avatar Sep 17 '23 13:09 silentworks

@silentworks Hi, the problem with cookies still persists at [email protected]

NastykSwED avatar Sep 19 '23 07:09 NastykSwED

@NastykSwED thanks for reporting this. I'm going to keep this issue open until there is a solution that truly fixes this issue. It might be useful to leave a reply on the Next.js issue to say it's still not working for you with a minimal reproducible example repo too. https://github.com/vercel/next.js/issues/45371#issuecomment-1719071552

silentworks avatar Sep 24 '23 16:09 silentworks