next-with-apollo icon indicating copy to clipboard operation
next-with-apollo copied to clipboard

Request: Support next.js 9.3

Open LassiterJ opened this issue 4 years ago • 26 comments

Would like an update for the new version of next.js. Specifically including the new data-fetching methods and examples of use.

LassiterJ avatar Mar 11 '20 17:03 LassiterJ

There's no need for this package, or of any Apollo packages at all if you want to enjoy the goodies of the latest release of Next.js.

For getStaticProps do a simple fetch for the data using a GraphQL query (you can use node-fetch or @zeit/fetch) and return it as props, there's no need to add Apollo for this case.

lfades avatar Mar 11 '20 21:03 lfades

There's no need for this package, or of any Apollo packages at all if you want to enjoy the goodies of the latest release of Next.js.

For getStaticProps do a simple fetch for the data using a GraphQL query (you can use node-fetch or @zeit/fetch) and return it as props, there's no need to add Apollo for this case.

Ok thank you. I wasn't sure if the update would change anything with this package. Especially since its deprecates getInitialProps. Could be nice to mention in docs... But anyways thanks for responding!

LassiterJ avatar Mar 11 '20 21:03 LassiterJ

@LassiterJ getInitialProps is not really deprecated and won't go away anytime soon (I'm part of the Next.js team), the package will stay as it is for now, once we see enough traction for SSG I'll update it.

lfades avatar Mar 12 '20 21:03 lfades

add support with getServerSideProps please

export const getServerSideProps = async ({ query, res }) => {
  const { slug } = query;
  if (!slug && res || slug.length < 2 && res) {
    res.statusCode = 404
  }
// it's necesary for display 404 custom error
  const { data, error } = useQuery(MOVIE_SLUG, { variables: { slug } }); // not work....
  if (data && data.movie === null) {
    res.statusCode = 404;
  }
 ...
};

juanpablocs avatar Mar 20 '20 01:03 juanpablocs

@juanpablocs That is not how you suppose to use useQuery hook. A React hook can only be used in a React component. getServerSideProps is like an HTTP API endpoint and is definitely not React.js.

hoangvvo avatar Apr 04 '20 21:04 hoangvvo

I imagine it would be difficult to make next-with-apollo to work with getServerSideProps because getServerSideProps is completely separated from the React component.

  • AppTree is not available. ssr is impossible.
  • no way to connect getServerSideProps to the the component (to restore cache / apolloState, apollo props).

Correct me if I am wrong @lfades

hoangvvo avatar Apr 04 '20 21:04 hoangvvo

This would be how we may use Apollo Client in getServerSideProps and getStaticProps. This is modified from the example.

Export clientFn (first arg in withApollo):

// lib/withApollo.js
import withApollo from 'next-with-apollo';
import ApolloClient, { InMemoryCache } from 'apollo-boost';
import { ApolloProvider } from '@apollo/react-hooks';

export const clientFn = ({ initialState } = {}) =>
  new ApolloClient({
    uri: 'https://mysite.com/graphql',
    cache: new InMemoryCache().restore(initialState || {}),
  });

export default withApollo(clientFn, {
  render: ({ Page, props }) => {
    return (
      <ApolloProvider client={props.apollo}>
        <Page {...props} />
      </ApolloProvider>
    );
  },
});
// pages/index.js
import gql from 'graphql-tag';
import { useQuery } from '@apollo/react-hooks';
import withApollo, { clientFn } from '../lib/withApollo';

const QUERY = gql`
  {
    title
  }
`;

const Index = () => {
  const { loading, data } = useQuery(QUERY);

  if (loading || !data) {
    return <h1>loading...</h1>;
  }
  return <h1>{data.title}</h1>;
};

export const getServerSideProps = async () => {
  const apolloClient = clientFn();
  // apolloClient.* https://www.apollographql.com/docs/react/api/apollo-client/#apolloclient-functions
  // You do not use hooks like useQuery, useMutation in here
  return {
    props: {
      apollo: apolloClient,
      apolloState: {
        data: apolloClient.cache.extract(),
      },
    },
  };
};

export default withApollo(Index);

hoangvvo avatar Apr 04 '20 23:04 hoangvvo

@hoangvvo You're not wrong, hence why we haven't updated the Apollo examples in the Next.js repo, but that surely will change with time, in the mean time is okay to use getInitialProps, it's not going anywhere

lfades avatar Apr 08 '20 18:04 lfades

@lfades , have you tried using the beta version of the @apollo/client with nextjs 9.3+?

vmptk avatar Apr 22 '20 00:04 vmptk

I haven't, is there something new that requires an update?

I still don't like the size of their package, keeps getting bigger: https://bundlephobia.com/result?p=@apollo/[email protected] :fearful:

lfades avatar Apr 22 '20 16:04 lfades

I just tried it with Next 9.3 and it seems to work. I do see loading state for some of my content when I view source though - unsure if that is related?

wesbos avatar Apr 23 '20 18:04 wesbos

@wesbos going to try Next 9.3.5 / TS with this package.

avxkim avatar Apr 24 '20 11:04 avxkim

@wesbos SSR is now disabled by default unless you manually enable it by importing and adding getDataFromTree to the options:

export default withApollo(Index, { getDataFromTree });

lfades avatar Apr 24 '20 17:04 lfades

Why are you disabling it by default?

And does this lib mirror the official with-apollo next.js example? I'm using that example but I think I should just get rid of it and use this lib.

Thanks!

samuelcastro avatar Apr 25 '20 00:04 samuelcastro

@samuelcastro Feel free to use that example of this lib, you can't go wrong with either (I also helped trying to keep that example updated).

SSR is disabled by default because static pages are faster, better overall, and more reliable, and can also be dynamic when you take advantage of SSG, it just happens that it's harder to do with the current state of the Apollo library. But that surely will be improved in the future.

lfades avatar Apr 25 '20 00:04 lfades

@lfades i've read: https://github.com/zeit/next.js/discussions/10930#discussioncomment-5525 Is this a reason for not switching this library (next-with-apollo) to getServerSideProps?

avxkim avatar Apr 25 '20 07:04 avxkim

@webcoderkz Not really, getServerSideProps is better in the way that it's never executed in the client, so it's only included for the server bundle.

The reason I haven't migrated the library yet is because getInitialProps is a static method inside the page, and you can more easily add an Apollo HOC on top of your pages, If I change it to work with getStaticProps it would take additional steps to get the setup working.

lfades avatar Apr 26 '20 18:04 lfades

This question kind of extends beyond just the use of this library or Apollo, but I'm curious to know: is there a 'best practice' way to do what @juanpablocs mentioned above for dynamically returning a status code from the server using Apollo? I want to return an appropriate status code from the server based on what exists in a CMS and could just use fetch, but I would like to keep Apollo in some places in order to fetchMore on the frontend. Any advice would be greatly appreciated, and apologies if this isn't the right place for this.

charliedieter avatar Apr 28 '20 14:04 charliedieter

After a few hours trying to make this work with @apollo/[email protected], I can see that the actual implementation and examples are kind of working, with next-with-apollo, the request is made on the client event if the data is hydrated, showing a loading state (as @wesbos stated before) (I'm using getDataFromTree), I tested both app and per page HOC. With the example from the next.js page with apollo, it works but I have to explicitly set the ssrMode: true on both server and client (the Boolean(ctx) trick is not working), in order to not make the request again in the client, even with the hydrated data.

batusai513 avatar Apr 28 '20 22:04 batusai513

@CharlieDieter Why do you need to return a status code from the server to the client? what value is that providing for cases where it's not an error?

Overall I would like to just completely change the implementation of Apollo in the Next.js examples as it keeps breaking constantly for some reason :cry:

lfades avatar Apr 29 '20 16:04 lfades

In my case, I have a CMS backend where content creators can build pages with a pathname. So if you go to /example, the page at /pages/[uid].js will make a graphql request for a document with uid: example. That's fine, but then if you go to /asdfasdf and no page is found by uid: asdfsdf in the CMS, the CMS will return an error and that should return a 404 not found error to the client. Another possible example of wanting to change the status code could be to do a 301 or 302 redirect.

charliedieter avatar Apr 29 '20 19:04 charliedieter

I just tried it with Next 9.3 and it seems to work. I do see loading state for some of my content when I view source though - unsure if that is related?

Looks like it is related. I'm having the same issue. When viewing source I see the loading state.

SoorajChandran avatar Apr 30 '20 19:04 SoorajChandran

I'm trying to make getServerSideProps work with apollo with the following code(based on @hoangvvo 's suggestion)


Type Props = user

const UserProfilePage: React.FunctionComponent<Props> = (props) => {
  const router = useRouter()
 // this is for CSR
  const { data, loading, error } = useQuery(USER_QUERY, {
    variables: { userName: router.query.userId },
  })

  if (error) {
    return (
      <Layout title="Something">
        <div className="mt-4">
          <PageLoading />
        </div>
      </Layout>
    )
  }

  if (loading) {
    const user: User = props
    return (
      <Layout
        title={`Something`}
        description={`${user ? user.about : ''}'s profile`}
      >
        {user && <UserDetail user={user} />}
      </Layout>
    )
  }

  if (data) {
    const user: User = data.user
    return (
      <Layout
        title={`${user ? user.name : ''}'s profile`}
        description={`${user ? user.about : ''}`}
      >
        {user && <UserDetail user={user} />}
      </Layout>
    )
  }

  return null
}

export const getServerSideProps: GetServerSideProps = async (context) => {
  const apolloClient = clientFn()
 // this is for SSR
  const response = await apolloClient.query({
    query: USER_QUERY,
    variables: { userName: context.params?.userId },
  })

  const userFromServer = response.data.user
  return { props: userFromServer }
}

export default UserProfilePage

This worked for me. Just in case if anyone is looking.

SoorajChandran avatar Apr 30 '20 20:04 SoorajChandran

@SoorajChandran Isn't this a different implementation from @hoangvvo's proposed one that passes client and apolloState, since it passes the value obtained by SSR directly as props?

km-tr avatar May 07 '20 08:05 km-tr

@SoorajChandran hi i use graphql-code-generator do you known how to generate this code to typescript code ?

 const response = await apolloClient.query({
    query: USER_QUERY,
    variables: { userName: context.params?.userId },
  })

i want to query, variables type checking

ITSWYoo avatar Jun 16 '20 17:06 ITSWYoo

I'm currently evaluating the options to move from getInitialProps to either full getStaticProps(SSG) or a hybrid and based on what I've read in this thread it doesn't seem to be needed? I'm currently setting up the apollo client and then using it in pages to query the data at build time for full SSG and it seems to work, but i'm not able to use cached queries - posted a discussion here if you're interested @lfades : https://github.com/vercel/next.js/discussions/14471

aamorozov avatar Jun 22 '20 21:06 aamorozov