Next.js Discord

Discord Forum

data fetching on server side

Unanswered
Brown bear posted this in #help-forum
Open in Discord
Brown bearOP
hello guys - how can I retrieve a firebase token or firebase user state in a layout.tsx from the client in order for me to fetch data on the serverside?
Ex:
export default async function DashboardLayout({
  children
}: {
  children: React.ReactNode
}) {
  //context will not work as context is client side
  const { user } = useAuth();
  if (user === null) {
    return <div>loading...</div>
  }
  const token = await user.getIdToken();
  const data = await fetch('/api/get-admin-organization', {
    headers: { 
      'Authorization': `Bearer ${token}`
    }
  });
  
  console.log(data);

36 Replies

Brown bearOP
export default async function DashboardLayout({
  children
}: {
  children: React.ReactNode
}) {
  //context will not work as context is client side
  const { user } = useAuth();
  if (user === null) {
    return <div>loading...</div>
  }
  const token = await user.getIdToken();
  const data = await fetch('/api/get-admin-organization', {
    headers: { 
      'Authorization': `Bearer ${token}`
    }
  });
  
  console.log(data);

  return (
    <section>
    <DataProvider>
    <div className="page-background flex flex-col">
      <Topbar />
      <Sidebar />
      <div className="flex-grow ml-64 mt-12 mr-4">
        {children}
      </div>
    </div>
    </DataProvider>
    </section>
  )
}

The rootlayout:
'use client'

import '@styles/index.css'
import '@styles/globals.css'
import type { Metadata } from 'next'
import { useRouter } from 'next/navigation';
import { AuthProvider } from '@contexts/AuthProvider';
import { DataProvider } from '@contexts/DataProvider';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
import {
  QueryClient,
  QueryClientProvider,
} from '@tanstack/react-query'
import { Toaster } from 'react-hot-toast';

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

const queryClient = new QueryClient()

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {

  return (
    <html lang="en">
      <body>
        <QueryClientProvider client={queryClient}>
          <AuthProvider>
              {children}
            <ReactQueryDevtools initialIsOpen={false} />
            <Toaster
              position="bottom-right"
              reverseOrder={false}
            />
          </AuthProvider>
        </QueryClientProvider>
      </body>
    </html>
  )
}
Masai Lion
your gone do like dataProvider or AuthProvider
and put put what client side need
try and tell me if it work
@Masai Lion your gone do like dataProvider or AuthProvider
Brown bearOP
wdym by that?
Masai Lion
more or less the same look
your gone take the same form and put this
const { user } = useAuth();
if (user === null) {
return <div>loading...</div>
}
after in your layout its gone look like that
importe providers
and put children between
it can not work and i possibly did not understand what you want but you can try @Brown bear
@Brown bear export default async function DashboardLayout({ children }: { children: React.ReactNode }) { //context will not work as context is client side const { user } = useAuth(); if (user === null) { return <div>loading...</div> } const token = await user.getIdToken(); const data = await fetch('/api/get-admin-organization', { headers: { 'Authorization': `Bearer ${token}` } }); console.log(data); return ( <section> <DataProvider> <div className="page-background flex flex-col"> <Topbar /> <Sidebar /> <div className="flex-grow ml-64 mt-12 mr-4"> {children} </div> </div> </DataProvider> </section> ) } The rootlayout: 'use client' import '@styles/index.css' import '@styles/globals.css' import type { Metadata } from 'next' import { useRouter } from 'next/navigation'; import { AuthProvider } from '@contexts/AuthProvider'; import { DataProvider } from '@contexts/DataProvider'; import { ReactQueryDevtools } from '@tanstack/react-query-devtools' import { QueryClient, QueryClientProvider, } from '@tanstack/react-query' import { Toaster } from 'react-hot-toast'; export const metadata: Metadata = { title: 'Create Next App', description: 'Generated by create next app', } const queryClient = new QueryClient() export default function RootLayout({ children, }: { children: React.ReactNode }) { return ( <html lang="en"> <body> <QueryClientProvider client={queryClient}> <AuthProvider> {children} <ReactQueryDevtools initialIsOpen={false} /> <Toaster position="bottom-right" reverseOrder={false} /> </AuthProvider> </QueryClientProvider> </body> </html> ) }
root layout must be a server component. remove use client.
@DirtyCajunRice | AppDir root layout must be a server component. remove use client.
Brown bearOP
still does not work. How does auth work in server components? How can you retreive the session state or auth state if everything being fetched on server requires the client's auth state?
@DirtyCajunRice | AppDir i didnt say that would fix it. that addresses one of the many issues
Brown bearOP
yes I understand. But Is there a way to do auth though for server componetns with the new app router? On pages it was made more sense as everything is set to client
@Brown bear yes I understand. But Is there a way to do auth though for server componetns with the new app router? On pages it was made more sense as everything is set to client
it makes the same amount of sense now as it did before. its just no longer a sledgehammer approach to everything. There are examples in the next-auth docs.
@DirtyCajunRice | AppDir it makes the same amount of sense now as it did before. its just no longer a sledgehammer approach to everything. There are examples in the next-auth docs.
Brown bearOP
The thing is with firebase auth tokens, tokens only stay valid for an hour and need to be refreshed periodically. I looked at the docs here and found this where the token is set in cookies and refreshed using useEffect every 10 minutes .https://colinhacks.com/essays/nextjs-firebase-authentication

If my app is wrapped around a context provider but doesnt use the values inside the context, will the useEffect still be triggered in the layout if it is async?

EX:
import nookies from 'nookies';

const AuthContext = createContext<{ user: firebase.User | null }>({
  user: null,
});

export function AuthProvider({ children }: any) {
  const [user, setUser] = useState<firebase.User | null>(null);

  // listen for token changes
  // call setUser and write new token as a cookie
  useEffect(() => {
    return firebase.auth().onIdTokenChanged(async (user) => {
      if (!user) {
        setUser(null);
        nookies.set(undefined, 'token', '', { path: '/' });
      } else {
        const token = await user.getIdToken();
        setUser(user);
        nookies.set(undefined, 'token', token, { path: '/' });
      }
    });
  }, []);

  // force refresh the token every 10 minutes
  useEffect(() => {
    const handle = setInterval(async () => {
      const user = firebaseClient.auth().currentUser;
      if (user) await user.getIdToken(true);
    }, 10 * 60 * 1000);

    // clean up setInterval
    return () => clearInterval(handle);
  }, []);

  return (
    <AuthContext.Provider value={{ user }}>{children}</AuthContext.Provider>
  );
}

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {

  return (
    <html lang="en">
      <body>
          <AuthProvider>
              {children}
            <Toaster
              position="bottom-right"
              reverseOrder={false}
            />
          </AuthProvider>
      </body>
    </html>
  )
}
Brown bearOP
@DirtyCajunRice | AppDir
export const getServerSideProps = async (ctx: GetServerSidePropsContext) => {
  try {
    const cookies = nookies.get(ctx);
    console.log(JSON.stringify(cookies, null, 2));
    const token = await firebaseAdmin.auth().verifyIdToken(cookies.token);
    const { uid, email } = token;

    // the user is authenticated!
    // FETCH STUFF HERE

    return {
      props: { message: `Your email is ${email} and your UID is ${uid}.` },
    };
  } catch (err) {
    // either the `token` cookie didn't exist
    // or token verification failed
    // either way: redirect to the login page
    // either the `token` cookie didn't exist
    // or token verification failed
    // either way: redirect to the login page
    return {
      redirect: {
        permanent: false,
        destination: "/login",
      },
      // `as never` is required for correct type inference
      // by InferGetServerSidePropsType below
      props: {} as never,
    };
  }
};


Found this and will need to use this. How can I retrieve cookies on server side? How should ctx: GetServerSidePropsContext work now with the new changes to data fetching?
thats for pages
and your answer is in the docs
you really really need to read the docs. almost all of these are answered there
@DirtyCajunRice | AppDir you really really need to read the docs. almost all of these are answered there
Brown bearOP
https://nextjs.org/docs/app/building-your-application/data-fetching#fetching-data-on-the-server

Read this and it doesnt exactly say how to get the cookies data. And it just says that the getServerSideProps is not available in app router. Is there something Im missing or am I looking in the wrong place?
theres a search bar. type in “cookies”
@DirtyCajunRice | AppDir theres a search bar. type in “cookies”
Brown bearOP
ok found it, thanks. For my context question though, will it still be run since the root layout is wrapped with its provider? The context runs every 10 minutes and I need to know whether it runs before the server components can start retrieving the cookies. docs dont exactly specify.
@DirtyCajunRice | AppDir its a use effect. so no. that is client side stuff. lots happens before client side
Brown bearOP
ah damn. my backend is already based off firebase auth... Is it really impossible to use firebase authorization tokens with server components? There's gotta be a way, right?
with… the firebase adapter… one of the billion adapters they have
@DirtyCajunRice | AppDir with… the firebase adapter… one of the billion adapters they have
Brown bearOP
I see. However, it seems that adapters only adapt the databases though. I dont use firestore. In fact I use mongodb.

But I need to adapt firebase auth token conversions to firebase uids, since admins which are stored in documents use firebase uid. Reason I cant just swap to next/auth is because my kotlin app uses firebase and that is not compatible with next/auth. Firebase has great support for kotlin mobile apps.

I did do searching and found an adapter for firestore, not firebase auth.

Maybe there is a firebase auth adapter I'm not seeing?
Thanks a lot for your help btw, I really do appreciate it.