mirror of
https://github.com/terribleplan/next.js.git
synced 2024-01-19 02:48:18 +00:00
parent
6285cdedf2
commit
708193d227
8
examples/with-relay-modern/.babelrc
Normal file
8
examples/with-relay-modern/.babelrc
Normal file
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"presets": [
|
||||
"next/babel"
|
||||
],
|
||||
"plugins": [
|
||||
"relay"
|
||||
]
|
||||
}
|
1
examples/with-relay-modern/.env
Normal file
1
examples/with-relay-modern/.env
Normal file
|
@ -0,0 +1 @@
|
|||
RELAY_ENDPOINT=https://api.graph.cool/relay/v1/next-js-with-relay-modern-example
|
52
examples/with-relay-modern/README.md
Normal file
52
examples/with-relay-modern/README.md
Normal file
|
@ -0,0 +1,52 @@
|
|||
[![Deploy to now](https://deploy.now.sh/static/button.svg)](https://deploy.now.sh/?repo=https://github.com/zeit/next.js/tree/master/examples/with-relay-modern)
|
||||
# Relay Modern Example
|
||||
|
||||
## How to use
|
||||
|
||||
Download the example [or clone the repo](https://github.com/zeit/next.js):
|
||||
|
||||
```bash
|
||||
curl https://codeload.github.com/zeit/next.js/tar.gz/master | tar -xz --strip=2 next.js-master/examples/with-relay-modern
|
||||
cd with-relay-modern
|
||||
```
|
||||
|
||||
Install it:
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
Setup Graphcool project
|
||||
|
||||
```bash
|
||||
npm run graphcool-init
|
||||
```
|
||||
|
||||
After successful initialization, copy Graphcool Relay Endpoint URL from console and store it into `.env` file
|
||||
```bash
|
||||
RELAY_ENDPOINT=your relay endpoint here
|
||||
```
|
||||
|
||||
Run Relay ahead-of-time compilation (should be re-run after any edits to components that query data with Relay)
|
||||
```bash
|
||||
npm run relay
|
||||
```
|
||||
|
||||
Run the project
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Deploy it to the cloud with [now](https://zeit.co/now) ([download](https://zeit.co/download)):
|
||||
|
||||
```bash
|
||||
now
|
||||
```
|
||||
|
||||
## The idea behind the example
|
||||
|
||||
[Relay Modern](https://facebook.github.io/relay/docs/relay-modern.html) 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.
|
||||
|
||||
In this simple example, we integrate Relay Modern seamlessly with Next by wrapping our *pages* inside a [higher-order component (HOC)](https://facebook.github.io/react/docs/higher-order-components.html). Using the HOC pattern we're able to pass down a query result data created by Relay into our React component hierarchy defined inside each page of our Next application. The HOC takes `options` argument that allows to specify a `query` that will be executed on the server when a page is being loaded.
|
||||
|
||||
This example relies on [graph.cool](https://www.graph.cool) for its GraphQL backend.
|
17
examples/with-relay-modern/components/BlogPostPreview.js
Normal file
17
examples/with-relay-modern/components/BlogPostPreview.js
Normal file
|
@ -0,0 +1,17 @@
|
|||
import React from 'react'
|
||||
import { createFragmentContainer, graphql } from 'react-relay'
|
||||
|
||||
const BlogPostPreview = props => {
|
||||
return (
|
||||
<div key={props.post.id}>{props.post.title}</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default createFragmentContainer(BlogPostPreview, {
|
||||
post: graphql`
|
||||
fragment BlogPostPreview_post on BlogPost {
|
||||
id
|
||||
title
|
||||
}
|
||||
`
|
||||
})
|
28
examples/with-relay-modern/components/BlogPosts.js
Normal file
28
examples/with-relay-modern/components/BlogPosts.js
Normal file
|
@ -0,0 +1,28 @@
|
|||
import React from 'react'
|
||||
import { createFragmentContainer, graphql } from 'react-relay'
|
||||
import BlogPostPreview from './BlogPostPreview'
|
||||
|
||||
const BlogPosts = props => {
|
||||
return (
|
||||
<div>
|
||||
<h1>Blog posts</h1>
|
||||
{props.viewer.allBlogPosts.edges.map(({ node }) =>
|
||||
<BlogPostPreview post={node} />
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default createFragmentContainer(BlogPosts, {
|
||||
viewer: graphql`
|
||||
fragment BlogPosts_viewer on Viewer {
|
||||
allBlogPosts(first: 10, orderBy: createdAt_DESC) {
|
||||
edges {
|
||||
node {
|
||||
...BlogPostPreview_post
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
})
|
31
examples/with-relay-modern/lib/RelayProvider.js
Normal file
31
examples/with-relay-modern/lib/RelayProvider.js
Normal file
|
@ -0,0 +1,31 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
// Thank you https://github.com/robrichard
|
||||
// https://github.com/robrichard/relay-context-provider
|
||||
|
||||
class RelayProvider extends React.Component {
|
||||
getChildContext () {
|
||||
return {
|
||||
relay: {
|
||||
environment: this.props.environment,
|
||||
variables: this.props.variables
|
||||
}
|
||||
}
|
||||
}
|
||||
render () {
|
||||
return this.props.children
|
||||
}
|
||||
}
|
||||
|
||||
RelayProvider.childContextTypes = {
|
||||
relay: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
RelayProvider.propTypes = {
|
||||
environment: PropTypes.object.isRequired,
|
||||
variables: PropTypes.object.isRequired,
|
||||
children: PropTypes.node
|
||||
}
|
||||
|
||||
export default RelayProvider
|
50
examples/with-relay-modern/lib/createRelayEnvironment.js
Normal file
50
examples/with-relay-modern/lib/createRelayEnvironment.js
Normal file
|
@ -0,0 +1,50 @@
|
|||
import { Environment, Network, RecordSource, Store } from 'relay-runtime'
|
||||
import fetch from 'isomorphic-unfetch'
|
||||
|
||||
let relayEnvironment = null
|
||||
|
||||
// Define a function that fetches the results of an operation (query/mutation/etc)
|
||||
// and returns its results as a Promise:
|
||||
function fetchQuery (
|
||||
operation,
|
||||
variables,
|
||||
cacheConfig,
|
||||
uploadables,
|
||||
) {
|
||||
return fetch(process.env.RELAY_ENDPOINT, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json'
|
||||
}, // Add authentication and other headers here
|
||||
body: JSON.stringify({
|
||||
query: operation.text, // GraphQL text from input
|
||||
variables
|
||||
})
|
||||
}).then(response => response.json())
|
||||
}
|
||||
|
||||
export default function initEnvironment ({ records = {} } = {}) {
|
||||
// Create a network layer from the fetch function
|
||||
const network = Network.create(fetchQuery)
|
||||
const store = new Store(new RecordSource(records))
|
||||
|
||||
// Make sure to create a new Relay environment for every server-side request so that data
|
||||
// isn't shared between connections (which would be bad)
|
||||
if (!process.browser) {
|
||||
return new Environment({
|
||||
network,
|
||||
store
|
||||
})
|
||||
}
|
||||
|
||||
// reuse Relay environment on client-side
|
||||
if (!relayEnvironment) {
|
||||
relayEnvironment = new Environment({
|
||||
network,
|
||||
store
|
||||
})
|
||||
}
|
||||
|
||||
return relayEnvironment
|
||||
}
|
53
examples/with-relay-modern/lib/withData.js
Normal file
53
examples/with-relay-modern/lib/withData.js
Normal file
|
@ -0,0 +1,53 @@
|
|||
import React from 'react'
|
||||
import initEnvironment from './createRelayEnvironment'
|
||||
import { fetchQuery } from 'react-relay'
|
||||
import RelayProvider from './RelayProvider'
|
||||
|
||||
export default (ComposedComponent, options = {}) => {
|
||||
return class WithData extends React.Component {
|
||||
static displayName = `WithData(${ComposedComponent.displayName})`
|
||||
|
||||
static async getInitialProps (ctx) {
|
||||
// Evaluate the composed component's getInitialProps()
|
||||
let composedInitialProps = {}
|
||||
if (ComposedComponent.getInitialProps) {
|
||||
composedInitialProps = await ComposedComponent.getInitialProps(ctx)
|
||||
}
|
||||
|
||||
let queryProps = {}
|
||||
let queryRecords = {}
|
||||
const environment = initEnvironment()
|
||||
|
||||
if (options.query) {
|
||||
// Provide the `url` prop data in case a graphql query uses it
|
||||
// const url = { query: ctx.query, pathname: ctx.pathname }
|
||||
const variables = {}
|
||||
// TODO: Consider RelayQueryResponseCache
|
||||
// https://github.com/facebook/relay/issues/1687#issuecomment-302931855
|
||||
queryProps = await fetchQuery(environment, options.query, variables)
|
||||
queryRecords = environment.getStore().getSource().toJSON()
|
||||
}
|
||||
|
||||
return {
|
||||
...composedInitialProps,
|
||||
...queryProps,
|
||||
queryRecords
|
||||
}
|
||||
}
|
||||
|
||||
constructor (props) {
|
||||
super(props)
|
||||
this.environment = initEnvironment({
|
||||
records: props.queryRecords
|
||||
})
|
||||
}
|
||||
|
||||
render () {
|
||||
return (
|
||||
<RelayProvider environment={this.environment} variables={{}}>
|
||||
<ComposedComponent {...this.props} />
|
||||
</RelayProvider>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
22
examples/with-relay-modern/next.config.js
Normal file
22
examples/with-relay-modern/next.config.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
require('dotenv').config()
|
||||
|
||||
const path = require('path')
|
||||
const Dotenv = require('dotenv-webpack')
|
||||
|
||||
module.exports = {
|
||||
webpack: (config) => {
|
||||
config.plugins = config.plugins || []
|
||||
|
||||
config.plugins = [
|
||||
...config.plugins,
|
||||
|
||||
// Read the .env file
|
||||
new Dotenv({
|
||||
path: path.join(__dirname, '.env'),
|
||||
systemvars: true
|
||||
})
|
||||
]
|
||||
|
||||
return config
|
||||
}
|
||||
}
|
29
examples/with-relay-modern/package.json
Normal file
29
examples/with-relay-modern/package.json
Normal file
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"name": "with-relay-modern",
|
||||
"version": "3.0.3",
|
||||
"description": "Example of Next.js with Relay Modern SSR",
|
||||
"scripts": {
|
||||
"graphcool-init": "graphcool init --schema schema/init-schema.graphql",
|
||||
"dev": "next",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"relay": "relay-compiler --src ./ --exclude **/.next/** **/node_modules/** **/test/** **/__generated__/** --schema ./schema/schema.graphql",
|
||||
"schema": "node scripts/getSchema.js"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"dotenv": "^4.0.0",
|
||||
"dotenv-webpack": "^1.5.4",
|
||||
"isomorphic-unfetch": "^2.0.0",
|
||||
"next": "^3.0.3",
|
||||
"react": "^15.6.1",
|
||||
"react-dom": "^15.6.1",
|
||||
"react-relay": "^1.2.0-rc.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-plugin-relay": "^1.1.0",
|
||||
"graphcool": "^1.2.1",
|
||||
"relay-compiler": "^1.2.0-rc.1"
|
||||
}
|
||||
}
|
26
examples/with-relay-modern/pages/index.js
Normal file
26
examples/with-relay-modern/pages/index.js
Normal file
|
@ -0,0 +1,26 @@
|
|||
import React, { Component } from 'react'
|
||||
import { graphql } from 'react-relay'
|
||||
import withData from '../lib/withData'
|
||||
import BlogPosts from '../components/BlogPosts'
|
||||
|
||||
class Index extends Component {
|
||||
static displayName = `Index`
|
||||
|
||||
render (props) {
|
||||
return (
|
||||
<div>
|
||||
<BlogPosts viewer={this.props.viewer} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default withData(Index, {
|
||||
query: graphql`
|
||||
query pages_indexQuery {
|
||||
viewer {
|
||||
...BlogPosts_viewer
|
||||
}
|
||||
}
|
||||
`
|
||||
})
|
7
examples/with-relay-modern/schema/init-schema.graphql
Normal file
7
examples/with-relay-modern/schema/init-schema.graphql
Normal file
|
@ -0,0 +1,7 @@
|
|||
type BlogPost implements Node {
|
||||
content: String!
|
||||
createdAt: DateTime!
|
||||
id: ID! @isUnique
|
||||
title: String!
|
||||
updatedAt: DateTime!
|
||||
}
|
28
examples/with-relay-modern/scripts/getSchema.js
Normal file
28
examples/with-relay-modern/scripts/getSchema.js
Normal file
|
@ -0,0 +1,28 @@
|
|||
var fetch = require('node-fetch')
|
||||
var fs = require('fs')
|
||||
var path = require('path')
|
||||
|
||||
require('dotenv').config()
|
||||
|
||||
const {
|
||||
buildClientSchema,
|
||||
introspectionQuery,
|
||||
printSchema
|
||||
} = require('graphql/utilities')
|
||||
|
||||
console.log(introspectionQuery)
|
||||
|
||||
fetch(process.env.RELAY_ENDPOINT, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ 'query': introspectionQuery })
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
console.log(res)
|
||||
const schemaString = printSchema(buildClientSchema(res.data))
|
||||
fs.writeFileSync(path.join(__dirname, '..', 'schema', 'schema.graphql'), schemaString)
|
||||
})
|
Loading…
Reference in a new issue