-
-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
18210c6
commit 492a23f
Showing
4 changed files
with
452 additions
and
26 deletions.
There are no files selected for viewing
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import { Context } from '@nuxt/types-edge' | ||
import consola from 'consola' | ||
|
||
export default ({ store }: Context) => { | ||
return { | ||
httpEndpoint: process.server | ||
? 'http://postgraphile:5000/graphql' | ||
: 'https://postgraphile.' + | ||
(process.env.NUXT_ENV_STACK_DOMAIN || 'maevsi.test') + | ||
'/graphql', | ||
getAuth: (_tokenName: string) => { | ||
const jwt = store.state.jwt | ||
|
||
if (jwt) { | ||
consola.trace('Apollo request authenticated with: ' + jwt) | ||
return `Bearer ${jwt}` | ||
} else { | ||
consola.trace('Apollo request without authentication.') | ||
return '' | ||
} | ||
}, | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,48 +1,312 @@ | ||
import { Inject } from '@nuxt/types-edge/app' | ||
import { Context } from '@nuxt/types-edge' | ||
import { ApolloClient } from 'apollo-client' | ||
import { createHttpLink } from 'apollo-link-http' | ||
import { InMemoryCache } from 'apollo-cache-inmemory' | ||
import { ApolloClient, ApolloClientOptions, Resolvers } from 'apollo-client' | ||
import { ApolloLink, DocumentNode, from } from 'apollo-link' | ||
import { setContext } from 'apollo-link-context' | ||
import { createPersistedQueryLink } from 'apollo-link-persisted-queries' | ||
import { HttpOptions } from 'apollo-link-http-common' | ||
import { withClientState, ClientStateConfig } from 'apollo-link-state' | ||
import { | ||
InMemoryCache, | ||
InMemoryCacheConfig, | ||
NormalizedCacheObject, | ||
} from 'apollo-cache-inmemory' | ||
import { createUploadLink } from 'apollo-upload-client' | ||
import Vue from 'vue' | ||
import VueApollo from 'vue-apollo' | ||
import consola from 'consola' | ||
import { parse } from 'cookie' | ||
|
||
import config from './apollo-config' | ||
|
||
export interface ApolloClientClientConfig<TCacheShape> { | ||
apollo?: ApolloClientOptions<TCacheShape> | ||
cache?: InMemoryCache | false | ||
clientState?: ClientStateConfig | ||
defaultHttpLink?: boolean | ||
httpEndpoint?: string | ||
httpLinkOptions?: HttpOptions | ||
inMemoryCacheOptions?: InMemoryCacheConfig | ||
link?: ApolloLink | ||
persisting?: boolean | ||
resolvers?: Resolvers | Resolvers[] | ||
ssr?: boolean | ||
tokenName?: string | ||
typeDefs?: string | string[] | DocumentNode | DocumentNode[] | ||
websocketsOnly?: boolean | ||
wsEndpoint?: string | ||
getAuth?: (tokenName: string) => string | void | ||
onCacheInit?: (cache: InMemoryCache) => Promise<void> | ||
validateToken?: (token: string) => boolean | ||
} | ||
|
||
Vue.use(VueApollo) | ||
|
||
export default ({ app }: Context, inject: Inject) => { | ||
const httpLink = createHttpLink({ | ||
uri: process.server | ||
? 'http://postgraphile:5000/graphql' | ||
: 'https://postgraphile.' + | ||
(process.env.NUXT_ENV_STACK_DOMAIN || 'maevsi.test') + | ||
'/graphql', | ||
export default (ctx: Context) => { | ||
const { app, beforeNuxtRender, req } = ctx | ||
|
||
const tokenName = 'apollo-token' | ||
// const cookieAttributes = { expires: 7, path: '/', secure: false } | ||
const authenticationType = 'Bearer' | ||
const cookies = req && req.headers.cookie && parse(req.headers.cookie) | ||
|
||
const clientConfig = config(ctx) as ApolloClientClientConfig<unknown> | ||
const defaultValidateToken = () => true | ||
|
||
function defaultGetAuth() { | ||
const token = cookies && cookies[tokenName] | ||
return token && | ||
clientConfig.validateToken && | ||
clientConfig.validateToken(token) | ||
? `${authenticationType} ${token}` | ||
: '' | ||
} | ||
|
||
if (!clientConfig.validateToken) { | ||
clientConfig.validateToken = defaultValidateToken | ||
} | ||
|
||
const defaultCache = clientConfig.cache | ||
? clientConfig.cache | ||
: new InMemoryCache( | ||
clientConfig.inMemoryCacheOptions | ||
? clientConfig.inMemoryCacheOptions | ||
: undefined | ||
) | ||
|
||
if ( | ||
!process.server && | ||
window.__NUXT__ && | ||
window.__NUXT__.apollo && | ||
window.__NUXT__.apollo.defaultClient | ||
) { | ||
defaultCache.restore(window.__NUXT__.apollo.defaultClient) | ||
} | ||
|
||
if (!clientConfig.getAuth) { | ||
clientConfig.getAuth = defaultGetAuth | ||
} | ||
|
||
// if (process.client && clientConfig.browserHttpEndpoint) { | ||
// clientConfig.httpEndpoint = clientConfig.browserHttpEndpoint | ||
// } | ||
|
||
clientConfig.ssr = !!process.server | ||
clientConfig.cache = defaultCache | ||
// clientConfig.tokenName = defaultTokenName | ||
|
||
if (process.server && req && req.headers && req.headers.cookie) { | ||
if (!clientConfig.httpLinkOptions) { | ||
clientConfig.httpLinkOptions = {} | ||
} | ||
if (!clientConfig.httpLinkOptions.headers) { | ||
clientConfig.httpLinkOptions.headers = {} | ||
} | ||
clientConfig.httpLinkOptions.headers.cookie = req.headers.cookie | ||
} | ||
|
||
const defaultApolloCreation = createApolloClient({ | ||
...clientConfig, | ||
}) | ||
const cache = new InMemoryCache() | ||
|
||
if (process.client) { | ||
const vueApolloOptions = { | ||
defaultClient: defaultApolloCreation.apolloClient, | ||
errorHandler(error: Error) { | ||
consola.log( | ||
'%cError', | ||
'background: red; color: white; padding: 2px 4px; border-radius: 3px; font-weight: bold;', | ||
error.message | ||
) | ||
}, | ||
} | ||
|
||
const apolloProvider = new VueApollo(vueApolloOptions) | ||
|
||
app.apolloProvider = apolloProvider | ||
|
||
if (process.server) { | ||
const apolloSSR = require('vue-apollo/ssr') | ||
beforeNuxtRender(({ nuxtState }) => { | ||
nuxtState.apollo = apolloSSR.getStates(apolloProvider) | ||
}) | ||
} | ||
} | ||
|
||
export function createApolloClient<TCacheShape>( | ||
options: ApolloClientClientConfig<TCacheShape> | ||
): { | ||
apolloClient: ApolloClient<NormalizedCacheObject> | ||
stateLink: ApolloLink | undefined | ||
} { | ||
const defaultOptions = { | ||
clientId: 'defaultClient', | ||
tokenName: 'apollo-token', | ||
persisting: false, | ||
ssr: false, | ||
websocketsOnly: false, | ||
preAuthLinks: [], | ||
defaultHttpLink: true, | ||
httpLinkOptions: {}, | ||
inMemoryCacheOptions: {}, | ||
apollo: {}, | ||
getAuth: defaultGetAuth, | ||
} | ||
|
||
const optionsMerged = { | ||
...defaultOptions, | ||
...options, | ||
} | ||
|
||
let { cache, link, persisting } = optionsMerged | ||
|
||
const { | ||
apollo, | ||
clientId, | ||
clientState, | ||
defaultHttpLink, | ||
httpEndpoint, | ||
httpLinkOptions, | ||
inMemoryCacheOptions, | ||
preAuthLinks, | ||
resolvers, | ||
ssr, | ||
tokenName, | ||
typeDefs, | ||
websocketsOnly, | ||
wsEndpoint, | ||
getAuth, | ||
onCacheInit, | ||
} = optionsMerged | ||
|
||
let authLink, stateLink | ||
const disableHttp = websocketsOnly && !ssr && wsEndpoint | ||
|
||
// Apollo cache | ||
if (!cache) { | ||
cache = new InMemoryCache(inMemoryCacheOptions) | ||
} | ||
|
||
if (!disableHttp) { | ||
const httpLink = createUploadLink({ | ||
uri: httpEndpoint, | ||
...httpLinkOptions, | ||
}) | ||
|
||
if (!link) { | ||
link = httpLink | ||
} else if (defaultHttpLink) { | ||
link = from([link, httpLink]) | ||
} | ||
|
||
// HTTP Auth header injection | ||
authLink = setContext(async (_, { headers }) => { | ||
const Authorization = await getAuth(tokenName) | ||
const authorizationHeader = Authorization ? { Authorization } : {} | ||
return { | ||
headers: { | ||
...headers, | ||
...authorizationHeader, | ||
}, | ||
} | ||
}) | ||
|
||
// Concat all the http link parts | ||
link = authLink.concat(link) | ||
|
||
if (preAuthLinks.length) { | ||
link = from(preAuthLinks).concat(authLink) | ||
} | ||
} | ||
|
||
// On the server, we don't want WebSockets and Upload links | ||
if (!ssr) { | ||
// If on the client, recover the injected state | ||
if (typeof window !== 'undefined') { | ||
// eslint-disable-next-line no-underscore-dangle | ||
const state = window.__APOLLO_STATE__ | ||
if (state) { | ||
cache.restore(state.defaultClient) | ||
if (state && state[clientId]) { | ||
// Restore state | ||
cache.restore(state[clientId]) | ||
} | ||
} | ||
|
||
if (!disableHttp) { | ||
let persistingOpts = {} | ||
if (typeof persisting === 'object' && persisting != null) { | ||
persistingOpts = persisting | ||
persisting = true | ||
} | ||
if (persisting === true && link) { | ||
link = createPersistedQueryLink(persistingOpts).concat(link) | ||
} | ||
} | ||
} | ||
|
||
if (clientState && link) { | ||
consola.warn( | ||
'clientState is deprecated, see https://vue-cli-plugin-apollo.netlify.com/guide/client-state.html' | ||
) | ||
stateLink = withClientState({ | ||
cache, | ||
...clientState, | ||
resolvers: undefined, | ||
}) | ||
link = from([stateLink, link]) | ||
} | ||
|
||
const apolloClient = new ApolloClient({ | ||
link: httpLink, | ||
link, | ||
cache, | ||
...(process.server | ||
// Additional options | ||
...(ssr | ||
? { | ||
// Set this on the server to optimize queries when SSR | ||
ssrMode: true, | ||
} | ||
: { | ||
// This will temporary disable query force-fetching | ||
ssrForceFetchDelay: 100, | ||
// Apollo devtools | ||
connectToDevTools: process.env.NODE_ENV !== 'production', | ||
}), | ||
}) | ||
const apolloProvider = new VueApollo({ | ||
defaultClient: apolloClient, | ||
typeDefs, | ||
resolvers, | ||
...apollo, | ||
}) | ||
|
||
app.apolloProvider = apolloProvider | ||
// Re-write the client state defaults on cache reset | ||
if (stateLink) { | ||
apolloClient.onResetStore(stateLink.writeDefaults) | ||
} | ||
|
||
if (onCacheInit) { | ||
onCacheInit(cache) | ||
apolloClient.onResetStore(() => { | ||
if (cache) { | ||
return onCacheInit(cache) | ||
} else { | ||
return Promise.resolve() | ||
} | ||
}) | ||
} | ||
|
||
return { | ||
apolloClient, | ||
stateLink, | ||
} | ||
} | ||
|
||
inject('apolloProvider', apolloProvider) | ||
function defaultGetAuth(tokenName: string) { | ||
if (typeof window !== 'undefined') { | ||
// get the authentication token from local storage if it exists | ||
const token = window.localStorage.getItem(tokenName) | ||
// return the headers to the context so httpLink can read them | ||
return token ? `Bearer ${token}` : '' | ||
} | ||
} | ||
|
||
declare global { | ||
interface Window { | ||
__APOLLO_STATE__: Record<string, NormalizedCacheObject> | ||
__NUXT__: Record<string, NormalizedCacheObject> | ||
} | ||
} |
Oops, something went wrong.