diff options
| author | Wes Bos <wesbos@gmail.com> | 2018-01-30 12:41:01 -0500 |
|---|---|---|
| committer | Wes Bos <wesbos@gmail.com> | 2018-01-30 12:41:01 -0500 |
| commit | cd0b07b3adb36fb5ec098f9e511ab45d774fee7b (patch) | |
| tree | a203e5010219ccea1acc6bbd2c0a4bcfa0a78615 /frontend/lib | |
| parent | 0a1648bf47490b312169c531aeb51ef4725e8d2e (diff) | |
Move to Prisma Backend
Diffstat (limited to 'frontend/lib')
| -rw-r--r-- | frontend/lib/formatMoney.js | 3 | ||||
| -rw-r--r-- | frontend/lib/image.js | 6 | ||||
| -rw-r--r-- | frontend/lib/initApollo.js | 62 | ||||
| -rw-r--r-- | frontend/lib/withData.js | 76 |
4 files changed, 147 insertions, 0 deletions
diff --git a/frontend/lib/formatMoney.js b/frontend/lib/formatMoney.js new file mode 100644 index 0000000..edddf48 --- /dev/null +++ b/frontend/lib/formatMoney.js @@ -0,0 +1,3 @@ +export default function(amount) { + return '$' + (amount / 100).toLocaleString(); +} diff --git a/frontend/lib/image.js b/frontend/lib/image.js new file mode 100644 index 0000000..5b6efb9 --- /dev/null +++ b/frontend/lib/image.js @@ -0,0 +1,6 @@ +import { imageEndpoint } from '../config'; + +export default function(image, width = 300, height = 300) { + if (!image || !image.secret) return ''; + return `${imageEndpoint}/${image.secret}/${width}x${height}`; +} diff --git a/frontend/lib/initApollo.js b/frontend/lib/initApollo.js new file mode 100644 index 0000000..35f5e08 --- /dev/null +++ b/frontend/lib/initApollo.js @@ -0,0 +1,62 @@ +import { ApolloClient } from 'apollo-client'; +import { ApolloLink, concat } from 'apollo-link'; +import { HttpLink } from 'apollo-link-http'; +import { InMemoryCache } from 'apollo-cache-inmemory'; +import fetch from 'isomorphic-fetch'; +import { endpoint } from '../config'; + +let apolloClient = null; + +// Polyfill fetch() on the server (used by apollo-client) +if (!process.browser) { + global.fetch = fetch; +} + +function create(initialState) { + // First we create three peices of middleware + + // 1. Create a link for local state to be managed in Apollo + + + // 1. connect to our GraphQL API + const httpLink = new HttpLink({ + uri: endpoint, // Server URL (must be absolute) + opts: { + credentials: 'same-origin', + }, + }); + + // 2. One to tack on our auth token on each request + const authMiddleware = new ApolloLink((operation, forward) => { + const headers = {}; + + if (typeof localStorage !== 'undefined' && localStorage.getItem('auth0IdToken')) { + headers.authorization = `Bearer ${localStorage.getItem('auth0IdToken')}`; + } + + operation.setContext({ headers }); + return forward(operation); + }); + + return new ApolloClient({ + connectToDevTools: process.browser, + ssrMode: !process.browser, // Disables forceFetch on the server (so queries are only run once) + link: concat(authMiddleware, httpLink), + cache: new InMemoryCache().restore(initialState || {}), + }); +} + +export default function initApollo(initialState) { + // 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); + } + + // Reuse client on the client-side + if (!apolloClient) { + apolloClient = create(initialState); + } + + return apolloClient; +} diff --git a/frontend/lib/withData.js b/frontend/lib/withData.js new file mode 100644 index 0000000..0c68f49 --- /dev/null +++ b/frontend/lib/withData.js @@ -0,0 +1,76 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { ApolloProvider, getDataFromTree } from 'react-apollo'; +import Head from 'next/head'; +import initApollo from './initApollo'; + +// Gets the display name of a JSX component for dev tools +function getComponentDisplayName(Component) { + return Component.displayName || Component.name || 'Unknown'; +} + +export default ComposedComponent => + class WithData extends React.Component { + static displayName = `WithData(${getComponentDisplayName(ComposedComponent)})`; + static propTypes = { + serverState: PropTypes.object.isRequired, + }; + + static async getInitialProps(ctx) { + let serverState = { apollo: {} }; + + // Evaluate the composed component's getInitialProps() + let composedInitialProps = {}; + if (ComposedComponent.getInitialProps) { + composedInitialProps = await ComposedComponent.getInitialProps(ctx); + } + + // Run all GraphQL queries in the component tree + // and extract the resulting data + if (!process.browser) { + const apollo = initApollo(); + // Provide the `url` prop data in case a GraphQL query uses it + const url = { query: ctx.query, pathname: ctx.pathname }; + try { + // Run all GraphQL queries + await getDataFromTree( + <ApolloProvider client={apollo}> + <ComposedComponent url={url} {...composedInitialProps} /> + </ApolloProvider> + ); + } catch (error) { + // Prevent Apollo Client GraphQL errors from crashing SSR. + // Handle them in components via the data.error prop: + // http://dev.apollodata.com/react/api-queries.html#graphql-query-data-error + } + // getDataFromTree does not call componentWillUnmount + // head side effect therefore need to be cleared manually + Head.rewind(); + + // Extract query data from the Apollo store + serverState = { + apollo: { + data: apollo.cache.extract(), + }, + }; + } + + return { + serverState, + ...composedInitialProps, + }; + } + + constructor(props) { + super(props); + this.apollo = initApollo(this.props.serverState.apollo.data); + } + + render() { + return ( + <ApolloProvider client={this.apollo}> + <ComposedComponent {...this.props} /> + </ApolloProvider> + ); + } + }; |
