Next.js middleware for supabase
Table of contents
No headings in the article.
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
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*"],
};
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
}
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);
}
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! ๐