How to make global middleware and regular middleware in NextJS 14?
Unanswered
Bigheaded ant posted this in #help-forum

Bigheaded antOP
Hello everyone, can anyone help me out how we can make the global and regular middleware in NextJS. So I have a case like there are two middlewares one is withHeadersMiddleware (global) and two is authMiddleware (only in specific pages applied). How can I set each of this middleware?
48 Replies

Bigheaded antOP
This is my code
import { NextRequest, NextResponse } from 'next/server';
import { getCookies } from './services/cookies';
import { toast } from './hooks/use-toast';
export async function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
// Fetch user data and token from cookies
const userCookie = await getCookies("__u__");
const tokenCookie = await getCookies("__tk__");
// Create new headers to pass additional data
const requestHeaders = new Headers(request.headers);
// Append URL and pathname to headers
requestHeaders.set('x-url', request.url);
requestHeaders.set('x-pathname', pathname);
console.log(request.url, pathname)
// Redirect to login if accessing dashboard without authentication
if (pathname.startsWith("/dashboard") && (!userCookie || !tokenCookie)) {
toast({
title: "Akses ditolak",
description: "Harap login terlebih dahulu",
variant: "destructive",
});
return NextResponse.redirect(new URL('/auth/login', request.url));
}
// Redirect to dashboard if already logged in and trying to access login page
if ((
pathname.startsWith("/auth/login") ||
pathname.startsWith("/auth/register")
) && userCookie && tokenCookie) {
return NextResponse.redirect(new URL('/dashboard', request.url));
}
// Continue request with updated headers
return NextResponse.next({
request: {
headers: requestHeaders,
},
});
}
export const config = {
matcher: [
"/dashboard",
"/dashboard/profile",
"/dashboard/vitae/create",
"/dashboard/vitae/:id/edit",
"/auth/login",
"/auth/register"
]
}
The case seems the same from this thread. But I'm not using next-auth for the authentication.
https://stackoverflow.com/questions/77977609/next-middleware-combination-next-auth-and-next-international
https://stackoverflow.com/questions/77977609/next-middleware-combination-next-auth-and-next-international

@Bigheaded ant Hello everyone, can anyone help me out how we can make the global and regular middleware in NextJS. So I have a case like there are two middlewares one is withHeadersMiddleware (global) and two is authMiddleware (only in specific pages applied). How can I set each of this middleware?

nextjs provides only one middleware right now. You can still split your middleware in multiple functions and call those different functions like
withHeadersMIdleware
and authMiddleware
. So just call the functions inside this one middlelware and you are good to go 👍
@B33fb0n3 nextjs provides only one middleware right now. You can still split your middleware in multiple functions and call those different functions like withHeadersMIdleware and authMiddleware. So just call the functions inside this one middlelware and you are good to go 👍

Bigheaded antOP
So to set the pages that need authentication I should hardcore every pages that I have to include it in the paths to activate the auth middleware?
something like this.
const authPages = [
"dashboard": {
title: {
isDynamic: false,
static: "Dashboard",
withSitename: true
},
description: {
isDynamic: false,
static: "It's dashboard page.",
withSitename: true,
},
isAuthMiddleware: true,
},
...another pages
]

@Bigheaded ant something like this.
const authPages = [
"dashboard": {
title: {
isDynamic: false,
static: "Dashboard",
withSitename: true
},
description: {
isDynamic: false,
static: "It's dashboard page.",
withSitename: true,
},
isAuthMiddleware: true,
},
...another pages
]

yea.. I wouldn't do it like you did, as it's waaay to much effort for me, but you can do it like that. I would do it like
const protectedRoutes = ["/abc", "/def", "/admin"]

@B33fb0n3 yea.. I wouldn't do it like you did, as it's waaay to much effort for me, but you can do it like that. I would do it like
tsx
const protectedRoutes = ["/abc", "/def", "/admin"]

Bigheaded antOP
I did that to support seo, so all-in-one, started from the title page, meta data, middleware, etc. Then what I should do, just call one file from that.
I named this file as
seo.ts
I set the seo in the layout only and not from every pages.

@Bigheaded ant you can either set it on the page itself (using the metadata object) or if you want to have the same metadata everywhere, you can use your layout

@B33fb0n3 <@1079709612078534676> you can either set it on the page itself (using the metadata object) or if you want to have the same metadata everywhere, you can use your layout

Bigheaded antOP
Yeah that's what I am doing now. I did something like this in my layout app.tsx to make the title and metadata becomes dynamic in every pages.
// app.tsx
import { Inter } from "next/font/google";
import "./globals.css";
import { Toaster } from "@/components/ui/toaster";
import { headers } from "next/headers";
import { generatePageMetadata } from "@/lib/get-page-metadata";
import { SessionProvider } from "@/app/providers/session-provider"; // Import dari file yang baru dibuat
const inter = Inter({
subsets: ["latin"],
display: "swap",
});
// Dynamic metadata handler
export async function generateMetadata({
params,
}: {
params: Promise<{ slug: string }>;
}) {
// Ambil URL path dari headers API
const currentPathname = headers().get("x-pathname") || "/"; // Default "/" jika kosong
const slug = currentPathname === "/" ? "index" : currentPathname.slice(1); // Ambil slug dari path
// Generate metadata berdasarkan slug
const metadata = await generatePageMetadata({ slug });
return metadata; // Return hasil metadata
}
export default async function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<head>
<script defer src="/plugins/observer-tailwindcss-intersect.min.js" />
</head>
<body className={`${inter.className} antialiased`}>
{/* Bungkus children dengan SessionProvider */}
<SessionProvider>
{children}
</SessionProvider>
<Toaster />
</body>
</html>
);
}

@Bigheaded ant Yeah that's what I am doing now. I did something like this in my layout app.tsx to make the title and metadata becomes dynamic in every pages.
// app.tsx
import { Inter } from "next/font/google";
import "./globals.css";
import { Toaster } from "@/components/ui/toaster";
import { headers } from "next/headers";
import { generatePageMetadata } from "@/lib/get-page-metadata";
import { SessionProvider } from "@/app/providers/session-provider"; // Import dari file yang baru dibuat
const inter = Inter({
subsets: ["latin"],
display: "swap",
});
// Dynamic metadata handler
export async function generateMetadata({
params,
}: {
params: Promise<{ slug: string }>;
}) {
// Ambil URL path dari headers API
const currentPathname = headers().get("x-pathname") || "/"; // Default "/" jika kosong
const slug = currentPathname === "/" ? "index" : currentPathname.slice(1); // Ambil slug dari path
// Generate metadata berdasarkan slug
const metadata = await generatePageMetadata({ slug });
return metadata; // Return hasil metadata
}
export default async function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<head>
<script defer src="/plugins/observer-tailwindcss-intersect.min.js" />
</head>
<body className={`${inter.className} antialiased`}>
{/* Bungkus children dengan SessionProvider */}
<SessionProvider>
{children}
</SessionProvider>
<Toaster />
</body>
</html>
);
}

eww.. why do you need the pathname? Normally your layout is inside a specific path and then you already know the path automatically. Also, how does your
generatePageMetadata
function look like?
@B33fb0n3 eww.. why do you need the pathname? Normally your layout is inside a specific path and then you already know the path automatically. Also, how does your `generatePageMetadata` function look like?

Bigheaded antOP
It to generates the page metadata like title, description, etc.
// @ts-nocheck
import { TITLE_PAGES } from "./constants/seo";
export async function generatePageMetadata({
slug,
}: {
slug: string;
}): Promise<any> {
const siteName = process?.env.APP_NAME ?? "My App"; // Replace with your site's name
// Get page-specific metadata
const pageSEO = TITLE_PAGES[slug];
if (!pageSEO) {
return {
title: "Page Not Found",
description: "The requested page does not exist.",
};
}
// Title generation
const title = pageSEO?.title?.withSitename
? `${pageSEO?.title?.static} | ${siteName}`
: pageSEO?.title?.static;
// Description handling
const description = pageSEO?.description?.isDynamic
? "Dynamic description here" // Dynamically fetch or process if needed
: pageSEO?.description?.static || "";
// Icon handling
const icon = pageSEO?.icons?.icon
? pageSEO?.icons?.icon : "";
return {
title,
description,
openGraph: {
title: pageSEO?.title?.withSitename
? `${pageSEO?.title?.static} | ${siteName}`
: pageSEO?.title?.static,
description: pageSEO?.og?.description?.static || "",
},
twitter: {
title: pageSEO?.twitter?.title?.withSitename
? `${pageSEO?.twitter?.title?.static} | ${siteName}`
: pageSEO?.twitter?.title?.static,
description: pageSEO?.twitter?.description?.static || "",
},
icon
};
}

@B33fb0n3 oh wow.. while reading that much code I forgot wheres the actual problem right now. Can you clarify your issue?

Bigheaded antOP
Initially, it was the auth middleware that I wanted to separate it because I wanted this auth middleware to only be implemented on certain pages. Then there is the global middleware, which is middleware for inserting URLs/pathnames into request headers, which later uses the URL/pathname to detect whether the URL on the current page is the same as the URL as in SEO or not, if yes then all the metadata or title on the page will use one of these SEO.
So for example, I visited page /auth/login then in the current page will use the seo that related to that path.
and for checking the auth middleware I set a data called
isAuthMiddleware
to check whether this page is protected or not.
ok got it. Why don't you create an auth middleware and "delete" the other middlware and set your page metadata like everyone else does inside the page.tsx via the metadata object?

@B33fb0n3 ok got it. Why don't you create an auth middleware and "delete" the other middlware and set your page metadata like everyone else does inside the page.tsx via the metadata object?

Bigheaded antOP
What do u mean by "delete" the other middleware sir?
So I don't should set the middleware in my SEO?

@B33fb0n3 ok got it. Why don't you create an auth middleware and "delete" the other middlware and set your page metadata like everyone else does inside the page.tsx via the metadata object?

Bigheaded antOP
Wouldn't it override the title and the title will be blank or empty?

@Bigheaded ant So I don't should set the middleware in my SEO?

yea, dont set SEO through middleware. Set it inside your page.tsx (then you already have the pathname as well and it will be invisible, when he is unauthenticated)

@B33fb0n3 yea, dont set SEO through middleware. Set it inside your page.tsx (then you already have the pathname as well and it will be invisible, when he is unauthenticated)

Bigheaded antOP
U mean, it looks like this if I implemented it in one of pages.
import Banner from "@/components/banner";
import ShieldUserIcon from "@/components/icon/shield-user-icon";
import CreateFormJobs from "./_components/form";
export const metadata = {
isAuthMiddleware: true
}
export default function Page(){
return (
<div className="container my-5 flex flex-col gap-5">
</div>
)
}

@Bigheaded ant U mean, it looks like this if I implemented it in one of pages.
import Banner from "@/components/banner";
import ShieldUserIcon from "@/components/icon/shield-user-icon";
import CreateFormJobs from "./_components/form";
export const metadata = {
isAuthMiddleware: true
}
export default function Page(){
return (
<div className="container my-5 flex flex-col gap-5">
</div>
)
}

no.
1. Create a middleware with matcher configured to all protected routes. Inside this middleware you check the auth
2. In each page.tsx you create a metadata object for your title, description, ...:
1. Create a middleware with matcher configured to all protected routes. Inside this middleware you check the auth
2. In each page.tsx you create a metadata object for your title, description, ...:
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: '...',
description: '...',
}
export default function Page() {}

@B33fb0n3 no.
1. Create a middleware with matcher configured to all protected routes. Inside this middleware you check the auth
2. In each page.tsx you create a metadata object for your title, description, ...:
tsx
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: '...',
description: '...',
}
export default function Page() {}

Bigheaded antOP
I've updated my
middleware.ts
like this
@B33fb0n3 no.
1. Create a middleware with matcher configured to all protected routes. Inside this middleware you check the auth
2. In each page.tsx you create a metadata object for your title, description, ...:
tsx
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: '...',
description: '...',
}
export default function Page() {}

Bigheaded antOP
But still the data of isAuthMiddleware need to put it in the SEO file, right? to check whether this path or route is need a authentication or not.

@Bigheaded ant But still the data of isAuthMiddleware need to put it in the SEO file, right? to check whether this path or route is need a authentication or not.

the authMiddleware should check which pages are protected.
1. Do that via the matcher (see attached).
2. Remove the
3. Go into your protected page.tsx file and add metadata.
4. See that it works 🙂
1. Do that via the matcher (see attached).
2. Remove the
withHeadersMiddleware
middleware as you dont need it.3. Go into your protected page.tsx file and add metadata.
4. See that it works 🙂


@B33fb0n3 the authMiddleware should check which pages are protected.
1. Do that via the matcher (see attached).
2. Remove the `withHeadersMiddleware` middleware as you dont need it.
3. Go into your protected page.tsx file and add metadata.
4. See that it works 🙂

Bigheaded antOP
But I still need to pass the url and pathname in the headers to make the title based on seo file.
Why I should delete
withHeadersMiddleware
?I want the title,metadata, etc manage based in one file only. So I don't need to search the specific page if I want to change the title or metadata as you know it's confusing while search the specific page in Next because their name must be 'page'.
That's why I make the generatePageMetadata become dynamic in the layout.

@Bigheaded ant you are right, you would then need to create multiple metadata objects in each of your page.tsx and you can still reference to your seo file like:
When using the
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: seoFile.adminPage.title,
description: '...',
}
export default function Page() {}
When using the
headers()
dynamic function, it will turn your page into a dynamic page that would make your app slower when it can be fast
@B33fb0n3 the authMiddleware should check which pages are protected.
1. Do that via the matcher (see attached).
2. Remove the `withHeadersMiddleware` middleware as you dont need it.
3. Go into your protected page.tsx file and add metadata.
4. See that it works 🙂

Bigheaded antOP
But how about the page that needs authentication, do I still need to put the data of 'isAuthMiddleware' in the SEO file or just use the matcher?
But I still don't know how to set the path that needs an authentication in matcher.
Because there's a page that for the guest too. And I'm afraid when I'm using the matcher, this page for the guest will be impacted by the auth middleware.

@Bigheaded ant But how about the page that needs authentication, do I still need to put the data of 'isAuthMiddleware' in the SEO file or just use the matcher?

the SEO part is fully independend from the auth part. So for the SEO part you use your metadata object (maybe reference to your file)
And for your auth part, you use your middleware (or also inside the page)
And for your auth part, you use your middleware (or also inside the page)

@B33fb0n3 the SEO part is fully independend from the auth part. So for the SEO part you use your metadata object (maybe reference to your file)
And for your auth part, you use your middleware (or also inside the page)

Bigheaded antOP
what do you mean middleware inside the page?

@Bigheaded ant what do you mean middleware inside the page?

you can check the auth also inside each page

Bigheaded antOP
But how about the method that I'm using now? is it also a good practice?
or there's still some drawback.

@B33fb0n3 you can check the auth also inside each page

Bigheaded antOP
If it's fine, I will still use the way as now.

@Bigheaded ant But how about the method that I'm using now? is it also a good practice?

I don't think it's a good pratice, as it seems to be unflexable (as you see now) and makes your page slower (as it uses a dynamic function). Of course you can still continue your path, but earlier or later you will run into the next issue. I would change it, like I mentioned

@B33fb0n3 I don't think it's a good pratice, as it seems to be unflexable (as you see now) and makes your page slower (as it uses a dynamic function). Of course you can still continue your path, but earlier or later you will run into the next issue. I would change it, like I mentioned

Bigheaded antOP
Sir I want to ask again, how about my new middleware for protecting the auth middleware for the pages and api routes.
I need your advice.

@Bigheaded ant Sir I want to ask again, how about my new middleware for protecting the auth middleware for the pages and api routes.

boddy, you already got my advice about that. I would use a middleware that checks only the auth (or when guests are allowed I would check it on page) and settings all the SEO related metadata inside the page itself