I am using Next.js v14, graphql, ApolloClient and ApolloGraphQL Experimental support for NextJS
{
"@apollo/client": "^3.12.0-rc.3",
"@apollo/experimental-nextjs-app-support": "^0.11.6",
}
I have been following this blog post so far on how to use ApolloClient with NextJS v13+ https://www.apollographql.com/blog/how-to-use-apollo-client-with-next-js-13.
I am trying to make a SSR front page that showcases user posts. It is paginated, such that it initially loads X amount and then loads more if the user chooses to load more.
However, I am confused on how to implement pagination. Looking at the docs on pagination https://www.apollographql.com/docs/react/pagination/core-api#the-fetchmore-function.
It's easy to follow, however, it's using useQuery
from @apollo/client
, which only works
on csr components. Because my page is SSR, using the code from the first blog, I have to use getClient().query()
, but this does not have any fetchMore()
methods on the response result. I implemented a fetchMore
function that I think would work, but because the page is SSR, button's can't have onClick
handlers. Am I missing something? What's the solution? I want to keep the retrieval of the data on the server.
Here is my relevant code:
import { getClient } from '@/lib/apollo';
import { gql } from '@apollo/client';
const GET_ALL_POSTS = gql`
query GetAllPosts($limit: Int!, $cursor: String) {
posts(limit: $limit, cursor: $cursor) {
posts {
id
title
}
hasMore
}
}
`;
export const revalidate = 0;
export default async function Home() {
const queryResult = await getClient().query({
query: GET_ALL_POSTS,
variables: {
limit: 10,
},
});
// irrelevant code for handling loading state
const data = queryResult.data?.posts;
// example idea for fetching more, but can't have onClick for buttons with SSR
async function fetchMore() {
await getClient().query({
query: GET_ALL_POSTS,
variables: {
limit: 10,
cursor: data.posts[data.posts.length - 1].createdAt,
},
});
getClient().cache.writeQuery({
query: GET_ALL_POSTS,
data: {
posts: {
posts: [...data.posts, ...queryResult.data.posts.posts],
hasMore: queryResult.data.posts.hasMore,
},
},
});
}
return (<div>
// irrelevant code for showcasing posts
{queryResult.data && data.hasMore && (
<button
onClick={fetchMore}
>
Load more
</button>
)}
</div>);
}
I suppose even if I did have a fetchMore
call available to me, the button would still not be able to call it since its SSR page and can't have an onClick
event. So maybe just making it a CSR page is the correct thing to do. Is there no way to make all the queries run on the server?
Stuff like this will only work in Client Components - in your React Server components, every render starts off with a completely empty cache that isn't connected to any previous user request and, as you already noticed, you ship non-interactive HTML to the browser.
That said, you probably do not have any real reason to use React Server Components here in the first place - just use useSuspenseQuery
in a Client Component. Client Components server-side render on first page load, too, but then they will continue interactively in the browser. (The Nextjs/Apollo package you linked to will ensure correct hydration in the browser).
That is probably what you want.