@joshroy/relay-nextjs 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +232 -0
- package/package.json +48 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2021 Revere CRE
|
|
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.
|
package/README.md
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
<h1 align="center">
|
|
2
|
+
Relay + Next.js
|
|
3
|
+
</h1>
|
|
4
|
+
|
|
5
|
+
[](https://badge.fury.io/js/@joshroy/relay-nextjs)
|
|
6
|
+

|
|
7
|
+

|
|
8
|
+
|
|
9
|
+
<p align="center">
|
|
10
|
+
<a href="https://reverecre.github.io/relay-nextjs"><b>Documentation</b></a> |
|
|
11
|
+
<a href="https://github.com/RevereCRE/relay-nextjs/discussions"><b>Discussion</b></a> |
|
|
12
|
+
<a href="https://github.com/RevereCRE/relay-nextjs/releases"><b>Latest Releases</b></a>
|
|
13
|
+
</p>
|
|
14
|
+
|
|
15
|
+
<p align="center">
|
|
16
|
+
<code>relay-nextjs</code> is the best way to use Relay and Next.js in the same project! It supports
|
|
17
|
+
<b>incremental migration</b>, is <b>suspense ready</b>, and is <b>run in production</b> by major
|
|
18
|
+
companies.
|
|
19
|
+
</p>
|
|
20
|
+
|
|
21
|
+
## Overview
|
|
22
|
+
|
|
23
|
+
`relay-nextjs` wraps page components, a GraphQL query, and some helper methods
|
|
24
|
+
to automatically hook up data fetching using Relay. On initial load a Relay
|
|
25
|
+
environment is created, the data is fetched server-side, the page is rendered,
|
|
26
|
+
and resulting state is serialized as a script tag. On boot in the client a new
|
|
27
|
+
Relay environment and preloaded query are created using that serialized state.
|
|
28
|
+
Data is fetched using the client-side Relay environment on subsequent
|
|
29
|
+
navigations.
|
|
30
|
+
|
|
31
|
+
Note: `relay-nextjs` does not support [Nextjs 13 App Router](https://nextjs.org/docs/app) at the moment.
|
|
32
|
+
|
|
33
|
+
- See [GitHub issue #89](https://github.com/RevereCRE/relay-nextjs/issues/89) for more info.
|
|
34
|
+
|
|
35
|
+
## Getting Started
|
|
36
|
+
|
|
37
|
+
Install using npm or your other favorite package manager:
|
|
38
|
+
|
|
39
|
+
```sh
|
|
40
|
+
$ npm install @joshroy/relay-nextjs
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
`relay-nextjs` must be configured in `_app` to properly intercept and handle
|
|
44
|
+
routing.
|
|
45
|
+
|
|
46
|
+
### Setting up the Relay Environment
|
|
47
|
+
|
|
48
|
+
For basic information about the Relay environment please see the
|
|
49
|
+
[Relay docs](https://relay.dev/docs/getting-started/step-by-step-guide/#42-configure-relay-runtime).
|
|
50
|
+
|
|
51
|
+
`relay-nextjs` was designed with both client-side and server-side rendering in
|
|
52
|
+
mind. As such it needs to be able to use either a client-side or server-side
|
|
53
|
+
Relay environment. The library knows how to handle which environment to use, but
|
|
54
|
+
we have to tell it how to create these environments. For this we will define two
|
|
55
|
+
functions: `getClientEnvironment` and `createServerEnvironment`. Note the
|
|
56
|
+
distinction — on the client only one environment is ever created because there
|
|
57
|
+
is only one app, but on the server we must create an environment per-render to
|
|
58
|
+
ensure the cache is not shared between requests.
|
|
59
|
+
|
|
60
|
+
First we'll define `getClientEnvironment`:
|
|
61
|
+
|
|
62
|
+
```tsx
|
|
63
|
+
// lib/client_environment.ts
|
|
64
|
+
import { Environment, Network, Store, RecordSource } from 'relay-runtime';
|
|
65
|
+
|
|
66
|
+
export function createClientNetwork() {
|
|
67
|
+
return Network.create(async (params, variables) => {
|
|
68
|
+
const response = await fetch('/api/graphql', {
|
|
69
|
+
method: 'POST',
|
|
70
|
+
credentials: 'include',
|
|
71
|
+
headers: {
|
|
72
|
+
'Content-Type': 'application/json',
|
|
73
|
+
},
|
|
74
|
+
body: JSON.stringify({
|
|
75
|
+
query: params.text,
|
|
76
|
+
variables,
|
|
77
|
+
}),
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const json = await response.text();
|
|
81
|
+
return JSON.parse(json);
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
let clientEnv: Environment | undefined;
|
|
86
|
+
export function getClientEnvironment() {
|
|
87
|
+
if (typeof window === 'undefined') return null;
|
|
88
|
+
|
|
89
|
+
if (clientEnv == null) {
|
|
90
|
+
clientEnv = new Environment({
|
|
91
|
+
network: createClientNetwork(),
|
|
92
|
+
store: new Store(new RecordSource()),
|
|
93
|
+
isServer: false,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return clientEnv;
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
and then `createServerEnvironment`:
|
|
102
|
+
|
|
103
|
+
```tsx
|
|
104
|
+
import { graphql } from 'graphql';
|
|
105
|
+
import { GraphQLResponse, Network } from 'relay-runtime';
|
|
106
|
+
import { schema } from 'lib/schema';
|
|
107
|
+
|
|
108
|
+
export function createServerNetwork() {
|
|
109
|
+
return Network.create(async (text, variables) => {
|
|
110
|
+
const context = {
|
|
111
|
+
token,
|
|
112
|
+
// More context variables here
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const results = await graphql({
|
|
116
|
+
schema,
|
|
117
|
+
source: text.text!,
|
|
118
|
+
variableValues: variables,
|
|
119
|
+
contextValue: context,
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
return JSON.parse(JSON.stringify(results)) as GraphQLResponse;
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function createServerEnvironment() {
|
|
127
|
+
return new Environment({
|
|
128
|
+
network: createServerNetwork(),
|
|
129
|
+
store: new Store(new RecordSource()),
|
|
130
|
+
isServer: true,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Note in the example server environment we’re executing against a local schema
|
|
136
|
+
but you may fetch from a remote API as well.
|
|
137
|
+
|
|
138
|
+
### Configuring `_app`
|
|
139
|
+
|
|
140
|
+
```tsx
|
|
141
|
+
// pages/_app.tsx
|
|
142
|
+
import { RelayEnvironmentProvider } from 'react-relay/hooks';
|
|
143
|
+
import { useRelayNextjs } from '@joshroy/relay-nextjs/app';
|
|
144
|
+
import { getClientEnvironment } from '../lib/client_environment';
|
|
145
|
+
|
|
146
|
+
function MyApp({ Component, pageProps }: AppProps) {
|
|
147
|
+
const { env, ...relayProps } = useRelayNextjs(pageProps, {
|
|
148
|
+
createClientEnvironment: () => getClientSideEnvironment()!,
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
return (
|
|
152
|
+
<>
|
|
153
|
+
<RelayEnvironmentProvider environment={env}>
|
|
154
|
+
<Component {...pageProps} {...relayProps} />
|
|
155
|
+
</RelayEnvironmentProvider>
|
|
156
|
+
</>
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export default MyApp;
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Usage in a Page
|
|
164
|
+
|
|
165
|
+
```tsx
|
|
166
|
+
// src/pages/user/[uuid].tsx
|
|
167
|
+
import { withRelay, RelayProps } from '@joshroy/relay-nextjs';
|
|
168
|
+
import { graphql, usePreloadedQuery } from 'react-relay/hooks';
|
|
169
|
+
|
|
170
|
+
// The $uuid variable is injected automatically from the route.
|
|
171
|
+
const ProfileQuery = graphql`
|
|
172
|
+
query profile_ProfileQuery($uuid: ID!) {
|
|
173
|
+
user(id: $uuid) {
|
|
174
|
+
id
|
|
175
|
+
firstName
|
|
176
|
+
lastName
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
`;
|
|
180
|
+
|
|
181
|
+
function UserProfile({ preloadedQuery }: RelayProps<{}, profile_ProfileQuery>) {
|
|
182
|
+
const query = usePreloadedQuery(ProfileQuery, preloadedQuery);
|
|
183
|
+
|
|
184
|
+
return (
|
|
185
|
+
<div>
|
|
186
|
+
Hello {query.user.firstName} {query.user.lastName}
|
|
187
|
+
</div>
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function Loading() {
|
|
192
|
+
return <div>Loading...</div>;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export default withRelay(UserProfile, UserProfileQuery, {
|
|
196
|
+
// Fallback to render while the page is loading.
|
|
197
|
+
// This property is optional.
|
|
198
|
+
fallback: <Loading />,
|
|
199
|
+
// Create a Relay environment on the client-side.
|
|
200
|
+
// Note: This function must always return the same value.
|
|
201
|
+
createClientEnvironment: () => getClientEnvironment()!,
|
|
202
|
+
// variablesFromContext allows you to declare and customize variables for the graphql query.
|
|
203
|
+
// by default variablesFromContext is ctx.query
|
|
204
|
+
variablesFromContext: (ctx: NextRouter | NextPageContext) => ({ ...ctx.query, otherVariable: true }),
|
|
205
|
+
// Gets server side props for the page.
|
|
206
|
+
serverSideProps: async (ctx) => {
|
|
207
|
+
// This is an example of getting an auth token from the request context.
|
|
208
|
+
// If you don't need to authenticate users this can be removed and return an
|
|
209
|
+
// empty object instead.
|
|
210
|
+
const { getTokenFromCtx } = await import('lib/server/auth');
|
|
211
|
+
const token = await getTokenFromCtx(ctx);
|
|
212
|
+
if (token == null) {
|
|
213
|
+
return {
|
|
214
|
+
redirect: { destination: '/login', permanent: false },
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return { token };
|
|
219
|
+
},
|
|
220
|
+
// Server-side props can be accessed as the second argument
|
|
221
|
+
// to this function.
|
|
222
|
+
createServerEnvironment: async (
|
|
223
|
+
ctx,
|
|
224
|
+
// The object returned from serverSideProps. If you don't need a token
|
|
225
|
+
// you can remove this argument.
|
|
226
|
+
{ token }: { token: string }
|
|
227
|
+
) => {
|
|
228
|
+
const { createServerEnvironment } = await import('lib/server_environment');
|
|
229
|
+
return createServerEnvironment(token);
|
|
230
|
+
},
|
|
231
|
+
});
|
|
232
|
+
```
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@joshroy/relay-nextjs",
|
|
3
|
+
"version": "3.1.0",
|
|
4
|
+
"description": "Use Relay in your Next.js apps!",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "tsc && cp -r dist/* . && rm -rf dist",
|
|
8
|
+
"clean": "rm *.js *.d.ts",
|
|
9
|
+
"format": "prettier --write .",
|
|
10
|
+
"check": "npm run check:prettier",
|
|
11
|
+
"check:prettier": "prettier --check ."
|
|
12
|
+
},
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+https://github.com/JRoy/relay-nextjs.git"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"relay",
|
|
19
|
+
"next",
|
|
20
|
+
"nextjs",
|
|
21
|
+
"graphql"
|
|
22
|
+
],
|
|
23
|
+
"author": "Ryan Delaney <rrdelaney@outlook.com>",
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/lodash.isequal": "^4.5.6",
|
|
27
|
+
"@types/node": "^22.19.11",
|
|
28
|
+
"@types/react": "^19.1.2",
|
|
29
|
+
"@types/react-dom": "^19.1.0",
|
|
30
|
+
"@types/react-relay": "^18.2.1",
|
|
31
|
+
"@types/relay-runtime": "^18.2.5",
|
|
32
|
+
"@types/serialize-javascript": "^5.0.4",
|
|
33
|
+
"graphql": "^15.5.0",
|
|
34
|
+
"next": "^15.5.12",
|
|
35
|
+
"react": "^19.0.0",
|
|
36
|
+
"react-dom": "^19.0.0",
|
|
37
|
+
"prettier": "^2.2.1",
|
|
38
|
+
"typescript": "^5.0.4"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"lodash.isequal": "^4.5.0",
|
|
42
|
+
"serialize-javascript": "^7.0.2"
|
|
43
|
+
},
|
|
44
|
+
"files": [
|
|
45
|
+
"*.js",
|
|
46
|
+
"*.d.ts"
|
|
47
|
+
]
|
|
48
|
+
}
|