Next.js Discord

Discord Forum

How to fetch data on the client with Route Handlers?

Unanswered
Cape lion posted this in #help-forum
Open in Discord
Avatar
Cape lionOP
I'm using App Router with Next.js 14. I've read https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating#fetching-data-on-the-client-with-route-handlers and https://nextjs.org/docs/app/building-your-application/routing/route-handlers but I see only examples of writing Route Handlers. Can you please show me actual examples of fetching data in a client component with Route Handlers, which show how the result data is returned to the client and displayed in the page? Ideally with a spinner displayed while the client waits for the response. Thank you!

I first asked in #gpt-help here: #Examples of Fetching Data on the Client with Route Handlers. But &1089670008898080870 only confirmed that it's not documented.

32 Replies

Avatar
Peterbald
Hi @Cape lion
I can help you
Avatar
Cape lionOP
Thanks Dene!
Avatar
Peterbald
So you are going to fetch data on client side?
Avatar
Cape lionOP
Yes. I don't think my case is good for Server Actions. I'm better with a standard API. And I understand that with Next.js 14, the way to go is Route Handlers. What I want to achieve is simple: there will be just a button and when clicked, it will launch a (potentially long) process by calling the API. The user will have to wait (that's why a spinner would be nice) and then the API will return if succeeded or failed, and the message will be inserted in the page, in place of the spinner.
Avatar
Peterbald
Yeah I understood what you want
So please show me your code
I will assist you
Avatar
Cape lionOP
I don't have code actually 🙂 That's why I was looking for examples to start from.
I just did a simple test but it makes no sense as it's all server side.

/app/store/page.tsx

export default async function Page() {
const res = await fetch("http://localhost:3000/store/api");
const hello = await res.json();

return (
#Unknown Channel
<div>Welcome to the Store</div>
<div><button>Install app</button></div>
<div>Response from API: {hello.message}</div>
</>
)
}

/app/store/api/route.ts

export async function GET() {
return Response.json({ message: "Hello, World!" })
}
I do have the Hello, World! message displayed in the page so it worked. But it was all pre-rendered. What I want is to fetch and return the response when the user clicks the button.
Avatar
Peterbald
Yeah I understand
I will explain. If you want to make the spinner then you have to make the component as a client
If you use "use client" in front of top component, then the component should be client component.
In client component you can call api in useEffect function.
and make the loading status state in the component.
Avatar
Cape lionOP
Oh ok so useEffect is still the recommended way to do this in Next.js 14? I was not sure if I needed to dig into Suspense (https://nextjs.org/docs/app/building-your-application/routing/loading-ui-and-streaming)
Avatar
Peterbald
Yes
That is one kind of solution
Avatar
Cape lionOP
Avatar
Peterbald
Suspense module is good loading for React
I will share one component
import React, { useState, useEffect } from 'react';

// Example of a simple spinner component
const Spinner = () => {
return <div>Loading...</div>;
};

const DataFetchingComponent = () => {
const [data, setData] = useState(null);
const [isLoading, setIsLoading] = useState(false);

useEffect(() => {
// Set loading to true when the fetch starts
setIsLoading(true);

// Replace this URL with your actual data source
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => {
setData(data); // Set the data
setIsLoading(false); // Set loading to false once data is fetched
})
.catch(error => {
console.error('Error fetching data:', error);
setIsLoading(false); // Set loading to false if fetch fails
});
}, []); // Empty dependency array means this effect runs once on mount

if (isLoading) {
return <Spinner />;
}

return (
<div>
{data ? (
// Render your data here
<div>{JSON.stringify(data)}</div>
) : (
<div>No data available</div>
)}
</div>
);
};

export default DataFetchingComponent;
This is normal way to implement loading and fetching data in client component in Next.js
If the response from API does not come out then loading component will render.
if the response from API comes out then set the data as a state and data component will render
Avatar
Peterbald
I DM you
Avatar
Cape lionOP
Works!
Just needed to add "use client" at top of the file.
Avatar
Peterbald
That is what I said before
Avatar
Cape lionOP
Yep, sorry
I've added a timer in my route.ts and I can see the "Loading..." before the final result.

import { setTimeout } from "timers/promises";

export async function GET() {
await setTimeout(2000);
return Response.json({ message: "Hello, World!" })
}
Avatar
Peterbald
Yeah but I think it is not good proper solution
Avatar
Cape lionOP
I can't make it to work with a click on a button.

Thank you again for your example. But the request to the API is triggered on load of page. Can you be kind and show me how to trigger on click of a button? Thanks! 🙏