Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

jstn's avatar
Level 1

Laravel Breeze API + NextJS Starter Kit API: Call on unauthenticated users

This is more of a question about the reference implementation of NextJS for Laravel's Breeze API that Laravel provides.

I noticed when on the index page there is a repeating call to api/user even if the user has never logged in before. This is because the index page contains the useAuth hook to determine whether or not to display Login / Register or Dashboard link.

This doesn't seem correct to me. I feel it should not be trying to call api/user if the user has never logged in before.

Has anyone encountered this before?

0 likes
7 replies
Niush's avatar

That is by design. Suppose the user opened multiple browser tabs of your application. And, they login, in one of the tab. Then the other tab will not have the login state. It is confusing and does not feel reactive. So, instead the useSWR hook in Next.js calls the /api/user/ route on tab refocus, connection reconnect etc.

Similar case is when, suppose the user updated their name in one tab, then the old name will be visible in another tab. useSWR also revalidates/re-fetches stale data.

If you want, you can disable this feature. https://swr.vercel.app/docs/revalidation

In hooks/auth.js file update the useSWR to:

const { data: user, error, revalidate } = useSWR('/api/user', () =>
    axios
        .get('/api/user')
        .then(res => res.data)
        .catch(error => {
            if (error.response.status !== 409) throw error

            router.push('/verify-email')
        }), {
          revalidateIfStale: false,
          revalidateOnFocus: false,
          revalidateOnReconnect: false
    })

The user data will only be revalidated when you call revalidate() like in register, login functions.

jstn's avatar
Level 1

Thanks for the reply @niush, that's interesting. It's by design to hit a restricted API end point when a user has never logged in before? Even if we take SWR out of the equasion, there would still be a call to the api/user endpoint. My thinking is, there would be a check if the user has ever logged in before via cookie/local storage?

sr57's avatar

@jstn

via cookie/local storage?

Cookies are on the client ...

... and don't you know GDPR ?

That's said, do you intend to store all the past users? can be a waste of storage room ...

jstn's avatar
Level 1

@sr57

Cookies are on the client ...

Yes...yes, they are...

Take for instance the following example:

const [cookie, useCookie] = useCookie()

const login = async ({ setErrors, setStatus, ...props }) => {
    await csrf()

    setErrors([])
    setStatus(null)

    axios
        .post('/login', props)
        .then(() => {
            setCookie('authenticated', true)
            revalidate()
        })
        .catch(error => {
            if (error.response.status !== 422) throw error

            setCookie('authenticated', false)
            setErrors(Object.values(error.response.data.errors).flat())
        })
}

const {
    data: user,
    error,
    revalidate,
}: responseInterface<object, object> = useSWR('/api/user', () => {
    if (cookie.get('authenticated')) {
        return axios
            .get('/api/user')
            .then(res => res.data)
            .catch(error => {
                if (error.response.status !== 409) {
                    throw error
                }

                router.push('/verify-email')
            })
    }
    return undefined
})

This is just my thinking. Basically, I guess my question is, is calling the api/user repeatedly and returning a 401 error best practice? Any resources discussing this is appreciated.

jstn's avatar
Level 1

@sr57

shorter response time than api/user?

I don't understand your question. Are you asking if retrieving a client side cookie is faster than making a (presumably unnecessary) network call? Then I say, yes.

jstn's avatar
jstn
OP
Best Answer
Level 1

For anyone interested, I went ahead with the cookie solution. I was unable to find any additional information about allowing a user who never logged in hit the restricted api/user endpoint repeatedly which just didn't sit right with me.

After reviewing the SWF docs, I found they have conditional fetching. I used the check on a cookie I set once a user logs in.

Now there is no longer an unnecessary api/user call to unauthenticated users and I can keep the revalidate feature of SWF, once the user logs in.

Note: This requires react-cookies otherwise you can implement your own setter/getter for cookies.

User:

const [cookies, setCookies, removeCookies] = useCookies()

const {
  data: user,
  error,
  mutate
}: responseInterface<User, ErrorResponse> = useSWR(
  cookies.isAuth ? '/api/user' : null, // Conditional fetching
  () =>
    axios
      .get('/api/user')
      .then(res => res.data)
      .catch(error => {
        if (error.response.status !== 409) throw error

        router.push('/verify-email')
      })
)

Login:

const login = async ({ setErrors, setStatus, ...props }: LoginParams) => {
  await csrf()

  setErrors([])
  setStatus(null)

  axios
    .post('/login', props)
    .then(() => {
      setCookies('isAuth', true, { sameSite: 'lax' }) // Setting cookie
      mutate()
    })
    .catch(error => {
      removeCookies('isAuth')
      if (error.response.status !== 422) throw error

      setErrors(
        Object.values(
          error.response.data.errors
        ).flat() as []
      )
    })
}

Logout:

const logout = async () => {
  if (!error) {
    await axios.post('/logout').then(() => {
      removeCookies('isAuth', { sameSite: 'lax' }) // Remove cookie
    })
  }

  window.location.pathname = '/login'
}
2 likes

Please or to participate in this conversation.