DEV Community

Hiram
Hiram

Posted on

Next.js middleware for supabase

Mein kumpeln! It is time for another interesting snippet for Next.js v13. Lately I've been tinkering with this framework and Supabase and I have to say the integrations this BaaS offers are amazing. You definitely need to check out this firebase alternative.

Now regarding with the snippet I want to share with you it is related with the admin section I am building inside the portal. This allows you to validate the access before rendering the pages or access the api functions Next.js offers.

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

export async function middleware(req: NextRequest) {
  const adminPath = "/admin";
  const apiAdminPath = "/api/admin";

  const res = NextResponse.next();
  const supabase = createMiddlewareSupabaseClient({ req, res });
  const {
    data: { session },
  } = await supabase.auth.getSession();

  if (!session || session?.user.user_metadata?.role !== "admin") {
    if (req.nextUrl.pathname.startsWith(apiAdminPath)) {
      return new NextResponse(
        JSON.stringify({ message: "authorization failed" }),
        { status: 403, headers: { "Content-Type": "application/json" } }
      );
    } else if (req.nextUrl.pathname.startsWith(adminPath)) {
      const redirectUrl = req.nextUrl.clone();
      redirectUrl.pathname = "/";
      return NextResponse.redirect(redirectUrl);
    }
  }
}

export const config = {
  matcher: ["/api/admin/:path*", "/admin/:path*"],
};

// src/middleware.ts
Enter fullscreen mode Exit fullscreen mode

You probably got an idea of what is happening here, but let's explain it step by step:

The matchers aka the folders (or pages/api): These are the routes on which this middleware is going to be applied. Any other routes under /user or /whatever paths are not going to be filtered. So you have the ability to define validations for every (sub)path you might have.

export const config = {
  matcher: ["/api/admin/:path*", "/admin/:path*"],
};
Enter fullscreen mode Exit fullscreen mode

The validation, in my case I leveraged it from supabase and its helper library to retrieve the active session as shown here:

  const supabase = createMiddlewareSupabaseClient({ req, res });
  const {
    data: { session },
  } = await supabase.auth.getSession();

  if (!session || session?.user.user_metadata?.role !== "admin") {
    // put your validations here  
  }
Enter fullscreen mode Exit fullscreen mode

This is something you can easily swap with your custom auth provider.

The next() handler, known as the next step in the middleware flow, here we are asking the type of route in order to response with a redirect or a json.

    if (req.nextUrl.pathname.startsWith(apiAdminPath)) {
          return new NextResponse(
            JSON.stringify({ message: "authorization failed" }),
            { status: 403, headers: { "Content-Type": "application/json" } }
      );
    } else if (req.nextUrl.pathname.startsWith(adminPath)) {
      const redirectUrl = req.nextUrl.clone();
      redirectUrl.pathname = "/";
      return NextResponse.redirect(redirectUrl);
    }
Enter fullscreen mode Exit fullscreen mode

It is important to highlight that I am responding explicitly to what is causing failed validations. Redirect for browser navigation and JSON for API routes. The next() valid step is implicit when you are not returning it by using the res variable. By adding the return res; statement at the end of the function you will cause the same flow as if not defined.

As usual, you should stick to the official docs, for next.js you have it here: https://nextjs.org/docs/advanced-features/middleware

For supabase and its helper library just follow this link: https://supabase.com/docs/guides/auth/auth-helpers/nextjs

I hope you find it useful and don't hesitate to leave your comments, happy coding pals! 😎

Top comments (1)

Collapse
 
kritik profile image
kritik sah • Edited

`
import { createServerClient, type CookieOptions } from "@supabase/ssr";
import { NextResponse, type NextRequest } from "next/server";
import { URL } from "url";

export async function updateSession(request: NextRequest) {
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) {
request.cookies.set({
name,
value,
...options,
});
response = NextResponse.next({
request: {
headers: request.headers,
},
});
response.cookies.set({
name,
value,
...options,
});
},
remove(name: string, options: CookieOptions) {
request.cookies.set({
name,
value: "",
...options,
});
response = NextResponse.next({
request: {
headers: request.headers,
},
});
response.cookies.set({
name,
value: "",
...options,
});
},
},
}
);

const {
data: { user },
} = await supabase.auth.getUser();

if (user) {
console.log(user);
return response;
}
return NextResponse.rewrite(new URL("/signin", request.url));
}
`

what about this using ssr but its not working, need help!