How to initialize API headers from next-auth
Unanswered
Gull Dong posted this in #help-forum
Gull DongOP
I'm using appDir and Next.JS 13, and I'm struggling with how to initialize interceptors for my API calls so that all of my API calls (to a separate backend) will include an authorization header based on the session.
I'm primarily focused on making API calls from client-side components (not SSR)
I can use getSession, but that will make a server request to get the session data each time, which is wasteful - since the client already knows the session.
I can call useSession in my component and pass that into my API calls, but that creates call for useSession in every component, adds lots of extra code, and is not DRY at all.
What I want to do is to call useSession inside of my API helper object - but if I do that, I'll be told "Invalid hook call. Hooks can only be called inside of the body of a function component."
I'd also love to just set an axios or wretch interceptor from a next-auth callback, but it seems like those callbacks are only invoked when the user actually logs in. So if I just reload a page or something - they won't be invoked...so I won't be able to set the interceptors at that point.
This seems like a really basic thing to do, but I'm struggling to find any examples or posts about this - so maybe I'm missing something obvious. I'd appreciate any best practices or example apps for Next.JS 13 that show a pattern for always setting Authorization headers based on session - that are pretty DRY and don't require calling useSession in every client-side component.
Thanks!
I'm primarily focused on making API calls from client-side components (not SSR)
I can use getSession, but that will make a server request to get the session data each time, which is wasteful - since the client already knows the session.
I can call useSession in my component and pass that into my API calls, but that creates call for useSession in every component, adds lots of extra code, and is not DRY at all.
What I want to do is to call useSession inside of my API helper object - but if I do that, I'll be told "Invalid hook call. Hooks can only be called inside of the body of a function component."
I'd also love to just set an axios or wretch interceptor from a next-auth callback, but it seems like those callbacks are only invoked when the user actually logs in. So if I just reload a page or something - they won't be invoked...so I won't be able to set the interceptors at that point.
This seems like a really basic thing to do, but I'm struggling to find any examples or posts about this - so maybe I'm missing something obvious. I'd appreciate any best practices or example apps for Next.JS 13 that show a pattern for always setting Authorization headers based on session - that are pretty DRY and don't require calling useSession in every client-side component.
Thanks!
25 Replies
the point of using React Context provider and consume context like useSession is to eliminate the need of propdrilling which is basically to eliminate DRY problems
What I want to do is to call useSession inside of my API helper object - but if I do that, I'll be told "Invalid hook call. Hooks can only be called inside of the body of a function component."You might want to check out
getServerSession(authOption) thenbut it seems like those callbacks are only invoked when the user actually logs in. So if I just reload a page or something - they won't be invoked...so I won't be able to set the interceptors at that point.to me they are invoked everytime <SessionProvider> or useSession triggers a session checks
you can set authenticated routes in middleware or root layout page to prevent access to child components so that you don't have to do it on every component
Gull DongOP
Thanks @alfon By "every component" I just meant every component that uses data - which is quite a few in my application. So the best practice would be to just call useSession in every client-side component that needs data, and then pass that into an API helper? There is no way to DRY that up so that the API helper can just pull the session data and include it in an Authorization header?
In fact, I'm actually using hooks, which use react-query, which then invoke fetch/axios. So the annoying thing is needing to call useSession in a client component, pass that to a hook, which passes that to a react-query queryFn.
I had hoped there was some way to just initialize "axios.defaults.headers.Authorization" for example - so that I don't have to pass the session around everywhere.
I had hoped there was some way to just initialize "axios.defaults.headers.Authorization" for example - so that I don't have to pass the session around everywhere.
@Gull Dong Thanks <@194128415954173952> By "every component" I just meant every component that uses data - which is quite a few in my application. So the best practice would be to just call useSession in every client-side component that needs data, and then pass that into an API helper? There is no way to DRY that up so that the API helper can just pull the session data and include it in an Authorization header?
What is your API helper comprises of? Axios stuff?
Gull DongOP
Yea - it wraps fetch or axios and sets the default headers, prepends my base API url, etc.
You dont need to pass your session to axios since the session is already stored in cookies in headers
Cookies n headers
And you can get client's session in backend using cookie() or headers()
Gull DongOP
I'm hitting a custom backend - so I need to use the session data to set Authorization headers. But you're right that if I could set the default headers, baseUrl, etc. - I wouldn't even need an API wrapper.
Ugh but ure not using nextjs backend nvm
Gull DongOP
yea - sorry =/
Why not use nextjs as a middleman?
Middlebackend
Gull DongOP
Is that a good practice? I guess the server could then modify the headers when rewriting requests? Seems a little inefficient - since instead of just direclty making the request, you'd have to pass them through a proxy - but I did think about that.
Not really, id thought itd be redundant lol
Gull DongOP
Haha
I really just want a hook that gets called anytime the session changes...I wonder if I could just do a useEffect hook at the highest client-side component in my app that gets invoked whenever the session changes. And it sets some global axios headers
I tried to do that in my app/layout...but I was outside of SessionProvider so it didn't work...but I could probably rearrange to find some way to make that work. I just figured there might be a better best practice 🙂
It's too bad next-auth doesn't just provide a sessionUpdated hook that always gets called when a session is initialized or changed
Or even one that gets called only when a page loads or user logs in/out
Gull DongOP
So, I ended up creating an AuthProvider component that I used in my main app layout. What do you think of this solution?
'use client';
import axios from 'axios';
import { useSession } from 'next-auth/react';
import { useEffect } from 'react';
export default function AuthProvider(
{children}: {children?: React.ReactNode}
): JSX.Element {
const {status, data: session} = useSession()
useEffect(() => {
if( status == 'authenticated' ) {
axios.defaults.headers.Authorization = `Bearer ${session.user.id}`
}
}, [session])
return (
<>
{status == 'authenticated'
? <>{children}</>
: <p> </p>
}
</>
);
}