DEV Community

Ramu Narasinga
Ramu Narasinga

Posted on • Edited on

Comparison of the middleware implementation between Supabase Auth documentation and the nextjs-stripe-supabase.

In this article, we are going to compare the middleware implementation between Supabase Auth documentation and the nextjs-stripe-supabase.

  • Supabase Auth Documentation

This is a good starting point for us to understand how the Supabase Auth middleware is configured so we can use that knowledge and draw some conclusions when we compare it with opensource projects such as nextjs-stripe-supabase

But what’s the point of this? well, you will learn few techniques about how middleware for Supabase Auth can be configured.

Image description

Middleware in Supabase Auth Documentation

At step 4 — Hook up middleware in Supabase docs, you will find

code for middleware.ts and utils/supabase/middleware.ts.

middleware.ts

This below code is picked from Supabase documentation.

import { type NextRequest } from 'next/server'
import { updateSession } from '@/utils/supabase/middleware'
export async function middleware(request: NextRequest) {
 return await updateSession(request)
}
export const config = {
 matcher: [
 /*
 * Match all request paths except for the ones starting with:
 * - _next/static (static files)
 * - _next/image (image optimization files)
 * - favicon.ico (favicon file)
 * Feel free to modify this pattern to include more paths.
 */
 '/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)',
 ],
}
Enter fullscreen mode Exit fullscreen mode

middleware.ts has a function named middleware and const named config. These tiny details are important to keep in mind when comparing middleware in other projects in the later section of this article.

utils/supabase/middleware.ts

This below code is picked from Supabase documentation.

import { createServerClient, type CookieOptions } from '@supabase/ssr'
import { cookies } from 'next/headers'

export async function createClient() {
  const cookieStore = await cookies()

  return createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      cookies: {
        getAll() {
          return cookieStore.getAll()
        },
        setAll(cookiesToSet) {
          try {
            cookiesToSet.forEach(({ name, value, options }) =>
              cookieStore.set(name, value, options)
            )
          } catch {
            // The `setAll` method was called from a Server Component.
            // This can be ignored if you have middleware refreshing
            // user sessions.
          }
        },
      },
    }
  )
}
Enter fullscreen mode Exit fullscreen mode

Important steps performed here are:

  1. Create a response object using NextResponse.

  2. Then create a Supabase client using CreateServerClient, response from above step is to set and get cookies.

  3. Make a call to getUser(). This step is really important and make sure not to write any additional logic between createServerClient and this getUser function.

  4. Redirect if the user is not logged in.

  5. Return SupabaseResponse.

Also make sure to read the comments provided to help you understand the code better.

Middleware in Nextjs-Subscription-Payments

Based on the information from the above, found in Supabase Auth documentation, we need to review these below two files:

middleware.ts file

This below code is picked from nextjs-subscriptions-payments.

import { type NextRequest } from 'next/server';
import { updateSession } from '@/utils/supabase/middleware';

export async function middleware(request: NextRequest) {
  return await updateSession(request);
}

export const config = {
  matcher: [
    /*
     * Match all request paths except:
     * - _next/static (static files)
     * - _next/image (image optimization files)
     * - favicon.ico (favicon file)
     * - images - .svg, .png, .jpg, .jpeg, .gif, .webp
     * Feel free to modify this pattern to include more paths.
     */
    '/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)'
  ]
};
Enter fullscreen mode Exit fullscreen mode

Comparing this with what is provided in Supabase auth documentation, there isn’t much difference.

utils/supabase/middleware.ts

This below code is picked from utils/supabase/middleware.ts.

import { createServerClient, type CookieOptions } from '@supabase/ssr';
import { type NextRequest, NextResponse } from 'next/server';

export const createClient = (request: NextRequest) => {
  // Create an unmodified response
  let response = NextResponse.next({
    request: {
      headers: request.headers
    }
  });

  const supabase = createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      cookies: {
        get(name: string) {
          return request.cookies.get(name)?.value;
        },
        set(name: string, value: string, options: CookieOptions) {
          // If the cookie is updated, update the cookies for the request and response
          request.cookies.set({
            name,
            value,
            ...options
          });
          response = NextResponse.next({
            request: {
              headers: request.headers
            }
          });
          response.cookies.set({
            name,
            value,
            ...options
          });
        },
        remove(name: string, options: CookieOptions) {
          // If the cookie is removed, update the cookies for the request and response
          request.cookies.set({
            name,
            value: '',
            ...options
          });
          response = NextResponse.next({
            request: {
              headers: request.headers
            }
          });
          response.cookies.set({
            name,
            value: '',
            ...options
          });
        }
      }
    }
  );

  return { supabase, response };
};

export const updateSession = async (request: NextRequest) => {
  try {
    // This `try/catch` block is only here for the interactive tutorial.
    // Feel free to remove once you have Supabase connected.
    const { supabase, response } = createClient(request);

    // This will refresh session if expired - required for Server Components
    // https://supabase.com/docs/guides/auth/server-side/nextjs
    await supabase.auth.getUser();

    return response;
  } catch (e) {
    // If you are here, a Supabase client could not be created!
    // This is likely because you have not set up environment variables.
    // Check out http://localhost:3000 for Next Steps.
    return NextResponse.next({
      request: {
        headers: request.headers
      }
    });
  }
};
Enter fullscreen mode Exit fullscreen mode

Comparing this with documentation, you will see the below differences:

  1. updateSession function is found in both, the documentation and this example above.

  2. There is a difference in the way client is created. In the Supabase Auth documentation, Supabase client is created with in the updateSession function itself, you do not see another function called. This is the

    case in updateSession. createClient function is called.

  3. createClient looks very similar to the documentation. Except the cookies are handled differently. You will see functions such as set and get in this above example, whereas the Supabase auth documentation

    uses methods such as setAll and getAll. Both of these found to update the necessary cookies on request and response.

  4. The important step here is to call the await supabase.auth.getUser() and the comment just above this line in the above example —



    // This will refresh session if expired — required for Server Components

    // https://supabase.com/docs/guides/auth/server-side/nextjs



    signifies that this line here is really important.

  5. There are redirects found to be happening in this example’s updateSession, but in the Supabase Auth documentation, you will see some redirects happening if the user does not exist. That’s not to say

    nextjs-supscription-payments does not have redirects, it just happens in a different file. For example — signin/[id]/page.tsx

About me:

Hey, my name is Ramu Narasinga. I study large open-source projects and create content about their codebase architecture and best practices, sharing it through articles, videos.

I am open to work on an interesting project. Send me an email at ramu.narasinga@gmail.com

My Github - https://github.com/ramu-narasinga
My website - https://ramunarasinga.com
My Youtube channel - https://www.youtube.com/@thinkthroo
Learning platform - https://thinkthroo.com
Codebase Architecture - https://app.thinkthroo.com/architecture
Best practices - https://app.thinkthroo.com/best-practices
Production-grade projects - https://app.thinkthroo.com/production-grade-projects

References:

  1. https://supabase.com/docs/guides/auth/server-side/nextjs

  2. https://github.com/midday-ai/midday/blob/main/apps/dashboard/src/middleware.ts

  3. https://github.com/vercel/nextjs-subscription-payments/blob/main/middleware.ts

  4. https://github.com/vercel/nextjs-subscription-payments/blob/main/app/signin/%5Bid%5D/page.tsx#L47-L55

Top comments (0)