Comments (8)
One more comment here:
export function makeClient() {
// Adjust to use NextSSRApolloClient for SSR with appropriate caching and link setup
return new NextSSRApolloClient({
link: typeof window === "undefined"
? ApolloLink.from([new SSRMultipartLink({ stripDefer: true }), httpLink])
: httpLink,
- cache: new InMemoryCache(),
+ cache: new NextSSRInMemoryCache()
});
}
Apart from that, I think at this point you're set up correctly :)
I think your questions here are solved, so I'm closing this issue.
Generally, if you have usage questions like this, please consider asking in the Apollo GraphQL Discord first - the issue tracker here is meant more for bug reports.
from apollo-client.
It seems like you do not have an <ApolloProvider
in your application.
Generally, please note that you should not be using @apollo/client
on it's own in the app directory.
We have a separate package to integrate Apollo Client with the Next.js app router:
from apollo-client.
Now I have the following set up and the error I'm facing is : -
- error node_modules/@apollo/experimental-nextjs-app-support/dist/ssr/RehydrationContext.js (28:0) @ RehydrationContextProvider
- error Error: When using Next SSR, you must use the `NextSSRApolloClient`
import { GetStaticPaths, GetStaticProps, InferGetStaticPropsType } from 'next';
import React from 'react';
import { gql } from '@apollo/client';
import { documentToReactComponents } from '@contentful/rich-text-react-renderer';
import { getClient } from '../libs/apolloServer'; // Adjust the import path as necessary
import { BlogPost as Post } from '../libs/blogTypes';
import { FacebookShareButton, TwitterShareButton } from 'next-share';
import { FaFacebookF, FaTwitter } from "react-icons/fa";
// Type for the object that includes the slug
interface PostSlug {
slug: string;
}
// GraphQL queries to fetch the slugs for all posts and the details of a post by its slug
const GET_ALL_POSTS_SLUGS = gql`
query GetAllPostsSlugs {
pageBlogPostCollection {
items {
slug
}
}
}
`;
const GET_POST_BY_SLUG = gql`
query GetPostBySlug($slug: String!) {
pageBlogPostCollection(where: { slug: $slug }, limit: 1) {
items {
sys {
id
}
title
slug
shortDescription
featuredImage {
url
title
}
content {
json
}
}
}
}
`;
export const getStaticPaths: GetStaticPaths = async () => {
const apolloClient = getClient();
const { data } = await apolloClient.query({ query: GET_ALL_POSTS_SLUGS });
const paths = data.pageBlogPostCollection.items.map(({ slug }: PostSlug) => ({
params: { slug },
}));
return { paths, fallback: 'blocking' };
};
export const getStaticProps: GetStaticProps<{ post?: Post }> = async ({ params }) => {
if (!params?.slug || typeof params.slug !== 'string') {
return { notFound: true };
}
const apolloClient = getClient();
const { data } = await apolloClient.query({
query: GET_POST_BY_SLUG,
variables: { slug: params.slug },
});
const post = data.pageBlogPostCollection.items.length > 0 ? data.pageBlogPostCollection.items[0] : null;
if (!post) {
return { notFound: true };
}
return { props: { post }, revalidate: 1 };
};
const PostPage: React.FC<InferGetStaticPropsType<typeof getStaticProps>> = ({ post }) => {
if (!post) {
return <div>Post not found.</div>;
}
const postUrl = `https://yourdomain.com/blog/${post.slug}`;
return (
<article>
<h1>{post.title}</h1>
{post.shortDescription && <p>{post.shortDescription}</p>}
{post.featuredImage && (
<img
src={post.featuredImage.url}
alt={post.featuredImage.title || 'Featured image'}
style={{ width: '100%' }}
/>
)}
<div>{documentToReactComponents(post.content.json)}</div>
<div className="social-share-buttons">
<FacebookShareButton url={postUrl}>
<FaFacebookF size={32} />
</FacebookShareButton>
<TwitterShareButton url={postUrl}>
<FaTwitter size={32} />
</TwitterShareButton>
</div>
</article>
);
};
export default PostPage;
'use client';
import React from 'react';
import { useQuery } from '@apollo/client';
import Link from 'next/link';
import { documentToReactComponents } from '@contentful/rich-text-react-renderer';
import { GET_ALL_POSTS_QUERY } from '../libs/contentful';
import { BlogPost as Post } from '../libs/blogTypes';
const BlogClient = () => {
const { data, loading, error } = useQuery<{ pageBlogPostCollection: { items: Post[] } }>(GET_ALL_POSTS_QUERY);
if (loading) return <div className="blog-loading">Loading...</div>;
if (error) return <div className="blog-error">Error: {error.message}</div>;
return (
<div className="blog-container">
{data?.pageBlogPostCollection.items.map((post) => (
<article key={post.sys.id} className="blog-post">
<h2 className="post-title">
<Link href={`/blog/${post.slug}`}>
{post.title}
</Link>
</h2>
{post.shortDescription && (
<p className="post-subtitle">{post.shortDescription}</p>
)}
{post.featuredImage && (
<img
className="post-image"
src={post.featuredImage.url}
alt={post.featuredImage.title || 'Featured image'}
/>
)}
<div className="post-excerpt">
{documentToReactComponents(post.content.json)}
</div>
</article>
))}
</div>
);
};
export default BlogClient;
import React from 'react';
import BlogClient from './BlogClient';
import ClientOnly from '@/app/components/ClientOnly';
const BlogPage = () => {
return (
<ClientOnly>
<BlogClient />
</ClientOnly>
);
};
export default BlogPage;
import React, { Suspense } from 'react';
import { gql } from '@apollo/client';
import { useSuspenseQuery } from "@apollo/experimental-nextjs-app-support/ssr";
import { ApolloWrapper } from '../libs/ApolloWrapper'; // Adjust the import path as necessary
import { BlogPost } from '../libs/blogTypes'; // Adjust the import path as necessary
import { documentToReactComponents } from '@contentful/rich-text-react-renderer';
const BLOG_POSTS_QUERY = gql`
query GetBlogPosts {
blogPostsCollection { // Update with actual query and fields
items {
sys {
id
}
title
slug
shortDescription
featuredImage {
url
title
}
content {
json
}
}
}
}
`;
const PollPage = () => {
const { data } = useSuspenseQuery<{ blogPostsCollection: { items: BlogPost[] } }>(BLOG_POSTS_QUERY);
const blogPosts = data.blogPostsCollection.items;
return (
<div>
{blogPosts.map((post) => (
<article key={post.sys.id}>
<h2>{post.title}</h2>
{post.shortDescription && <p>{post.shortDescription}</p>}
{post.featuredImage && (
<img
src={post.featuredImage.url}
alt={post.featuredImage.title || 'Featured Image'}
/>
)}
<div>{documentToReactComponents(post.content.json)}</div>
</article>
))}
</div>
);
};
const PollPageWithSuspense = () => (
<ApolloWrapper>
<Suspense fallback={<div>Loading...</div>}>
<PollPage />
</Suspense>
</ApolloWrapper>
);
export default PollPageWithSuspense;
import { ApolloClient, HttpLink, InMemoryCache, ApolloLink } from '@apollo/client';
import { NextSSRInMemoryCache, SSRMultipartLink } from '@apollo/experimental-nextjs-app-support/ssr';
// Configuration for connecting to your GraphQL endpoint
const space = process.env.NEXT_PUBLIC_CONTENTFUL_SPACE_ID;
const accessToken = process.env.NEXT_PUBLIC_CONTENTFUL_ACCESS_TOKEN;
const endpoint = `https://graphql.contentful.com/content/v1/spaces/${space}/environments/master`;
// HTTP Link setup for fetching GraphQL data
const httpLink = new HttpLink({
uri: endpoint,
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
// Client Components Apollo Client setup with SSR support
export function makeClient() {
return new ApolloClient({
cache: new NextSSRInMemoryCache(),
link: typeof window === "undefined"
? ApolloLink.from([new SSRMultipartLink({ stripDefer: true }), httpLink])
: httpLink,
});
}
import { ApolloClient, HttpLink, InMemoryCache } from '@apollo/client'; // Import ApolloClient here
import { registerApolloClient } from '@apollo/experimental-nextjs-app-support/rsc';
// Configuration for connecting to your GraphQL endpoint
const space = process.env.NEXT_PUBLIC_CONTENTFUL_SPACE_ID;
const accessToken = process.env.NEXT_PUBLIC_CONTENTFUL_ACCESS_TOKEN;
const endpoint = `https://graphql.contentful.com/content/v1/spaces/${space}/environments/master`;
// HTTP Link setup for fetching GraphQL data
const httpLink = new HttpLink({
uri: endpoint,
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
// Server Components Apollo Client setup
export const { getClient } = registerApolloClient(() => new ApolloClient({ // ApolloClient is now correctly imported
cache: new InMemoryCache(),
link: httpLink,
}));
'use client';
import React from 'react';
import { ApolloNextAppProvider } from '@apollo/experimental-nextjs-app-support/ssr';
import { makeClient } from './apolloClient'; // Adjust the import path as necessary
export function ApolloWrapper({ children }: React.PropsWithChildren<{}>): JSX.Element {
return (
<ApolloNextAppProvider makeClient={makeClient}>
{children}
</ApolloNextAppProvider>
);
}
import { ApolloClient, InMemoryCache, HttpLink, gql } from '@apollo/client';
import { BlogPost as Post } from './blogTypes';
const space = process.env.NEXT_PUBLIC_CONTENTFUL_SPACE_ID;
const accessToken = process.env.NEXT_PUBLIC_CONTENTFUL_ACCESS_TOKEN;
const client = new ApolloClient({
link: new HttpLink({
uri: `https://graphql.contentful.com/content/v1/spaces/${space}`,
headers: {
Authorization: `Bearer ${accessToken}`,
},
}),
cache: new InMemoryCache(),
});
// GraphQL queries
export const GET_ALL_POSTS_QUERY = gql`
query GetAllPosts {
pageBlogPostCollection {
items {
sys {
id
}
title
slug
shortDescription
featuredImage {
url
title
description
}
content {
json
}
}
}
}
`;
export const GET_POST_BY_SLUG_QUERY = gql`
query GetPostBySlug($slug: String!) {
pageBlogPostCollection(where: { slug: $slug }, limit: 1) {
items {
sys {
id
}
title
slug
shortDescription
featuredImage {
url
title
description
}
content {
json
}
}
}
}
`;
// Fetch all blog posts
export async function fetchEntries(): Promise<Post[]> {
const { data } = await client.query<{ pageBlogPostCollection: { items: Post[] } }>({ query: GET_ALL_POSTS_QUERY });
return data.pageBlogPostCollection.items;
}
// Fetch a single post by slug
export async function fetchSingleEntry(slug: string): Promise<Post | null> {
const { data } = await client.query<{ pageBlogPostCollection: { items: Post[] } }>({
query: GET_POST_BY_SLUG_QUERY,
variables: { slug },
});
return data.pageBlogPostCollection.items.length ? data.pageBlogPostCollection.items[0] : null;
}
import { gql } from '@apollo/client';
import { getClient } from './apolloServer'; // Expects a server-side Apollo Client setup
import { makeClient } from './apolloClient'; // Expects a client-side Apollo Client setup
// Interfaces for typing the responses from Contentful's GraphQL API
interface Sys {
id: string;
}
interface BlogPost {
sys: Sys;
title: string;
slug: string;
content: string; // Adjust according to your actual content model
}
interface EntryCollectionResponse {
entryCollection: {
items: BlogPost[];
};
}
interface SingleEntryResponse {
blogPost: BlogPost;
}
const GET_ENTRIES_QUERY = gql`
query GetEntries($contentType: String!) {
entryCollection(where: { contentType: $contentType }) {
items {
sys {
id
}
... on BlogPost {
title
slug
content
}
}
}
}
`;
const GET_SINGLE_ENTRY_QUERY = gql`
query GetSingleEntry($entryId: String!) {
blogPost(id: $entryId) {
sys {
id
}
title
slug
content
}
}
`;
// Function to dynamically select the Apollo Client based on execution context
function useApolloClient() {
// This function decides whether to use the server or client instance
// of Apollo Client based on the environment.
return typeof window === 'undefined' ? getClient : makeClient;
}
// Fetch multiple entries by content type
export async function fetchEntries(contentType: string): Promise<BlogPost[]> {
const client = useApolloClient();
const { data } = await client().query<EntryCollectionResponse>({
query: GET_ENTRIES_QUERY,
variables: { contentType },
});
return data.entryCollection.items;
}
// Fetch a single entry by entry ID
export async function fetchSingleEntry(entryId: string): Promise<BlogPost | null> {
const client = useApolloClient();
const { data } = await client().query<SingleEntryResponse>({
query: GET_SINGLE_ENTRY_QUERY,
variables: { entryId },
});
return data.blogPost;
}
I also imported the Apollo Wrapper into my layout and wrapped it around all children components as shown here:
return (
<html lang="en">
<body className={font.className}>
<ApolloWrapper>
<ClientOnly>
<ToasterProvider />
<LoginModal />
<RegisterModal />
<SearchModal />
<RentModal />
<ConfirmModal />
<WelcomeModal />
<DialogModal />
<DeleteDialogModal />
<ProfileProvider>
<ProfileInfoModal />
</ProfileProvider>
<Navbar currentUser={currentUser} />
<ProfileProvider>
<ProfileModal userId={currentUser?.id} email={currentUser?.email} />
</ProfileProvider>
</ClientOnly>
<div className="flex flex-col min-h-screen">
<div className="flex-grow pb-20 pt-28">
<GoogleTagManager id="GTM-PFV4NM5Z" />
{children}
<Analytics />
</div>
<Footer />
</div>
</ApolloWrapper>
</body>
</html>
)
}
from apollo-client.
Given the Client Components Setup with SSR Support using ApolloClient directly was causing the error because for SSR scenarios, NextSSRApolloClient should be used instead, as indicated by the error messages, I updated the following files and now it works:
'use client';
import React from 'react';
import { ApolloProvider } from '@apollo/client';
import { makeClient } from './apolloClient';
// Explicitly type the props for the component, including typing children as React.ReactNode
interface ApolloWrapperProps {
children: React.ReactNode;
}
const ApolloWrapper: React.FC<ApolloWrapperProps> = ({ children }) => {
const client = makeClient();
return <ApolloProvider client={client}>{children}</ApolloProvider>;
};
export default ApolloWrapper;
'use client';
import React from 'react';
import { ApolloProvider } from '@apollo/client';
import { makeClient } from './apolloClient';
// Explicitly type the props for the component, including typing children as React.ReactNode
interface ApolloWrapperProps {
children: React.ReactNode;
}
const ApolloWrapper: React.FC<ApolloWrapperProps> = ({ children }) => {
const client = makeClient();
return <ApolloProvider client={client}>{children}</ApolloProvider>;
};
export default ApolloWrapper;
Don't see the immediate benefits I was expecting from implementing GraphQL but its been quite something implementing this. I just hope it improves the fetching of my blog content as promised.
from apollo-client.
I'm very sorry you have to go through all this - unfortunately, at this time it's no fun using Next.js with a third-party fetching library, since Next.js runs your code
- in a React Server Component build run
- in a SSR run (use client)
- in the browser (use client)
and all of those need slightly different setup.
Unfortunately, there is not much we can do about that added complexity :/
Given the Client Components Setup with SSR Support using ApolloClient directly was causing the error because for SSR scenarios, NextSSRApolloClient should be used instead, as indicated by the error messages,
I get that it looks like it is working for you now, but this will execute the same queries once during the SSR run and then again in the browser - is there a reason why you are not using the NextSSRApolloClient
as asked by that error message?
I know that the setup is no fun since you have to set up for three environments, but there is really no way around it :/
from apollo-client.
I forgot to mention this, the main issue with respect to the NextSSRApolloClient issue was that it needed to be distinguished between the client and server side of the apolloClient when in the browser so I needed to ensure that when the ApolloClient got called from fetchContentful.ts it was checked first. The following code displays the separation of the apolloClient.ts into two components (apolloClient.ts and apolloServer.ts) that would run separately and Called when needed by Contentful.
import { HttpLink, InMemoryCache } from '@apollo/client';
import { ApolloLink } from '@apollo/client';
import { NextSSRApolloClient, SSRMultipartLink } from '@apollo/experimental-nextjs-app-support/ssr';
import { loadErrorMessages, loadDevMessages } from "@apollo/client/dev";
// Load development-specific error messages
if (process.env.NODE_ENV !== "production") {
loadDevMessages();
loadErrorMessages();
}
// Configuration for connecting to your GraphQL endpoint
const space = process.env.NEXT_PUBLIC_CONTENTFUL_SPACE_ID;
const accessToken = process.env.NEXT_PUBLIC_CONTENTFUL_ACCESS_TOKEN;
const endpoint = `https://graphql.contentful.com/content/v1/spaces/${space}/environments/master`;
// HTTP Link setup for fetching GraphQL data
const httpLink = new HttpLink({
uri: endpoint,
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
// Function to create an Apollo Client instance
export function makeClient() {
// Adjust to use NextSSRApolloClient for SSR with appropriate caching and link setup
return new NextSSRApolloClient({
link: typeof window === "undefined"
? ApolloLink.from([new SSRMultipartLink({ stripDefer: true }), httpLink])
: httpLink,
cache: new InMemoryCache(),
});
}
import { ApolloClient, HttpLink, InMemoryCache } from '@apollo/client';
import { registerApolloClient } from '@apollo/experimental-nextjs-app-support/rsc';
// Configuration for connecting to your GraphQL endpoint
const space = process.env.NEXT_PUBLIC_CONTENTFUL_SPACE_ID;
const accessToken = process.env.NEXT_PUBLIC_CONTENTFUL_ACCESS_TOKEN;
const endpoint = `https://graphql.contentful.com/content/v1/spaces/${space}/environments/master`;
// HTTP Link setup for fetching GraphQL data
const httpLink = new HttpLink({
uri: endpoint,
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
// Server Components Apollo Client setup
export const { getClient } = registerApolloClient(() => new ApolloClient({
cache: new InMemoryCache(),
link: httpLink,
}));
import { gql } from '@apollo/client';
import { getClient } from './apolloServer';
import { makeClient } from './apolloClient';
// Interfaces for typing the responses from Contentful's GraphQL API
interface Sys {
id: string;
}
interface BlogPost {
sys: Sys;
title: string;
slug: string;
content: string; // Adjust according to your actual content model
}
interface EntryCollectionResponse {
entryCollection: {
items: BlogPost[];
};
}
interface SingleEntryResponse {
pageBlogPost: BlogPost;
}
const GET_ENTRIES_QUERY = gql`
query GetEntries {
pageBlogPostCollection {
items {
sys {
id
}
title
slug
content {
json
}
}
}
}
`;
const GET_SINGLE_ENTRY_QUERY = gql`
query GetSingleEntry($entryId: String!) {
pageBlogPost(id: $entryId) {
sys {
id
}
title
slug
content {
json
}
}
}
`;
function getApolloClient() {
// If window is undefined, we are on the server, otherwise we are on the client.
return typeof window === 'undefined' ? getClient() : makeClient();
}
// Fetch multiple entries by content type
export async function fetchEntries(contentType: string): Promise<BlogPost[]> {
const client = getApolloClient(); // Use the refactored function here.
const { data } = await client.query<EntryCollectionResponse>({
query: GET_ENTRIES_QUERY,
variables: { contentType },
});
return data.entryCollection.items;
}
// Fetch a single entry by entry ID
export async function fetchSingleEntry(entryId: string): Promise<BlogPost | null> {
const client = getApolloClient(); // And here.
const { data } = await client.query<SingleEntryResponse>({
query: GET_SINGLE_ENTRY_QUERY,
variables: { entryId },
});
return data.pageBlogPost;
}
from apollo-client.
Do you have any feedback for the maintainers? Please tell us by taking a one-minute survey. Your responses will help us understand Apollo Client usage and allow us to serve you better.
from apollo-client.
This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
For general questions, we recommend using StackOverflow or our discord server.
from apollo-client.
Related Issues (20)
- AutoCleanedWeakCache causes failure when running tests in fakeAsync zone HOT 4
- useQuery fails to resolve ssr renderPromise due to JSON.stringify reordering HOT 6
- useSuspenseQuery returns an error instead of throwing an exception HOT 6
- Initializing ApolloClient before DataDog RUM causes GraphQL calls to not have the correct headers HOT 5
- Skipped Query returns outdated data after cache clearing HOT 7
- Merge function at the type level not working (+ suggested fix) HOT 12
- Unexpected Data Fetching with `useBackgroundQuery`, StrictMode, `fetchPolicy`: `network-only` (or `no-cache`), and state changes HOT 5
- When use the Apollo Client server side rendering, How to share the cache in cluster server (k8s)? ( did we have file cache? or db cache? ) HOT 2
- Feedback for schema-driven testing utilities HOT 1
- Add `subscribeToMore` functionality to `useQueryRefHandlers`, `useBackgroundQuery`
- [Docs] Anchor links seem to be stripped HOT 4
- Incomprehensible type mismatch with query generics HOT 3
- Changes to `context` cause refetches in `useQuery`
- Add an option in ApolloClient constructor to return a deep copy of query results (or a way to globally transform results after the caching step) HOT 2
- Potentially unintentionally large AoE breaking changing to typescript `MockedResponse` in type 3.9 HOT 4
- clearStore causes a crash when there is an active query HOT 3
- Getting A on the result.data HOT 1
- BatchHTTPLink is not monitoring friendly HOT 5
- Support extensions in useSubscription HOT 1
- React functionality 'useContext' is not available in this environment [Nextjs app] HOT 3
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from apollo-client.