Search code examples
reactjstypescriptnext.jssupabasenext.js13

Issue with Next.js 13, Supabase, and Server Actions in layout.tsx


I'm working on a Next.js 13 application and trying to manage user authentication with Supabase. I'm running into problems when I attempt to check if a user is logged in using an asynchronous function inside my layout component. Specifically, I'm using server actions with the getSessionStatus function to see if a user is logged in.

Here's the code snippet for my RootLayout component:

import Link from 'next/link';
import './globals.css';
import { createServerComponentClient } from "@supabase/auth-helpers-nextjs";
import { Database } from "@/types/supabase";
import { cookies } from "next/headers";

export const metadata = {
  title: 'Create Next App',
  description: 'Generated by create next app',
};

export default function RootLayout({ children }: { children: React.ReactNode }) {
  async function getSessionStatus() {
    'use server';
    const supabase = createServerComponentClient<Database>({ cookies });
    const {
      data: { session },
    } = await supabase.auth.getSession();

    return Boolean(session);
  }

  const isLoggedIn = getSessionStatus();

  return (
    <html lang="en">
      <head>
        {/* Add any necessary head tags here */}
      </head>
      <body>
        <nav className="flex items-center justify-between flex-wrap bg-teal-500 p-6">
          <div className="flex items-center flex-shrink-0 text-white mr-6">
            {/* ... logo code ... */}
          </div>
          <div className="block lg:hidden">
            {/* ... button code ... */}
          </div>
          <div className="w-full block flex-grow lg:flex lg:items-center lg:w-auto">
            <div className="text-sm lg:flex-grow">
              <Link href="/ingresos" passHref>
                <span className="block mt-4 lg:inline-block lg:mt-0 text-teal-200 hover:text-white">
                  Ingresos
                </span>
              </Link>
            </div>
            <div>
              {isLoggedIn ? (
                <button className="inline-block text-sm px-4 py-2 leading-none border rounded text-white border-white hover:border-transparent hover:text-teal-500 hover:bg-white lg:mt-0">Log Out</button>
              ) : (
                <>
                  <button className="inline-block text-sm px-4 py-2 leading-none border rounded text-white border-white hover:border-transparent hover:text-teal-500 hover:bg-white lg:mt-0 mr-2">Log In</button>
                  <button className="inline-block text-sm px-4 py-2 leading-none border rounded text-white border-white hover:border-transparent hover:text-teal-500 hover:bg-white lg:mt-0">Sign Up</button>
                </>
              )}
            </div>
          </div>
        </nav>
        <main className="min-h-screen bg-background flex flex-col items-center">
          {children}
        </main>
      </body>
    </html>
  );
}

In this code, I'm using the getSessionStatus function, defined as a server action, to see if a user is logged in and then displaying different buttons based on that status.

I'm facing this issue:

  1. TypeScript Error: I receive a TypeScript error stating This condition will always return true since this 'Promise<boolean>' is always defined.ts(2801). How should I correctly handle this Promise?

I've been reading the documentation but still can't figure out how to implement this correctly in my layout component with the use of server actions. Any guidance would be greatly appreciated!


Solution

  • The issue with your code is that getSessionStatus() is returning a Promise, which is always true.

    Which is the TypeScript error:

    This condition will always return true since this 'Promise<boolean>' is always defined.ts(2801)
    

    To fix that, we shall use await for response of getSessionStatus().

    To handle await in your layout, we need to make the RootLayout function as async.

    Here is the fixed code:

    export default async function RootLayout({ children }: { children: React.ReactNode }) {
      async function getSessionStatus() {
        'use server';
        const supabase = createServerComponentClient<Database>({ cookies });
        const {
          data: { session },
        } = await supabase.auth.getSession();
    
        return Boolean(session);
      }
    
      const isLoggedIn = await getSessionStatus();
    
      // return html code
    }