Skip to content

Commit

Permalink
Merge pull request lfades#92 from lfades/per-page
Browse files Browse the repository at this point in the history
Update packages and support single page integrations
  • Loading branch information
Luis Alvarez D authored Jan 21, 2020
2 parents 28aedb1 + 18c0660 commit 5e281c4
Showing 42 changed files with 3,886 additions and 2,288 deletions.
139 changes: 92 additions & 47 deletions README.md
Original file line number Diff line number Diff line change
@@ -2,99 +2,144 @@

![Actions Status](https://github.com/lfades/next-with-apollo/workflows/Node%20CI/badge.svg)

Apollo HOC for Next.js
Apollo HOC for Next.js.

For `Next v9` use the latest version
For `Next v9` use the latest version.

For `Next v6-v8` use the version `3.4.0`
For `Next v6-v8` use the version `3.4.0`.

For `Next v5` and lower go [here](./README_v1.md) and use the version `1.0`.

## How to use

Install the package with npm
Install the package with npm:

```sh
npm install next-with-apollo
```

or with yarn
or with yarn:

```sh
yarn add next-with-apollo
```

> Note: [apollo-boost](https://github.com/apollographql/apollo-client/tree/master/packages/apollo-boost) is used in this example because is the fastest way to create an `ApolloClient`, but is not required. </br>
> Previously this package had some configs to create an `ApolloClient`, those were removed but you can see an example of how to create the same `ApolloClient` by yourself [here](https://github.com/lfades/next-with-apollo/issues/13#issuecomment-390289449).
Create the HOC using a basic setup and [apollo-boost](https://github.com/apollographql/apollo-client/tree/master/packages/apollo-boost):

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

export default withApollo(
({ ctx, headers, initialState }) =>
new ApolloClient({
uri: GRAPHQL_URL,
({ initialState }) => {
return new ApolloClient({
uri: 'https://mysite.com/graphql',
cache: new InMemoryCache().restore(initialState || {})
})
});
},
{
render: ({ Page, props }) => {
return (
<ApolloProvider client={props.apollo}>
<Page {...props} />
</ApolloProvider>
);
}
}
);
```

`withApollo` accepts a function that receives `{ ctx, headers }` in the first render with SSR (Server Side Rendering). This is done to fetch your queries and [hydrate the store](https://dev-blog.apollodata.com/how-server-side-rendering-works-with-react-apollo-20f31b0c7348)
before we send the page to the browser.
> **Note**: [apollo-boost](https://github.com/apollographql/apollo-client/tree/master/packages/apollo-boost) is used in this example because is the fastest way to create an `ApolloClient`, but is not required. </br>
`withApollo` will receive `{ initialState }` if the render is happening in the browser, with the following line we're hydrating our cache with the initial state created in the server:
> **Note**: If using `react-apollo`, you will need to import the `ApolloProvider` from `react-apollo` instead of `@apollo/react-hooks`.
```js
cache: new InMemoryCache().restore(initialState || {});
```
Now let's use `lib/withApollo.js` in one of our pages:

Now let's wrap Next's `App` in `pages/_app.js`:
```jsx
// pages/index.js
import gql from 'graphql-tag';
import { useQuery } from '@apollo/react-hooks';
import withApollo from '../lib/with-apollo';
// import { getDataFromTree } from '@apollo/react-ssr';

```js
import App from 'next/app';
import { ApolloProvider } from '@apollo/react-hooks';
import withApollo from '../lib/withApollo';
const QUERY = gql`
{
title
}
`;

class MyApp extends App {
render() {
const { Component, pageProps, apollo } = this.props;
const Index = () => {
const { loading, data } = useQuery(QUERY);

return (
<ApolloProvider client={apollo}>
<Component {...pageProps} />
</ApolloProvider>
);
if (loading || !data) {
return <h1>loading...</h1>;
}
}
return <h1>{data.title}</h1>;
};

export default withApollo(Index);

export default withApollo(MyApp);
// You can also override the configs for withApollo here, so if you want
// this page to have SSR (and to be a lambda) for SEO purposes and remove
// the loading state, uncomment the import at the beginning and this:
//
// export default withApollo(Index, { getDataFromTree });
```

> Note: If using `react-apollo`, you will need to import the `ApolloProvider` from `react-apollo` instead of `@apollo/react-hooks`.
Now your page can use anything from `@apollo/react-hooks` or `react-apollo`. If you want to add Apollo in `_app` instead of per page, go to [Using \_app](#using-_app).

## withApollo API

`withApollo` receives 2 parameters, the first one is a function that returns the Apollo Client, this function receives an object with the following properties:

- `ctx` - This is the [context object](https://nextjs.org/docs/api-reference/data-fetching/getInitialProps#context-object) sent by Next.js to the `getInitialProps` of your page. It's only available for SSR, in the client it will be `undefined`
- `initialState` - If `getDataFromTree` is sent, this will be the initial data required by the queries in your page, otherwise it will be `undefined`
- `headers` - This is `ctx.req.headers`, in the client it will be `undefined`.

The second, optional parameter, received by `withApollo`, is an `object` with the following props:

Now every page in `pages/` can use anything from `@apollo/react-hooks` or `react-apollo`. Pages can access the `ApolloClient` too:
- `getDataFromTree` - implementation of [`getDataFromTree`](https://www.apollographql.com/docs/react/api/react-ssr/#getdatafromtree), defaults to `undefined`. **It's recommended to never set this prop**, otherwise the page will be a lambda without [Automatic Static Optimization](https://nextjs.org/docs/advanced-features/automatic-static-optimization)
- `render` - A function that receives and object (`{ Page, props }`) with the current `Page` Component to be rendered, and its `props`. It can be used to wrap your pages with `<ApolloProvider>`. It's optional

```js
### Using `getInitialProps`

Pages with `getInitialProps` can access the Apollo Client like so:

```jsx
Page.getInitialProps = ctx => {
const apolloClient = ctx.apolloClient;
};
```

**withApollo** can also receive some options as second parameter:
Next.js applies very good optimizations by default, including [Automatic Static Optimization](https://nextjs.org/docs/advanced-features/automatic-static-optimization), and as long as the `getDataFromTree` config is not added, your pages will always be static and can be served directly from a CDN, instead of having a serverless function being executed for every new request, which is also more expensive.

If your page has `getDataFromTree` to remove the loading states of Apollo Queries, you should consider handling the loading states by yourself, fetching all queries per request and before sending the initial HTML will slow down the first render, and the user may end up waiting a long time without any feedback.

| Key | Type | Default | Note |
| ----------------- | -------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `getDataFromTree` | `string` | `always` | Should the apollo store be hydrated before the first render?, allowed values are `always`, `never` or `ssr` (don't hydrate on client side navigation) |
## Using \_app

Usage example:
If you want to add Apollo to all pages, you can use `pages/_app.js`, like so:

```js
export default withApollo(() => new ApolloClient({ uri: GRAPHQL_URL }), {
getDataFromTree: 'always'
});
```jsx
import withApollo from 'next-with-apollo';
import { ApolloProvider } from '@apollo/react-hooks';
import ApolloClient, { InMemoryCache } from 'apollo-boost';

const App = ({ Component, pageProps, apollo }) => (
<ApolloProvider client={apollo}>
<Component {...pageProps} />
</ApolloProvider>
);

export default withApollo(({ initialState }) => {
return new ApolloClient({
uri: 'https://mysite.com/graphql',
cache: new InMemoryCache().restore(initialState || {})
});
})(App);
```

It's better to add Apollo in every page instead if you have pages that don't need Apollo.

If you add the `getDataFromTree` config, it will turn all pages into lambdas and disable [Automatic Static Optimization](https://nextjs.org/docs/advanced-features/automatic-static-optimization).
87 changes: 0 additions & 87 deletions integration/integration.test.ts

This file was deleted.

Loading

0 comments on commit 5e281c4

Please sign in to comment.