next-with-apollo
next-with-apollo copied to clipboard
WIP feat: apollo-client@2 → @apollo/client@3
Move from apollo-client@2 to @apollo/client@3 by manual: https://www.apollographql.com/docs/react/v3.0-beta/migrating/apollo-client-3-migration/
Changes work for my project over yarn link
.
How to fix tests? :)
yarn run v1.13.0
$ yarn tslint && yarn build && jest
$ tslint -c tslint.json -p tsconfig.json -t codeFrame
$ tsc
PASS integration/using-app-no-ssr/index.test.ts (11.847s)
● Console
console.log integration/next-test-utils.ts:69
Running command "next build /Users/Valeriy/Workspaces/VTB/next-with-apollo/integration/using-app-no-ssr"
FAIL integration/using-app/index.test.ts (11.876s)
● Console
console.log integration/next-test-utils.ts:69
Running command "next build /Users/Valeriy/Workspaces/VTB/next-with-apollo/integration/using-app"
● Using _app › react-apollo support › loads <Query /> data on the server
expect(received).toContain(expected) // indexOf
Expected substring: "<p>Next Apollo</p>"
Received string: "<!DOCTYPE html><html><head><meta charSet=\"utf-8\"/><meta name=\"viewport\" content=\"width=device-width,minimum-scale=1,initial-scale=1\"/><meta name=\"next-head-cou
nt\" content=\"2\"/><link rel=\"preload\" href=\"/_next/static/vvRYybnUAnpoCcdtTMKi4/pages/index.js\" as=\"script\"/><link rel=\"preload\" href=\"/_next/static/vvRYybnUAnpoCcdtTMKi4/pages/_ap
p.js\" as=\"script\"/><link rel=\"preload\" href=\"/_next/static/runtime/webpack-035ac2b14bde147cb4a8.js\" as=\"script\"/><link rel=\"preload\" href=\"/_next/static/chunks/commons.43c2bb084f9
a1f33defd.js\" as=\"script\"/><link rel=\"preload\" href=\"/_next/static/runtime/main-e56967ff532601537c41.js\" as=\"script\"/></head><body><div id=\"__next\"><p>loading</p></div><script id=\
"__NEXT_DATA__\" type=\"application/json\">{\"dataManager\":\"[]\",\"props\":{\"apolloState\":{\"data\":{\"User:uniqueid\":{\"__typename\":\"User\",\"id\":\"uniqueid\",\"name\":\"Next Apollo\"},\"ROOT_QUERY\":{\"__typename\":\"Query\",\"hire\":{\"__ref\":\"User:uniqueid\"}}}},\"apollo\":null},\"page\":\"/\",\"query\":{},\"buildId\":\"vvRYybnUAnpoCcdtTMKi4\"}</script><script nomodule=\"\" src=\"/_next/static/runtime/polyfills-38a99feee50c8bacb1bb.js\"></script><script async=\"\" data-next-page=\"/\" src=\"/_next/static/vvRYybnUAnpoCcdtTMKi4/pages/index.js\"></script><script async=\"\" data-next-page=\"/_app\" src=\"/_next/static/vvRYybnUAnpoCcdtTMKi4/pages/_app.js\"></script><script src=\"/_next/static/runtime/webpack-035ac2b14bde147cb4a8.js\" async=\"\"></script><script src=\"/_next/static/chunks/commons.43c2bb084f9a1f33defd.js\" async=\"\"></script><script src=\"/_next/static/runtime/main-e56967ff532601537c41.js\" async=\"\"></script></body></html>"
43 | it('loads <Query /> data on the server', async () => {
44 | const html = await renderViaHTTP(appPort, '/');
> 45 | expect(html).toContain('<p>Next Apollo</p>');
| ^
46 |
47 | const { apolloState } = extractNextData(html);
48 | expect(apolloState).toMatchSnapshot();
at using-app/index.test.ts:45:20
at step (using-app/index.test.ts:33:23)
at Object.next (using-app/index.test.ts:14:53)
at fulfilled (using-app/index.test.ts:5:58)
● Using _app › @apollo/react-hooks support › loads useQuery data on the server
expect(received).toContain(expected) // indexOf
Expected substring: "<p>Next Apollo</p>"
Received string: "<!DOCTYPE html><html><head><meta charSet=\"utf-8\"/><meta name=\"viewport\" content=\"width=device-width,minimum-scale=1,initial-scale=1\"/><meta name=\"next-head-count\" content=\"2\"/><link rel=\"preload\" href=\"/_next/static/vvRYybnUAnpoCcdtTMKi4/pages/hooks.js\" as=\"script\"/><link rel=\"preload\" href=\"/_next/static/vvRYybnUAnpoCcdtTMKi4/pages/_app.js\" as=\"script\"/><link rel=\"preload\" href=\"/_next/static/runtime/webpack-035ac2b14bde147cb4a8.js\" as=\"script\"/><link rel=\"preload\" href=\"/_next/static/chunks/commons.43c2bb084f9a1f33defd.js\" as=\"script\"/><link rel=\"preload\" href=\"/_next/static/runtime/main-e56967ff532601537c41.js\" as=\"script\"/></head><body><div id=\"__next\"><p>loading</p></div><script id=\"__NEXT_DATA__\" type=\"application/json\">{\"dataManager\":\"[]\",\"props\":{\"apolloState\":{\"data\":{\"User:uniqueid\":{\"__typename\":\"User\",\"id\":\"uniqueid\",\"name\":\"Next Apollo\"},\"ROOT_QUERY\":{\"__typename\":\"Query\",\"hire\":{\"__ref\":\"User:uniqueid\"}}}},\"apollo\":null},\"page\":\"/hooks\",\"query\":{},\"buildId\":\"vvRYybnUAnpoCcdtTMKi4\"}</script><script nomodule=\"\" src=\"/_next/static/runtime/polyfills-38a99feee50c8bacb1bb.js\"></script><script async=\"\" data-next-page=\"/hooks\" src=\"/_next/static/vvRYybnUAnpoCcdtTMKi4/pages/hooks.js\"></script><script async=\"\" data-next-page=\"/_app\" src=\"/_next/static/vvRYybnUAnpoCcdtTMKi4/pages/_app.js\"></script><script src=\"/_next/static/runtime/webpack-035ac2b14bde147cb4a8.js\" async=\"\"></script><script src=\"/_next/static/chunks/commons.43c2bb084f9a1f33defd.js\" async=\"\"></script><script src=\"/_next/static/runtime/main-e56967ff532601537c41.js\" async=\"\"></script></body></html>"
53 | it('loads useQuery data on the server', async () => {
54 | const html = await renderViaHTTP(appPort, '/hooks');
> 55 | expect(html).toContain('<p>Next Apollo</p>');
| ^
56 |
57 | const { apolloState } = extractNextData(html);
58 | expect(apolloState).toMatchSnapshot();
at using-app/index.test.ts:55:20
at step (using-app/index.test.ts:33:23)
at Object.next (using-app/index.test.ts:14:53)
at fulfilled (using-app/index.test.ts:5:58)
Test Suites: 1 failed, 1 passed, 2 of 4 total
Tests: 2 failed, 4 passed, 6 total
Snapshots: 0 total
Time: 12.209s
Ran all test suites.
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
What's the progress on this issue, would like to upgrade my application to Apollo Client 3.0
@mvantoorn Hello! I use https://www.npmjs.com/package/@sotnikov/next-with-apollo it works.
What's the status of this pull request?
next-with-apollo works fine with Apollo Client 3.0. It's independent of apollo version. What's your problem with Apollo 3? Doesn't work?
@chemicalkosek The current version (5.1.0) still imports from packages like apollo-client
, not @apollo/client
– I'm getting e.g. various TypeScript errors. @sotnikov-link's fork resolves that.
Tips for getting it work
I've successfully set up @sotnikov/next-with-apollo
(this PR) + Apollo Client 3.0 + Next.js 9.4, here are a few notes.
This is my pages/_app.tsx
:
import React from 'react';
import { ApolloProvider, ApolloClient } from '@apollo/client';
import NextApp, { AppProps } from 'next/app';
import { getApolloClient } from '../utils/apolloClient';
import withApollo from '@sotnikov/next-with-apollo';
import { getDataFromTree } from '@apollo/client/react/ssr';
type Props = AppProps & {
apollo: ApolloClient<{}>;
};
const App = ({ Component, pageProps, apollo }: Props) => (
<ApolloProvider client={apollo}>
<Component {...pageProps} />
</ApolloProvider>
);
App.getInitialProps = async (appContext: any) => {
const appProps = await NextApp.getInitialProps(appContext);
return { ...appProps };
};
export default withApollo(getApolloClient, { getDataFromTree })(App);
❗️ getDataFromTree
must be imported from @apollo/client/react/ssr
, not ~~@apollo/react-ssr
~~. I initially forgot that and was getting this error:
GraphQL error occurred [getDataFromTree] Error: Invalid hook call. Hooks can only be called inside of the body of a function component.
Importing from @apollo/client/react/ssr
made the problem go away.
Another gotcha was getting "Loading..." instead of the rendered output from pages/index.js
:
import React from "react";
import { gql, useQuery } from "@apollo/client";
const PRODUCTS_QUERY = gql`
query {
products {
id
name
}
}
`;
const IndexPage = () => {
const { loading, error, data } = useQuery(PRODUCTS_QUERY);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {JSON.stringify(error)}</p>;
return (
<ul>
{data.products.map((p) => (
<li key={p.id}>{p.name}</li>
))}
</ul>
);
};
export default IndexPage;
The problem here is the condition if (loading)
– it should be if (!data && loading)
, see https://github.com/apollographql/apollo-client/issues/6422#issuecomment-650070761.
With that, I got properly rendered SSR page.
Hi @borekb ,
I'm really interested what you did in "import { getApolloClient } from '../utils/apolloClient';" Could you post that file so I can take a look what I'm doing wrong?
I'm stuck and can't get withApollo to work, I'm stuck with Invalid hook call even tho I tried to reproduce what you mentioned in your post.
I would really appreciate it !
This is my version of apolloClient:
import { ApolloClient, ApolloLink, HttpLink, InMemoryCache } from '@apollo/client';
import { useMemo } from 'react';
let apolloClient;
const firstApiGraphqlURL = process.env.FIRST_URL;
const secondApiGraphqlURL = process.env.SECOND_URL;
function createApolloClient(headers) {
const firstApiLink = new HttpLink({
credentials: 'include',
uri: firstApiGraphqlURL,
headers: {
cookie: headers?.cookie,
},
});
const secondApiLink = new HttpLink({
uri: `${secondApiGraphqlURL}/graphql`,
});
return new ApolloClient({
ssrMode: typeof window === 'undefined',
link: ApolloLink.from([
// eslint-disable-next-line no-unused-vars
ApolloLink.split(
operation => operation.getContext().clientName === 'secondApiLink',
firstApiLink,
secondApiLink,
),
]),
cache: new InMemoryCache(),
});
}
export function initializeApollo(initialState = null) {
const _apolloClient = apolloClient ?? createApolloClient();
if (initialState) {
_apolloClient.cache.restore(initialState);
}
if (typeof window === 'undefined') return _apolloClient;
if (!apolloClient) apolloClient = _apolloClient;
return _apolloClient;
}
export function useApollo(initialState) {
const store = useMemo(() => initializeApollo(initialState), [initialState]);
return store;
}
Error that I get is :
Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
> 56 | const store = useMemo(() => initializeApollo(initialState), [initialState]);
| ^
57 | return store;
58 | }
@ilackovic Actually we've moved utils/apolloClient
to _app.tsx
since it's so short, the entire file now looks like this:
import React from 'react';
import { ApolloProvider, ApolloClient, HttpLink, InMemoryCache } from '@apollo/client';
import NextApp, { AppProps } from 'next/app';
import withApollo, { InitApolloOptions } from '@sotnikov/next-with-apollo';
import { getDataFromTree } from '@apollo/client/react/ssr';
type Props = AppProps & {
apollo: ApolloClient<{}>;
};
const App = ({ Component, pageProps, apollo }: Props) => (
<ApolloProvider client={apollo}>
<Component {...pageProps} />
</ApolloProvider>
);
App.getInitialProps = async (appContext: any) => {
const appProps = await NextApp.getInitialProps(appContext);
return { ...appProps };
};
export default withApollo(
({ initialState }: InitApolloOptions<any>): ApolloClient<any> => {
return new ApolloClient({
link: new HttpLink({
uri: 'http://localhost:3000/api/graphql',
}),
cache: new InMemoryCache().restore(initialState || {}),
});
},
{ getDataFromTree }
)(App);
@ilackovic Actually we've moved
utils/apolloClient
to_app.tsx
since it's so short, the entire file now looks like this:import React from 'react'; import { ApolloProvider, ApolloClient, HttpLink, InMemoryCache } from '@apollo/client'; import NextApp, { AppProps } from 'next/app'; import withApollo, { InitApolloOptions } from '@sotnikov/next-with-apollo'; import { getDataFromTree } from '@apollo/client/react/ssr'; type Props = AppProps & { apollo: ApolloClient<{}>; }; const App = ({ Component, pageProps, apollo }: Props) => ( <ApolloProvider client={apollo}> <Component {...pageProps} /> </ApolloProvider> ); App.getInitialProps = async (appContext: any) => { const appProps = await NextApp.getInitialProps(appContext); return { ...appProps }; }; export default withApollo( ({ initialState }: InitApolloOptions<any>): ApolloClient<any> => { return new ApolloClient({ link: new HttpLink({ uri: 'http://localhost:3000/api/graphql', }), cache: new InMemoryCache().restore(initialState || {}), }); }, { getDataFromTree } )(App);
@borekb ,
Thank you for answering !
Sadly, this doesn't work for me, I just can't get SSR to work.
I even created a new Next project and tried to reproduce your code exactly as you posted it, but no luck. My page always renders "Loading" div first (in source code) and initialState from _app.tsx is undefined in all cases.
Since I literally went ahead and copy pasted anything, I really don't understand what I'm missing if it works for you... Any thoughts?
I saw a lot of posts in the last few months where people have issues with SSR and Apollo... It's a shame SSR documentation for this case is practically non-existent or out of date... Next team is pushing people on SSG without the regard of why some of us picked this framework in the first place (SSR obviously)..
I use SSR and it works.
I use SSR and it works.
You are right.
I tweaked the example repo I created and I managed to make it work there.
But, with the same configuration, same query, same package.json, it doesn't work in my main project...
My only conclusion here is that something in my project is blocking SSR somehow.
Thank you for your help !
Any plans to merge this PR?
@Uniiq The PR still has some conflicts and it hasn't been updated since April. Personally I haven't had the time to try out Apollo 3 but I'll be happy to review a PR that adds support for it.
@lfades is there any movement on this? The PR looks really good, and doesn't seem to conflict with the main branch.
@dyyyl Yeah looks like it was updated. I'll try to review it soon and get it merged. In the meantime please remember that the package itself only uses Apollo to get TS types and for tests, so getting this PR merged doesn't change any actual functionality. The package should already work with the latest Apollo version.
In the meantime please remember that the package itself only uses Apollo to get TS types and for tests, so getting this PR merged doesn't change any actual functionality. The package should already work with the latest Apollo version.
@lfades this is the error I get, after cleaning up the package.json and only using "@apollo/client" with yours next-with-apollo
It's dev mode, so I am assuming that you expect apollo-client being installed as peerDependencies. However like I said, when you clean up the obsolete packages you'll face these errors.
Update: "@sotnikov/next-with-apollo" works 🎉
works also for me on "@apollo/client": "^3.2.9" - any plans on merging that soon?
@lfades please merge, this will be helpful !
There there is a package.json
conflicting file only — could be resolved and merged?
Any plans to merge this? Seems to be breaking for me w/r/t this issue: https://github.com/apollographql/apollo-client/issues/9122