I am using Next.js 14 App router. I know we can fetch data using server actions inside a Server Component. But, I require the page.tsx of a specific route to be a client component due to some reasons. Now, to fetch data inside a client component, we can use useEffect()
and call the external api endpoint using fetch()
. But, I am getting a CORS error in console even though the server to which I am making request is enabled to allow requests from all origins.
Access to fetch at 'http://localhost:9000/endpoint' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
And getting the following error in Next.js error panel:
Unhandled Runtime Error
TypeError: Failed to fetch
Also, I am able to fetch through the same endpoint, using the same fetch()
through server actions.
There is one temporary solution to overcome this using rewrites in next.config.js
as follows:
/** @type {import('next').NextConfig} */
const nextConfig = {
async rewrites() {
return [
{
source: "/endpoint",
destination:
"https://exampleapi.com/endpoint",
},
];
},
};
module.exports = nextConfig;
Even though this overcomes CORS and solves the Failed to fetch
runtime error. But, would have to create this for every endpoint. Is there any other way to fetch data in client component inside an useEffect using fetch()
without updating next.config.js
?
I think you should be using SWR (it was created by NextJs team)
It is recommend by Nextjs in the documentation:
Fetching Data on the Client with third-party libraries You can also fetch data on the client using a third-party library such as SWR.
Example usage:
import useSWR from 'swr'
function Profile () {
const { data, error, isLoading } = useSWR('/api/user/123', fetcher)
if (error) return <div>failed to load</div>
if (isLoading) return <div>loading...</div>
// render data
return <div>hello {data.name}!</div>
}