Skip to content

Commit 3b660ff

Browse files
added urql
1 parent 75eae4e commit 3b660ff

File tree

15 files changed

+320
-63
lines changed

15 files changed

+320
-63
lines changed

.env

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
NEXT_PUBLIC_GRAPHQL_ENDPOINT=https://graphqlzero.almansi.me/api

.env.development

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
NEXT_PUBLIC_ENV=development

README.md

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,3 +130,92 @@
130130
}
131131
}
132132
```
133+
134+
### [URQL](https://www.npmjs.com/package/next-urql)
135+
136+
1. install urql and nextjs bindings
137+
138+
```sh
139+
pnpm i urql graphql next-urql react-is @urql/exchange-graphcache
140+
pnpm i -D @urql/devtools
141+
```
142+
143+
1. add `lib/urql/getUrqlClientOptions.ts`
144+
145+
```ts
146+
import { devtoolsExchange } from '@urql/devtools';
147+
import { cacheExchange } from '@urql/exchange-graphcache';
148+
import { NextUrqlClientConfig } from 'next-urql';
149+
import { debugExchange, dedupExchange, fetchExchange } from 'urql';
150+
import getIsClient from 'lib/utils/getIsClient';
151+
152+
const getUrqlClientOptions: NextUrqlClientConfig = (ssrCache) => {
153+
const isClient = typeof window !== 'undefined';
154+
const isProd = process.env.NEXT_PUBLIC_ENV === 'production';
155+
return {
156+
url: process.env.NEXT_PUBLIC_GRAPHQL_ENDPOINT || '',
157+
exchanges: [
158+
...(isClient && !isProd
159+
? [devtoolsExchange, debugExchange]
160+
: []),
161+
dedupExchange,
162+
cacheExchange({}),
163+
ssrCache, // ssrExchange has to come before fetchExchange
164+
fetchExchange,
165+
],
166+
};
167+
};
168+
169+
export default getUrqlClientOptions;
170+
```
171+
172+
1. add graphql query, i.e. `graphql/query/userQuery.ts`
173+
174+
```ts
175+
export const USER_QUERY = `
176+
query {
177+
post(id: 1) {
178+
id
179+
title
180+
body
181+
}
182+
}
183+
`;
184+
```
185+
186+
1. instantiate graphql client in one of the `getStaticProps` or `getServerSideProps` methods
187+
188+
```ts
189+
import type { GetStaticProps } from 'next';
190+
import getUrqlClientOptions from 'lib/urql/getUrqlClientOptions';
191+
import { initUrqlClient } from 'next-urql';
192+
import { USER_QUERY } from 'graphql/query/userQuery';
193+
import { ssrExchange, useQuery } from 'urql';
194+
195+
export const getStaticProps: GetStaticProps<PageProps> = async () => {
196+
const ssrCache = ssrExchange({ isClient: false });
197+
const urqlClientOption = getUrqlClientOptions(ssrCache);
198+
const client = initUrqlClient(urqlClientOption, false);
199+
200+
const result = await client?.query(USER_QUERY).toPromise();
201+
202+
return {
203+
props: {
204+
urqlState: ssrCache.extractData(),
205+
},
206+
revalidate: 600,
207+
};
208+
};
209+
```
210+
211+
1. wrap the page with `withUrqlClient`
212+
213+
```ts
214+
import { withUrqlClient, initUrqlClient, SSRData } from 'next-urql';
215+
216+
export default withUrqlClient(getUrqlClientOptions, {
217+
neverSuspend: true, // don't use Suspend on server side
218+
ssr: false, // don't generate getInitialProps for the page
219+
staleWhileRevalidate: true, // tell client to do network-only data fetching again if the cached data is outdated
220+
})(TestGraphql);
221+
```

__tests__/jest.config.js

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,11 @@ const createJestConfig = nextJest({
55
});
66

77
const customJestConfig = {
8-
setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
9-
moduleDirectories: [
10-
'node_modules',
11-
'<rootDir>/',
12-
],
8+
rootDir: '../',
9+
setupFilesAfterEnv: ['<rootDir>/__tests__/jest.setup.ts'],
10+
moduleDirectories: ['node_modules', '<rootDir>/'],
1311
testRegex: '__tests__/.*\\.test\\.tsx?$',
1412
testEnvironment: 'jest-environment-jsdom',
1513
};
1614

17-
module.exports = createJestConfig(
18-
customJestConfig
19-
);
15+
module.exports = createJestConfig(customJestConfig);

__tests__/pages/__snapshots__/index.test.tsx.snap

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ exports[`Home renders homnepage unchanged 1`] = `
1111
<h1
1212
class="title"
1313
>
14-
Welcome to
15-
14+
Welcome to
1615
<a
1716
href="https://nextjs.org"
1817
>

__tests__/pages/index.test.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
1-
import {
2-
render,
3-
screen,
4-
} from '@testing-library/react';
5-
import Home from '../../pages/index';
1+
import { render, screen } from '@testing-library/react';
2+
import Home from 'pages/index';
63

74
describe('Home', () => {
85
it('renders a heading', () => {

graphql/query/userQuery.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export const USER_QUERY = `
2+
query {
3+
post(id: 1) {
4+
id
5+
title
6+
body
7+
}
8+
}
9+
`;

lib/urql/getUrqlClientOptions.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { devtoolsExchange } from '@urql/devtools';
2+
import { cacheExchange } from '@urql/exchange-graphcache';
3+
import { NextUrqlClientConfig } from 'next-urql';
4+
import { debugExchange, dedupExchange, fetchExchange } from 'urql';
5+
import getIsClient from 'lib/utils/getIsClient';
6+
7+
const getUrqlClientOptions: NextUrqlClientConfig = (ssrCache) => {
8+
const isClient = getIsClient();
9+
const isProd = process.env.NEXT_PUBLIC_ENV === 'production';
10+
return {
11+
url: process.env.NEXT_PUBLIC_GRAPHQL_ENDPOINT || '',
12+
exchanges: [
13+
...(isClient && !isProd ? [devtoolsExchange, debugExchange] : []),
14+
dedupExchange,
15+
cacheExchange({}),
16+
ssrCache, // ssrExchange has to come before fetchExchange
17+
fetchExchange,
18+
],
19+
};
20+
};
21+
22+
export default getUrqlClientOptions;

lib/utils/getIsClient.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
const getIsClient = () => typeof window !== 'undefined';
2+
3+
export default getIsClient;

package.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,22 @@
1111
"test:watch": "jest --config ./__tests__/jest.config.js --watch"
1212
},
1313
"dependencies": {
14+
"@urql/exchange-graphcache": "^4.3.6",
15+
"graphql": "^16.3.0",
1416
"next": "12.1.0",
17+
"next-urql": "^3.3.2",
18+
"normalize.css": "^8.0.1",
1519
"react": "17.0.2",
16-
"react-dom": "17.0.2"
20+
"react-dom": "17.0.2",
21+
"react-is": "^17.0.2",
22+
"urql": "^2.2.0"
1723
},
1824
"devDependencies": {
1925
"@testing-library/jest-dom": "^5.16.2",
2026
"@testing-library/react": "^12.1.4",
2127
"@types/node": "17.0.21",
2228
"@types/react": "17.0.39",
29+
"@urql/devtools": "^2.0.3",
2330
"eslint": "8.10.0",
2431
"eslint-config-next": "12.1.0",
2532
"eslint-config-prettier": "^8.5.0",

pages/_app.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1-
import '../styles/globals.css';
1+
import 'normalize.css';
2+
import 'styles/globals.css';
23
import type { AppProps } from 'next/app';
34

4-
function MyApp({
5-
Component,
6-
pageProps,
7-
}: AppProps) {
5+
function MyApp({ Component, pageProps }: AppProps) {
86
return <Component {...pageProps} />;
97
}
108

pages/index.tsx

Lines changed: 11 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,51 @@
11
import type { NextPage } from 'next';
22
import Head from 'next/head';
33
import Image from 'next/image';
4-
import styles from '../styles/Home.module.css';
4+
import styles from 'styles/Home.module.css';
55

66
const Home: NextPage = () => (
77
<div className={styles.container}>
88
<Head>
99
<title>Create Next App</title>
10-
<meta
11-
name="description"
12-
content="Generated by create next app"
13-
/>
10+
<meta name="description" content="Generated by create next app" />
1411
<link rel="icon" href="/favicon.ico" />
1512
</Head>
1613

1714
<main className={styles.main}>
1815
<h1 className={styles.title}>
19-
Welcome to{' '}
20-
<a href="https://nextjs.org">Next.js!</a>
16+
Welcome to <a href="https://nextjs.org">Next.js!</a>
2117
</h1>
2218

2319
<p className={styles.description}>
2420
Get started by editing{' '}
25-
<code className={styles.code}>
26-
pages/index.tsx
27-
</code>
21+
<code className={styles.code}>pages/index.tsx</code>
2822
</p>
2923

3024
<div className={styles.grid}>
31-
<a
32-
href="https://nextjs.org/docs"
33-
className={styles.card}
34-
>
25+
<a href="https://nextjs.org/docs" className={styles.card}>
3526
<h2>Documentation &rarr;</h2>
36-
<p>
37-
Find in-depth information about
38-
Next.js features and API.
39-
</p>
27+
<p>Find in-depth information about Next.js features and API.</p>
4028
</a>
4129

42-
<a
43-
href="https://nextjs.org/learn"
44-
className={styles.card}
45-
>
30+
<a href="https://nextjs.org/learn" className={styles.card}>
4631
<h2>Learn &rarr;</h2>
47-
<p>
48-
Learn about Next.js in an interactive
49-
course with quizzes!
50-
</p>
32+
<p>Learn about Next.js in an interactive course with quizzes!</p>
5133
</a>
5234

5335
<a
5436
href="https://github.com/vercel/next.js/tree/canary/examples"
5537
className={styles.card}
5638
>
5739
<h2>Examples &rarr;</h2>
58-
<p>
59-
Discover and deploy boilerplate
60-
example Next.js projects.
61-
</p>
40+
<p>Discover and deploy boilerplate example Next.js projects.</p>
6241
</a>
6342

6443
<a
6544
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
6645
className={styles.card}
6746
>
6847
<h2>Deploy &rarr;</h2>
69-
<p>
70-
Instantly deploy your Next.js site to
71-
a public URL with Vercel.
72-
</p>
48+
<p>Instantly deploy your Next.js site to a public URL with Vercel.</p>
7349
</a>
7450
</div>
7551
</main>
@@ -82,12 +58,7 @@ const Home: NextPage = () => (
8258
>
8359
Powered by{' '}
8460
<span className={styles.logo}>
85-
<Image
86-
src="/vercel.svg"
87-
alt="Vercel Logo"
88-
width={72}
89-
height={16}
90-
/>
61+
<Image src="/vercel.svg" alt="Vercel Logo" width={72} height={16} />
9162
</span>
9263
</a>
9364
</footer>

pages/test-graphql.tsx

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import type { GetStaticProps, NextPage } from 'next';
2+
import styles from 'styles/Home.module.css';
3+
import { withUrqlClient, initUrqlClient, SSRData } from 'next-urql';
4+
import { ssrExchange, useQuery } from 'urql';
5+
import { USER_QUERY } from 'graphql/query/userQuery';
6+
import getUrqlClientOptions from 'lib/urql/getUrqlClientOptions';
7+
interface PageProps {
8+
urqlState: SSRData;
9+
}
10+
11+
export const getStaticProps: GetStaticProps<PageProps> = async () => {
12+
const ssrCache = ssrExchange({ isClient: false });
13+
const urqlClientOption = getUrqlClientOptions(ssrCache);
14+
const client = initUrqlClient(urqlClientOption, false);
15+
16+
const result = await client?.query(USER_QUERY).toPromise();
17+
18+
return {
19+
props: {
20+
urqlState: ssrCache.extractData(),
21+
},
22+
revalidate: 600,
23+
};
24+
};
25+
26+
const TestGraphql: NextPage<PageProps> = (props) => {
27+
const [result] = useQuery({ query: USER_QUERY });
28+
return (
29+
<div className={styles.container}>
30+
<h1 className={styles.title}>Graphql Tester</h1>
31+
<div className={styles.grid}>
32+
<div className="graphql-container">
33+
<button className={styles.card} type="button">
34+
try fetch
35+
</button>
36+
<div className="grapql-result">
37+
<p className={styles.code}>{JSON.stringify(result.data)}</p>
38+
</div>
39+
</div>
40+
</div>
41+
<style jsx>{`
42+
.graphql-testing-container {
43+
display: flex;
44+
flex-flow: row nowrap;
45+
justify-content: space-between;
46+
align-items: flex-start;
47+
}
48+
.graphql-testing-container > div {
49+
width: 100%;
50+
border-color: black;
51+
}
52+
button.${styles.card} {
53+
cursor: pointer;
54+
background: transparent;
55+
}
56+
button.${styles.card}:active {
57+
border-color: lightblue;
58+
color: lightblue;
59+
}
60+
`}</style>
61+
</div>
62+
);
63+
};
64+
65+
export default withUrqlClient(getUrqlClientOptions, {
66+
neverSuspend: true, // don't use Suspend on server side
67+
ssr: false, // don't generate getInitialProps for the page
68+
staleWhileRevalidate: true, // tell client to do network-only data fetching again if the cached data is outdated
69+
})(TestGraphql);

0 commit comments

Comments
 (0)