Skip to content

Commit

Permalink
Example: Add with-relay-hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
jesstelford committed Feb 2, 2022
1 parent 39d3210 commit d042230
Show file tree
Hide file tree
Showing 12 changed files with 334 additions and 1 deletion.
4 changes: 4 additions & 0 deletions examples/with-relay-hooks/.babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"presets": ["next/babel"],
"plugins": ["relay"]
}
1 change: 1 addition & 0 deletions examples/with-relay-hooks/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
NEXT_PUBLIC_RELAY_ENDPOINT=https://swapi-graphql.netlify.app/.netlify/functions/index
38 changes: 38 additions & 0 deletions examples/with-relay-hooks/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# 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*

# local env files
.env.local
.env.development.local
.env.test.local
.env.production.local

# vercel
.vercel

# relay
__generated__/
schema.graphql
53 changes: 53 additions & 0 deletions examples/with-relay-hooks/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Relay Hooks Example

Relay is a JavaScript framework for building data-driven React applications.

- **Declarative:** Never again communicate with your data store using an imperative API. Simply declare your data requirements using GraphQL and let Relay figure out how and when to fetch your data.
- **Colocation:** Queries live next to the views that rely on them, so you can easily reason about your app. Relay aggregates queries into efficient network requests to fetch only what you need.
- **Mutations:** Relay lets you mutate data on the client and server using GraphQL mutations, and offers automatic data consistency, optimistic updates, and error handling.

[Relay Hooks](https://relay.dev/) is the easiest-to-use, safest Relay API. It relies on suspense, and is safe to use in React concurrent mode.

This example relies on [SWAPI GraphQL](https://github.com/graphql/swapi-graphql) for its GraphQL backend.

## Deploy your own

Deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-example):

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/git/external?repository-url=https://github.com/vercel/next.js/tree/canary/examples/with-relay-hooks&project-name=with-relay-hooks&repository-name=with-relay-hooks)

## How to use

Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init) or [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/) to bootstrap the example:

```bash
npx create-next-app --example with-relay-hooks with-relay-hooks-app
# or
yarn create next-app --example with-relay-hooks with-relay-hooks-app
```

Download schema introspection data from configured Relay endpoint

```bash
npm run schema
# or
yarn schema
```

Run Relay ahead-of-time compilation (should be re-run after any edits to components that query data with Relay)

```bash
npm run relay
# or
yarn relay
```

Run the project

```bash
npm run dev
# or
yarn dev
```

Deploy it to the cloud with [Vercel](https://vercel.com/new?utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)).
23 changes: 23 additions & 0 deletions examples/with-relay-hooks/components/SWPilot.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { graphql, useFragment } from 'react-relay'

export const SWPilot = ({ pilot }) => {
const data = useFragment(
graphql`
fragment SWPilot_person on Person {
id
name
homeworld {
id
name
}
}
`,
pilot
)

return (
<li>
{data.name} ({data.homeworld.name})
</li>
)
}
41 changes: 41 additions & 0 deletions examples/with-relay-hooks/components/SWStarship.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { graphql, useFragment } from 'react-relay'
import { SWPilot } from './SWPilot'

export const SWStarship = ({ starship }) => {
const data = useFragment(
graphql`
fragment SWStarship_starship on Starship {
id
name
pilotConnection {
totalCount
edges {
node {
id
...SWPilot_person
}
}
}
}
`,
starship
)

return (
<li>
<p>
<strong>{data.name}</strong>
</p>
{data.pilotConnection.totalCount > 0 ? (
<p>
Pilots:
<ul>
{data.pilotConnection.edges.map(({ node: pilot }) => (
<SWPilot key={pilot.id} pilot={pilot} />
))}
</ul>
</p>
) : null}
</li>
)
}
71 changes: 71 additions & 0 deletions examples/with-relay-hooks/lib/relay.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { useMemo } from 'react'
import { Environment, Network, RecordSource, Store } from 'relay-runtime'

export const RELAY_INITIAL_RECORDS_PROP = '__RELAY_INITIAL_RECORDS__'

let relayEnvironment

// Define a function that fetches the results of an operation (query/mutation/etc)
// and returns its results as a Promise
const fetchRelay = async (operation, variables) => {
const response = await fetch(process.env.NEXT_PUBLIC_RELAY_ENDPOINT, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
query: operation.text,
variables,
}),
})
return await response.json()
}

const createEnvironment = () =>
new Environment({
// Create a network layer from the fetch function
network: Network.create(fetchRelay),
store: new Store(new RecordSource()),
})

export const initializeRelay = (initialRecords) => {
// Create a network layer from the fetch function
const environment = relayEnvironment ?? createEnvironment()

// If your page has Next.js data fetching methods that use Relay, the initial records
// will get hydrated here
if (initialRecords) {
environment.getStore().publish(new RecordSource(initialRecords))
}

if (typeof window === 'undefined') {
// Tell relay to stop its normal garbage collection processes. This prevents
// data being lost between calling relay's `fetchQuery` and our
// `finalizeRelay` method below
environment.getStore().holdGC()

// For SSG and SSR always create a new Relay environment
return environment
}

// Create the Relay environment once in the client
if (!relayEnvironment) relayEnvironment = environment

return relayEnvironment
}

export const finalizeRelay = (environment, pageProps) => {
pageProps.props = pageProps.props ?? {}
pageProps.props[RELAY_INITIAL_RECORDS_PROP] = environment
.getStore()
.getSource()
.toJSON()

return pageProps
}

export const useRelayEnvironment = (pageProps) => {
const initialRecords = pageProps[RELAY_INITIAL_RECORDS_PROP]
return useMemo(() => initializeRelay(initialRecords), [initialRecords])
}
26 changes: 26 additions & 0 deletions examples/with-relay-hooks/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"private": true,
"scripts": {
"dev": "yarn run relay && next",
"build": "yarn run relay && next build",
"start": "yarn run relay && next start",
"relay": "yarn run relay-compiler $@",
"schema": "export $(grep -v '^#' .env | xargs) && get-graphql-schema $NEXT_PUBLIC_RELAY_ENDPOINT > schema.graphql"
},
"relay": {
"src": "./",
"schema": "./schema.graphql"
},
"dependencies": {
"next": "latest",
"react": "^18.0.0-rc.0",
"react-dom": "^18.0.0-rc.0",
"relay-runtime": "^13.0.2",
"react-relay": "^13.0.2"
},
"devDependencies": {
"babel-plugin-relay": "^13.0.2",
"get-graphql-schema": "^2.1.2",
"relay-compiler": "^13.0.2"
}
}
16 changes: 16 additions & 0 deletions examples/with-relay-hooks/pages/_app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Suspense } from 'react'
import { RelayEnvironmentProvider } from 'react-relay/hooks'

import { useRelayEnvironment } from '../lib/relay'

export default function App({ Component, pageProps }) {
const relayEnvironment = useRelayEnvironment(pageProps)

return (
<RelayEnvironmentProvider environment={relayEnvironment}>
<Suspense fallback={'Loading...'}>
<Component {...pageProps} />
</Suspense>
</RelayEnvironmentProvider>
)
}
12 changes: 12 additions & 0 deletions examples/with-relay-hooks/pages/about.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import Link from 'next/link'

export default function About() {
return (
<div>
<Link href="/">
<a>Home</a>
</Link>
<p>This is the about page</p>
</div>
)
}
48 changes: 48 additions & 0 deletions examples/with-relay-hooks/pages/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import Link from 'next/link'
import { graphql, fetchQuery } from 'react-relay'

import { initializeRelay, finalizeRelay } from '../lib/relay'
import { SWStarship } from '../components/SWStarship'

const Index = ({ allStarships }) => (
<div>
<Link href="/about">
<a>About</a>
</Link>
<h1>StarWars Starships</h1>
<ul>
{allStarships.edges.map(({ node: starship }) => (
<SWStarship key={starship.id} starship={starship} />
))}
</ul>
</div>
)

export async function getStaticProps() {
const environment = initializeRelay()

const result = await fetchQuery(
environment,
graphql`
query pagesIndexQuery {
allStarships(first: 5) {
edges {
node {
id
...SWStarship_starship
}
}
}
}
`
).toPromise()

return finalizeRelay(environment, {
props: {
...result,
},
revalidate: 1,
})
}

export default Index
2 changes: 1 addition & 1 deletion examples/with-relay-modern/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Relay Modern Example

[Relay Modern](https://relay.dev/) is a new version of Relay designed from the ground up to be easier to use, more extensible and, most of all, able to improve performance on mobile devices. Relay Modern accomplishes this with static queries and ahead-of-time code generation.
Relay Modern is an old version of Relay heavily focused on Containers. It is recommended to use [Relay Hooks](https://relay.dev/) going forward. See the [`with-relay-hooks` example](../with-relay-hooks).

This example relies on [Prisma + Nexus](https://github.com/prisma-labs/nextjs-graphql-api-examples) for its GraphQL backend.

Expand Down

0 comments on commit d042230

Please sign in to comment.