Search code examples
reactjsnext.jsnext.js13app-router

Generic redirect to login after serverside request in Next.js app router


I want to do a server side request and fetch user data, which is authenticated via cookie. I forward the cookies via the cookies function (not shown in example).

When the authentification fails and I get a 401 back, I want to do a redirect to /login?redirect=[page where the request failed]

My /app/profile/page.tsx:

import { RedirectType, redirect } from "next/navigation";

const MyProfile = async () => {
  const profile = await fetch("https://example.com/api/user/profile"); // authentificated via cookie

  if (profile.status === 401) {
    // "/profile" is hardcoded here, but it should be the current path
    redirect("/login?redirect=/profile", RedirectType.replace);
  }

  if (!profile) return <div>Loading...</div>;

  const profileData = await profile.json();

  return <div>My name is {profileData.name}</div>;
};

export default MyProfile;

I need to do authenticated requests on nearly every page - is there a way not to hardcode the redirect param on the server side? Do I have another conceptual error?

Thanks!


Solution

  • You can return a client component in your if statement that does the redirect automatically for you by uisng the usePathname function, without you needing to hardcode the redirect param every time.

    You can call the client component RedirectComponent.tsx, and this is how it is going to probably look like:

    'use client'
     
    import { usePathname, redirect, RedirectType } from 'next/navigation'
     
    export function RedirectComponent() {
      const pathname = usePathname()
      redirect("/login?redirect=" + pathname, RedirectType.replace);
    
      return <></>
    }
    

    This client component does not return any jsx, or you can modify it to return any jsx you want, but as soon as you call or return this client component in your server component, the usePathname will read the current URL's pathname and append it to the url you want to redirect to, in this case login?redirect + the current url pathname.

    And in your code you can do something like this to call it:

    import { RedirectType, redirect } from "next/navigation";
    import {RedirectComponent} from "./RedirectComponent" 
    
    const MyProfile = async () => {
      const profile = await fetch("https://example.com/api/user/profile"); // authentificated via cookie
    
      if (profile.status === 401) {
        // return the RedirectComponent here to automatically redirect you to the desired page
        return <RedirectComponent />
      }
    
      if (!profile) return <div>Loading...</div>;
    
      const profileData = await profile.json();
    
      return <div>My name is {profileData.name}</div>;
    };
    
    export default MyProfile;
    

    Why must it be in a client component? And if I use a client side component will it degrade my application?

    Here is an answer from the nextjs docs:

    usePathname intentionally requires using a Client Component. It's important to note Client Components are not a de-optimization. They are an integral part of the Server Components architecture.