Skip to main content

relay

  • This example shows:
    • how the data fetching and store initialization logic look like
    • how /graphql middleware can be included in the webpack.config.js
    • how the app can switch to client side rendering with a querystring ?ssr=false

1. Check webpack.config.js

examples/relay/webpack.config.js
...
const graphql = require("./graphql");
...

module.exports = (env, argv) => {
const PROD = argv.mode === "production";
const version = `manifest.${argv.mode}`;

return {
"devServer": {
...
"setupMiddlewares": (middlewares, devServer) => {
graphql(devServer); // check examples/relay/graphql/index.js

return [
...middlewares,
{
"name": "ReactSSRWebpackPlugin",
"path": "*",
"middleware": ReactSSRMiddleware(
devServer.compiler,
{
"reqToProps": (req) => ({
"port": req.socket.localPort, // for relay.test.js
"host": req.socket.localAddress, // for relay.test.js
"url": url.parse(req.originalUrl, true),
}),
version,
"patchGlobal": (global) => {
global.fetch = require("node-fetch");
},
}
),
},
];
},
},
...
}
note

graphql endpoint can be registered inside webpack.config.js

2. Check index.node.jsx

export default async (props) => {
const sheet = new ServerStyleSheet();

const ssr = props.url.query.ssr !== "false"; // this allows clients to perform client side rendering with ?ssr=false without a rebuild
const graphqlUrl = `http://${props.host}:${props.port}/graphql`;
const network = new RelayNetworkLayer([
urlMiddleware({
"url": graphqlUrl,
}),
]);
const environment = createEnvironment({
"isServer": true,
network,
});
const variables = {
"id": props.id || "3j881qym",
};

ssr && (await fetchQuery(environment, rootQuery, variables).toPromise()); // this performs the data fetching
const __html = ssr ? renderToString(sheet.collectStyles(<App environment={environment} variables={variables} />)) : "";
const body = "<!DOCTYPE html>" + renderToStaticMarkup(<html>
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<link rel="icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkqAcAAIUAgUW0RjgAAAAASUVORK5CYII="></link>

<script dangerouslySetInnerHTML={{"__html": [
`globalThis.__RELAY_STORE__ = ${JSON.stringify(ssr ? environment.getStore().getSource().toJSON() : {}).replace(/</g, "\\u003c")};`, // this performs the store initialization for hydration
`globalThis.__PROPS__ = ${JSON.stringify({...props, variables, "hydrate": ssr}).replace(/</g, "\\u003c")};`,
`globalThis.__GRAPHQL_URL__ = ${JSON.stringify(graphqlUrl)};`,
].join("")}} />

<script dangerouslySetInnerHTML={{"__html": __SOURCES__["index.js"]}} />
{ssr ? sheet.getStyleElement() : ""}
</head>
<body>
<div id="root" dangerouslySetInnerHTML={{"__html": __html}}></div>
<script integrity={__DIGESTS__["vendors.js"]} src={`${__webpack_public_path__}${__FILES__["vendors.js"]}`} crossOrigin="anonymous" />
</body>
</html>);

return {
body,
"statusCode": 200,
};
};

3. Build and run it

npm run relay-compiler
npm run relay:start

4a. Inspect http://127.0.0.1:8080/index



<!DOCTYPE html>
<html>
<head>
<meta charSet="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<link rel="icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkqAcAAIUAgUW0RjgAAAAASUVORK5CYII="/>
<script>
// the store initialization -->
globalThis.__RELAY_STORE__ = {
"client:root": {
"__id": "client:root",
"__typename": "__Root",
"config(id:\"3j881qym\")": {
"__ref": "3j881qym"
}
},
...
};
...
// both client and server are using the same graphql endpoint
globalThis.__GRAPHQL_URL__ = "http://127.0.0.1:8080/graphql";
</script>
...
</head>
<body>
<div id="root">
<!--$-->
<!-- the body rendered by the server -->
<div class="Navigation__Div-sc-3tl8bl-0 iQCpPf">
<a href="//apple.com/mac" class="NavigationItem__A-sc-n3rxo5-0 gjLZxB">Mac</a>
<a href="//apple.com/ipad" class="NavigationItem__A-sc-n3rxo5-0 gjLZxB">iPad</a>
<a href="//apple.com/iphone" class="NavigationItem__A-sc-n3rxo5-0 gjLZxB">iPhone</a>
<a href="//apple.com/watch" class="NavigationItem__A-sc-n3rxo5-0 gjLZxB">Watch</a>
</div>
<!--/$-->
</div>
<script integrity="sha256-H7F+964gzVOFQxAasYdtLpN0sfT+3p7hFGiFE28kfNs=" src="vendors.8927c448274b07aaf1ee.js" crossorigin="anonymous"></script>
</body>
</html>

4b. Inspect http://127.0.0.1:8080/index?ssr=false



<!DOCTYPE html>
<html>
<head>
<meta charSet="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<link rel="icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkqAcAAIUAgUW0RjgAAAAASUVORK5CYII="/>
<script>
// as this is client side rendering, the store is empty -->
globalThis.__RELAY_STORE__ = {};
...
globalThis.__GRAPHQL_URL__ = "http://127.0.0.1:8080/graphql";
</script>
...
</head>
<body>
<!-- as the store is empty, the body is empty as well -->
<div id="root"></div>
<script integrity="sha256-H7F+964gzVOFQxAasYdtLpN0sfT+3p7hFGiFE28kfNs=" src="vendors.8927c448274b07aaf1ee.js" crossorigin="anonymous"></script>
</body>
</html>