Reputation: 338
I'm working on a Next.js/Django project, which the user is able to add some redirect logic from the admin panel like:
[
{ source: "/about", destination: "google.com" },
{ source: "/about1", destination: "google1.com" },
{ source: "/about2", destination: "google2.com" },
]
and the web application should be able to handle these dynamic redirects.
As the Nextjs docs says, we can do this in next.config.js
. The problem is that we can't have dynamic data in next.config.js
. With every change in this file, server must be restarted.
Here we need a logic that gets the urls using an API on website load, loops through them and listens for every route calls to see if they match the redirect data or not.
I have tried some other ways too, like trying to use useEffect, but this way causes the website to render 404 page first and then it redirects to the desired url, which is not that nice for user experience viewpoints.
Upvotes: 7
Views: 7689
Reputation: 5142
I modified Julio's solution to address Kyoss's concern and cache the redirects
object whilst also allowing for an on-demand refresh (via a webhook).
const redirectSecret = process.env.REDIRECT_SECRET;
const redirectPath = process.env.REDIRECT_PATH;
var redirects: any;
const getRedirects = async () => {
/*
Example of redirects object:
{
'/about-us': {'destination': '/about', 'isPermanent': true},
'/google': {'destination': 'https://google.com', 'isPermanent': false}
}
NB.: dictionary for constant lookup time ;-)
*/
return await (await fetch("http://localhost:3000/api/redirects")).json()
}
export default async function middleware(req: NextRequest) {
const url = req.nextUrl
if (!redirects) {
redirects = await getRedirects()
}
else if (req.method == "POST" && url.pathname == redirectPath) {
if (req.headers.get("x-secret") != redirectSecret) {
return NextResponse.json({
message: "Unauthorized"
}, {
status: 401
})
}
redirects = await getRedirects()
return NextResponse.json({
message: "Accepted"
}, {
status: 202
})
}
console.log(url.pathname)
const redirect = redirects[url.pathname]
if (redirect) {
if (redirect.destination.startsWith('http')) {
return NextResponse.redirect(new URL(redirect.destination));
}
return NextResponse.redirect(new URL(redirect.destination, req.url));
}
/// and continue...
}
This initially getRedirects
and cache the object in memory.
To update the cache, all is needed is a POST
to REDIRECT_PATH
with the correct REDIRECT_SECRET
.
Obviously, you would need to use Redis or similar if you were to deploy to a cluster...
Upvotes: 1
Reputation: 50448
You can use Next.js Middleware to fetch the dynamic redirects
array from the API, and add your own logic to handle the redirection.
Unlike redirects
in the next.config.js
that run at build time, Next.js Middleware runs on each incoming request to your pages and is able to dynamically fetch the redirects every time.
export async function middleware(req) {
// Fetch redirects array from API
const res = await fetch('https://example.com/api/redirects');
const redirects = await res.json();
/* Assuming the returned `redirects` array would have the following structure:
[
{ source: '/about-us', destination: '/about' },
{ source: '/google', destination: 'https://google.com' }
]
*/
// Find the matching redirect object
const redirect = redirects.find((redirect) => req.nextUrl.pathname === redirect.source);
if (redirect) {
if (redirect.destination.startsWith('http')) {
// Handle external URL redirects
return NextResponse.redirect(new URL(redirect.destination));
}
// Handle internal URL redirects
return NextResponse.redirect(new URL(redirect.destination, req.url));
}
// Continue if no matching redirect is found
return NextResponse.next();
}
Upvotes: 5