├── .babelrc ├── .eslintrc.js ├── .gitignore ├── .npmignore ├── .prettierrc.js ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── __tests__ ├── init-urql-client.spec.ts └── with-urql-client.spec.tsx ├── assets ├── next_logo.png └── urql_logo.png ├── examples ├── 1-with-urql-client │ ├── .gitignore │ ├── README.md │ ├── components │ │ └── pokemon_list.tsx │ ├── next-env.d.ts │ ├── next.config.js │ ├── package.json │ ├── pages │ │ └── index.tsx │ ├── public │ │ └── types │ │ │ ├── Bug@2x.png │ │ │ ├── Dark@2x.png │ │ │ ├── Dragon@2x.png │ │ │ ├── Electric@2x.png │ │ │ ├── Fairy@2x.png │ │ │ ├── Fight@2x.png │ │ │ ├── Fire@2x.png │ │ │ ├── Flying@2x.png │ │ │ ├── Ghost@2x.png │ │ │ ├── Grass@2x.png │ │ │ ├── Ground@2x.png │ │ │ ├── Ice@2x.png │ │ │ ├── Normal@2x.png │ │ │ ├── Poison@2x.png │ │ │ ├── Psychic@2x.png │ │ │ ├── Rock@2x.png │ │ │ ├── Steel@2x.png │ │ │ └── Water@2x.png │ ├── tsconfig.json │ └── yarn.lock ├── 2-with-_app.js │ ├── .gitignore │ ├── README.md │ ├── components │ │ └── pokemon_list.tsx │ ├── next-env.d.ts │ ├── next.config.js │ ├── package.json │ ├── pages │ │ ├── _app.tsx │ │ └── index.tsx │ ├── public │ │ ├── favicon.ico │ │ └── types │ │ │ ├── Bug@2x.png │ │ │ ├── Dark@2x.png │ │ │ ├── Dragon@2x.png │ │ │ ├── Electric@2x.png │ │ │ ├── Fairy@2x.png │ │ │ ├── Fight@2x.png │ │ │ ├── Fire@2x.png │ │ │ ├── Flying@2x.png │ │ │ ├── Ghost@2x.png │ │ │ ├── Grass@2x.png │ │ │ ├── Ground@2x.png │ │ │ ├── Ice@2x.png │ │ │ ├── Normal@2x.png │ │ │ ├── Poison@2x.png │ │ │ ├── Psychic@2x.png │ │ │ ├── Rock@2x.png │ │ │ ├── Steel@2x.png │ │ │ └── Water@2x.png │ ├── tsconfig.json │ └── yarn.lock └── 3-with-custom-exchange │ ├── .gitignore │ ├── README.md │ ├── components │ └── pokemon_list.tsx │ ├── next-env.d.ts │ ├── next.config.js │ ├── package.json │ ├── pages │ └── index.tsx │ ├── public │ ├── favicon.ico │ └── types │ │ ├── Bug@2x.png │ │ ├── Dark@2x.png │ │ ├── Dragon@2x.png │ │ ├── Electric@2x.png │ │ ├── Fairy@2x.png │ │ ├── Fight@2x.png │ │ ├── Fire@2x.png │ │ ├── Flying@2x.png │ │ ├── Ghost@2x.png │ │ ├── Grass@2x.png │ │ ├── Ground@2x.png │ │ ├── Ice@2x.png │ │ ├── Normal@2x.png │ │ ├── Poison@2x.png │ │ ├── Psychic@2x.png │ │ ├── Rock@2x.png │ │ ├── Steel@2x.png │ │ └── Water@2x.png │ ├── tsconfig.json │ ├── utils │ └── url-exchange.ts │ └── yarn.lock ├── jest.config.js ├── package.json ├── rollup.config.js ├── src ├── index.ts ├── init-urql-client.ts ├── types.ts └── with-urql-client.tsx ├── tsconfig.json └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/env", "@babel/typescript"], 3 | "plugins": [ 4 | "@babel/proposal-object-rest-spread", 5 | [ 6 | "babel-plugin-transform-async-to-promises", 7 | { 8 | "inlineHelpers": true, 9 | "externalHelpers": true 10 | } 11 | ] 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: '@typescript-eslint/parser', 3 | extends: [ 4 | 'plugin:@typescript-eslint/recommended', 5 | 'plugin:react/recommended', 6 | 'plugin:prettier/recommended', 7 | ], 8 | parserOptions: { 9 | ecmaVersion: 2018, 10 | sourceType: 'module', 11 | }, 12 | plugins: ['react-hooks'], 13 | rules: { 14 | '@typescript-eslint/no-explicit-any': 'off', 15 | '@typescript-eslint/explicit-function-return-type': 'off', 16 | 'react/prop-types': 'off', 17 | 'react-hooks/rules-of-hooks': 'error', 18 | 'react-hooks/exhaustive-deps': 'warn', 19 | }, 20 | settings: { 21 | react: { 22 | version: 'detect', 23 | }, 24 | }, 25 | }; 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | next-urql.tgz 5 | .vscode 6 | coverage -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /* 2 | !/src/*.{ts,tsx,d.ts} 3 | !/dist/*.{ts,tsx,d.ts,js.js.map} 4 | !index.d.ts 5 | !react-ssr-prepass.d.ts 6 | !/package.json 7 | !/README.md 8 | !LICENSE 9 | !CHANGELOG.md -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | semi: true, 3 | trailingComma: 'all', 4 | singleQuote: true, 5 | printWidth: 80, 6 | tabWidth: 2, 7 | }; 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | notifications: 2 | email: false 3 | 4 | branches: 5 | only: 6 | - master 7 | - develop 8 | - /^v\d+\.\d+\.\d+/ 9 | 10 | language: node_js 11 | 12 | node_js: 13 | - 10 14 | - 12 15 | 16 | install: 17 | - yarn install --frozen-lockfile --non-interactive 18 | 19 | jobs: 20 | include: 21 | - &checkTs 22 | stage: Check TypeScript 23 | script: 24 | - yarn check:types 25 | - <<: *checkTs 26 | node_js: 12 27 | - &lint 28 | stage: Lint 29 | script: 30 | - yarn lint 31 | - <<: *lint 32 | node_js: 12 33 | - &checkFormatting 34 | stage: Check Formatting 35 | script: 36 | - yarn check:format 37 | - <<: *checkFormatting 38 | node_js: 12 39 | - &build 40 | stage: Build 41 | script: 42 | - yarn build 43 | - <<: *build 44 | node_js: 12 45 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [0.3.0] - 2020-02-20 9 | 10 | This release adds support for different `urql` Client configurations between the client-side and the server-side when using `next-urql`. 11 | 12 | **Warning:** To support this, access to Next's context object, `ctx`, **can only happen on the server**. 13 | 14 | ### Added 15 | 16 | - An example showing how to use a custom exchange with `next-urql`. PR by @ryan-gilb [here](https://github.com/FormidableLabs/next-urql/pull/32). 17 | - Instructions for using `next-urql` with ReasonML. PR by @parkerziegler [here](https://github.com/FormidableLabs/next-urql/pull/28). 18 | 19 | ### Fixed 20 | 21 | - `clientOptions` are no longer serialized inside of `withUrql`'s `getInitialProps` method. This ensures that users can use different Client configurations between the client and server. PR by @parkerziegler [here](https://github.com/FormidableLabs/next-urql/pull/33). 22 | - Proper support for forwarding `pageProps` when using `withUrqlClient` with an `_app.js` component. The `urql` Client instance is also attached to `ctx` for `_app.js` `getInitialProps`. PR by @parkerziegler [here](https://github.com/FormidableLabs/next-urql/pull/38). 23 | - `react-ssr-prepass` dependency upgraded to `1.1.2` to support `urql` `>= 1.9.0`. PR by @JoviDeCroock [here](https://github.com/FormidableLabs/next-urql/pull/37). 24 | 25 | ### Diff 26 | 27 | https://github.com/FormidableLabs/next-urql/compare/v0.2.5...v0.3.0 28 | 29 | ## [0.2.5] - 2020-01-20 30 | 31 | This release encompasses small changes to our TypeScript definitions for `next-urql`, with an upgrade to using `next@9` as the basis for new type definitions in lieu of `@types/next`. The examples were also converted over to TypeScript from JavaScript. 32 | 33 | ### Added 34 | 35 | - All example projects now use TypeScript 🎉 PRs by @ryan-gilb [here](https://github.com/FormidableLabs/next-urql/pull/19) and [here](https://github.com/FormidableLabs/next-urql/pull/21). This gives us stronger guarantees around library types. 36 | 37 | ### Fixed 38 | 39 | - Upgraded type definitions to use types from `next@9`. PR by @ryan-gilb [here](https://github.com/FormidableLabs/next-urql/pull/22). If accessing the `NextContextWithAppTree` `interface`, the name has changed to `NextUrqlContext`. 40 | 41 | ### Diff 42 | 43 | https://github.com/FormidableLabs/next-urql/compare/v0.2.4...v0.2.5 44 | 45 | ## [0.2.4] - 2020-01-08 46 | 47 | This release adds support for accessing the `urqlClient` instance off of Next's context object. 48 | 49 | ### Added 50 | 51 | - `urqlClient` is now added to Next's context object, `ctx`, such that it can be accessed by other components lower in the tree. PR by @BjoernRave [here](https://github.com/FormidableLabs/next-urql/pull/15). 52 | 53 | ### Diff 54 | 55 | https://github.com/FormidableLabs/next-urql/compare/v0.2.3...v0.2.4 56 | 57 | ## [0.2.3] - 2019-12-31 58 | 59 | This release fixes support for using `withUrqlClient` with `_app.js`. 60 | 61 | ### Added 62 | 63 | - Examples are now separated into an `examples` directory. The first, `1-with-urql-client`, shows recommended usage by wrapping a Page component, while the second, `2-with-_app.js` shows how to set up `next-urql` with `_app.js`. 64 | 65 | ### Fixed 66 | 67 | - Be sure to check for `urqlClient` in both direct props and `pageProps` to handle `_app.js` usage with `withUrqlClient`. PR by @bmathews and @parkerziegler [here](https://github.com/FormidableLabs/next-urql/pull/13). 68 | 69 | ### Diff 70 | 71 | https://github.com/FormidableLabs/next-urql/compare/v0.2.2...v0.2.3 72 | 73 | ## [0.2.2] - 2019-12-17 74 | 75 | This release fixes a small discrepancy in the types used by `withUrqlClient` and the public API defined by our `index.d.ts` file. 76 | 77 | ### Fixed 78 | 79 | - Use `NextUrqlClientConfig` in lieu of `NextUrqlClientOptions` in `index.d.ts` to match implementation of `withUrqlClient`. PR by @kylealwyn [here](https://github.com/FormidableLabs/next-urql/pull/9). 80 | 81 | ### Diff 82 | 83 | https://github.com/FormidableLabs/next-urql/compare/v0.2.1...v0.2.2 84 | 85 | ## [0.2.1] - 2019-12-17 86 | 87 | This release fixes a regression introduced in 0.2.0 involving circular structures created bt `withUrqlClient`'s `getInitialProps` method. 88 | 89 | ### Fixed 90 | 91 | - Amend circular structure in `withUrqlClient` caused by returning `ctx` in `getInitialProps`. PR by @parkerziegler [here](https://github.com/FormidableLabs/next-urql/pull/7). 92 | - Fix dependency resolution issues in the `example` project. Update `example` documentation. PR by @parkerziegler [here](https://github.com/FormidableLabs/next-urql/pull/7). 93 | 94 | ### Diff 95 | 96 | https://github.com/FormidableLabs/next-urql/compare/v0.2.0...v0.2.1 97 | 98 | ## [0.2.0] - 2019-12-08 [Deprecated] 99 | 100 | This release adds support for accessing Next's context object, `ctx`, to instantiate your `urql` Client instance. 101 | 102 | ### Added 103 | 104 | - Support for accessing Next's context object, `ctx`, when initializing `withUrqlClient` and creating client options. This should assist users who need to access some data stored in `ctx` to instantiate their `urql` Client instance. PR by @parkerziegler [here](https://github.com/FormidableLabs/next-urql/pull/4). 105 | 106 | ### Diff 107 | 108 | https://github.com/FormidableLabs/next-urql/compare/v0.1.1...v0.2.0 109 | 110 | ## [0.1.1] - 2019-11-11 111 | 112 | This release adds TypeScript definitions to `next-urql`, alongside important pieces like a License (MIT), and improved documentation for users and contributors. 113 | 114 | ### Added 115 | 116 | - TypeScript definitions for the public API of `next-urql` now ship with the library. PR by @parkerziegler [here](https://github.com/FormidableLabs/next-urql/pull/2). 117 | - MIT License. 118 | - Improved README documentation around `withUrqlClient` usage. 119 | - CONTRIBUTING.md to help new contributors to the project get involved. 120 | 121 | ### Diff 122 | 123 | https://github.com/FormidableLabs/next-urql/compare/v0.1.0...v0.1.1 124 | 125 | ## [0.1.0] - 2019-11-02 126 | 127 | This is the initial release of `next-urql` in its Beta API. The package is not meant to be consumed yet, and this purely serves as a prerelease for local testing. 128 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Thanks for contributing! 4 | 5 | ## Development 6 | 7 | ### Installing dependencies 8 | 9 | We use [`yarn`](https://yarnpkg.com/en/docs/getting-started). 10 | 11 | Install all dependencies by running: 12 | 13 | ```sh 14 | $ yarn 15 | ``` 16 | 17 | ### Running the Example Project 18 | 19 | We have an in repo example project in the `/example` directory that can be used to test local changes to `next-urql`. The example works by packing a tarball of the `src` directory using `yarn pack`. All you need to do to get up and running is: 20 | 21 | ```sh 22 | # Navigate to the example directory. 23 | cd example 24 | 25 | # Run the automated install script. 26 | # This will handle setting up your dependencies correctly. 27 | bash install_deps.sh 28 | 29 | # Start the development server. 30 | yarn dev 31 | ``` 32 | 33 | You can also follow the instructions in the example project's [`README`](/example/README.md). 34 | 35 | ### Testing 36 | 37 | Run tests using `yarn test`. 38 | 39 | ### Linting, Formatting, and Type Checking 40 | 41 | To lint your code from the command line: 42 | 43 | ```sh 44 | yarn lint 45 | ``` 46 | 47 | To run Prettier over files from the command line and edit them in place: 48 | 49 | ```sh 50 | yarn format 51 | ``` 52 | 53 | To check types: 54 | 55 | ```sh 56 | yarn check:types 57 | ``` 58 | 59 | ### Before submitting a PR 60 | 61 | Thanks for taking the time to help us make `next-urql` even better! Before you go ahead and submit a PR, make sure that you have done the following: 62 | 63 | - Consider if your changes should be incorporated in the current example project or a new one. Like a new feature, option, etc. Let's try out everything we add! 64 | - Add an `## UNRELEASED` `CHANGELOG.md` entry for later publishing ease. 65 | - Check that all of the examples build: `yarn build-examples`. 66 | - Run all checks using `yarn run check` 67 | 68 | ### Releasing a new version to NPM 69 | 70 | _Only for project administrators_. 71 | 72 | 1. Update `CHANGELOG.md`, following format for previous versions 73 | 2. Commit as "Changes for version VERSION" 74 | 3. Run `npm version patch` (or `minor|major|VERSION`) to run tests and lint, 75 | build published directories, then update `package.json` + add a git tag. 76 | 4. Run `npm publish` and publish to NPM if all is well. 77 | 5. Run `git push && git push --tags` 78 | 79 | ## Contributor Covenant Code of Conduct 80 | 81 | ### Our Pledge 82 | 83 | In the interest of fostering an open and welcoming environment, we as 84 | contributors and maintainers pledge to making participation in our project and 85 | our community a harassment-free experience for everyone, regardless of age, body 86 | size, disability, ethnicity, gender identity and expression, level of experience, 87 | nationality, personal appearance, race, religion, or sexual identity and 88 | orientation. 89 | 90 | ### Our Standards 91 | 92 | Examples of behavior that contributes to creating a positive environment 93 | include: 94 | 95 | - Using welcoming and inclusive language 96 | - Being respectful of differing viewpoints and experiences 97 | - Gracefully accepting constructive criticism 98 | - Focusing on what is best for the community 99 | - Showing empathy towards other community members 100 | 101 | Examples of unacceptable behavior by participants include: 102 | 103 | - The use of sexualized language or imagery and unwelcome sexual attention or 104 | advances 105 | - Trolling, insulting/derogatory comments, and personal or political attacks 106 | - Public or private harassment 107 | - Publishing others' private information, such as a physical or electronic 108 | address, without explicit permission 109 | - Other conduct which could reasonably be considered inappropriate in a 110 | professional setting 111 | 112 | ### Our Responsibilities 113 | 114 | Project maintainers are responsible for clarifying the standards of acceptable 115 | behavior and are expected to take appropriate and fair corrective action in 116 | response to any instances of unacceptable behavior. 117 | 118 | Project maintainers have the right and responsibility to remove, edit, or 119 | reject comments, commits, code, wiki edits, issues, and other contributions 120 | that are not aligned to this Code of Conduct, or to ban temporarily or 121 | permanently any contributor for other behaviors that they deem inappropriate, 122 | threatening, offensive, or harmful. 123 | 124 | ### Scope 125 | 126 | This Code of Conduct applies both within project spaces and in public spaces 127 | when an individual is representing the project or its community. Examples of 128 | representing a project or community include using an official project e-mail 129 | address, posting via an official social media account, or acting as an appointed 130 | representative at an online or offline event. Representation of a project may be 131 | further defined and clarified by project maintainers. 132 | 133 | ### Enforcement 134 | 135 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 136 | reported by contacting the project team at parker.ziegler@formidable.com. All 137 | complaints will be reviewed and investigated and will result in a response that 138 | is deemed necessary and appropriate to the circumstances. The project team is 139 | obligated to maintain confidentiality with regard to the reporter of an incident. 140 | Further details of specific enforcement policies may be posted separately. 141 | 142 | Project maintainers who do not follow or enforce the Code of Conduct in good 143 | faith may face temporary or permanent repercussions as determined by other 144 | members of the project's leadership. 145 | 146 | ### Attribution 147 | 148 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 149 | available at [http://contributor-covenant.org/version/1/4][version] 150 | 151 | [homepage]: http://contributor-covenant.org 152 | [version]: http://contributor-covenant.org/version/1/4/ 153 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Formidable 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Project Moved 2 | 3 | As of March 7, 2020, `next-urql` has been moved into a core package in the [`urql` monorepo](https://github.com/FormidableLabs/urql). In an effort to better consolidate and centralize work on the extended `urql` ecosystem, we at Formidable feels it makes sense to group these projects under a single umbrella. This also promotes better discoverability for contributors and future maintainers. 4 | 5 | All new issues, pull requests, and code discussion for this project should happen there. All new releases will be published from that package. Thanks so much for using `next-urql`! 6 | 7 |
8 | NextJS 9 | urql 10 |
11 | 12 | NPM Version 13 | 14 | 15 | Test Status 16 | 17 |
18 | 19 | ## `next-urql` 20 | 21 | A set of convenience utilities for using `urql` with NextJS. 22 | 23 | ### Motivation 24 | 25 | Using GraphQL with server-side rendering in React is a challenging problem. Currently, React has no support for `Suspense` for data fetching on the server. To get around this, a prepass step can be used to walk the tree (or a subsection of the tree) of your React application and suspend when it encounters thrown `Promise`s. For more information, check out [`react-ssr-prepass`](https://github.com/FormidableLabs/react-ssr-prepass). 26 | 27 | `next-urql` handles integrating this prepass step for you, such that your NextJS application using `urql` will prefetch your GraphQL queries on the server before sending down markup to the Client. 28 | 29 | ### Installation 30 | 31 | Install `next-urql` along with its `peerDependencies`. 32 | 33 | ```sh 34 | npm install --save next-urql urql react-is isomorphic-unfetch 35 | ``` 36 | 37 | `react-is`, `styled-components`, and `isomorphic-unfetch` help to support server-side `Suspense` with `react-ssr-prepass`. This assumes you have followed the basic installation steps for `urql` [here](https://github.com/FormidableLabs/urql#installation). 38 | 39 | ### Usage 40 | 41 | To use `next-urql`, first `import` the `withUrqlClient` higher order component. 42 | 43 | ```javascript 44 | import { withUrqlClient } from 'next-urql'; 45 | ``` 46 | 47 | Then, for any page in your `pages` directory for which you want to prefetch GraphQL queries, wrap the page in `withUrqlClient`. For example, let's say you have an `index.js` page that renders two components that make GraphQL requests using `urql`, `PokemonList` and `PokemonTypes`. To run their queries initially on the server-side you'd do something like the following: 48 | 49 | ```javascript 50 | import React from 'react'; 51 | import Head from 'next/head'; 52 | import { withUrqlClient } from 'next-urql'; 53 | 54 | import PokemonList from '../components/pokemon_list'; 55 | import PokemonTypes from '../components/pokemon_types'; 56 | 57 | const Root = () => ( 58 |
59 | 60 | Root 61 | 62 | 63 | 64 | 65 | 66 |
67 | ); 68 | 69 | export default withUrqlClient({ url: 'https://graphql-pokemon.now.sh' })(Root); 70 | ``` 71 | 72 | Read more below in the [API](#API) section to learn more about the arguments that can be passed to `withUrqlClient`. 73 | 74 | #### Integration with `_app.js` 75 | 76 | Next allows you to override the root of your application using a special page called [`_app.js`](https://nextjs.org/docs#custom-app). If you want to have all GraphQL requests in your application fetched on the server-side, you _could_ wrap the component exported by `_app.js` in `withUrqlClient`. However, be aware that this will opt you out of [automatic static optimization](https://nextjs.org/docs#automatic-static-optimization) for your entire application. In general, it's recommended practice to only use `withUrqlClient` on the pages that have GraphQL operations in their component tree. Read more in the [Caveats](#Caveats) section. Check out our example for using `next-urql` with `_app.js` [here](/examples/2-with-_app.js/README.md). 77 | 78 | ### API 79 | 80 | `next-urql` exposes a single higher order component, `withUrqlClient`. This HoC accepts two arguments: 81 | 82 | #### `clientOptions` (Required) 83 | 84 | The `clientOptions` argument is required. It represents all of the options you want to enable on your `urql` Client instance. It has the following union type: 85 | 86 | ```typescript 87 | type NextUrqlClientConfig = 88 | | Omit 89 | | ((ctx: NextPageContext) => Omit); 90 | ``` 91 | 92 | The `ClientOptions` `interface` comes from `urql` itself and has the following type: 93 | 94 | ```typescript 95 | interface ClientOptions { 96 | /** The GraphQL endpoint your application is using. */ 97 | url: string; 98 | /** Any additional options to pass to fetch. */ 99 | fetchOptions?: RequestInit | (() => RequestInit); 100 | /** An alternative fetch implementation. */ 101 | fetch?: typeof fetch; 102 | /** The exchanges used by the Client. See mergeExchanges below for information on modifying exchanges in next-urql. */ 103 | exchanges?: Exchange[]; 104 | /** A flag to enable suspense on the server. next-urql handles this for you. */ 105 | suspense?: boolean; 106 | /** The default request policy for requests. */ 107 | requestPolicy?: RequestPolicy; 108 | /** Use HTTP GET for queries. */ 109 | preferGetMethod?: boolean; 110 | /** Mask __typename from results. */ 111 | maskTypename?: boolean; 112 | } 113 | ``` 114 | 115 | This means you have two options for creating your `urql` Client. The first involves just passing the options as an object directly: 116 | 117 | ```typescript 118 | withUrqlClient({ 119 | url: 'http://localhost:3000', 120 | fetchOptions: { 121 | referrer: 'no-referrer', 122 | redirect: 'follow', 123 | }, 124 | }); 125 | ``` 126 | 127 | The second involves passing a function, which receives Next's context object, `ctx`, as an argument and returns `urql`'s Client options. This is helpful if you need to access some part of Next's context to instantiate your Client options. **Note: `ctx` is _only_ available on the initial server-side render and _not_ on client-side navigation**. This is necessary to allow for different Client configurations between server and client. 128 | 129 | ```typescript 130 | withUrqlClient(ctx => ({ 131 | url: 'http://localhost:3000', 132 | fetchOptions: { 133 | headers: { 134 | Authorization: ctx 135 | ? `Bearer ${ctx?.req?.headers?.Authorization ?? ''}` 136 | : localStorage.getItem('token') ?? '', 137 | }, 138 | }, 139 | })); 140 | ``` 141 | 142 | In client-side SPAs using `urql`, you typically configure the Client yourself and pass it as the `value` prop to `urql`'s context `Provider`. `withUrqlClient` handles setting all of this up for you under the hood. By default, you'll be opted into server-side `Suspense` and have the necessary `exchanges` set up for you, including the [`ssrExchange`](https://formidable.com/open-source/urql/docs/api/#ssrexchange-exchange-factory). If you need to customize your exchanges beyond the defaults `next-urql` provides, use the second argument to `withUrqlClient`, `mergeExchanges`. 143 | 144 | #### `mergeExchanges` (Optional) 145 | 146 | The `mergeExchanges` argument is optional. This is a function that takes the `ssrExchange` created by `next-urql` as its only argument and allows you to configure your exchanges as you wish. It has the following type signature: 147 | 148 | ```typescript 149 | (ssrExchange: SSRExchange) => Exchange[] 150 | ``` 151 | 152 | By default, `next-urql` will incorprate the `ssrExchange` into your `exchanges` array in the correct location (after any other caching exchanges, but _before_ the `fetchExchange` – read more [here](https://formidable.com/open-source/urql/docs/basics/#setting-up-the-client)). Use this argument if you want to configure your Client with additional custom `exchanges`, or access the `ssrCache` directly to extract or restore data from its cache. 153 | 154 | ### Different Client configurations on the client and the server 155 | 156 | There are use cases where you may need different configurations for your `urql` Client on the client-side and the server-side; for example, you may want to interact with one GraphQL endpoint on the server-side and another on the client-side. `next-urql` supports this as of v0.3.0. We recommend using `typeof window === 'undefined'` or a `process.browser` check. 157 | 158 | ```typescript 159 | withUrqlClient({ 160 | url: 161 | typeof window === 'undefined' 162 | ? 'https://my-server-graphql-api.com/graphql' 163 | : 'https://my-client-graphql-api.com/graphql', 164 | }); 165 | ``` 166 | 167 | If you want more customization of your Client instance to modify requests on specific routes, for instance, consider using a custom exchange. You can find an example of that [here](/examples/3-with-custom-exchange/README.md). 168 | 169 | ### Accessing the `urql` Client inside a Page component's `getInitialProps` method 170 | 171 | There are use cases where you may want to access the `urql` Client instance inside your Page component's `getInitialProps` method. To support this, we actually attach the `urql` Client instance to the `ctx` object passed to your Page's `getInitialProps` method. You can access it like so: 172 | 173 | ```typescript 174 | import { NextUrqlPageContext } from 'next-urql'; 175 | 176 | const Page = () => { 177 | return
; 178 | }; 179 | 180 | Page.getInitialProps = async (ctx: NextUrqlPageContext) => { 181 | // Do something with the urql Client instance! 182 | let client = ctx.urqlClient; 183 | 184 | return { 185 | ...props, 186 | }; 187 | }; 188 | ``` 189 | 190 | ### Usage with ReasonML 191 | 192 | While there are no official bindings for using `next-urql` with ReasonML, porting `next-urql` to Reason is not too difficult. Moreover, having your own bindings means you can select only the pieces you need from the library. Here's an example of how you could bind `next-urql` if you only needed access to the non-functional `clientOptions` API, and only wanted to pass a `url` and `fetchOptions`. This assumes BuckleScript 7 to take advantage of records compiling into plain JS objects and assumes usage of [`bs-fetch`](https://github.com/reasonml-community/bs-fetch). 193 | 194 | ```reason 195 | type clientOptions = { 196 | url: string, 197 | fetchOptions: Fetch.requestInit 198 | }; 199 | 200 | [@bs.module "next-urql"] 201 | external withUrqlClient: 202 | (. clientOptions) => 203 | (. React.component('props)) => React.component('props) = 204 | "withUrqlClient"; 205 | ``` 206 | 207 | Which can then be used like so: 208 | 209 | ```reason 210 | let headers = Fetch.HeadersInit.make({ "Content-Type": "application/json" }); 211 | let client = { 212 | url: "https://mygraphqlapi.com/graphql", 213 | fetchOptions: Fetch.RequestInit.make(~headers, ~method_=POST, ()) 214 | }; 215 | 216 | [@react.component] 217 | let make = () => { 218 |

"Heck yeah, next-urql with Reason!"->React.string

219 | }; 220 | 221 | let default = (withUrqlClient(. clientOptions))(. make); 222 | ``` 223 | 224 | The great part about writing thin bindings like this is that they are zero cost – in fact, the above bindings get totally compiled away by BuckleScript, so you get the full benefits of type safety with absolutely zero runtime cost! 225 | 226 | ### Examples 227 | 228 | You can see simple example projects using `next-urql` in the `examples` directory or on [CodeSandbox](https://codesandbox.io/s/next-urql-pokedex-oqj3x). 229 | 230 | ### Caveats 231 | 232 | `withUrqlClient` implements Next's unique `getInitialProps` method under the hood. This means that any page containing a component wrapped by `withUrqlClient` will be opted out of [automatic static optimization](https://nextjs.org/docs#automatic-static-optimization). Automatic static optimization was added in Next v9, so you shouldn't worry about this if using an earlier version of Next. This is **not** unique to `next-urql` – any implementation of `getInitialProps` by any component in your application will cause Next to opt out of automatic static optimization. 233 | -------------------------------------------------------------------------------- /__tests__/init-urql-client.spec.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ssrExchange, 3 | debugExchange, 4 | dedupExchange, 5 | cacheExchange, 6 | fetchExchange, 7 | } from 'urql'; 8 | 9 | import { initUrqlClient } from '../src/init-urql-client'; 10 | import { SSRData } from 'urql/dist/types/exchanges/ssr'; 11 | 12 | describe('initUrqlClient', () => { 13 | it('should return the urqlClient instance and ssrCache', () => { 14 | const [urqlClient, ssrCache] = initUrqlClient({ 15 | url: 'http://localhost:3000', 16 | }); 17 | 18 | expect(urqlClient).toHaveProperty('url', 'http://localhost:3000'); 19 | expect(urqlClient).toHaveProperty('suspense', true); 20 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion 21 | expect(ssrCache!.toString()).toEqual(ssrExchange().toString()); 22 | }); 23 | 24 | it('should accept an optional mergeExchanges function to allow for exchange composition', () => { 25 | const [urqlClient, ssrCache] = initUrqlClient( 26 | { 27 | url: 'http://localhost:3000', 28 | }, 29 | ssrEx => [ 30 | debugExchange, 31 | dedupExchange, 32 | cacheExchange, 33 | ssrEx, 34 | fetchExchange, 35 | ], 36 | ); 37 | 38 | expect(urqlClient).toHaveProperty('url', 'http://localhost:3000'); 39 | expect(urqlClient).toHaveProperty('suspense', true); 40 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion 41 | expect(ssrCache!.toString()).toEqual(ssrExchange().toString()); 42 | }); 43 | 44 | it('should accept some initial state to populate the cache', () => { 45 | const initialState: SSRData = { 46 | 123: { data: { name: 'Kadabra', type: 'Psychic' } }, 47 | 456: { data: { name: 'Butterfree', type: ['Psychic', 'Bug'] } }, 48 | }; 49 | 50 | const [urqlClient, ssrCache] = initUrqlClient( 51 | { 52 | url: 'http://localhost:3000', 53 | }, 54 | undefined, 55 | initialState, 56 | ); 57 | 58 | expect(urqlClient).toHaveProperty('url', 'http://localhost:3000'); 59 | expect(urqlClient).toHaveProperty('suspense', true); 60 | 61 | const data = ssrCache && ssrCache.extractData(); 62 | expect(data).toEqual(initialState); 63 | }); 64 | }); 65 | -------------------------------------------------------------------------------- /__tests__/with-urql-client.spec.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { shallow, configure } from 'enzyme'; 3 | import Adapter from 'enzyme-adapter-react-16'; 4 | import { Client, defaultExchanges, composeExchanges } from 'urql'; 5 | 6 | import { withUrqlClient, NextUrqlPageContext } from '../src'; 7 | import * as init from '../src/init-urql-client'; 8 | 9 | const MockApp: React.FC = () => { 10 | return
; 11 | }; 12 | 13 | const MockAppTree: React.FC = () => { 14 | return
; 15 | }; 16 | 17 | describe('withUrqlClient', () => { 18 | const spyInitUrqlClient = jest.spyOn(init, 'initUrqlClient'); 19 | const mockMergeExchanges = jest.fn(() => defaultExchanges); 20 | let Component: any; 21 | 22 | beforeAll(() => { 23 | configure({ adapter: new Adapter() }); 24 | }); 25 | 26 | afterEach(() => { 27 | spyInitUrqlClient.mockClear(); 28 | mockMergeExchanges.mockClear(); 29 | }); 30 | 31 | describe('with client options', () => { 32 | beforeEach(() => { 33 | Component = withUrqlClient({ url: 'http://localhost:3000' })(MockApp); 34 | }); 35 | 36 | const mockContext: NextUrqlPageContext = { 37 | AppTree: MockAppTree, 38 | pathname: '/', 39 | query: {}, 40 | asPath: '/', 41 | urqlClient: {} as Client, 42 | }; 43 | 44 | it('should create the client instance when the component mounts', () => { 45 | const tree = shallow(); 46 | const app = tree.find(MockApp); 47 | 48 | expect(app.props().urqlClient).toBeInstanceOf(Client); 49 | expect(app.props().urqlClient.url).toBe('http://localhost:3000'); 50 | expect(spyInitUrqlClient).toHaveBeenCalledTimes(1); 51 | }); 52 | 53 | it('should create the urql client instance server-side inside getInitialProps and client-side in the component', async () => { 54 | const props = 55 | Component.getInitialProps && 56 | (await Component.getInitialProps(mockContext)); 57 | expect(spyInitUrqlClient).toHaveBeenCalledTimes(1); 58 | 59 | const tree = shallow(); 60 | const app = tree.find(MockApp); 61 | 62 | expect(spyInitUrqlClient).toHaveBeenCalledTimes(2); 63 | expect(app.props().urqlClient).toBeInstanceOf(Client); 64 | expect(app.props().urqlClient.url).toEqual('http://localhost:3000'); 65 | }); 66 | }); 67 | 68 | describe('with ctx callback to create client options', () => { 69 | // Simulate a token that might be passed in a request to the server-rendered application. 70 | const token = Math.random() 71 | .toString(36) 72 | .slice(-10); 73 | 74 | const mockContext: NextUrqlPageContext = { 75 | AppTree: MockAppTree, 76 | pathname: '/', 77 | query: {}, 78 | asPath: '/', 79 | req: { 80 | headers: { 81 | cookie: token, 82 | }, 83 | } as NextUrqlPageContext['req'], 84 | urqlClient: {} as Client, 85 | }; 86 | 87 | beforeEach(() => { 88 | Component = withUrqlClient( 89 | ctx => ({ 90 | url: 'http://localhost:3000', 91 | fetchOptions: { 92 | headers: { Authorization: ctx?.req?.headers?.cookie ?? '' }, 93 | }, 94 | }), 95 | mockMergeExchanges, 96 | )(MockApp); 97 | }); 98 | 99 | it('should allow a user to access the ctx object from Next on the server', async () => { 100 | Component.getInitialProps && 101 | (await Component.getInitialProps(mockContext)); 102 | expect(spyInitUrqlClient).toHaveBeenCalledWith( 103 | { 104 | url: 'http://localhost:3000', 105 | fetchOptions: { headers: { Authorization: token } }, 106 | }, 107 | mockMergeExchanges, 108 | ); 109 | }); 110 | }); 111 | 112 | describe('with mergeExchanges provided', () => { 113 | beforeEach(() => { 114 | Component = withUrqlClient( 115 | { url: 'http://localhost:3000' }, 116 | mockMergeExchanges, 117 | )(MockApp); 118 | }); 119 | 120 | it('should call the user-supplied mergeExchanges function', () => { 121 | const tree = shallow(); 122 | const app = tree.find(MockApp); 123 | 124 | expect(app.props().urqlClient).toBeInstanceOf(Client); 125 | expect(app.props().urqlClient.exchange.toString()).toEqual( 126 | composeExchanges(defaultExchanges).toString(), 127 | ); 128 | expect(mockMergeExchanges).toHaveBeenCalledTimes(1); 129 | }); 130 | }); 131 | }); 132 | -------------------------------------------------------------------------------- /assets/next_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/assets/next_logo.png -------------------------------------------------------------------------------- /assets/urql_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/assets/urql_logo.png -------------------------------------------------------------------------------- /examples/1-with-urql-client/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | .env* 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | -------------------------------------------------------------------------------- /examples/1-with-urql-client/README.md: -------------------------------------------------------------------------------- 1 | ## `next-urql` – Page Component Example 2 | 3 | This example is a small reference of how to integrate `next-urql` with your NextJS application by wrapping a Page component. The project is a small Pokedex querying the excellent [Pokemon GraphQL API](https://github.com/lucasbento/graphql-pokemon). 4 | 5 | See this example on [CodeSandbox](https://codesandbox.io/s/next-urql-pokedex-oqj3x). 6 | 7 | ### Installation 8 | 9 | To get the example project running, follow these two steps: 10 | 11 | ```sh 12 | yarn 13 | yarn start 14 | ``` 15 | 16 | The example project should spin up at `http://localhost:3000`. `yarn start` will always run the build of the `next-urql` source, so you should see changes picked up once the dev server boots up. However, if you make changes to the `next-urql` source while the dev server is running, you'll need to run `yarn start` again to see those changes take effect. 17 | -------------------------------------------------------------------------------- /examples/1-with-urql-client/components/pokemon_list.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import gql from 'graphql-tag'; 3 | import { useQuery } from 'urql'; 4 | 5 | const typesToIcon = new Map([ 6 | ['Bug', '/types/Bug@2x.png'], 7 | ['Dark', '/types/Dark@2x.png'], 8 | ['Dragon', '/types/Dragon@2x.png'], 9 | ['Electric', '/types/Electric@2x.png'], 10 | ['Fairy', '/types/Fairy@2x.png'], 11 | ['Fighting', '/types/Fight@2x.png'], 12 | ['Fire', '/types/Fire@2x.png'], 13 | ['Ghost', '/types/Ghost@2x.png'], 14 | ['Grass', '/types/Grass@2x.png'], 15 | ['Ground', '/types/Ground@2x.png'], 16 | ['Ice', '/types/Ice@2x.png'], 17 | ['Normal', '/types/Normal@2x.png'], 18 | ['Poison', '/types/Poison@2x.png'], 19 | ['Psychic', '/types/Psychic@2x.png'], 20 | ['Rock', '/types/Rock@2x.png'], 21 | ['Steel', '/types/Steel@2x.png'], 22 | ['Water', '/types/Water@2x.png'], 23 | ]); 24 | 25 | const queryPokémon = gql` 26 | query pokemon($first: Int!) { 27 | pokemons(first: $first) { 28 | id 29 | name 30 | types 31 | resistant 32 | weaknesses 33 | image 34 | evolutions { 35 | name 36 | } 37 | } 38 | } 39 | `; 40 | 41 | interface PokémonData { 42 | pokemons: Pokémon[]; 43 | } 44 | 45 | interface PokémonVariables { 46 | first: number; 47 | } 48 | 49 | interface Pokémon { 50 | id: string; 51 | name: string; 52 | types: string[]; 53 | resistant: string[]; 54 | weaknesses: string[]; 55 | image: string; 56 | evolutions: { 57 | name: string; 58 | }; 59 | } 60 | 61 | const PokémonList: React.FC = () => { 62 | const [result] = useQuery({ 63 | query: queryPokémon, 64 | variables: { first: 20 }, 65 | }); 66 | 67 | if (result.fetching || !result.data) { 68 | return null; 69 | } 70 | 71 | if (result.error) { 72 | return null; 73 | } 74 | 75 | return ( 76 | <> 77 |
78 | {result.data.pokemons.map(pokémon => ( 79 |
80 | 81 |
82 |

{pokémon.name}

83 |
84 | {pokémon.types 85 | .filter(type => typesToIcon.has(type)) 86 | .map(type => ( 87 |
88 | 92 | 93 | {type} 94 | 95 |
96 | ))} 97 |
98 |
99 |
100 | ))} 101 |
102 | 139 | 140 | ); 141 | }; 142 | 143 | export default PokémonList; 144 | -------------------------------------------------------------------------------- /examples/1-with-urql-client/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /examples/1-with-urql-client/next.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | webpack(config) { 5 | config.resolve.alias.react = path.resolve( 6 | __dirname, 7 | '../../node_modules/react/', 8 | ); 9 | config.resolve.alias['react-dom'] = path.resolve( 10 | __dirname, 11 | '../../node_modules/react-dom/', 12 | ); 13 | config.resolve.alias['react-is'] = path.resolve( 14 | __dirname, 15 | '../../node_modules/react-is/', 16 | ); 17 | config.resolve.alias.urql = path.resolve( 18 | __dirname, 19 | '../../node_modules/urql/', 20 | ); 21 | config.resolve.alias['next-urql'] = path.resolve(__dirname, '../../'); 22 | return config; 23 | }, 24 | }; 25 | -------------------------------------------------------------------------------- /examples/1-with-urql-client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "0.2.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "next dev", 7 | "prestart": "npm run build --prefix ../../" 8 | }, 9 | "dependencies": { 10 | "graphql": "^14.5.8", 11 | "graphql-tag": "^2.10.1", 12 | "isomorphic-unfetch": "^3.0.0", 13 | "next": "^9.2.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /examples/1-with-urql-client/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { NextComponentType } from 'next'; 3 | import Head from 'next/head'; 4 | import { withUrqlClient, NextUrqlPageContext } from 'next-urql'; 5 | import PokémonList from '../components/pokemon_list'; 6 | 7 | interface InitialProps { 8 | title: string; 9 | } 10 | 11 | const Home: NextComponentType< 12 | NextUrqlPageContext, 13 | InitialProps, 14 | InitialProps 15 | > = ({ title }) => ( 16 |
17 | 18 | Home 19 | 20 | 21 |

{title}

22 | 23 |
24 | ); 25 | 26 | Home.getInitialProps = () => { 27 | return { 28 | title: 'Pokédex', 29 | }; 30 | }; 31 | 32 | export default withUrqlClient((ctx: NextUrqlPageContext) => { 33 | return { 34 | url: 'https://graphql-pokemon.now.sh', 35 | fetchOptions: { 36 | headers: { 37 | Authorization: `Bearer ${ctx?.req?.headers?.authorization ?? ''}`, 38 | }, 39 | }, 40 | }; 41 | })(Home); 42 | -------------------------------------------------------------------------------- /examples/1-with-urql-client/public/types/Bug@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/1-with-urql-client/public/types/Bug@2x.png -------------------------------------------------------------------------------- /examples/1-with-urql-client/public/types/Dark@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/1-with-urql-client/public/types/Dark@2x.png -------------------------------------------------------------------------------- /examples/1-with-urql-client/public/types/Dragon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/1-with-urql-client/public/types/Dragon@2x.png -------------------------------------------------------------------------------- /examples/1-with-urql-client/public/types/Electric@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/1-with-urql-client/public/types/Electric@2x.png -------------------------------------------------------------------------------- /examples/1-with-urql-client/public/types/Fairy@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/1-with-urql-client/public/types/Fairy@2x.png -------------------------------------------------------------------------------- /examples/1-with-urql-client/public/types/Fight@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/1-with-urql-client/public/types/Fight@2x.png -------------------------------------------------------------------------------- /examples/1-with-urql-client/public/types/Fire@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/1-with-urql-client/public/types/Fire@2x.png -------------------------------------------------------------------------------- /examples/1-with-urql-client/public/types/Flying@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/1-with-urql-client/public/types/Flying@2x.png -------------------------------------------------------------------------------- /examples/1-with-urql-client/public/types/Ghost@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/1-with-urql-client/public/types/Ghost@2x.png -------------------------------------------------------------------------------- /examples/1-with-urql-client/public/types/Grass@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/1-with-urql-client/public/types/Grass@2x.png -------------------------------------------------------------------------------- /examples/1-with-urql-client/public/types/Ground@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/1-with-urql-client/public/types/Ground@2x.png -------------------------------------------------------------------------------- /examples/1-with-urql-client/public/types/Ice@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/1-with-urql-client/public/types/Ice@2x.png -------------------------------------------------------------------------------- /examples/1-with-urql-client/public/types/Normal@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/1-with-urql-client/public/types/Normal@2x.png -------------------------------------------------------------------------------- /examples/1-with-urql-client/public/types/Poison@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/1-with-urql-client/public/types/Poison@2x.png -------------------------------------------------------------------------------- /examples/1-with-urql-client/public/types/Psychic@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/1-with-urql-client/public/types/Psychic@2x.png -------------------------------------------------------------------------------- /examples/1-with-urql-client/public/types/Rock@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/1-with-urql-client/public/types/Rock@2x.png -------------------------------------------------------------------------------- /examples/1-with-urql-client/public/types/Steel@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/1-with-urql-client/public/types/Steel@2x.png -------------------------------------------------------------------------------- /examples/1-with-urql-client/public/types/Water@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/1-with-urql-client/public/types/Water@2x.png -------------------------------------------------------------------------------- /examples/1-with-urql-client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "paths": { 5 | "next-urql": ["../../"] 6 | }, 7 | "target": "es5", 8 | "lib": ["dom", "dom.iterable", "esnext"], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "strict": false, 12 | "forceConsistentCasingInFileNames": true, 13 | "noEmit": true, 14 | "esModuleInterop": true, 15 | "module": "esnext", 16 | "moduleResolution": "node", 17 | "resolveJsonModule": true, 18 | "isolatedModules": true, 19 | "jsx": "preserve" 20 | }, 21 | "exclude": ["node_modules"], 22 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"] 23 | } 24 | -------------------------------------------------------------------------------- /examples/2-with-_app.js/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | .env* 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | -------------------------------------------------------------------------------- /examples/2-with-_app.js/README.md: -------------------------------------------------------------------------------- 1 | ## `next-urql` - `_app.js` Example 2 | 3 | This example is a small reference of how to integrate `next-urql` with your NextJS application by wrapping the component exported in `_app.js`. The project is a small Pokedex querying the excellent [Pokemon GraphQL API](https://github.com/lucasbento/graphql-pokemon). 4 | 5 | ### Installation 6 | 7 | To get the example project running, follow these two steps: 8 | 9 | ```sh 10 | yarn 11 | yarn start 12 | ``` 13 | 14 | The example project should spin up at `http://localhost:3000`. `yarn start` will always run the build of the `next-urql` source, so you should see changes picked up once the dev server boots up. However, if you make changes to the `next-urql` source while the dev server is running, you'll need to run `yarn start` again to see those changes take effect. 15 | -------------------------------------------------------------------------------- /examples/2-with-_app.js/components/pokemon_list.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import gql from 'graphql-tag'; 3 | import { useQuery } from 'urql'; 4 | 5 | const typesToIcon = new Map([ 6 | ['Bug', '/types/Bug@2x.png'], 7 | ['Dark', '/types/Dark@2x.png'], 8 | ['Dragon', '/types/Dragon@2x.png'], 9 | ['Electric', '/types/Electric@2x.png'], 10 | ['Fairy', '/types/Fairy@2x.png'], 11 | ['Fighting', '/types/Fight@2x.png'], 12 | ['Fire', '/types/Fire@2x.png'], 13 | ['Ghost', '/types/Ghost@2x.png'], 14 | ['Grass', '/types/Grass@2x.png'], 15 | ['Ground', '/types/Ground@2x.png'], 16 | ['Ice', '/types/Ice@2x.png'], 17 | ['Normal', '/types/Normal@2x.png'], 18 | ['Poison', '/types/Poison@2x.png'], 19 | ['Psychic', '/types/Psychic@2x.png'], 20 | ['Rock', '/types/Rock@2x.png'], 21 | ['Steel', '/types/Steel@2x.png'], 22 | ['Water', '/types/Water@2x.png'], 23 | ]); 24 | 25 | const queryPokémon = gql` 26 | query pokemon($first: Int!) { 27 | pokemons(first: $first) { 28 | id 29 | name 30 | types 31 | resistant 32 | weaknesses 33 | image 34 | evolutions { 35 | name 36 | } 37 | } 38 | } 39 | `; 40 | 41 | interface PokémonData { 42 | pokemons: Pokémon[]; 43 | } 44 | 45 | interface PokémonVariables { 46 | first: number; 47 | } 48 | 49 | interface Pokémon { 50 | id: string; 51 | name: string; 52 | types: string[]; 53 | resistant: string[]; 54 | weaknesses: string[]; 55 | image: string; 56 | evolutions: { 57 | name: string; 58 | }; 59 | } 60 | 61 | const PokémonList: React.FC = () => { 62 | const [result] = useQuery({ 63 | query: queryPokémon, 64 | variables: { first: 20 }, 65 | }); 66 | 67 | if (result.fetching || !result.data) { 68 | return null; 69 | } 70 | 71 | if (result.error) { 72 | return null; 73 | } 74 | 75 | return ( 76 | <> 77 |
78 | {result.data.pokemons.map(pokémon => ( 79 |
80 | 81 |
82 |

{pokémon.name}

83 |
84 | {pokémon.types 85 | .filter(type => typesToIcon.has(type)) 86 | .map(type => ( 87 |
88 | 92 | 93 | {type} 94 | 95 |
96 | ))} 97 |
98 |
99 |
100 | ))} 101 |
102 | 139 | 140 | ); 141 | }; 142 | 143 | export default PokémonList; 144 | -------------------------------------------------------------------------------- /examples/2-with-_app.js/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /examples/2-with-_app.js/next.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | webpack(config) { 5 | config.resolve.alias.react = path.resolve( 6 | __dirname, 7 | '../../node_modules/react/', 8 | ); 9 | config.resolve.alias['react-dom'] = path.resolve( 10 | __dirname, 11 | '../../node_modules/react-dom/', 12 | ); 13 | config.resolve.alias['react-is'] = path.resolve( 14 | __dirname, 15 | '../../node_modules/react-is/', 16 | ); 17 | config.resolve.alias.urql = path.resolve( 18 | __dirname, 19 | '../../node_modules/urql/', 20 | ); 21 | config.resolve.alias['next-urql'] = path.resolve(__dirname, '../../'); 22 | return config; 23 | }, 24 | }; 25 | -------------------------------------------------------------------------------- /examples/2-with-_app.js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "2-with-_app.js", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "next dev", 7 | "prestart": "npm run build --prefix ../../" 8 | }, 9 | "dependencies": { 10 | "graphql": "^14.5.8", 11 | "graphql-tag": "^2.10.1", 12 | "isomorphic-unfetch": "^3.0.0", 13 | "next": "^9.2.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /examples/2-with-_app.js/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { withUrqlClient, NextUrqlAppContext } from 'next-urql'; 3 | import NextApp, { AppProps } from 'next/app'; 4 | 5 | const App = ({ Component, pageProps }: AppProps) => { 6 | return ; 7 | }; 8 | 9 | // Implement getInitialProps if your Page components call getInitialProps themselves. 10 | // https://nextjs.org/docs/advanced-features/custom-app 11 | App.getInitialProps = async (ctx: NextUrqlAppContext) => { 12 | const appProps = await NextApp.getInitialProps(ctx); 13 | 14 | return { 15 | ...appProps, 16 | }; 17 | }; 18 | 19 | // eslint-disable-next-line @typescript-eslint/ban-ts-ignore 20 | // @ts-ignore 21 | export default withUrqlClient({ url: 'https://graphql-pokemon.now.sh' })(App); 22 | -------------------------------------------------------------------------------- /examples/2-with-_app.js/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { NextPage } from 'next'; 3 | import Head from 'next/head'; 4 | 5 | import PokémonList from '../components/pokemon_list'; 6 | 7 | interface InitialProps { 8 | title: string; 9 | } 10 | 11 | const Home: NextPage = ({ title }) => { 12 | return ( 13 |
14 | 15 | Home 16 | 17 | 18 |

{title}

19 | 20 |
21 | ); 22 | }; 23 | 24 | Home.getInitialProps = () => { 25 | return { 26 | title: 'Pokédex', 27 | }; 28 | }; 29 | 30 | export default Home; 31 | -------------------------------------------------------------------------------- /examples/2-with-_app.js/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/2-with-_app.js/public/favicon.ico -------------------------------------------------------------------------------- /examples/2-with-_app.js/public/types/Bug@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/2-with-_app.js/public/types/Bug@2x.png -------------------------------------------------------------------------------- /examples/2-with-_app.js/public/types/Dark@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/2-with-_app.js/public/types/Dark@2x.png -------------------------------------------------------------------------------- /examples/2-with-_app.js/public/types/Dragon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/2-with-_app.js/public/types/Dragon@2x.png -------------------------------------------------------------------------------- /examples/2-with-_app.js/public/types/Electric@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/2-with-_app.js/public/types/Electric@2x.png -------------------------------------------------------------------------------- /examples/2-with-_app.js/public/types/Fairy@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/2-with-_app.js/public/types/Fairy@2x.png -------------------------------------------------------------------------------- /examples/2-with-_app.js/public/types/Fight@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/2-with-_app.js/public/types/Fight@2x.png -------------------------------------------------------------------------------- /examples/2-with-_app.js/public/types/Fire@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/2-with-_app.js/public/types/Fire@2x.png -------------------------------------------------------------------------------- /examples/2-with-_app.js/public/types/Flying@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/2-with-_app.js/public/types/Flying@2x.png -------------------------------------------------------------------------------- /examples/2-with-_app.js/public/types/Ghost@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/2-with-_app.js/public/types/Ghost@2x.png -------------------------------------------------------------------------------- /examples/2-with-_app.js/public/types/Grass@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/2-with-_app.js/public/types/Grass@2x.png -------------------------------------------------------------------------------- /examples/2-with-_app.js/public/types/Ground@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/2-with-_app.js/public/types/Ground@2x.png -------------------------------------------------------------------------------- /examples/2-with-_app.js/public/types/Ice@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/2-with-_app.js/public/types/Ice@2x.png -------------------------------------------------------------------------------- /examples/2-with-_app.js/public/types/Normal@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/2-with-_app.js/public/types/Normal@2x.png -------------------------------------------------------------------------------- /examples/2-with-_app.js/public/types/Poison@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/2-with-_app.js/public/types/Poison@2x.png -------------------------------------------------------------------------------- /examples/2-with-_app.js/public/types/Psychic@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/2-with-_app.js/public/types/Psychic@2x.png -------------------------------------------------------------------------------- /examples/2-with-_app.js/public/types/Rock@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/2-with-_app.js/public/types/Rock@2x.png -------------------------------------------------------------------------------- /examples/2-with-_app.js/public/types/Steel@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/2-with-_app.js/public/types/Steel@2x.png -------------------------------------------------------------------------------- /examples/2-with-_app.js/public/types/Water@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/2-with-_app.js/public/types/Water@2x.png -------------------------------------------------------------------------------- /examples/2-with-_app.js/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "paths": { 5 | "next-urql": ["../../"] 6 | }, 7 | "target": "es5", 8 | "lib": ["dom", "dom.iterable", "esnext"], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "strict": false, 12 | "forceConsistentCasingInFileNames": true, 13 | "noEmit": true, 14 | "esModuleInterop": true, 15 | "module": "esnext", 16 | "moduleResolution": "node", 17 | "resolveJsonModule": true, 18 | "isolatedModules": true, 19 | "jsx": "preserve" 20 | }, 21 | "exclude": ["node_modules"], 22 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"] 23 | } 24 | -------------------------------------------------------------------------------- /examples/3-with-custom-exchange/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | .env* 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | -------------------------------------------------------------------------------- /examples/3-with-custom-exchange/README.md: -------------------------------------------------------------------------------- 1 | ## `next-urql` - Custom Exchange Example 2 | 3 | This example is a small reference of how to implement and use a custom exchange in your urql client with `next-urql`. The project is a small Pokedex querying the excellent [Pokemon GraphQL API](https://github.com/lucasbento/graphql-pokemon). 4 | 5 | ### Installation 6 | 7 | To get the example project running, follow these two steps: 8 | 9 | ```sh 10 | yarn 11 | yarn start 12 | ``` 13 | 14 | The example project should spin up at `http://localhost:3000`. `yarn start` will always run the build of the `next-urql` source, so you should see changes picked up once the dev server boots up. However, if you make changes to the `next-urql` source while the dev server is running, you'll need to run `yarn start` again to see those changes take effect. 15 | -------------------------------------------------------------------------------- /examples/3-with-custom-exchange/components/pokemon_list.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import gql from 'graphql-tag'; 3 | import { useQuery } from 'urql'; 4 | 5 | const typesToIcon = new Map([ 6 | ['Bug', '/types/Bug@2x.png'], 7 | ['Dark', '/types/Dark@2x.png'], 8 | ['Dragon', '/types/Dragon@2x.png'], 9 | ['Electric', '/types/Electric@2x.png'], 10 | ['Fairy', '/types/Fairy@2x.png'], 11 | ['Fighting', '/types/Fight@2x.png'], 12 | ['Fire', '/types/Fire@2x.png'], 13 | ['Ghost', '/types/Ghost@2x.png'], 14 | ['Grass', '/types/Grass@2x.png'], 15 | ['Ground', '/types/Ground@2x.png'], 16 | ['Ice', '/types/Ice@2x.png'], 17 | ['Normal', '/types/Normal@2x.png'], 18 | ['Poison', '/types/Poison@2x.png'], 19 | ['Psychic', '/types/Psychic@2x.png'], 20 | ['Rock', '/types/Rock@2x.png'], 21 | ['Steel', '/types/Steel@2x.png'], 22 | ['Water', '/types/Water@2x.png'], 23 | ]); 24 | 25 | const queryPokémon = gql` 26 | query pokemon($first: Int!) { 27 | pokemons(first: $first) { 28 | id 29 | name 30 | types 31 | resistant 32 | weaknesses 33 | image 34 | evolutions { 35 | name 36 | } 37 | } 38 | } 39 | `; 40 | 41 | interface PokémonData { 42 | pokemons: Pokémon[]; 43 | } 44 | 45 | interface PokémonVariables { 46 | first: number; 47 | } 48 | 49 | interface Pokémon { 50 | id: string; 51 | name: string; 52 | types: string[]; 53 | resistant: string[]; 54 | weaknesses: string[]; 55 | image: string; 56 | evolutions: { 57 | name: string; 58 | }; 59 | } 60 | 61 | const PokémonList: React.FC = () => { 62 | const [result] = useQuery({ 63 | query: queryPokémon, 64 | variables: { first: 20 }, 65 | }); 66 | 67 | if (result.fetching || !result.data) { 68 | return null; 69 | } 70 | 71 | if (result.error) { 72 | return null; 73 | } 74 | 75 | return ( 76 | <> 77 |
78 | {result.data.pokemons.map(pokémon => ( 79 |
80 | 81 |
82 |

{pokémon.name}

83 |
84 | {pokémon.types 85 | .filter(type => typesToIcon.has(type)) 86 | .map(type => ( 87 |
88 | 92 | 93 | {type} 94 | 95 |
96 | ))} 97 |
98 |
99 |
100 | ))} 101 |
102 | 139 | 140 | ); 141 | }; 142 | 143 | export default PokémonList; 144 | -------------------------------------------------------------------------------- /examples/3-with-custom-exchange/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /examples/3-with-custom-exchange/next.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | webpack(config) { 5 | config.resolve.alias.react = path.resolve( 6 | __dirname, 7 | '../../node_modules/react/', 8 | ); 9 | config.resolve.alias['react-dom'] = path.resolve( 10 | __dirname, 11 | '../../node_modules/react-dom/', 12 | ); 13 | config.resolve.alias['react-is'] = path.resolve( 14 | __dirname, 15 | '../../node_modules/react-is/', 16 | ); 17 | config.resolve.alias.urql = path.resolve( 18 | __dirname, 19 | '../../node_modules/urql/', 20 | ); 21 | config.resolve.alias['next-urql'] = path.resolve(__dirname, '../../'); 22 | return config; 23 | }, 24 | }; 25 | -------------------------------------------------------------------------------- /examples/3-with-custom-exchange/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "3-with-custom-exchange", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "next dev", 7 | "prestart": "npm run build --prefix ../../" 8 | }, 9 | "dependencies": { 10 | "graphql": "^14.5.8", 11 | "graphql-tag": "^2.10.1", 12 | "isomorphic-unfetch": "^3.0.0", 13 | "next": "^9.2.0", 14 | "wonka": "^4.0.7" 15 | }, 16 | "devDependencies": {} 17 | } 18 | -------------------------------------------------------------------------------- /examples/3-with-custom-exchange/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Head from 'next/head'; 3 | import { withUrqlClient } from 'next-urql'; 4 | import { dedupExchange, cacheExchange, fetchExchange } from 'urql'; 5 | import { SSRExchange } from 'urql/dist/types/exchanges/ssr'; 6 | 7 | import PokémonList from '../components/pokemon_list'; 8 | import { urlExchange } from '../utils/url-exchange'; 9 | 10 | const Home: React.FC = () => ( 11 |
12 | 13 | Home 14 | 15 | 16 | 17 | 18 |
19 | ); 20 | 21 | export default withUrqlClient( 22 | { url: 'https://graphql-pokemon.now.sh' }, 23 | (ssrExchange: SSRExchange) => [ 24 | dedupExchange, 25 | urlExchange, 26 | cacheExchange, 27 | ssrExchange, 28 | fetchExchange, 29 | ], 30 | )(Home); 31 | -------------------------------------------------------------------------------- /examples/3-with-custom-exchange/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/3-with-custom-exchange/public/favicon.ico -------------------------------------------------------------------------------- /examples/3-with-custom-exchange/public/types/Bug@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/3-with-custom-exchange/public/types/Bug@2x.png -------------------------------------------------------------------------------- /examples/3-with-custom-exchange/public/types/Dark@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/3-with-custom-exchange/public/types/Dark@2x.png -------------------------------------------------------------------------------- /examples/3-with-custom-exchange/public/types/Dragon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/3-with-custom-exchange/public/types/Dragon@2x.png -------------------------------------------------------------------------------- /examples/3-with-custom-exchange/public/types/Electric@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/3-with-custom-exchange/public/types/Electric@2x.png -------------------------------------------------------------------------------- /examples/3-with-custom-exchange/public/types/Fairy@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/3-with-custom-exchange/public/types/Fairy@2x.png -------------------------------------------------------------------------------- /examples/3-with-custom-exchange/public/types/Fight@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/3-with-custom-exchange/public/types/Fight@2x.png -------------------------------------------------------------------------------- /examples/3-with-custom-exchange/public/types/Fire@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/3-with-custom-exchange/public/types/Fire@2x.png -------------------------------------------------------------------------------- /examples/3-with-custom-exchange/public/types/Flying@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/3-with-custom-exchange/public/types/Flying@2x.png -------------------------------------------------------------------------------- /examples/3-with-custom-exchange/public/types/Ghost@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/3-with-custom-exchange/public/types/Ghost@2x.png -------------------------------------------------------------------------------- /examples/3-with-custom-exchange/public/types/Grass@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/3-with-custom-exchange/public/types/Grass@2x.png -------------------------------------------------------------------------------- /examples/3-with-custom-exchange/public/types/Ground@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/3-with-custom-exchange/public/types/Ground@2x.png -------------------------------------------------------------------------------- /examples/3-with-custom-exchange/public/types/Ice@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/3-with-custom-exchange/public/types/Ice@2x.png -------------------------------------------------------------------------------- /examples/3-with-custom-exchange/public/types/Normal@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/3-with-custom-exchange/public/types/Normal@2x.png -------------------------------------------------------------------------------- /examples/3-with-custom-exchange/public/types/Poison@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/3-with-custom-exchange/public/types/Poison@2x.png -------------------------------------------------------------------------------- /examples/3-with-custom-exchange/public/types/Psychic@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/3-with-custom-exchange/public/types/Psychic@2x.png -------------------------------------------------------------------------------- /examples/3-with-custom-exchange/public/types/Rock@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/3-with-custom-exchange/public/types/Rock@2x.png -------------------------------------------------------------------------------- /examples/3-with-custom-exchange/public/types/Steel@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/3-with-custom-exchange/public/types/Steel@2x.png -------------------------------------------------------------------------------- /examples/3-with-custom-exchange/public/types/Water@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FormidableLabs/next-urql/e812598a2261491fdf3612202c2ec1ab0f6f9796/examples/3-with-custom-exchange/public/types/Water@2x.png -------------------------------------------------------------------------------- /examples/3-with-custom-exchange/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "paths": { 5 | "next-urql": ["../../"] 6 | }, 7 | "target": "es5", 8 | "lib": ["dom", "dom.iterable", "esnext"], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "strict": false, 12 | "forceConsistentCasingInFileNames": true, 13 | "noEmit": true, 14 | "esModuleInterop": true, 15 | "module": "esnext", 16 | "moduleResolution": "node", 17 | "resolveJsonModule": true, 18 | "isolatedModules": true, 19 | "jsx": "preserve" 20 | }, 21 | "exclude": ["node_modules"], 22 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"] 23 | } 24 | -------------------------------------------------------------------------------- /examples/3-with-custom-exchange/utils/url-exchange.ts: -------------------------------------------------------------------------------- 1 | import { ExchangeInput, Operation } from 'urql'; 2 | import { map, pipe, Source } from 'wonka'; 3 | 4 | export const urlExchange = ({ forward }: ExchangeInput) => { 5 | return (operations$: Source) => { 6 | return pipe( 7 | operations$, 8 | map(op => { 9 | if (process.browser) { 10 | op.context.url = 'https://metaphysics-production.artsy.net/'; 11 | } 12 | return op; 13 | }), 14 | forward, 15 | ); 16 | }; 17 | }; 18 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | testEnvironment: 'node', 4 | globals: { 5 | 'ts-jest': { 6 | babelConfig: true, 7 | }, 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "next-urql", 3 | "version": "0.3.0", 4 | "description": "Convenience wrappers for using urql with NextJS.", 5 | "repository": "https://www.github.com/FormidableLabs/next-urql", 6 | "bugs": { 7 | "url": "https://github.com/FormidableLabs/next-urql/issues" 8 | }, 9 | "main": "dist/next-urql.js", 10 | "module": "dist/next-urql.es.js", 11 | "source": "src/index.ts", 12 | "types": "dist/types/index.d.ts", 13 | "author": "Formidable", 14 | "license": "MIT", 15 | "scripts": { 16 | "build": "rollup -c rollup.config.js", 17 | "check:ci": "run-s check:types lint format test", 18 | "check:format": "prettier --check {src,__tests__,examples}/*.{ts,tsx,d.ts,js}", 19 | "check:types": "tsc --noEmit", 20 | "clean": "rimraf ./dist ./node_modules/.cache", 21 | "format": "prettier --write {src,__tests__,examples}/*.{ts,tsx,d.ts,js}", 22 | "lint": "eslint src/**/*.{ts,tsx}", 23 | "preversion": "run-s clean test build", 24 | "test": "jest" 25 | }, 26 | "devDependencies": { 27 | "@babel/core": "^7.6.4", 28 | "@babel/plugin-proposal-object-rest-spread": "^7.6.2", 29 | "@babel/plugin-transform-object-assign": "^7.8.3", 30 | "@babel/preset-env": "^7.6.3", 31 | "@babel/preset-typescript": "^7.6.0", 32 | "@rollup/plugin-buble": "^0.21.1", 33 | "@rollup/plugin-commonjs": "^11.0.2", 34 | "@rollup/plugin-node-resolve": "^7.1.0", 35 | "@rollup/plugin-replace": "^2.3.1", 36 | "@types/enzyme": "^3.10.3", 37 | "@types/enzyme-adapter-react-16": "^1.0.5", 38 | "@types/jest": "^24.0.19", 39 | "@types/node-fetch": "^2.5.4", 40 | "@types/react": "^16.9.6", 41 | "@types/react-dom": "^16.9.2", 42 | "@typescript-eslint/eslint-plugin": "^2.4.0", 43 | "@typescript-eslint/parser": "^2.4.0", 44 | "babel-plugin-transform-async-to-promises": "^0.8.15", 45 | "enzyme": "^3.10.0", 46 | "enzyme-adapter-react-16": "^1.15.1", 47 | "eslint": "^6.5.1", 48 | "eslint-config-prettier": "^6.4.0", 49 | "eslint-plugin-prettier": "^3.1.1", 50 | "eslint-plugin-react": "^7.16.0", 51 | "eslint-plugin-react-hooks": "^2.3.0", 52 | "graphql": "^14.5.8", 53 | "graphql-tag": "^2.10.1", 54 | "isomorphic-unfetch": "^3.0.0", 55 | "jest": "^24.9.0", 56 | "next": "^9.2.1", 57 | "npm-run-all": "^4.1.5", 58 | "prettier": "^1.18.2", 59 | "react": "^16.8.6", 60 | "react-dom": "^16.8.6", 61 | "react-is": "^16.10.2", 62 | "rimraf": "^3.0.0", 63 | "rollup": "^1.25.0", 64 | "rollup-plugin-babel": "^4.3.3", 65 | "rollup-plugin-terser": "^5.1.2", 66 | "rollup-plugin-typescript2": "^0.24.3", 67 | "styled-components": "^4.4.0", 68 | "terser": "^4.3.9", 69 | "ts-jest": "^25.2.0", 70 | "tslib": "^1.10.0", 71 | "typescript": "^3.6.4", 72 | "urql": "^1.6.1" 73 | }, 74 | "dependencies": { 75 | "react-ssr-prepass": "^1.1.2" 76 | }, 77 | "peerDependencies": { 78 | "isomorphic-unfetch": "^3.0.0", 79 | "react": "^16.8.6", 80 | "react-dom": "^16.8.6", 81 | "react-is": "^16.8.6", 82 | "urql": "^1.1.0" 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/camelcase */ 2 | import commonjs from '@rollup/plugin-commonjs'; 3 | import resolve from '@rollup/plugin-node-resolve'; 4 | import babel from 'rollup-plugin-babel'; 5 | import typescript from 'rollup-plugin-typescript2'; 6 | import { terser } from 'rollup-plugin-terser'; 7 | import replace from '@rollup/plugin-replace'; 8 | import buble from '@rollup/plugin-buble'; 9 | 10 | import pkg from './package.json'; 11 | 12 | const extensions = ['.js', '.jsx', '.ts', '.tsx']; 13 | 14 | const external = []; 15 | 16 | if (pkg.peerDependencies) { 17 | external.push(...Object.keys(pkg.peerDependencies)); 18 | } 19 | 20 | if (pkg.dependencies) { 21 | external.push(...Object.keys(pkg.dependencies)); 22 | } 23 | 24 | const terserPretty = terser({ 25 | warnings: true, 26 | ecma: 5, 27 | keep_fnames: true, 28 | compress: { 29 | conditionals: false, 30 | if_return: false, 31 | join_vars: false, 32 | keep_fnames: true, 33 | loops: false, 34 | pure_getters: true, 35 | toplevel: true, 36 | sequences: false, 37 | }, 38 | mangle: false, 39 | output: { 40 | braces: true, 41 | indent_level: 2, 42 | }, 43 | }); 44 | 45 | const terserMinified = terser({ 46 | warnings: true, 47 | ecma: 5, 48 | toplevel: true, 49 | compress: { 50 | keep_infinity: true, 51 | passes: 10, 52 | pure_getters: true, 53 | }, 54 | }); 55 | 56 | const makePlugins = isProduction => 57 | [ 58 | resolve({ 59 | mainFields: ['module', 'jsnext', 'main'], 60 | browser: true, 61 | }), 62 | commonjs({ 63 | ignoreGlobal: true, 64 | include: /\/node_modules\//, 65 | namedExports: { 66 | // eslint-disable-next-line @typescript-eslint/no-var-requires 67 | react: Object.keys(require('react')), 68 | }, 69 | }), 70 | typescript({ 71 | typescript: require('typescript'), 72 | cacheRoot: './node_modules/.cache/.rts2_cache', 73 | useTsconfigDeclarationDir: true, 74 | tsconfigDefaults: { 75 | compilerOptions: { 76 | sourceMap: true, 77 | }, 78 | }, 79 | tsconfigOverride: { 80 | compilerOptions: { 81 | declaration: !isProduction, 82 | declarationDir: './dist/types', 83 | target: 'es6', 84 | }, 85 | include: ['src/**/*'], 86 | exclude: ['__tests__/**/*'], 87 | }, 88 | }), 89 | buble({ 90 | transforms: { 91 | unicodeRegExp: false, 92 | dangerousForOf: true, 93 | dangerousTaggedTemplateString: true, 94 | generator: false, 95 | }, 96 | objectAssign: 'Object.assign', 97 | exclude: 'node_modules/**', 98 | }), 99 | babel({ 100 | babelrc: false, 101 | extensions, 102 | include: ['src/**/*'], 103 | exclude: 'node_modules/**', 104 | plugins: [ 105 | '@babel/plugin-transform-object-assign', 106 | [ 107 | 'babel-plugin-transform-async-to-promises', 108 | { 109 | inlineHelpers: true, 110 | externalHelpers: true, 111 | }, 112 | ], 113 | ], 114 | }), 115 | isProduction && 116 | replace({ 117 | 'process.env.NODE_ENV': JSON.stringify('production'), 118 | }), 119 | isProduction ? terserMinified : terserPretty, 120 | ].filter(Boolean); 121 | 122 | export default [ 123 | { 124 | input: './src/index.ts', 125 | external, 126 | plugins: makePlugins(false), 127 | output: [ 128 | { 129 | file: './dist/next-urql.js', 130 | format: 'cjs', 131 | }, 132 | { 133 | file: './dist/next-urql.es.js', 134 | format: 'esm', 135 | }, 136 | ], 137 | }, 138 | { 139 | input: './src/index.ts', 140 | external, 141 | plugins: makePlugins(true), 142 | output: [ 143 | { 144 | file: './dist/next-urql.min.js', 145 | format: 'cjs', 146 | }, 147 | { 148 | file: './dist/next-urql.es.min.js', 149 | format: 'esm', 150 | }, 151 | ], 152 | }, 153 | ]; 154 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export { withUrqlClient } from './with-urql-client'; 2 | export * from './types'; 3 | -------------------------------------------------------------------------------- /src/init-urql-client.ts: -------------------------------------------------------------------------------- 1 | import { 2 | createClient, 3 | dedupExchange, 4 | cacheExchange, 5 | fetchExchange, 6 | ssrExchange, 7 | Client, 8 | Exchange, 9 | } from 'urql'; 10 | import { SSRData, SSRExchange } from 'urql/dist/types/exchanges/ssr'; 11 | import 'isomorphic-unfetch'; 12 | 13 | import { NextUrqlClientOptions } from './types'; 14 | 15 | let urqlClient: Client | null = null; 16 | let ssrCache: SSRExchange | null = null; 17 | 18 | export function initUrqlClient( 19 | clientOptions: NextUrqlClientOptions, 20 | mergeExchanges: (ssrEx: SSRExchange) => Exchange[] = ssrEx => [ 21 | dedupExchange, 22 | cacheExchange, 23 | ssrEx, 24 | fetchExchange, 25 | ], 26 | initialState?: SSRData, 27 | ): [Client | null, SSRExchange | null] { 28 | // Create a new Client for every server-side rendered request. 29 | // This ensures we reset the state for each rendered page. 30 | // If there is an exising client instance on the client-side, use it. 31 | const isServer = typeof window === 'undefined'; 32 | if (isServer || !urqlClient) { 33 | ssrCache = ssrExchange({ initialState }); 34 | 35 | urqlClient = createClient({ 36 | ...clientOptions, 37 | suspense: isServer, 38 | exchanges: mergeExchanges(ssrCache), 39 | }); 40 | } 41 | 42 | // Return both the Client instance and the ssrCache. 43 | return [urqlClient, ssrCache]; 44 | } 45 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | import { NextPageContext } from 'next'; 2 | import { ClientOptions, Exchange, Client } from 'urql'; 3 | import { SSRExchange, SSRData } from 'urql/dist/types/exchanges/ssr'; 4 | import { AppContext } from 'next/app'; 5 | 6 | export type NextUrqlClientOptions = Omit< 7 | ClientOptions, 8 | 'exchanges' | 'suspense' 9 | >; 10 | 11 | export type NextUrqlClientConfig = 12 | | NextUrqlClientOptions 13 | | ((ctx?: NextPageContext) => NextUrqlClientOptions); 14 | 15 | export type MergeExchanges = (ssrExchange: SSRExchange) => Exchange[]; 16 | 17 | export interface NextUrqlPageContext extends NextPageContext { 18 | urqlClient: Client; 19 | } 20 | 21 | export interface NextUrqlAppContext extends AppContext { 22 | urqlClient: Client; 23 | } 24 | 25 | export type NextUrqlContext = NextUrqlPageContext | NextUrqlAppContext; 26 | 27 | export interface WithUrqlState { 28 | urqlState?: SSRData; 29 | } 30 | 31 | export interface WithUrqlClient { 32 | urqlClient: Client; 33 | } 34 | 35 | export interface WithUrqlProps extends WithUrqlClient, WithUrqlState { 36 | [key: string]: any; 37 | } 38 | -------------------------------------------------------------------------------- /src/with-urql-client.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { NextPage, NextPageContext } from 'next'; 3 | import NextApp, { AppContext } from 'next/app'; 4 | import ssrPrepass from 'react-ssr-prepass'; 5 | import { Provider, dedupExchange, cacheExchange, fetchExchange } from 'urql'; 6 | 7 | import { initUrqlClient } from './init-urql-client'; 8 | import { 9 | NextUrqlClientConfig, 10 | MergeExchanges, 11 | NextUrqlContext, 12 | WithUrqlProps, 13 | } from './types'; 14 | 15 | function getDisplayName(Component: React.ComponentType) { 16 | return Component.displayName || Component.name || 'Component'; 17 | } 18 | 19 | export function withUrqlClient( 20 | clientConfig: NextUrqlClientConfig, 21 | mergeExchanges: MergeExchanges = ssrExchange => [ 22 | dedupExchange, 23 | cacheExchange, 24 | ssrExchange, 25 | fetchExchange, 26 | ], 27 | ) { 28 | return (AppOrPage: NextPage | typeof NextApp) => { 29 | const withUrql = ({ urqlClient, urqlState, ...rest }: WithUrqlProps) => { 30 | // eslint-disable-next-line react-hooks/rules-of-hooks 31 | const client = React.useMemo(() => { 32 | const clientOptions = 33 | typeof clientConfig === 'function' ? clientConfig() : clientConfig; 34 | 35 | return ( 36 | urqlClient || 37 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion 38 | initUrqlClient(clientOptions, mergeExchanges, urqlState)[0]! 39 | ); 40 | }, [urqlClient, urqlState]); 41 | 42 | return ( 43 | 44 | 45 | 46 | ); 47 | }; 48 | 49 | // Set the displayName to indicate use of withUrqlClient. 50 | withUrql.displayName = `withUrqlClient(${getDisplayName(AppOrPage)})`; 51 | 52 | withUrql.getInitialProps = async (appOrPageCtx: NextUrqlContext) => { 53 | const { AppTree } = appOrPageCtx; 54 | 55 | // Determine if we are wrapping an App component or a Page component. 56 | const isApp = !!(appOrPageCtx as AppContext).Component; 57 | const ctx = isApp 58 | ? (appOrPageCtx as AppContext).ctx 59 | : (appOrPageCtx as NextPageContext); 60 | 61 | const opts = 62 | typeof clientConfig === 'function' ? clientConfig(ctx) : clientConfig; 63 | const [urqlClient, ssrCache] = initUrqlClient(opts, mergeExchanges); 64 | 65 | if (urqlClient) { 66 | (ctx as NextUrqlContext).urqlClient = urqlClient; 67 | } 68 | 69 | // Run the wrapped component's getInitialProps function. 70 | let pageProps; 71 | if (AppOrPage.getInitialProps) { 72 | pageProps = await AppOrPage.getInitialProps(appOrPageCtx as any); 73 | } 74 | 75 | // Check the window object to determine whether or not we are on the server. 76 | // getInitialProps runs on the server for initial render, and on the client for navigation. 77 | // We only want to run the prepass step on the server. 78 | if (typeof window !== 'undefined') { 79 | return pageProps; 80 | } 81 | 82 | const props = { ...pageProps, urqlClient }; 83 | const appTreeProps = isApp ? props : { pageProps: props }; 84 | 85 | // Run the prepass step on AppTree. This will run all urql queries on the server. 86 | await ssrPrepass(); 87 | 88 | return { 89 | ...pageProps, 90 | urqlState: ssrCache?.extractData(), 91 | }; 92 | }; 93 | 94 | return withUrql; 95 | }; 96 | } 97 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "esModuleInterop": true, 5 | "jsx": "react", 6 | "lib": ["dom", "esnext"], 7 | "module": "es2015", 8 | "moduleResolution": "node", 9 | "noImplicitAny": true, 10 | "noUnusedLocals": true, 11 | "noUnusedParameters": true, 12 | "outDir": "dist", 13 | "pretty": true, 14 | "rootDir": "./src", 15 | "sourceMap": true, 16 | "strict": true, 17 | "target": "esnext" 18 | }, 19 | "include": ["src/**/*.ts", "src/**/*.tsx"] 20 | } 21 | --------------------------------------------------------------------------------