mirror of
https://github.com/terribleplan/next.js.git
synced 2024-01-19 02:48:18 +00:00
Apollo + Authentication + graph.cool example (#2161)
* Apollo + Authentication + graph.cool example * Fix linting errors
This commit is contained in:
parent
d6a96a82aa
commit
4a0278b5c0
16
examples/with-apollo-auth/.babelrc
Normal file
16
examples/with-apollo-auth/.babelrc
Normal file
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"env": {
|
||||
"development": {
|
||||
"presets": "next/babel"
|
||||
},
|
||||
"production": {
|
||||
"presets": "next/babel"
|
||||
},
|
||||
"test": {
|
||||
"presets": [
|
||||
["env", { "modules": "commonjs" }],
|
||||
"next/babel"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
80
examples/with-apollo-auth/.gitignore
vendored
Normal file
80
examples/with-apollo-auth/.gitignore
vendored
Normal file
|
@ -0,0 +1,80 @@
|
|||
.next
|
||||
|
||||
# Created by https://www.gitignore.io/api/vim,node
|
||||
|
||||
### Node ###
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (http://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Typescript v1 declaration files
|
||||
typings/
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
|
||||
|
||||
### Vim ###
|
||||
# swap
|
||||
[._]*.s[a-v][a-z]
|
||||
[._]*.sw[a-p]
|
||||
[._]s[a-v][a-z]
|
||||
[._]sw[a-p]
|
||||
# session
|
||||
Session.vim
|
||||
# temporary
|
||||
.netrwhist
|
||||
*~
|
||||
# auto-generated tag files
|
||||
tags
|
||||
|
||||
# End of https://www.gitignore.io/api/vim,node
|
56
examples/with-apollo-auth/README.md
Normal file
56
examples/with-apollo-auth/README.md
Normal file
|
@ -0,0 +1,56 @@
|
|||
[![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-apollo-auth)
|
||||
# Apollo With Authentication Example
|
||||
|
||||
## Demo
|
||||
|
||||
https://next-with-apollo-auth.now.sh
|
||||
|
||||
## 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-apollo-auth
|
||||
cd with-apollo-auth
|
||||
```
|
||||
|
||||
Install it and run:
|
||||
|
||||
```bash
|
||||
npm install
|
||||
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
|
||||
|
||||
This is an extention of the _[with Apollo](https://github.com/zeit/next.js/tree/master/examples/with-apollo#the-idea-behind-the-example)_ example:
|
||||
|
||||
> [Apollo](http://dev.apollodata.com) is a GraphQL client that allows you to easily query the exact data you need from a GraphQL server. In addition to fetching and mutating data, Apollo analyzes your queries and their results to construct a client-side cache of your data, which is kept up to date as further queries and mutations are run, fetching more results from the server.
|
||||
>
|
||||
> In this simple example, we integrate Apollo 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 central store of query result data created by Apollo into our React component hierarchy defined inside each page of our Next application.
|
||||
>
|
||||
> On initial page load, while on the server and inside `getInitialProps`, we invoke the Apollo method, [`getDataFromTree`](http://dev.apollodata.com/react/server-side-rendering.html#getDataFromTree). This method returns a promise; at the point in which the promise resolves, our Apollo Client store is completely initialized.
|
||||
>
|
||||
> This example relies on [graph.cool](https://www.graph.cool) for its GraphQL backend.
|
||||
>
|
||||
> *Note: Apollo uses Redux internally; if you're interested in integrating the client with your existing Redux store check out the [`with-apollo-and-redux`](https://github.com/zeit/next.js/tree/master/examples/with-apollo-and-redux) example.*
|
||||
|
||||
[graph.cool](https://www.graph.cool) can be setup with many different
|
||||
[authentication providers](https://www.graph.cool/docs/reference/integrations/overview-seimeish6e/#authentication-providers), the most basic of which is [email-password authentication](https://www.graph.cool/docs/reference/simple-api/user-authentication-eixu9osueb/#email-and-password). Once email-password authentication is enabled for your graph.cool project, you are provided with 2 useful mutations: `createUser` and `signinUser`.
|
||||
|
||||
On loading each route, we perform a `user` query to see if the current visitor is logged in (based on a cookie, more on that in a moment). Depending on the query result, and the route, the user may be [redirected](https://github.com/zeit/next.js/blob/master/examples/with-apollo-auth/lib/redirect.js) to a different page.
|
||||
|
||||
When creating an account, both the `createUser` and `signinUser` mutations are executed on graph.cool, which returns a token that can be used to [authenticate the user for future requests](https://www.graph.cool/docs/reference/auth/authentication-tokens-eip7ahqu5o/). The token is stored in a cookie for easy access (_note: This may have security implications. Please understand XSS and JWT before deploying this to production_).
|
||||
|
||||
A similar process is followed when signing in, except `signinUser` is the only mutation executed.
|
||||
|
||||
It is important to note the use of Apollo's `resetStore()` method after signing in and signing out to ensure that no user data is kept in the browser's memory.
|
||||
|
||||
To get this example running locally, you will need to create a graph.cool
|
||||
account, and provide [the `project.graphcool` schema](https://github.com/zeit/next.js/blob/master/examples/with-apollo-auth/project.graphcool).
|
19
examples/with-apollo-auth/lib/check-logged-in.js
Normal file
19
examples/with-apollo-auth/lib/check-logged-in.js
Normal file
|
@ -0,0 +1,19 @@
|
|||
import gql from 'graphql-tag'
|
||||
|
||||
export default (context, apolloClient) => (
|
||||
apolloClient.query({
|
||||
query: gql`
|
||||
query getUser {
|
||||
user {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
`
|
||||
}).then(({ data }) => {
|
||||
return { loggedInUser: data }
|
||||
}).catch(() => {
|
||||
// Fail gracefully
|
||||
return { loggedInUser: {} }
|
||||
})
|
||||
)
|
47
examples/with-apollo-auth/lib/init-apollo.js
Normal file
47
examples/with-apollo-auth/lib/init-apollo.js
Normal file
|
@ -0,0 +1,47 @@
|
|||
import { ApolloClient, createNetworkInterface } from 'react-apollo'
|
||||
import fetch from 'isomorphic-fetch'
|
||||
|
||||
let apolloClient = null
|
||||
|
||||
// Polyfill fetch() on the server (used by apollo-client)
|
||||
if (!process.browser) {
|
||||
global.fetch = fetch
|
||||
}
|
||||
|
||||
function create (initialState, { getToken }) {
|
||||
const networkInterface = createNetworkInterface({
|
||||
uri: 'https://api.graph.cool/simple/v1/cj3h80ffbllm20162alevpcby'
|
||||
})
|
||||
|
||||
networkInterface.use([{
|
||||
applyMiddleware (req, next) {
|
||||
if (!req.options.headers) {
|
||||
req.options.headers = {} // Create the header object if needed.
|
||||
}
|
||||
const token = getToken()
|
||||
req.options.headers.authorization = token ? `Bearer ${token}` : null
|
||||
next()
|
||||
}
|
||||
}])
|
||||
|
||||
return new ApolloClient({
|
||||
initialState,
|
||||
ssrMode: !process.browser, // Disables forceFetch on the server (so queries are only run once)
|
||||
networkInterface
|
||||
})
|
||||
}
|
||||
|
||||
export default function initApollo (initialState, options) {
|
||||
// Make sure to create a new client for every server-side request so that data
|
||||
// isn't shared between connections (which would be bad)
|
||||
if (!process.browser) {
|
||||
return create(initialState, options)
|
||||
}
|
||||
|
||||
// Reuse client on the client-side
|
||||
if (!apolloClient) {
|
||||
apolloClient = create(initialState, options)
|
||||
}
|
||||
|
||||
return apolloClient
|
||||
}
|
13
examples/with-apollo-auth/lib/redirect.js
Normal file
13
examples/with-apollo-auth/lib/redirect.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
import Router from 'next/router'
|
||||
|
||||
export default (context, target) => {
|
||||
if (context.res) {
|
||||
// server
|
||||
// 303: "See other"
|
||||
context.res.writeHead(303, { Location: target })
|
||||
context.res.end()
|
||||
} else {
|
||||
// In the browser, we just pretend like this never even happened ;)
|
||||
Router.replace(target)
|
||||
}
|
||||
}
|
94
examples/with-apollo-auth/lib/with-data.js
Normal file
94
examples/with-apollo-auth/lib/with-data.js
Normal file
|
@ -0,0 +1,94 @@
|
|||
import React from 'react'
|
||||
import cookie from 'cookie'
|
||||
import PropTypes from 'prop-types'
|
||||
import { ApolloProvider, getDataFromTree } from 'react-apollo'
|
||||
|
||||
import initApollo from './init-apollo'
|
||||
|
||||
function parseCookies (ctx = {}, options = {}) {
|
||||
return cookie.parse(
|
||||
ctx.req && ctx.req.headers.cookie
|
||||
? ctx.req.headers.cookie
|
||||
: document.cookie,
|
||||
options
|
||||
)
|
||||
}
|
||||
|
||||
export default ComposedComponent => {
|
||||
return class WithData extends React.Component {
|
||||
static displayName = `WithData(${ComposedComponent.displayName})`
|
||||
static propTypes = {
|
||||
serverState: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
static async getInitialProps (context) {
|
||||
let serverState = {}
|
||||
|
||||
// Setup a server-side one-time-use apollo client for initial props and
|
||||
// rendering (on server)
|
||||
let apollo = initApollo({}, {
|
||||
getToken: () => parseCookies(context).token
|
||||
})
|
||||
|
||||
// Evaluate the composed component's getInitialProps()
|
||||
let composedInitialProps = {}
|
||||
if (ComposedComponent.getInitialProps) {
|
||||
composedInitialProps = await ComposedComponent.getInitialProps(context, apollo)
|
||||
}
|
||||
|
||||
// Run all graphql queries in the component tree
|
||||
// and extract the resulting data
|
||||
if (!process.browser) {
|
||||
if (context.res && context.res.finished) {
|
||||
// When redirecting, the response is finished.
|
||||
// No point in continuing to render
|
||||
return
|
||||
}
|
||||
|
||||
// Provide the `url` prop data in case a graphql query uses it
|
||||
const url = {query: context.query, pathname: context.pathname}
|
||||
|
||||
// Run all graphql queries
|
||||
const app = (
|
||||
<ApolloProvider client={apollo}>
|
||||
<ComposedComponent url={url} {...composedInitialProps} />
|
||||
</ApolloProvider>
|
||||
)
|
||||
await getDataFromTree(app)
|
||||
|
||||
// Extract query data from the Apollo's store
|
||||
const state = apollo.getInitialState()
|
||||
|
||||
serverState = {
|
||||
apollo: { // Make sure to only include Apollo's data state
|
||||
data: state.data
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
serverState,
|
||||
...composedInitialProps
|
||||
}
|
||||
}
|
||||
|
||||
constructor (props) {
|
||||
super(props)
|
||||
// Note: Apollo should never be used on the server side beyond the initial
|
||||
// render within `getInitialProps()` above (since the entire prop tree
|
||||
// will be initialized there), meaning the below will only ever be
|
||||
// executed on the client.
|
||||
this.apollo = initApollo(this.props.serverState, {
|
||||
getToken: () => parseCookies().token
|
||||
})
|
||||
}
|
||||
|
||||
render () {
|
||||
return (
|
||||
<ApolloProvider client={this.apollo}>
|
||||
<ComposedComponent {...this.props} />
|
||||
</ApolloProvider>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
33
examples/with-apollo-auth/package.json
Normal file
33
examples/with-apollo-auth/package.json
Normal file
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"name": "with-apollo-auth",
|
||||
"version": "0.0.0",
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"scripts": {
|
||||
"dev": "next",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"test": "NODE_ENV=test ava"
|
||||
},
|
||||
"dependencies": {
|
||||
"cookie": "^0.3.1",
|
||||
"graphql": "^0.9.3",
|
||||
"isomorphic-fetch": "^2.2.1",
|
||||
"next": "latest",
|
||||
"prop-types": "^15.5.10",
|
||||
"react": "^15.5.4",
|
||||
"react-apollo": "^1.1.3",
|
||||
"react-dom": "^15.5.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"ava": "^0.19.1",
|
||||
"clear-require": "^2.0.0",
|
||||
"glob": "^7.1.2"
|
||||
},
|
||||
"ava": {
|
||||
"require": [
|
||||
"babel-register"
|
||||
],
|
||||
"babel": "inherit"
|
||||
}
|
||||
}
|
104
examples/with-apollo-auth/pages/create-account.js
Normal file
104
examples/with-apollo-auth/pages/create-account.js
Normal file
|
@ -0,0 +1,104 @@
|
|||
import React from 'react'
|
||||
import { graphql, withApollo, compose } from 'react-apollo'
|
||||
import cookie from 'cookie'
|
||||
import Link from 'next/link'
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
import withData from '../lib/with-data'
|
||||
import redirect from '../lib/redirect'
|
||||
import checkLoggedIn from '../lib/check-logged-in'
|
||||
|
||||
class CreateAccount extends React.Component {
|
||||
static async getInitialProps (context, apolloClient) {
|
||||
const { loggedInUser } = await checkLoggedIn(context, apolloClient)
|
||||
|
||||
if (loggedInUser.user) {
|
||||
// Already signed in? No need to continue.
|
||||
// Throw them back to the main page
|
||||
redirect(context, '/')
|
||||
}
|
||||
|
||||
return {}
|
||||
}
|
||||
|
||||
render () {
|
||||
return (
|
||||
<div>
|
||||
{/* this.props.create is the mutation function provided by apollo below */}
|
||||
<form onSubmit={this.props.create}>
|
||||
<input type='text' placeholder='Your Name' name='name' /><br />
|
||||
<input type='email' placeholder='Email' name='email' /><br />
|
||||
<input type='password' placeholder='Password' name='password' /><br />
|
||||
<button>Create account</button>
|
||||
</form>
|
||||
<hr />
|
||||
Already have an account? <Link prefetch href='/signin'><a>Sign in</a></Link>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
export default compose(
|
||||
// withData gives us server-side graphql queries before rendering
|
||||
withData,
|
||||
// withApollo exposes `this.props.client` used when logging out
|
||||
withApollo,
|
||||
graphql(
|
||||
// The `createUser` & `signinUser` mutations are provided by graph.cool by
|
||||
// default.
|
||||
// Multiple mutations are executed by graphql sequentially
|
||||
gql`
|
||||
mutation Create($name: String!, $email: String!, $password: String!) {
|
||||
createUser(name: $name, authProvider: { email: { email: $email, password: $password }}) {
|
||||
id
|
||||
}
|
||||
signinUser(email: { email: $email, password: $password }) {
|
||||
token
|
||||
}
|
||||
}
|
||||
`,
|
||||
{
|
||||
// Use an unambiguous name for use in the `props` section below
|
||||
name: 'createWithEmail',
|
||||
// Apollo's way of injecting new props which are passed to the component
|
||||
props: ({
|
||||
createWithEmail,
|
||||
// `client` is provided by the `withApollo` HOC
|
||||
ownProps: { client }
|
||||
}) => ({
|
||||
// `create` is the name of the prop passed to the component
|
||||
create: (event) => {
|
||||
/* global FormData */
|
||||
const data = new FormData(event.target)
|
||||
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
|
||||
createWithEmail({
|
||||
variables: {
|
||||
email: data.get('email'),
|
||||
password: data.get('password'),
|
||||
name: data.get('name')
|
||||
}
|
||||
}).then(({ data: { signinUser: { token } } }) => {
|
||||
// Store the token in cookie
|
||||
document.cookie = cookie.serialize('token', token, {
|
||||
maxAge: 30 * 24 * 60 * 60 // 30 days
|
||||
})
|
||||
|
||||
// Force a reload of all the current queries now that the user is
|
||||
// logged in
|
||||
client.resetStore().then(() => {
|
||||
// Now redirect to the homepage
|
||||
redirect({}, '/')
|
||||
})
|
||||
}).catch((error) => {
|
||||
// Something went wrong, such as incorrect password, or no network
|
||||
// available, etc.
|
||||
console.error(error)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
)(CreateAccount)
|
49
examples/with-apollo-auth/pages/index.js
Normal file
49
examples/with-apollo-auth/pages/index.js
Normal file
|
@ -0,0 +1,49 @@
|
|||
import React from 'react'
|
||||
import cookie from 'cookie'
|
||||
import { withApollo, compose } from 'react-apollo'
|
||||
|
||||
import withData from '../lib/with-data'
|
||||
import redirect from '../lib/redirect'
|
||||
import checkLoggedIn from '../lib/check-logged-in'
|
||||
|
||||
class Index extends React.Component {
|
||||
static async getInitialProps (context, apolloClient) {
|
||||
const { loggedInUser } = await checkLoggedIn(context, apolloClient)
|
||||
|
||||
if (!loggedInUser.user) {
|
||||
// If not signed in, send them somewhere more useful
|
||||
redirect(context, '/signin')
|
||||
}
|
||||
|
||||
return { loggedInUser }
|
||||
}
|
||||
|
||||
signout = () => {
|
||||
document.cookie = cookie.serialize('token', '', {
|
||||
maxAge: -1 // Expire the cookie immediately
|
||||
})
|
||||
|
||||
// Force a reload of all the current queries now that the user is
|
||||
// logged in, so we don't accidentally leave any state around.
|
||||
this.props.client.resetStore().then(() => {
|
||||
// Redirect to a more useful page when signed out
|
||||
redirect({}, '/signin')
|
||||
})
|
||||
}
|
||||
|
||||
render () {
|
||||
return (
|
||||
<div>
|
||||
Hello {this.props.loggedInUser.user.name}!<br />
|
||||
<button onClick={this.signout}>Sign out</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
export default compose(
|
||||
// withData gives us server-side graphql queries before rendering
|
||||
withData,
|
||||
// withApollo exposes `this.props.client` used when logging out
|
||||
withApollo
|
||||
)(Index)
|
97
examples/with-apollo-auth/pages/signin.js
Normal file
97
examples/with-apollo-auth/pages/signin.js
Normal file
|
@ -0,0 +1,97 @@
|
|||
import React from 'react'
|
||||
import { graphql, withApollo, compose } from 'react-apollo'
|
||||
import cookie from 'cookie'
|
||||
import Link from 'next/link'
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
import withData from '../lib/with-data'
|
||||
import redirect from '../lib/redirect'
|
||||
import checkLoggedIn from '../lib/check-logged-in'
|
||||
|
||||
class Signin extends React.Component {
|
||||
static async getInitialProps (context, apolloClient) {
|
||||
const { loggedInUser } = await checkLoggedIn(context, apolloClient)
|
||||
|
||||
if (loggedInUser.user) {
|
||||
// Already signed in? No need to continue.
|
||||
// Throw them back to the main page
|
||||
redirect(context, '/')
|
||||
}
|
||||
|
||||
return {}
|
||||
}
|
||||
|
||||
render () {
|
||||
return (
|
||||
<div>
|
||||
{/* this.props.signin is the mutation function provided by apollo below */}
|
||||
<form onSubmit={this.props.signin}>
|
||||
<input type='email' placeholder='Email' name='email' /><br />
|
||||
<input type='password' placeholder='Password' name='password' /><br />
|
||||
<button>Sign in</button>
|
||||
</form>
|
||||
<hr />
|
||||
New? <Link prefetch href='/create-account'><a>Create account</a></Link>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
export default compose(
|
||||
// withData gives us server-side graphql queries before rendering
|
||||
withData,
|
||||
// withApollo exposes `this.props.client` used when logging out
|
||||
withApollo,
|
||||
graphql(
|
||||
// The `signinUser` mutation is provided by graph.cool by default
|
||||
gql`
|
||||
mutation Signin($email: String!, $password: String!) {
|
||||
signinUser(email: { email: $email, password: $password }) {
|
||||
token
|
||||
}
|
||||
}
|
||||
`,
|
||||
{
|
||||
// Use an unambiguous name for use in the `props` section below
|
||||
name: 'signinWithEmail',
|
||||
// Apollo's way of injecting new props which are passed to the component
|
||||
props: ({
|
||||
signinWithEmail,
|
||||
// `client` is provided by the `withApollo` HOC
|
||||
ownProps: { client }
|
||||
}) => ({
|
||||
// `signin` is the name of the prop passed to the component
|
||||
signin: (event) => {
|
||||
/* global FormData */
|
||||
const data = new FormData(event.target)
|
||||
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
|
||||
signinWithEmail({
|
||||
variables: {
|
||||
email: data.get('email'),
|
||||
password: data.get('password')
|
||||
}
|
||||
}).then(({ data: { signinUser: { token } } }) => {
|
||||
// Store the token in cookie
|
||||
document.cookie = cookie.serialize('token', token, {
|
||||
maxAge: 30 * 24 * 60 * 60 // 30 days
|
||||
})
|
||||
|
||||
// Force a reload of all the current queries now that the user is
|
||||
// logged in
|
||||
client.resetStore().then(() => {
|
||||
// Now redirect to the homepage
|
||||
redirect({}, '/')
|
||||
})
|
||||
}).catch((error) => {
|
||||
// Something went wrong, such as incorrect password, or no network
|
||||
// available, etc.
|
||||
console.error(error)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
)(Signin)
|
22
examples/with-apollo-auth/project.graphcool
Normal file
22
examples/with-apollo-auth/project.graphcool
Normal file
|
@ -0,0 +1,22 @@
|
|||
# projectId: cj3h80ffbllm20162alevpcby
|
||||
# version: 3
|
||||
|
||||
type File implements Node {
|
||||
contentType: String!
|
||||
createdAt: DateTime!
|
||||
id: ID! @isUnique
|
||||
name: String!
|
||||
secret: String! @isUnique
|
||||
size: Int!
|
||||
updatedAt: DateTime!
|
||||
url: String! @isUnique
|
||||
}
|
||||
|
||||
type User implements Node {
|
||||
createdAt: DateTime!
|
||||
email: String @isUnique
|
||||
id: ID! @isUnique
|
||||
name: String!
|
||||
password: String
|
||||
updatedAt: DateTime!
|
||||
}
|
41
examples/with-apollo-auth/test/shared-apollo.js
Normal file
41
examples/with-apollo-auth/test/shared-apollo.js
Normal file
|
@ -0,0 +1,41 @@
|
|||
const clearRequire = require('clear-require')
|
||||
const glob = require('glob')
|
||||
const test = require('ava')
|
||||
|
||||
/**
|
||||
* Motivations:
|
||||
*
|
||||
* - Client-side getInitialProps() wont have access to the apollo client for
|
||||
* that page (because it's not shared across page bundles), so wont be able to
|
||||
* reset the state, leaving all the logged in user data there :(
|
||||
* - So, we have to have a shared module. BUT; next's code splitting means the
|
||||
* bundle for each page will include its own copy of the module, _unless it's
|
||||
* inlcuded in every page_.
|
||||
* - https://github.com/zeit/next.js/issues/659#issuecomment-271824223
|
||||
* - https://github.com/zeit/next.js/issues/1635#issuecomment-292236785
|
||||
* - Therefore, this test ensures that every page includes that module, and
|
||||
* hence it will be shared across every page, giving us a global store in
|
||||
* Apollo that we can clear, etc
|
||||
*/
|
||||
|
||||
const apolloFilePath = require.resolve('../lib/init-apollo')
|
||||
|
||||
test.beforeEach(() => {
|
||||
// Clean up the cache
|
||||
clearRequire.all()
|
||||
})
|
||||
|
||||
glob.sync('./pages/**/*.js').forEach((file) => {
|
||||
test(`.${file} imports shared apollo module`, (t) => {
|
||||
t.falsy(require.cache[apolloFilePath])
|
||||
|
||||
try {
|
||||
require(`.${file}`)
|
||||
} catch (error) {
|
||||
// Don't really care if it fails to execute, etc, just want to be
|
||||
// certain the expected require call was made
|
||||
}
|
||||
|
||||
t.truthy(require.cache[apolloFilePath])
|
||||
})
|
||||
})
|
Loading…
Reference in a new issue