Working SSR support
This commit is contained in:
@@ -11,7 +11,7 @@ Integrates [apollo](http://www.apollostack.com/) in your [Vue](http://vuejs.org)
|
||||
|
||||
[<img src="https://assets-cdn.github.com/favicon.ico" alt="icon" width="16" height="16"/> Apollo graphql server example](https://github.com/Akryum/apollo-server-example)
|
||||
|
||||
[<img src="https://assets-cdn.github.com/favicon.ico" alt="icon" width="16" height="16"/> Apollo "hello world" example app](https://github.com/Akryum/frontpage-vue-app)
|
||||
[<img src="https://assets-cdn.github.com/favicon.ico" alt="icon" width="16" height="16"/> Apollo "hello world" example app](https://github.com/Akryum/frontpage-vue-app) (outdated)
|
||||
|
||||
[<img src="https://cdn-static-1.medium.com/_/fp/icons/favicon-medium.TAS6uQ-Y7kcKgi0xjcYHXw.ico" alt="icon" width="16" height="16"/> Howto on Medium](https://dev-blog.apollodata.com/use-apollo-in-your-vuejs-app-89812429d8b2#.pdd4hmcrc)
|
||||
|
||||
@@ -1068,28 +1068,298 @@ tags: {
|
||||
|
||||
## Server-Side Rendering
|
||||
|
||||
(Work in progress)
|
||||
### Prefetch components
|
||||
|
||||
Use `apolloProvider.collect()` to being collect queries made against this provider. You get a function that returns a promise when all queries collected are ready. Note that the provider stops collecting queries when you call the function.
|
||||
On the queries you want to prefetch on the server, add the `prefetch` option. It can either be:
|
||||
- a variables object,
|
||||
- a function that gets the context object (which can contain the URL for example) and return a variables object,
|
||||
- `true` (query's `variables` is reused).
|
||||
|
||||
**Warning! You don't have access to the component instance when doing prefetching on the server. Don't use `this` in `prefetch`!**
|
||||
|
||||
Example:
|
||||
|
||||
```javascript
|
||||
const ensureReady = apolloProvider.collect({
|
||||
// If set to false, may resolve when partial/cache result is emitted
|
||||
waitForLoaded: true, // default
|
||||
})
|
||||
export default {
|
||||
apollo: {
|
||||
allPosts: {
|
||||
query: gql`query AllPosts {
|
||||
allPosts {
|
||||
id
|
||||
imageUrl
|
||||
description
|
||||
}
|
||||
}`,
|
||||
prefetch: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
new Vue({
|
||||
el: '#app',
|
||||
apolloProvider,
|
||||
render: h => h(App),
|
||||
})
|
||||
Example 2:
|
||||
|
||||
ensureReady().then(results => {
|
||||
console.log(results.length, 'queries ready')
|
||||
```javascript
|
||||
export default {
|
||||
apollo: {
|
||||
post: {
|
||||
query: gql`query Post($id: ID!) {
|
||||
post (id: $id) {
|
||||
id
|
||||
imageUrl
|
||||
description
|
||||
}
|
||||
}`,
|
||||
prefetch: ({ route }) => {
|
||||
return {
|
||||
id: route.params.id,
|
||||
}
|
||||
},
|
||||
variables () {
|
||||
return {
|
||||
id: this.id,
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can also tell vue-apollo that some components not used in a `router-view` (and thus not in vue-router `matchedComponents`) need to be prefetched, with the `willPrefetch` method:
|
||||
|
||||
```javascript
|
||||
import { willPrefetch } from 'vue-apollo'
|
||||
|
||||
export default willPrefetch({
|
||||
apollo: {
|
||||
allPosts: {
|
||||
query: gql`query AllPosts {
|
||||
allPosts {
|
||||
id
|
||||
imageUrl
|
||||
description
|
||||
}
|
||||
}`,
|
||||
prefetch: true, // Don't forget this
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
[White paper](./ssr.js)
|
||||
### On the server
|
||||
|
||||
To prefetch all the apollo queries you marked, use the `apolloProvider.prefetchAll` method. The first argument is the context object passed to the `prefetch` hooks (see above). It is recommended to pass the vue-router `currentRoute` object. The second argument is the array of component definition to include (e.g. from `router.getMatchedComponents` method). The third argument is an optional `options` object. It returns a promise resolved when all the apollo queries are loaded.
|
||||
|
||||
Here is an example with vue-router and a Vuex store:
|
||||
|
||||
```javascript
|
||||
return new Promise((resolve, reject) => {
|
||||
const { app, router, store, apolloProvider } = CreateApp({
|
||||
ssr: true,
|
||||
})
|
||||
|
||||
// set router's location
|
||||
router.push(context.url)
|
||||
|
||||
// wait until router has resolved possible async hooks
|
||||
router.onReady(() => {
|
||||
const matchedComponents = router.getMatchedComponents()
|
||||
|
||||
// no matched routes
|
||||
if (!matchedComponents.length) {
|
||||
reject({ code: 404 })
|
||||
}
|
||||
|
||||
let js = ''
|
||||
|
||||
// Call preFetch hooks on components matched by the route.
|
||||
// A preFetch hook dispatches a store action and returns a Promise,
|
||||
// which is resolved when the action is complete and store state has been
|
||||
// updated.
|
||||
Promise.all([
|
||||
// Vuex Store prefetch
|
||||
...matchedComponents.map(component => {
|
||||
return component.preFetch && component.preFetch(store)
|
||||
}),
|
||||
// Apollo prefetch
|
||||
apolloProvider.prefetchAll({
|
||||
route: router.currentRoute,
|
||||
}, matchedComponents),
|
||||
]).then(() => {
|
||||
// Inject the Vuex state and the Apollo cache on the page.
|
||||
// This will prevent unecessary queries.
|
||||
|
||||
// Vuex
|
||||
js += `window.__INITIAL_STATE__=${JSON.stringify(store.state)};`
|
||||
|
||||
// Apollo
|
||||
js += apolloProvider.exportStates()
|
||||
|
||||
resolve({
|
||||
app,
|
||||
js,
|
||||
})
|
||||
}).catch(reject)
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
The `options` argument defaults to:
|
||||
|
||||
```javascript
|
||||
{
|
||||
// Include components outside of the routes
|
||||
// that are registered with `willPrefetch`
|
||||
includeGlobal: true,
|
||||
}
|
||||
```
|
||||
|
||||
Use the `apolloProvider.exportStates` method to get the JavaScript code you need to inject into the generated page to pass the apollo cache data to the client.
|
||||
|
||||
It takes an `options` argument which defaults to:
|
||||
|
||||
```javascript
|
||||
{
|
||||
// Global variable name
|
||||
globalName: '__APOLLO_STATE__',
|
||||
// Global object on which the variable is set
|
||||
attachTo: 'window',
|
||||
// Prefix for the keys of each apollo client state
|
||||
exportNamespace: '',
|
||||
}
|
||||
```
|
||||
|
||||
### Creating the Apollo Clients
|
||||
|
||||
It is recommended to create the apollo clients inside a function with an `ssr` argument, which is `true` on the server and `false` on the client.
|
||||
|
||||
Here is an example:
|
||||
|
||||
```javascript
|
||||
// src/api/apollo.js
|
||||
|
||||
import Vue from 'vue'
|
||||
import { ApolloClient, createNetworkInterface } from 'apollo-client'
|
||||
import VueApollo from 'vue-apollo'
|
||||
|
||||
// Install the vue plugin
|
||||
Vue.use(VueApollo)
|
||||
|
||||
// Create the apollo client
|
||||
export function createApolloClient (ssr = false) {
|
||||
let initialState
|
||||
|
||||
// If on the client, recover the injected state
|
||||
if (!ssr && typeof window !== 'undefined') {
|
||||
const state = window.__APOLLO_STATE__
|
||||
if (state) {
|
||||
// If you have multiple clients, use `state.<client_id>`
|
||||
initialState = state.defaultClient
|
||||
}
|
||||
}
|
||||
|
||||
const apolloClient = new ApolloClient({
|
||||
networkInterface: createNetworkInterface({
|
||||
// You should use an absolute URL here
|
||||
uri: 'https://api.graph.cool/simple/v1/cj1jvw20v3n310152sv0sirl7',
|
||||
transportBatching: true,
|
||||
}),
|
||||
...(ssr ? {
|
||||
// Set this on the server to optimize queries when SSR
|
||||
ssrMode: true,
|
||||
} : {
|
||||
// Inject the state on the client
|
||||
initialState,
|
||||
// This will temporary disable query force-fetching
|
||||
ssrForceFetchDelay: 100,
|
||||
}),
|
||||
})
|
||||
|
||||
return apolloClient
|
||||
}
|
||||
```
|
||||
|
||||
Example for common `CreateApp` method:
|
||||
|
||||
```javascript
|
||||
import Vue from 'vue'
|
||||
|
||||
import VueRouter from 'vue-router'
|
||||
Vue.use(VueRouter)
|
||||
|
||||
import Vuex from 'vuex'
|
||||
Vue.use(Vuex)
|
||||
|
||||
import { sync } from 'vuex-router-sync'
|
||||
|
||||
import VueApollo from 'vue-apollo'
|
||||
import { createApolloClient } from './api/apollo'
|
||||
|
||||
import App from './ui/App.vue'
|
||||
import routes from './routes'
|
||||
import storeOptions from './store'
|
||||
|
||||
function createApp (context) {
|
||||
const router = new VueRouter({
|
||||
mode: 'history',
|
||||
routes,
|
||||
})
|
||||
|
||||
const store = new Vuex.Store(storeOptions)
|
||||
|
||||
// sync the router with the vuex store.
|
||||
// this registers `store.state.route`
|
||||
sync(store, router)
|
||||
|
||||
// Apollo
|
||||
const apolloClient = createApolloClient(context.ssr)
|
||||
const apolloProvider = new VueApollo({
|
||||
defaultClient: apolloClient,
|
||||
})
|
||||
|
||||
return {
|
||||
app: new Vue({
|
||||
el: '#app',
|
||||
router,
|
||||
store,
|
||||
apolloProvider,
|
||||
...App,
|
||||
}),
|
||||
router,
|
||||
store,
|
||||
apolloProvider,
|
||||
}
|
||||
}
|
||||
|
||||
export default createApp
|
||||
```
|
||||
|
||||
On the client:
|
||||
|
||||
```javascript
|
||||
import CreateApp from './app'
|
||||
|
||||
CreateApp({
|
||||
ssr: false,
|
||||
})
|
||||
```
|
||||
|
||||
On the server:
|
||||
|
||||
```javascript
|
||||
import { CreateApp } from './app'
|
||||
|
||||
const { app, router, store, apolloProvider } = CreateApp({
|
||||
ssr: true,
|
||||
})
|
||||
|
||||
// set router's location
|
||||
router.push(context.url)
|
||||
|
||||
// wait until router has resolved possible async hooks
|
||||
router.onReady(() => {
|
||||
// Prefetch, render HTML (see above)
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
|
||||
+142
-35
@@ -1,3 +1,7 @@
|
||||
import omit from 'lodash.omit'
|
||||
import { VUE_APOLLO_QUERY_KEYWORDS } from './consts'
|
||||
import { getMergedDefinition } from './utils'
|
||||
|
||||
export class ApolloProvider {
|
||||
constructor (options) {
|
||||
if (!options) {
|
||||
@@ -5,43 +9,146 @@ export class ApolloProvider {
|
||||
}
|
||||
this.clients = options.clients || {}
|
||||
this.clients.defaultClient = this.defaultClient = options.defaultClient
|
||||
this._collecting = false
|
||||
|
||||
this.prefetchQueries = []
|
||||
}
|
||||
|
||||
collect (options) {
|
||||
const finalOptions = Object.assign({}, {
|
||||
waitForLoaded: true,
|
||||
}, options)
|
||||
this._ready = false
|
||||
this._promises = []
|
||||
this._collectingOptions = finalOptions
|
||||
this._isCollecting = true
|
||||
this._ensureReadyPromise = null
|
||||
return this.ensureReady.bind(this)
|
||||
}
|
||||
|
||||
ensureReady () {
|
||||
if (this._ready) {
|
||||
return Promise.resolve()
|
||||
} else {
|
||||
if (!this._ensureReadyPromise) {
|
||||
this._ensureReadyPromise = this._ensureReady()
|
||||
}
|
||||
return this._ensureReadyPromise
|
||||
}
|
||||
}
|
||||
|
||||
_waitFor (promise) {
|
||||
if (this._isCollecting) {
|
||||
this._promises.push(promise)
|
||||
}
|
||||
}
|
||||
|
||||
_ensureReady () {
|
||||
this._isCollecting = false
|
||||
return Promise.all(this._promises).then((result) => {
|
||||
this._ready = true
|
||||
return result
|
||||
willPrefetchQuery (queryOptions, client) {
|
||||
this.prefetchQueries.push({
|
||||
queryOptions,
|
||||
client,
|
||||
})
|
||||
}
|
||||
|
||||
willPrefetch (component) {
|
||||
component = getMergedDefinition(component)
|
||||
const apolloOptions = component.apollo
|
||||
|
||||
if (!apolloOptions) {
|
||||
return
|
||||
}
|
||||
|
||||
const componentClient = apolloOptions.$client
|
||||
for (let key in apolloOptions) {
|
||||
const options = apolloOptions[key]
|
||||
if (
|
||||
!options.query || (
|
||||
(typeof options.ssr === 'undefined' || options.ssr) &&
|
||||
(typeof options.prefetch !== 'undefined' && options.prefetch)
|
||||
)
|
||||
) {
|
||||
this.willPrefetchQuery(options, options.client || componentClient)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
willPrefetchComponents (definitions) {
|
||||
for (const def of definitions) {
|
||||
this.willPrefetch(def)
|
||||
}
|
||||
}
|
||||
|
||||
prefetchAll (context, components, options) {
|
||||
// Optional components argument
|
||||
if (!options && components && !Array.isArray(components)) {
|
||||
options = components
|
||||
components = undefined
|
||||
}
|
||||
|
||||
const finalOptions = Object.assign({}, {
|
||||
includeGlobal: true,
|
||||
}, options)
|
||||
|
||||
if (components) {
|
||||
this.willPrefetchComponents(components)
|
||||
}
|
||||
|
||||
if (finalOptions.includeGlobal) {
|
||||
this.willPrefetchComponents(globalPrefetchs)
|
||||
}
|
||||
|
||||
return Promise.all(this.prefetchQueries.map(
|
||||
o => this.prefetchQuery(o.queryOptions, context, o.client)
|
||||
))
|
||||
}
|
||||
|
||||
prefetchQuery (queryOptions, context, client) {
|
||||
let variables
|
||||
|
||||
// Client
|
||||
if (!client) {
|
||||
client = this.defaultClient
|
||||
} else if (typeof client === 'string') {
|
||||
client = this.clients[client]
|
||||
if (!client) {
|
||||
throw new Error(`[vue-apollo] Missing client '${client}' in 'apolloProvider'`)
|
||||
}
|
||||
}
|
||||
|
||||
// Simple query
|
||||
if (!queryOptions.query) {
|
||||
queryOptions = {
|
||||
query: queryOptions,
|
||||
}
|
||||
} else {
|
||||
const prefetch = queryOptions.prefetch
|
||||
const prefetchType = typeof prefetch
|
||||
|
||||
// Resolve variables
|
||||
if (prefetchType !== 'undefined') {
|
||||
let result
|
||||
if (prefetchType === 'function') {
|
||||
result = prefetch(context)
|
||||
} else {
|
||||
result = prefetch
|
||||
}
|
||||
|
||||
const optVariables = queryOptions.variables
|
||||
|
||||
if (!result) {
|
||||
return Promise.resolve()
|
||||
} else if (prefetchType === 'boolean' && typeof optVariables !== 'undefined') {
|
||||
// Reuse `variables` option with `prefetch: true`
|
||||
if (typeof optVariables === 'function') {
|
||||
variables = optVariables.call(context)
|
||||
} else {
|
||||
variables = optVariables
|
||||
}
|
||||
} else {
|
||||
variables = result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Query
|
||||
return new Promise((resolve, reject) => {
|
||||
const options = omit(queryOptions, VUE_APOLLO_QUERY_KEYWORDS)
|
||||
options.variables = variables
|
||||
client.query(options).then(resolve, reject)
|
||||
})
|
||||
}
|
||||
|
||||
exportStates (options) {
|
||||
const finalOptions = Object.assign({}, {
|
||||
exportNamespace: '',
|
||||
globalName: '__APOLLO_STATE__',
|
||||
attachTo: 'window',
|
||||
}, options)
|
||||
|
||||
let js = `${finalOptions.attachTo}.${finalOptions.globalName} = {`
|
||||
for (const key in this.clients) {
|
||||
const client = this.clients[key]
|
||||
const state = { [client.reduxRootKey || 'apollo']: client.getInitialState() }
|
||||
js += `['${finalOptions.exportNamespace}${key}']:${JSON.stringify(state)},`
|
||||
}
|
||||
js += `};`
|
||||
return js
|
||||
}
|
||||
}
|
||||
|
||||
const globalPrefetchs = []
|
||||
|
||||
export function willPrefetch (component) {
|
||||
globalPrefetchs.push(component)
|
||||
return component
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
export const VUE_APOLLO_QUERY_KEYWORDS = [
|
||||
'variables',
|
||||
'watch',
|
||||
'update',
|
||||
'result',
|
||||
'error',
|
||||
'loadingKey',
|
||||
'watchLoading',
|
||||
'skip',
|
||||
'throttle',
|
||||
'debounce',
|
||||
]
|
||||
+2
-27
@@ -15,33 +15,8 @@ export class DollarApollo {
|
||||
return this._apolloProvider || this.vm.$root._apolloProvider
|
||||
}
|
||||
|
||||
_autoCollect (query) {
|
||||
if (this.provider._isCollecting) {
|
||||
let sub
|
||||
this.provider._waitFor(new Promise((resolve, reject) => {
|
||||
sub = query.subscribe({
|
||||
next: result => {
|
||||
if (!this.provider._collectingOptions.waitForLoaded || !result.loading) {
|
||||
resolve(result)
|
||||
}
|
||||
},
|
||||
error: error => {
|
||||
reject(error)
|
||||
},
|
||||
})
|
||||
}).then(result => {
|
||||
sub.unsubscribe()
|
||||
return result
|
||||
}, error => {
|
||||
sub.unsubscribe()
|
||||
return error
|
||||
}))
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
||||
query (options) {
|
||||
return this._autoCollect(this.getClient(options).query(options))
|
||||
return this.getClient(options).query(options)
|
||||
}
|
||||
|
||||
getClient (options) {
|
||||
@@ -77,7 +52,7 @@ export class DollarApollo {
|
||||
this._apolloSubscriptions.push(sub)
|
||||
return sub
|
||||
}
|
||||
return this._autoCollect(observable)
|
||||
return observable
|
||||
}
|
||||
|
||||
mutate (options) {
|
||||
|
||||
+6
-5
@@ -1,8 +1,7 @@
|
||||
import omit from 'lodash.omit'
|
||||
import { DollarApollo } from './dollar-apollo'
|
||||
import { ApolloProvider } from './apollo-provider'
|
||||
|
||||
let Vue
|
||||
import { Globals } from './utils'
|
||||
|
||||
const keywords = [
|
||||
'$subscribe',
|
||||
@@ -46,7 +45,7 @@ const launch = function launch () {
|
||||
let apollo = this.$options.apollo
|
||||
if (apollo) {
|
||||
if (apollo.subscribe) {
|
||||
Vue.util.warn('vue-apollo -> `subscribe` option is deprecated. Use the `$subscribe` option instead.')
|
||||
Globals.Vue.util.warn('vue-apollo -> `subscribe` option is deprecated. Use the `$subscribe` option instead.')
|
||||
}
|
||||
|
||||
if (apollo.$subscribe) {
|
||||
@@ -72,11 +71,11 @@ function defineReactiveSetter ($apollo, key, value) {
|
||||
}
|
||||
}
|
||||
|
||||
function install (pVue, options) {
|
||||
function install (Vue, options) {
|
||||
if (install.installed) return
|
||||
install.installed = true
|
||||
|
||||
Vue = pVue
|
||||
Globals.Vue = Vue
|
||||
|
||||
// Options merging
|
||||
const merge = Vue.config.optionMergeStrategies.methods
|
||||
@@ -133,3 +132,5 @@ function install (pVue, options) {
|
||||
ApolloProvider.install = install
|
||||
|
||||
export default ApolloProvider
|
||||
|
||||
export { willPrefetch } from './apollo-provider'
|
||||
|
||||
+2
-12
@@ -1,5 +1,6 @@
|
||||
import omit from 'lodash.omit'
|
||||
import { throttle, debounce } from './utils'
|
||||
import { VUE_APOLLO_QUERY_KEYWORDS } from './consts'
|
||||
|
||||
class SmartApollo {
|
||||
type = null
|
||||
@@ -137,18 +138,7 @@ class SmartApollo {
|
||||
|
||||
export class SmartQuery extends SmartApollo {
|
||||
type = 'query'
|
||||
vueApolloSpecialKeys = [
|
||||
'variables',
|
||||
'watch',
|
||||
'update',
|
||||
'result',
|
||||
'error',
|
||||
'loadingKey',
|
||||
'watchLoading',
|
||||
'skip',
|
||||
'throttle',
|
||||
'debounce',
|
||||
]
|
||||
vueApolloSpecialKeys = VUE_APOLLO_QUERY_KEYWORDS
|
||||
loading = false
|
||||
|
||||
constructor (vm, key, options, autostart = true) {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import loThrottle from 'lodash.throttle'
|
||||
import loDebounce from 'lodash.debounce'
|
||||
|
||||
export const Globals = {}
|
||||
|
||||
function factory (action) {
|
||||
return (cb, options) => {
|
||||
if (typeof options === 'number') {
|
||||
@@ -14,3 +16,7 @@ function factory (action) {
|
||||
export const throttle = factory(loThrottle)
|
||||
|
||||
export const debounce = factory(loDebounce)
|
||||
|
||||
export function getMergedDefinition (def) {
|
||||
return Globals.Vue.util.mergeOptions({}, def)
|
||||
}
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
// app.js
|
||||
|
||||
import VueApollo from 'vue-apollo'
|
||||
|
||||
Vue.use(VueApollo)
|
||||
|
||||
export default function createApp ({ createApolloClients }) {
|
||||
const router = new VueRouter({})
|
||||
const store = new Vuex.Store({})
|
||||
const { apolloClientA, apolloClientB } = createApolloClients()
|
||||
const apollo = new VueApollo({
|
||||
clients: {
|
||||
a: apolloClientA,
|
||||
b: apolloClientB,
|
||||
},
|
||||
defaultClient: apolloClientA,
|
||||
})
|
||||
|
||||
const ensureReady = apollo.collect()
|
||||
|
||||
const app = new Vue({
|
||||
el: '#app',
|
||||
router,
|
||||
store,
|
||||
apollo,
|
||||
...App,
|
||||
})
|
||||
|
||||
return { app, router, store, apollo, ensureReady }
|
||||
}
|
||||
|
||||
// server.js
|
||||
|
||||
import { ApolloClient, createNetworkInterface } from 'apollo-client'
|
||||
import { createLocalInterface } from 'apollo-local-query'
|
||||
import graphql from 'graphql'
|
||||
import { schema } from './graphql/schema'
|
||||
|
||||
function createApolloClients() {
|
||||
const apolloClientA = new ApolloClient({
|
||||
ssrMode: true,
|
||||
networkInterface: createLocalInterface(graphql, schema)
|
||||
})
|
||||
|
||||
const apolloClientB = new ApolloClient({
|
||||
ssrMode: true,
|
||||
networkInterface: createNetworkInterface({
|
||||
uri: 'http://my-awesome-api/graphql',
|
||||
transportBatching: true,
|
||||
}),
|
||||
})
|
||||
|
||||
return {
|
||||
apolloClientA,
|
||||
apolloClientB,
|
||||
}
|
||||
}
|
||||
|
||||
export default function render (context) {
|
||||
const { app, router, store, apollo, ensureReady } = createApp({ createApolloClients })
|
||||
router.push(context.url)
|
||||
router.onReady(async () => {
|
||||
// Wait for apollo queries to be loaded
|
||||
await ensureReady()
|
||||
|
||||
// Inject initial apollo states
|
||||
let js = `window.__APOLLO_STATE__ = {`
|
||||
for (const key in apollo.clients) {
|
||||
const client = apollo.clients[key]
|
||||
const state = {[client.reduxRootKey]: client.getInitialState() }
|
||||
js += `['${key}']:${JSON.stringify(state)},`
|
||||
}
|
||||
js += `};`
|
||||
|
||||
// TODO Render here
|
||||
|
||||
// TODO Add the js to the response
|
||||
})
|
||||
}
|
||||
|
||||
// client.js
|
||||
|
||||
import { ApolloClient, createNetworkInterface } from 'apollo-client'
|
||||
|
||||
function createApolloClients() {
|
||||
const state = window.__APOLLO_STATE__
|
||||
|
||||
const apolloClientA = new ApolloClient({
|
||||
networkInterface: createNetworkInterface({
|
||||
uri: '/graphql',
|
||||
transportBatching: true,
|
||||
}),
|
||||
initialState: state.a,
|
||||
ssrForceFetchDelay: 100,
|
||||
})
|
||||
|
||||
const apolloClientB = new ApolloClient({
|
||||
networkInterface: createNetworkInterface({
|
||||
uri: 'http://my-awesome-api/graphql',
|
||||
transportBatching: true,
|
||||
}),
|
||||
initialState: state.b,
|
||||
ssrForceFetchDelay: 100,
|
||||
})
|
||||
|
||||
export {
|
||||
apolloClientA,
|
||||
apolloClientB,
|
||||
}
|
||||
}
|
||||
|
||||
createApp({ createApolloClients })
|
||||
Reference in New Issue
Block a user