Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Relay example with Next.js v13 #241

Merged
merged 9 commits into from
Dec 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions issue-tracker-next-v13/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"next/core-web-vitals",
"plugin:relay/recommended"
],
"rules": {
"relay/generated-flow-types": "off"
},
"plugins": ["relay"]
}
1 change: 1 addition & 0 deletions issue-tracker-next-v13/.gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.graphql.ts auto eol=lf
39 changes: 39 additions & 0 deletions issue-tracker-next-v13/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# local env files
.env*.local

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts

# relay
/__generated__
4 changes: 4 additions & 0 deletions issue-tracker-next-v13/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Ignore artifacts:
__generated__
.next

1 change: 1 addition & 0 deletions issue-tracker-next-v13/.prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
4 changes: 4 additions & 0 deletions issue-tracker-next-v13/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"typescript.tsdk": "./node_modules/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true
}
1 change: 1 addition & 0 deletions issue-tracker-next-v13/.watchmanconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
26 changes: 26 additions & 0 deletions issue-tracker-next-v13/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Simple Issue Tracker Example (with Next.js 13)

## Overview

This is a simplified example of the [Issue Tracker Example](https://github.com/relayjs/relay-examples/tree/main/issue-tracker). But with few updates:

- Uses the React Server Components (Next.js v13) as entry points for [fetching](https://beta.nextjs.org/docs/data-fetching/fetching) root queries.

- `loadSerializableQuery` reference implementation of the data-fetching method that can be used in the RSC
- `useSerializablePreloadedQuery` an example hook that can convert serialized query results into preloaded query.

- Has TypesScript Setup.

## Workflow

The query fetching is happening in the `page.tsx` async server component that is calling `loadSerializableQuery`. Then, preloaded results are passed to the root client component (`MainViewClientComponent`, for example). These preloaded results are converted into Relay's `PreloadedQuery` object, which is passed to the root Relay component that renders the query with the `usePreloadedQuery` hook.

```mermaid
flowchart LR;
RSC(Root React Server Component)--Serialized Query Results-->RCC(Root Client Component);
RCC(Root Client Component)--Preloaded Query-->RRC(Root Relay Component);
```

## Installation

For installation instructions, please follow [this steps](https://github.com/relayjs/relay-examples/tree/main/issue-tracker#setup) from the `issue-tracker` example.
34 changes: 34 additions & 0 deletions issue-tracker-next-v13/app/MainViewClientComponent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"use client";

import MainView from "src/components/MainView";
import { Suspense } from "react";
import { SerializablePreloadedQuery } from "src/relay/loadSerializableQuery";
import MainViewQueryNode, {
MainViewQuery,
} from "__generated__/MainViewQuery.graphql";
import { getCurrentEnvironment } from "src/relay/environment";
import { RelayEnvironmentProvider } from "react-relay";
import useSerializablePreloadedQuery from "src/relay/useSerializablePreloadedQuery";

const MainViewClientComponent = (props: {
preloadedQuery: SerializablePreloadedQuery<
typeof MainViewQueryNode,
MainViewQuery
>;
}) => {
const environment = getCurrentEnvironment();
const queryRef = useSerializablePreloadedQuery(
environment,
props.preloadedQuery
);

return (
<RelayEnvironmentProvider environment={environment}>
<Suspense fallback="Loading...">
<MainView queryRef={queryRef} />
</Suspense>
</RelayEnvironmentProvider>
);
};

export default MainViewClientComponent;
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"use client";

import { RelayEnvironmentProvider } from "react-relay";
import Issue from "src/components/Issue";
import { Suspense } from "react";
import IssueQueryNode, { IssueQuery } from "__generated__/IssueQuery.graphql";
import { SerializablePreloadedQuery } from "src/relay/loadSerializableQuery";
import { getCurrentEnvironment } from "src/relay/environment";
import useSerializablePreloadedQuery from "src/relay/useSerializablePreloadedQuery";

const Root = (props: {
preloadedQuery: SerializablePreloadedQuery<typeof IssueQueryNode, IssueQuery>;
}) => {
const environment = getCurrentEnvironment();
const queryRef = useSerializablePreloadedQuery(
environment,
props.preloadedQuery
);

return (
<RelayEnvironmentProvider environment={environment}>
<Suspense fallback="Loading...">
<Issue queryRef={queryRef} />
</Suspense>
</RelayEnvironmentProvider>
);
};

export default Root;
25 changes: 25 additions & 0 deletions issue-tracker-next-v13/app/issues/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import loadSerializableQuery from "src/relay/loadSerializableQuery";
import IssueQueryNode, { IssueQuery } from "__generated__/IssueQuery.graphql";
import IssueViewClientComponent from "./IssueViewClientComponent";
import styles from "styles/Issue.module.css";

export default async function IssuePage({
params,
}: {
params: { id: string };
}) {
const preloadedQuery = await loadSerializableQuery<
typeof IssueQueryNode,
IssueQuery
>(IssueQueryNode.params, {
owner: "facebook",
name: "relay",
issueNumber: parseInt(params.id, 10),
});

return (
<div className={styles.issue}>
<IssueViewClientComponent preloadedQuery={preloadedQuery} />
</div>
);
}
16 changes: 16 additions & 0 deletions issue-tracker-next-v13/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import "styles/globals.css";

export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html>
<head>
<title>Github Issues: Relay</title>
</head>
<body>{children}</body>
</html>
);
}
26 changes: 26 additions & 0 deletions issue-tracker-next-v13/app/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import loadSerializableQuery from "src/relay/loadSerializableQuery";
import styles from "styles/MainView.module.css";
import MainViewQueryNode, {
MainViewQuery,
} from "__generated__/MainViewQuery.graphql";
import MainViewClientComponent from "./MainViewClientComponent";

const Page = async () => {
const preloadedQuery = await loadSerializableQuery<
typeof MainViewQueryNode,
MainViewQuery
>(MainViewQueryNode.params, {
owner: "facebook",
name: "relay",
});

return (
<div className={styles.main}>
<MainViewClientComponent preloadedQuery={preloadedQuery} />
</div>
);
};

export default Page;

export const revalidate = 0;
15 changes: 15 additions & 0 deletions issue-tracker-next-v13/next.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
swcMinify: true,
compiler: {
relay: {
src: "./",
language: "typescript",
artifactDirectory: "__generated__",
},
},
experimental: { appDir: true },
};

module.exports = nextConfig;
40 changes: 40 additions & 0 deletions issue-tracker-next-v13/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"name": "relay-resolvers-example",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "mkdir -p __generated__ && concurrently \"relay-compiler --watch\" \"next dev\"",
"build": "relay-compiler --validate && next build",
"start": "next start",
"lint": "next lint",
"prettier": "prettier --write .",
"relay": "relay-compiler"
},
"dependencies": {
"next": "^13.0.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-relay": "main",
"relay-runtime": "main",
"uuid": "^9.0.0"
},
"devDependencies": {
"@types/node": "18.11.3",
"@types/react": "18.0.21",
"@types/react-dom": "18.0.6",
"@types/react-relay": "14.1.2",
"@types/relay-runtime": "^14.1.3",
"@types/uuid": "^8.3.4",
"@typescript-eslint/eslint-plugin": "^5.42.0",
"concurrently": "^7.5.0",
"eslint": "8.26.0",
"eslint-config-next": "^13.0.2",
"eslint-plugin-relay": "^1.8.3",
"prettier": "^2.7.1",
"relay-compiler": "main",
"typescript": "4.8.4"
},
"browser": {
"./src/stores/RemoteClient.ts": false
}
}
22 changes: 22 additions & 0 deletions issue-tracker-next-v13/relay.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"root": ".",
"sources": {
"src": "my_app",
"app": "my_app"
},
"projects": {
"my_app": {
"language": "typescript",
"schema": "schema.graphql",
"output": "__generated__",
"useImportTypeSyntax": true,
"featureFlags": {
"relay_resolver_model_syntax_enabled": true,
"use_named_imports_for_relay_resolvers": true,
"enable_relay_resolver_transform": true,
"relay_resolver_enable_terse_syntax": true
},
"eagerEsModules": true
}
}
}
Loading