-
-
Save tbantle22/ec75d042bfa8e08818d0616c482e282f to your computer and use it in GitHub Desktop.
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 { FeatureProvider } from "@dolthub/shared-components"; | |
import App from "next/app"; | |
import Head from "next/head"; | |
import React, { ReactNode } from "react"; | |
import { SWRConfig } from "swr"; | |
import { | |
ServerConfigProvider, | |
useServerConfig, | |
} from "../contexts/serverConfig"; | |
import { withApollo } from "../lib/apollo"; | |
// configure fetch for use with SWR | |
const fetcher = async (input: RequestInfo, init: RequestInit) => { | |
const res = await fetch(input, init); | |
if (!res.ok) { | |
throw await res.json(); | |
} | |
return res.json(); | |
}; | |
type Props = { | |
children: ReactNode; | |
}; | |
export default class DoltHub extends App { | |
public render() { | |
const { Component, pageProps, router } = this.props; | |
// this.props.pageProps are the initial props fetched on a server side render. | |
// The following keeps the pageProps updated with the client navigation. | |
// This is necessary for pages that call getServerSideProps AND are routed to | |
// from within the app (i.e. router.push()). | |
pageProps.params = { | |
...router.query, | |
}; | |
const WrappedPage = withApollo()(Component); | |
return ( | |
<> | |
<Head> | |
{/* Add script and link tags here */} | |
</Head> | |
<SWRConfig value={{ fetcher }}> | |
<ServerConfigProvider> | |
<WrappedPage {...pageProps} /> | |
</ServerConfigProvider> | |
</SWRConfig> | |
</> | |
); | |
} | |
} |
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
// lib/apollo.tsx | |
import { | |
ApolloClient, | |
ApolloProvider, | |
HttpLink, | |
InMemoryCache, | |
} from "@apollo/client"; | |
import { NormalizedCacheObject } from "@apollo/client/cache"; | |
import { IncomingMessage } from "http"; | |
import fetch from "isomorphic-unfetch"; | |
import { NextPage, NextPageContext } from "next"; | |
import getConfig from "next/config"; | |
import React from "react"; | |
import { possibleTypes } from "../gen/fragmentTypes.json"; | |
const { | |
publicRuntimeConfig: { graphqlApiUrl }, | |
} = getConfig(); | |
export function createApolloClient( | |
uri: string, | |
initialState?: NormalizedCacheObject, | |
req?: IncomingMessage, | |
) { | |
// Add headers here | |
const headers = {}; | |
const cache = new InMemoryCache({ | |
possibleTypes, | |
}).restore(initialState || {}); | |
return new ApolloClient({ | |
cache, | |
link: new HttpLink({ | |
fetch, | |
credentials: "include", | |
uri, | |
headers, | |
}), | |
}); | |
} | |
// On the client, we store the Apollo Client in the following variable. | |
// This prevents the client from reinitializing between page transitions. | |
let globalApolloClient: ApolloClient<NormalizedCacheObject> | undefined; | |
/** | |
* Always creates a new apollo client on the server | |
* Creates or reuses apollo client in the browser. | |
* @param {NormalizedCacheObject} initialState | |
* @param {IncomingMessage} req | |
*/ | |
export const initApolloClient = ( | |
initialState?: NormalizedCacheObject, | |
req?: IncomingMessage, | |
) => { | |
// 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( | |
process.env.INTERNAL_GRAPHQLAPI_URL!, | |
initialState, | |
req, | |
); | |
} | |
// Reuse client on the client-side | |
if (!globalApolloClient) { | |
globalApolloClient = createApolloClient(graphqlApiUrl, initialState, req); | |
} | |
return globalApolloClient; | |
}; | |
type ApolloContext = { | |
apolloClient?: ApolloClient<NormalizedCacheObject>; | |
apolloState?: NormalizedCacheObject; | |
}; | |
type ContextWithClient = NextPageContext & ApolloContext; | |
/** | |
* Installs the Apollo Client on NextPageContext | |
* Useful if you want to use apolloClient | |
* inside getStaticProps, getStaticPaths or getServerSideProps | |
* @param {NextPageContext | NextAppContext} ctx | |
*/ | |
export const initOnContext = (ctx: NextPageContext) => { | |
const newCtx: ContextWithClient = ctx; | |
// Initialize ApolloClient if not already done | |
const apolloClient = | |
newCtx.apolloClient ?? initApolloClient(newCtx.apolloState ?? {}, ctx.req); | |
// We send the Apollo Client as a prop to the component to avoid calling initApollo() twice in the server. | |
// Otherwise, the component would have to call initApollo() again but this | |
// time without the context. Once that happens, the following code will make sure we send | |
// the prop as `null` to the browser. | |
(apolloClient as any).toJSON = () => null; | |
// Add apolloClient to NextPageContext. | |
newCtx.apolloClient = apolloClient; | |
return newCtx; | |
}; | |
/** | |
* Creates a withApollo HOC | |
* that provides the apolloContext | |
* to a next.js Page or AppTree. | |
* @returns {(PageComponent: ReactNode) => ReactNode} | |
*/ | |
export function withApollo< | |
P extends Record<string, unknown> = Record<string, unknown> | |
>() { | |
return (PageComponent: NextPage<P>) => { | |
const WithApollo = ({ | |
apolloClient, | |
apolloState, | |
...pageProps | |
}: ApolloContext) => { | |
let client: ApolloClient<NormalizedCacheObject>; | |
if (apolloClient) { | |
// Happens on: getDataFromTree & next.js ssr | |
client = apolloClient; | |
} else { | |
// Happens on: next.js csr | |
client = initApolloClient(apolloState, undefined); | |
} | |
return ( | |
<ApolloProvider client={client}> | |
<PageComponent {...(pageProps as P)} /> | |
</ApolloProvider> | |
); | |
}; | |
// Set the correct displayName in development | |
if (process.env.NODE_ENV !== "production") { | |
const displayName = | |
(PageComponent.displayName ?? PageComponent.name) || "Component"; | |
WithApollo.displayName = `withApollo(${displayName})`; | |
} | |
return WithApollo; | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment