Search code examples
next.jsswr

NextJS - useSWR with token from session


I'm working with NextJS, Next-auth and Django as backend. I'm using the credentials provider to authenticate users. Users are authenticated against the Django backend and the user info together with the accesstoken is stored in the session.
I'm trying to use useSWR now to fetch data from the backend. (no preloading for this page required, that's why I'm working with SWR) I need to send the access
_token from the session in the fetcher method from useSWR. However I don't know how to use useSWR after the session is authenticated. Maybe I need another approach here.

I tried to wait for the session to be authenticated and then afterwards send the request with useSWR, but I get this error: **Error: Rendered more hooks than during the previous render.
**
Could anybody help with a better approach to handle this? What I basically need is to make sure an accesstoken, which I received from a custom backend is included in every request in the Authorization Header. I tried to find something in the documentation of NextJS, Next-Auth or SWR, but I only found ways to store a custom access_token in the session, but not how to include it in the Header of following backend requests.

This is the code of the component:

import { useSession } from "next-auth/react";
import useSWR from 'swr';
import axios from 'axios'

export default function Profile() {

    const { data: session, status } = useSession();

    // if session is authenticated then fetch data
    if (status == "authenticated") {
        // create config with access_token for fetcher method
        const config = {
            headers: { Authorization: `Bearer ${session.access_token}` }
        };

        const url = "http://mybackend.com/user/"
        const fetcher = url => axios.get(url, config).then(res => res.data)
        const { data, error } = useSWR(url, fetcher)
    }

    if (status == "loading") {
        return (
            <>
                <span>Loading...</span>
            </>
        )
    } else {
        return (
            <>
                {data.email}
            </>
        )
    }
}

Solution

  • you don't need to check status every time. what you need to do is to add this function to your app.js file

    function Auth({ children }) {
      const router = useRouter();
      const { status } = useSession({
        required: true,
        onUnauthenticated() {
          router.push("/sign-in");
        },
      });
    
      if (status === "loading") {
        return (
          <div> Loading... </div>
        );
      }
      return children;
    }
    

    then add auth proprety to every page that requires a session

    Page.auth = {};
    

    finally update your const App like this

    <SessionProvider session={pageProps.session}>
       <Layout>
          {Component.auth ? (
            <Auth>
              <Component {...pageProps} />
            </Auth>
          ) : (
            <Component {...pageProps} />
          )}
        </Layout>
    </SessionProvider>
    

    so every page that has .auth will be wrapped with the auth component and this will do the work for it

    now get rid of all those if statments checking if session is defined since you page will be rendered only if the session is here