Created
December 23, 2019 15:34
-
-
Save BryceAMcDaniel/a710afe4fd877a04e55a921e4e74a21c to your computer and use it in GitHub Desktop.
auth0 specific withApolloHOC for use with SSR in next.js.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import auth0 from '../../lib/Auth0/config'; | |
export default async function session(req, res) { | |
try { | |
// console.log(req); | |
const { accessToken } = await auth0.getSession(req); | |
if (accessToken) res.send(accessToken); | |
res.status(500).end(accessToken); | |
} catch (error) { | |
// console.error(error); | |
res.status(error.status || 500).end(error.message); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* eslint-disable @typescript-eslint/no-use-before-define */ | |
import React from 'react'; | |
import Head from 'next/head'; | |
import { ApolloProvider } from '@apollo/react-hooks'; | |
import { ApolloClient } from 'apollo-client'; | |
import { InMemoryCache } from 'apollo-cache-inmemory'; | |
import { createHttpLink } from 'apollo-link-http'; | |
import { setContext } from 'apollo-link-context'; | |
import fetch from 'isomorphic-unfetch'; | |
import auth0 from '../Auth0/config'; | |
// HERE STARTS THE NEXT JS APOLLO EXAMPLE. | |
let apolloClient = null; | |
// const isBrowser = typeof window !== 'undefined'; | |
/** | |
* Creates and provides the apolloContext | |
* to a next.js PageTree. Use it by wrapping | |
* your PageComponent via HOC pattern. | |
* @param {Function|Class} PageComponent | |
* @param {Object} [config] | |
* @param {Boolean} [config.ssr=true] | |
*/ | |
// eslint-disable-next-line import/prefer-default-export | |
export function withApollo(PageComponent, { ssr = true } = {}) { | |
const WithApollo = ({ apolloClient, apolloState, ...pageProps }) => { | |
const client = apolloClient || initApolloClient(apolloState); | |
return ( | |
<ApolloProvider client={client}> | |
<PageComponent {...pageProps} /> | |
</ApolloProvider> | |
); | |
}; | |
// Set the correct displayName in development | |
if (process.env.NODE_ENV !== 'production') { | |
const displayName = PageComponent.displayName || PageComponent.name || 'Component'; | |
if (displayName === 'App') { | |
// eslint-disable-next-line no-console | |
console.warn('This withApollo HOC only works with PageComponents.'); | |
} | |
WithApollo.displayName = `withApollo(${displayName})`; | |
} | |
if (ssr || PageComponent.getInitialProps) { | |
WithApollo.getInitialProps = async (ctx) => { | |
const { AppTree, req } = ctx; | |
// Initialize ApolloClient, add it to the ctx object so | |
// we can use it in `PageComponent.getInitialProp`. | |
// eslint-disable-next-line no-multi-assign | |
const apolloClient = (ctx.apolloClient = initApolloClient({}, req)); | |
// Run wrapped getInitialProps methods | |
let pageProps = {}; | |
if (PageComponent.getInitialProps) { | |
pageProps = await PageComponent.getInitialProps(ctx); | |
} | |
// Only on the server: | |
if (typeof window === 'undefined') { | |
// When redirecting, the response is finished. | |
// No point in continuing to render | |
if (ctx.res && ctx.res.finished) { | |
return pageProps; | |
} | |
// Only if ssr is enabled | |
if (ssr) { | |
try { | |
// Run all GraphQL queries | |
const { getDataFromTree } = await import('@apollo/react-ssr'); | |
await getDataFromTree( | |
<AppTree | |
pageProps={{ | |
...pageProps, | |
apolloClient, | |
}} | |
/>, | |
); | |
} catch (error) { | |
// Prevent Apollo Client GraphQL errors from crashing SSR. | |
// Handle them in components via the data.error prop: | |
// https://www.apollographql.com/docs/react/api/react-apollo.html#graphql-query-data-error | |
// eslint-disable-next-line no-console | |
console.error('Error while running `getDataFromTree`', error); | |
} | |
// getDataFromTree does not call componentWillUnmount | |
// head side effect therefore need to be cleared manually | |
Head.rewind(); | |
} | |
} | |
// Extract query data from the Apollo store | |
const apolloState = apolloClient.cache.extract(); | |
return { | |
...pageProps, | |
apolloState, | |
}; | |
}; | |
} | |
return WithApollo; | |
} | |
/** | |
* Always creates a new apollo client on the server | |
* Creates or reuses apollo client in the browser. | |
* @param {Object} initialState | |
*/ | |
function initApolloClient(initialState, req) { | |
// 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 (typeof window === 'undefined') { | |
return createApolloClient(initialState, req); | |
} | |
// Reuse client on the client-side | |
if (!apolloClient) { | |
apolloClient = createApolloClient(initialState, req); | |
} | |
return apolloClient; | |
} | |
/** | |
* Creates and configures the ApolloClient | |
* @param {Object} [initialState={}] | |
*/ | |
function createApolloClient(initialState = {}, req) { | |
// Check out https://github.com/zeit/next.js/pull/4611 if you want to use the AWSAppSyncClient | |
const isBrowser = typeof window !== 'undefined'; | |
const httpLink = createHttpLink({ | |
uri: 'http://localhost:3001', | |
fetch, | |
credentials: 'omit', | |
// credentials: 'include', | |
}); | |
// Custom Auth link that fetches the access_token from local and supplies it with every request. | |
const authLink = setContext(async (_, { headers }) => { | |
// const isBrowser = typeof window !== 'undefined'; | |
// If we already have an accessToken, there is no need to try and fetch one. | |
// (This is because server side reqeusts already include the cooke, no need to fetch it again.) | |
// Client-side request dont have access to httpOnly cookies, and require an accessor | |
// like /api/getToken | |
// If there is a req object, try to pull the access token from it. | |
if (req) { | |
const { accessToken } = await auth0.getSession(req); | |
return { | |
headers: { | |
...headers, | |
authorization: accessToken ? `Bearer ${accessToken}` : '', | |
}, | |
}; | |
} | |
const response = await fetch('http://localhost:3000/api/getToken', { | |
credentials: 'same-origin', | |
}); | |
const token = await response.text(); | |
// return the headers to the context so httpLink can read them | |
return { | |
headers: { | |
...headers, | |
authorization: token ? `Bearer ${token}` : '', | |
}, | |
}; | |
}); | |
return new ApolloClient({ | |
ssrMode: isBrowser, // Disables forceFetch on the server (so queries are only run once) | |
link: authLink.concat(httpLink), | |
connectToDevTools: isBrowser, | |
cache: new InMemoryCache().restore(initialState), | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment