# Apollo and GraphQL for Vue.js [![npm](https://img.shields.io/npm/v/vue-apollo.svg) ![npm](https://img.shields.io/npm/dm/vue-apollo.svg)](https://www.npmjs.com/package/vue-apollo) [![vue1](https://img.shields.io/badge/apollo-2.x-blue.svg)](https://www.apollographql.com/) [![vue1](https://img.shields.io/badge/vue-1.x-brightgreen.svg) ![vue2](https://img.shields.io/badge/vue-2.2+-brightgreen.svg)](https://vuejs.org/) ![schema](https://cdn-images-1.medium.com/max/800/1*H9AANoofLqjS10Xd5TwRYw.png) **Warning! This README is related to Apollo 2.x support. For the old release (supporting only Apollo 1.x), see [here](https://github.com/Akryum/vue-apollo/tree/apollo-1).** Integrates [apollo](https://www.apollographql.com/) in your [Vue](http://vuejs.org) components with declarative queries. Compatible with Vue 1.0+ and 2.0+. [Live demo](https://jsfiddle.net/Akryum/oyejk2qL/) [icon Vue-cli plugin](https://github.com/Akryum/vue-cli-plugin-apollo) [icon More vue-apollo examples](https://github.com/Akryum/vue-apollo-example) [icon Apollo graphql server example](https://github.com/Akryum/apollo-server-example) [icon How to GraphQL](https://www.howtographql.com/vue-apollo/0-introduction/) [icon VueConf 2017 demo](https://github.com/Akryum/vueconf-2017-demo) & [slides](http://slides.com/akryum/graphql#/) [icon Devfest Summit Example](https://github.com/Akryum/devfest-nantes-2017) (with lots of features like SSR, OAuth, Realtime updates, Apollo Optics...) ## Table of contents - [Installation](#installation) - [Create a provider](#create-a-provider) - [Usage in components](#usage-in-components) - [Queries](#queries) - [Simple query](#simple-query) - [Query with parameters](#query-with-parameters) - [Loading state](#loading-state) - [Option function](#option-function) - [Reactive query definition](#reactive-query-definition) - [Reactive parameters](#reactive-parameters) - [Skipping the query](#skipping-the-query) - [Advanced options](#advanced-options) - [Reactive Query Example](#reactive-query-example) - [Manually adding a smart Query](#manually-adding-a-smart-query) - [Mutations](#mutations) - [Subscriptions](#subscriptions) - [subscribeToMore](#subscribetomore) - [subscribe](#subscribe) - [Skipping the subscription](#skipping-the-subscription) - [Manually adding a smart Subscription](#manually-adding-a-smart-subscription) - [Pagination with `fetchMore`](#pagination-with-fetchmore) - [Special options](#special-options) - [Skip all](#skip-all) - [Multiple clients](#multiple-clients) - [Components](#components) - [Query Components](#query-components) - [Mutation Components](#mutation-components) - [Server-Side Rendering](#server-side-rendering) - [Local state](#local-state) - [Migration](#migration) - [API Reference](#api-reference) ## Installation **If you are using vue-cli 3.x, you can [use this vue-cli plugin](https://github.com/Akryum/vue-cli-plugin-apollo) to get started in a few minutes!** Try and install these packages before server side set (of packages), add apollo to meteor.js before then, too. npm install --save vue-apollo graphql apollo-client apollo-link apollo-link-http apollo-cache-inmemory graphql-tag In your app, create an `ApolloClient` instance and install the `VueApollo` plugin: ```javascript import Vue from 'vue' import { ApolloClient } from 'apollo-client' import { HttpLink } from 'apollo-link-http' import { InMemoryCache } from 'apollo-cache-inmemory' import VueApollo from 'vue-apollo' const httpLink = new HttpLink({ // You should use an absolute URL here uri: 'http://localhost:3020/graphql', }) // Create the apollo client const apolloClient = new ApolloClient({ link: httpLink, cache: new InMemoryCache(), connectToDevTools: true, }) // Install the vue plugin Vue.use(VueApollo) ``` ## Create a provider Like `vue-router` or `vuex`, you need to specify the `apolloProvider` object on your root components. A provider holds the apollo client instances that can then be used by all the child components. ```javascript const apolloProvider = new VueApollo({ defaultClient: apolloClient, }) new Vue({ el: '#app', provide: apolloProvider.provide(), render: h => h(App), }) ``` ## Usage in components To declare apollo queries in your Vue component, add an `apollo` object : ```javascript new Vue({ apollo: { // Apollo specific options }, }) ``` You can access the [apollo-client](https://www.apollographql.com/docs/react/) instances with `this.$apollo.provider.defaultClient` or `this.$apollo.provider.clients.` (for [Multiple clients](#multiple-clients)) in all your vue components. ## Queries In the `apollo` object, add an attribute for each property you want to feed with the result of an Apollo query. ### Simple query Use `gql` to write your GraphQL queries: ```javascript import gql from 'graphql-tag' ``` Put the [gql](https://github.com/apollographql/graphql-tag) query directly as the value: ```javascript apollo: { // Simple query that will update the 'hello' vue property hello: gql`{hello}`, }, ``` You can then access the query with `this.$apollo.queries.`. You can initialize the property in your vue component's `data` hook: ```javascript data () { return { // Initialize your apollo data hello: '', }, }, ``` Server-side, add the corresponding schema and resolver: ```javascript export const schema = ` type Query { hello: String } schema { query: Query } ` export const resolvers = { Query: { hello(root, args, context) { return "Hello world!" }, }, } ``` For more info, visit the [apollo doc](https://www.apollographql.com/docs/apollo-server/). You can then use your property as usual in your vue component: ```html ``` ### Query with parameters You can add variables (read parameters) to your `gql` query by declaring `query` and `variables` in an object: ```javascript // Apollo-specific options apollo: { // Query with parameters ping: { // gql query query: gql`query PingMessage($message: String!) { ping(message: $message) }`, // Static parameters variables: { message: 'Meow', }, }, }, ``` You can use the apollo `watchQuery` options in the object, like: - `fetchPolicy` - `pollInterval` - ... See the [apollo doc](https://www.apollographql.com/docs/react/api/apollo-client.html#ApolloClient.watchQuery) for more details. For example, you could add the `fetchPolicy` apollo option like this: ```javascript apollo: { // Query with parameters ping: { query: gql`query PingMessage($message: String!) { ping(message: $message) }`, variables: { message: 'Meow' }, // Additional options here fetchPolicy: 'cache-and-network', }, }, ``` Again, you can initialize your property in your vue component: ```javascript data () { return { // Initialize your apollo data ping: '', } }, ``` Server-side, add the corresponding schema and resolver: ```javascript export const schema = ` type Query { ping(message: String!): String } schema { query: Query } ` export const resolvers = { Query: { ping(root, { message }, context) { return `Answering ${message}` }, }, } ``` And then use it in your vue component: ```html ``` ### Loading state You can display a loading state thanks to the `$apollo.loading` prop: ```html
Loading...
``` Or for this specific `ping` query: ```html
Loading...
``` ### Option function You can use a function to initialize the key: ```javascript // Apollo-specific options apollo: { // Query with parameters ping () { // This will called one when the component is created // It must return the option object return { // gql query query: gql`query PingMessage($message: String!) { ping(message: $message) }`, // Static parameters variables: { message: 'Meow', }, } }, }, ``` **This will be called once when the component is created and it must return the option object.** *This also works for [subscriptions](#subscriptions).* ### Reactive query definition You can use a function for the `query` option. This will update the graphql query definition automatically: ```javascript // The featured tag can be either a random tag or the last added tag featuredTag: { query () { // Here you can access the component instance with 'this' if (this.showTag === 'random') { return gql`{ randomTag { id label type } }` } else if (this.showTag === 'last') { return gql`{ lastTag { id label type } }` } }, // We need this to assign the value of the 'featuredTag' component property update: data => data.randomTag || data.lastTag, }, ``` *This also works for [subscriptions](#subscriptions).* ### Reactive parameters Use a function instead to make the parameters reactive with vue properties: ```javascript // Apollo-specific options apollo: { // Query with parameters ping: { query: gql`query PingMessage($message: String!) { ping(message: $message) }`, // Reactive parameters variables() { // Use vue reactive properties here return { message: this.pingInput, } }, }, }, ``` This will re-fetch the query each time a parameter changes, for example: ```html ``` ### Skipping the query If the query is skipped, it will disable it and the result will not be updated anymore. You can use the `skip` option: ```javascript // Apollo-specific options apollo: { tags: { // GraphQL Query query: gql`query tagList ($type: String!) { tags(type: $type) { id label } }`, // Reactive variables variables() { return { type: this.type, } }, // Disable the query skip() { return this.skipQuery }, }, }, ``` Here, `skip` will be called automatically when the `skipQuery` component property changes. You can also access the query directly and set the `skip` property: ```javascript this.$apollo.queries.tags.skip = true ``` ### Advanced options These are the available advanced options you can use: - `update(data) {return ...}` to customize the value that is set in the vue property, for example if the field names don't match. - `result(ApolloQueryResult)` is a hook called when a result is received (see documentation for [ApolloQueryResult](https://github.com/apollographql/apollo-client/blob/master/packages/apollo-client/src/core/types.ts)). - `error(error)` is a hook called when there are errors. `error` is an Apollo error object with either a `graphQLErrors` property or a `networkError` property. - `loadingKey` will update the component data property you pass as the value. You should initialize this property to `0` in the component `data()` hook. When the query is loading, this property will be incremented by 1; when it is no longer loading, it will be decremented by 1. That way, the property can represent a counter of currently loading queries. - `watchLoading(isLoading, countModifier)` is a hook called when the loading state of the query changes. The `countModifier` parameter is either equal to `1` when the query is loading, or `-1` when the query is no longer loading. - `manual` is a boolean to disable the automatic property update. If you use it, you then need to specify a `result` callback (see example below). - `deep` is a boolean to use `deep: true` on Vue watchers. ```javascript // Apollo-specific options apollo: { // Advanced query with parameters // The 'variables' method is watched by vue pingMessage: { query: gql`query PingMessage($message: String!) { ping(message: $message) }`, // Reactive parameters variables() { // Use vue reactive properties here return { message: this.pingInput, } }, // Variables: deep object watch deep: false, // We use a custom update callback because // the field names don't match // By default, the 'pingMessage' attribute // would be used on the 'data' result object // Here we know the result is in the 'ping' attribute // considering the way the apollo server works update(data) { console.log(data) // The returned value will update // the vue property 'pingMessage' return data.ping }, // Optional result hook result({ data, loading, networkStatus }) { console.log("We got some result!") }, // Error handling error(error) { console.error('We\'ve got an error!', error) }, // Loading state // loadingKey is the name of the data property // that will be incremented when the query is loading // and decremented when it no longer is. loadingKey: 'loadingQueriesCount', // watchLoading will be called whenever the loading state changes watchLoading(isLoading, countModifier) { // isLoading is a boolean // countModifier is either 1 or -1 }, }, }, ``` If you use `ES2015`, you can also write the `update` like this: ```javascript update: data => data.ping ``` Manual mode example: ```javascript { query: gql`...`, manual: true, result ({ data, loading }) { if (!loading) { this.items = data.items } }, } ``` ### Reactive Query Example Here is a reactive query example using polling: ```javascript // Apollo-specific options apollo: { // 'tags' data property on vue instance tags: { query: gql`query tagList { tags { id, label } }`, pollInterval: 300, // ms }, }, ``` Here is how the server-side looks like: ```javascript export const schema = ` type Tag { id: Int label: String } type Query { tags: [Tag] } schema { query: Query } ` // Fake word generator import casual from 'casual' // Let's generate some tags var id = 0 var tags = [] for (let i = 0; i < 42; i++) { addTag(casual.word) } function addTag(label) { let t = { id: id++, label, } tags.push(t) return t } export const resolvers = { Query: { tags(root, args, context) { return tags }, }, } ``` ### Manually adding a smart Query You can manually add a smart query with the `$apollo.addSmartQuery(key, options)` method: ```javascript created () { this.$apollo.addSmartQuery('comments', { // Same options like above }) } ``` *Internally, this method is called for each query entry in the component `apollo` option.* ## Mutations Mutations are queries that change your data state on your apollo server. For more info, visit the [apollo doc](https://www.apollographql.com/docs/react/reference/index.html#ApolloClient\.mutate). There is a mutation-focused [example app](https://github.com/Akryum/vue-apollo-todos) you can look at. **You shouldn't send the `__typename` fields in the variables, so it is not recommended to send an Apollo result object directly.** ```javascript methods: { addTag() { // We save the user input in case of an error const newTag = this.newTag // We clear it early to give the UI a snappy feel this.newTag = '' // Call to the graphql mutation this.$apollo.mutate({ // Query mutation: gql`mutation ($label: String!) { addTag(label: $label) { id label } }`, // Parameters variables: { label: newTag, }, // Update the cache with the result // The query will be updated with the optimistic response // and then with the real result of the mutation update: (store, { data: { newTag } }) => { // Read the data from our cache for this query. const data = store.readQuery({ query: TAGS_QUERY }) // Add our tag from the mutation to the end data.tags.push(newTag) // Write our data back to the cache. store.writeQuery({ query: TAGS_QUERY, data }) }, // Optimistic UI // Will be treated as a 'fake' result as soon as the request is made // so that the UI can react quickly and the user be happy optimisticResponse: { __typename: 'Mutation', addTag: { __typename: 'Tag', id: -1, label: newTag, }, }, }).then((data) => { // Result console.log(data) }).catch((error) => { // Error console.error(error) // We restore the initial user input this.newTag = newTag }) }, }, ``` Server-side: ```javascript export const schema = ` type Tag { id: Int label: String } type Query { tags: [Tag] } type Mutation { addTag(label: String!): Tag } schema { query: Query mutation: Mutation } ` // Fake word generator import faker from 'faker' // Let's generate some tags var id = 0 var tags = [] for (let i = 0; i < 42; i++) { addTag(faker.random.word()) } function addTag(label) { let t = { id: id++, label, } tags.push(t) return t } export const resolvers = { Query: { tags(root, args, context) { return tags }, }, Mutation: { addTag(root, { label }, context) { console.log(`adding tag '${label}'`) return addTag(label) }, }, } ``` ## Subscriptions *For the server implementation, you can take a look at [this simple example](https://github.com/Akryum/apollo-server-example).* To make enable the websocket-based subscription, a bit of additional setup is required: ``` npm install --save apollo-link-ws apollo-utilities ``` ```javascript import Vue from 'vue' import { ApolloClient } from 'apollo-client' import { HttpLink } from 'apollo-link-http' import { InMemoryCache } from 'apollo-cache-inmemory' // New Imports import { split } from 'apollo-link' import { WebSocketLink } from 'apollo-link-ws' import { getMainDefinition } from 'apollo-utilities' import VueApollo from 'vue-apollo' const httpLink = new HttpLink({ // You should use an absolute URL here uri: 'http://localhost:3020/graphql', }) // Create the subscription websocket link const wsLink = new WebSocketLink({ uri: 'ws://localhost:3000/subscriptions', options: { reconnect: true, }, }) // using the ability to split links, you can send data to each link // depending on what kind of operation is being sent const link = split( // split based on operation type ({ query }) => { const { kind, operation } = getMainDefinition(query) return kind === 'OperationDefinition' && operation === 'subscription' }, wsLink, httpLink ) // Create the apollo client const apolloClient = new ApolloClient({ link, cache: new InMemoryCache(), connectToDevTools: true, }) // Install the vue plugin like before Vue.use(VueApollo) ``` ### subscribeToMore If you need to update a query result from a subscription, the best way is using the `subscribeToMore` query method. Just add a `subscribeToMore` to your query: ```javascript apollo: { tags: { query: TAGS_QUERY, subscribeToMore: { document: gql`subscription name($param: String!) { itemAdded(param: $param) { id label } }`, // Variables passed to the subscription. Since we're using a function, // they are reactive variables () { return { param: this.param, } }, // Mutate the previous result updateQuery: (previousResult, { subscriptionData }) => { // Here, return the new result from the previous with the new data }, } } } ``` *Note that you can pass an array of subscriptions to `subscribeToMore` to subscribe to multiple subscriptions on this query.* #### Alternate usage You can access the queries you defined in the `apollo` option with `this.$apollo.queries.`, so it would look like this: ```javascript this.$apollo.queries.tags.subscribeToMore({ // GraphQL document document: gql`subscription name($param: String!) { itemAdded(param: $param) { id label } }`, // Variables passed to the subscription variables: { param: '42', }, // Mutate the previous result updateQuery: (previousResult, { subscriptionData }) => { // Here, return the new result from the previous with the new data }, }) ``` If the related query is stopped, the subscription will be automatically destroyed. Here is an example: ```javascript // Subscription GraphQL document const TAG_ADDED = gql`subscription tags($type: String!) { tagAdded(type: $type) { id label type } }` // SubscribeToMore tags // We have different types of tags // with one subscription 'channel' for each type this.$watch(() => this.type, (type, oldType) => { if (type !== oldType || !this.tagsSub) { // We need to unsubscribe before re-subscribing if (this.tagsSub) { this.tagsSub.unsubscribe() } // Subscribe on the query this.tagsSub = this.$apollo.queries.tags.subscribeToMore({ document: TAG_ADDED, variables: { type, }, // Mutate the previous result updateQuery: (previousResult, { subscriptionData }) => { // If we added the tag already don't do anything // This can be caused by the `updateQuery` of our addTag mutation if (previousResult.tags.find(tag => tag.id === subscriptionData.data.tagAdded.id)) { return previousResult } return { tags: [ ...previousResult.tags, // Add the new tag subscriptionData.data.tagAdded, ], } }, }) } }, { immediate: true, }) ``` ### subscribe **:warning: If you want to update a query with the result of the subscription, use `subscribeToMore`. The methods below are suitable for a 'notify' use case.** Use the `$apollo.subscribe()` method to subscribe to a GraphQL subscription that will get killed automatically when the component is destroyed: ```javascript mounted() { const subQuery = gql`subscription tags($type: String!) { tagAdded(type: $type) { id label type } }` const observer = this.$apollo.subscribe({ query: subQuery, variables: { type: 'City', }, }) observer.subscribe({ next(data) { console.log(data) }, error(error) { console.error(error) }, }) }, ``` You can declare subscriptions in the `apollo` option with the `$subscribe` keyword: ```javascript apollo: { // Subscriptions $subscribe: { // When a tag is added tagAdded: { query: gql`subscription tags($type: String!) { tagAdded(type: $type) { id label type } }`, // Reactive variables variables() { // This works just like regular queries // and will re-subscribe with the right variables // each time the values change return { type: this.type, } }, // Result hook result(data) { console.log(data) }, }, }, }, ``` You can then access the subscription with `this.$apollo.subscriptions.`. *Just like for queries, you can declare the subscription [with a function](#option-function), and you can declare the `query` option [with a reactive function](#reactive-query-definition).* ### Skipping the subscription If the subscription is skipped, it will disable it and it will not be updated anymore. You can use the `skip` option: ```javascript // Apollo-specific options apollo: { // Subscriptions $subscribe: { // When a tag is added tags: { query: gql`subscription tags($type: String!) { tagAdded(type: $type) { id label type } }`, // Reactive variables variables() { return { type: this.type, } }, // Result hook result(data) { // Let's update the local data this.tags.push(data.tagAdded) }, // Skip the subscription skip() { return this.skipSubscription } }, }, }, ``` Here, `skip` will be called automatically when the `skipSubscription` component property changes. You can also access the subscription directly and set the `skip` property: ```javascript this.$apollo.subscriptions.tags.skip = true ``` ### Manually adding a smart Subscription You can manually add a smart subscription with the `$apollo.addSmartSubscription(key, options)` method: ```javascript created () { this.$apollo.addSmartSubscription('tagAdded', { // Same options like '$subscribe' above }) } ``` *Internally, this method is called for each entry of the `$subscribe` object in the component `apollo` option.* ## Pagination with `fetchMore` *[Here](https://github.com/Akryum/apollo-server-example/blob/master/schema.js#L21) is a simple example for the server.* Use the `fetchMore()` method on the query: ```javascript ``` **Don't forget to include the `__typename` to the new result.** ## Special options The special options begin with `$` in the `apollo` object. - `$skip` to disable all queries and subscriptions (see below) - `$skipAllQueries` to disable all queries (see below) - `$skipAllSubscriptions` to disable all subscriptions (see below) - `$deep` to watch with `deep: true` on the properties above when a function is provided - `$error` to catch errors in a default handler (see `error` advanced options for smart queries) - `$query` to apply default options to all the queries in the component Example: ```html ``` You can define in the apollo provider a default set of options to apply to the `apollo` definitions. For example: ```javascript const apolloProvider = new VueApollo({ defaultClient: apolloClient, defaultOptions: { // apollo options applied to all queries in components $query: { loadingKey: 'loading', fetchPolicy: 'cache-and-network', }, }, }) ``` ## Skip all You can disable all the queries for the component with `skipAllQueries`, all the subscriptions with `skipAllSubscriptions` and both with `skipAll`: ```javascript this.$apollo.skipAllQueries = true this.$apollo.skipAllSubscriptions = true this.$apollo.skipAll = true ``` You can also declare these properties in the `apollo` option of the component. They can be booleans: ```javascript apollo: { $skipAll: true } ``` Or reactive functions: ```javascript apollo: { $skipAll () { return this.foo === 42 } } ``` ## Multiple clients You can specify multiple apollo clients if your app needs to connect to different GraphQL endpoints: ```javascript const apolloProvider = new VueApollo({ clients: { a: apolloClient, b: otherApolloClient, }, defaultClient: apolloClient, }) ``` In the component `apollo` option, you can define the client for all the queries, subscriptions and mutations with `$client` (only for this component): ```javascript export default { apollo: { $client: 'b', }, } ``` You can also specify the client in individual queries, subscriptions and mutations with the `client` property in the options: ```javascript tags: { query: gql`...`, client: 'b', } ``` ## Components ### Query components (WIP) You can use the `ApolloQuery` (or `apollo-query`) component to make watched Apollo queries directly in your template: ```html ``` Props: - `query`: GraphQL query (transformed by `graphql-tag`) - `variables`: Object of GraphQL variables - `fetchPolicy`: See [apollo fetchPolicy](https://www.apollographql.com/docs/react/basics/queries.html#graphql-config-options-fetchPolicy) - `pollInterval`: See [apollo pollInterval](https://www.apollographql.com/docs/react/basics/queries.html#graphql-config-options-pollInterval) - `notifyOnNetworkStatusChange`: See [apollo notifyOnNetworkStatusChange](https://www.apollographql.com/docs/react/basics/queries.html#graphql-config-options-notifyOnNetworkStatusChange) - `context`: See [apollo context](https://www.apollographql.com/docs/react/basics/queries.html#graphql-config-options-context) - `skip`: Boolean disabling query fetching - `clientId`: Used to resolve the Apollo Client used (defined in ApolloProvider) - `deep`: Boolean to use deep Vue watchers - `tag`: String HTML tag name (default: `div`) Scoped slot props: - `result`: Apollo Query result - `result.data`: Data returned by the query - `result.loading`: Boolean indicating that a request is in flight - `result.error`: Eventual error for the current result - `result.networkStatus`: See [apollo networkStatus](https://www.apollographql.com/docs/react/basics/queries.html#graphql-query-data-networkStatus) - `result.times`: number of times the result was updated - `query`: Smart Query associated with the component Events: - `result(resultObject)` - `error(errorObject)` (WIP) You can subscribe to more data with the `ApolloSubscribeToMore` (or `apollo-subscribe-to-more`) component: ```html ``` *You can put as many of those as you want inside a `` component.* ### Mutation component (WIP) You can use the `ApolloMutation` (or `apollo-mutation`) component to call Apollo mutations directly in your template: ```html ``` Props: - `mutation`: GraphQL query (transformed by `graphql-tag`) - `variables`: Object of GraphQL variables - `optimisticResponse`: See [optimistic UI](https://www.apollographql.com/docs/react/features/optimistic-ui.html) - `update`: See [updating cache after mutation](https://www.apollographql.com/docs/react/api/react-apollo.html#graphql-mutation-options-update) - `refetchQueries`: See [refetching queries after mutation](https://www.apollographql.com/docs/react/api/react-apollo.html#graphql-mutation-options-refetchQueries) - `tag`: String HTML tag name (default: `div`) Scoped slot props: - `mutate(options = undefined)`: Function to call the mutation. You can override the mutation options (for example: `mutate({ variables: { foo: 'bar } })`) - `loading`: Boolean indicating that the request is in flight - `error`: Eventual error for the last mutation call Events: - `done(resultObject)` - `error(errorObject)` ## Server-Side Rendering ### Prefetch components 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). If you are returning a variables object in the `prefetch` option, make sure it matches the result of the `variables` option. If they do not match, the query's data property will not be populated while rendering the template server-side. **Warning! You don't have access to the component instance when doing prefetching on the server. Don't use `this` in `prefetch`!** Example: ```javascript export default { apollo: { allPosts: { query: gql`query AllPosts { allPosts { id imageUrl description } }`, prefetch: true, } } } ``` Example 2: ```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 } } }) ``` The second parameter is optional: it's a callback that gets the context and should return a boolean indicating if the component should be prefetched: ```js willPrefetch({ // Component definition... }, context => context.url === '/foo') ``` ### 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 unnecessary 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: '', } ``` You can also use the `apolloProvider.getStates` method to get the JS object instead of the script string. It takes an `options` argument which defaults to: ```javascript { // 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 } from 'apollo-client' import { HttpLink } from 'apollo-link-http' import { InMemoryCache } from 'apollo-cache-inmemory' import VueApollo from 'vue-apollo' // Install the vue plugin Vue.use(VueApollo) // Create the apollo client export function createApolloClient (ssr = false) { const httpLink = new HttpLink({ // You should use an absolute URL here uri: ENDPOINT + '/graphql', }) const cache = new InMemoryCache() // If on the client, recover the injected state if (!ssr) { // If on the client, recover the injected state if (typeof window !== 'undefined') { const state = window.__APOLLO_STATE__ if (state) { // If you have multiple clients, use `state.` cache.restore(state.defaultClient) } } } const apolloClient = new ApolloClient({ link: httpLink, cache, ...(ssr ? { // Set this on the server to optimize queries when SSR ssrMode: true, } : { // 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, provide: apolloProvider.provide(), ...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) }) ``` ## Local state If you need to manage local data, you can do so with [apollo-link-state](https://github.com/apollographql/apollo-link-state) and the `@client` directive: ```js export default { apollo: { hello: gql` query { hello @client { msg } } ` }, mounted() { // mutate the hello message this.$apollo .mutate({ mutation: gql` mutation($msg: String!) { updateHello(message: $msg) @client } `, variables: { msg: 'hello from link-state!' } }) } } ``` [Example project](https://codesandbox.io/s/zqqj82396p) (thx @chriswingler) --- # Migration ## Migrating from vue-apollo 2.x and apollo 1.x The main changes are related to the apollo client setup. Your components code shouldn't be affected. Apollo now uses a more flexible [apollo-link](https://github.com/apollographql/apollo-link) system that allows compositing multiple links together to add more features (like batching, offline support and more). ### Installation #### Packages Before: ``` npm install --save vue-apollo apollo-client ``` After: ``` npm install --save vue-apollo@next graphql apollo-client apollo-link apollo-link-http apollo-cache-inmemory graphql-tag ``` #### Imports Before: ```js import Vue from 'vue' import { ApolloClient, createBatchingNetworkInterface } from 'apollo-client' import VueApollo from 'vue-apollo' ``` After: ```js import Vue from 'vue' import { ApolloClient } from 'apollo-client' import { HttpLink } from 'apollo-link-http' import { InMemoryCache } from 'apollo-cache-inmemory' import VueApollo from 'vue-apollo' ``` #### Plugin Setup Before: ```js // Create the apollo client const apolloClient = new ApolloClient({ networkInterface: createBatchingNetworkInterface({ uri: 'http://localhost:3020/graphql', }), connectToDevTools: true, }) // Install the vue plugin Vue.use(VueApollo) ``` After: ```js const httpLink = new HttpLink({ // You should use an absolute URL here uri: 'http://localhost:3020/graphql', }) // Create the apollo client const apolloClient = new ApolloClient({ link: httpLink, cache: new InMemoryCache(), connectToDevTools: true, }) // Install the vue plugin Vue.use(VueApollo) ``` ### Mutations Query reducers have been removed. Use the `update` API to update the cache now. ### Subscriptions #### Packages Before: ``` npm install --save subscriptions-transport-ws ``` After: ``` npm install --save apollo-link-ws apollo-utilities ``` #### Imports Before: ```js import { SubscriptionClient, addGraphQLSubscriptions } from 'subscriptions-transport-ws' ``` After: ```js import { split } from 'apollo-link' import { WebSocketLink } from 'apollo-link-ws' import { getMainDefinition } from 'apollo-utilities' ``` #### Apollo Setup Before: ```js // Create the network interface const networkInterface = createNetworkInterface({ uri: 'http://localhost:3000/graphql', transportBatching: true, }) // Create the subscription websocket client const wsClient = new SubscriptionClient('ws://localhost:3000/subscriptions', { reconnect: true, }) // Extend the network interface with the subscription client const networkInterfaceWithSubscriptions = addGraphQLSubscriptions( networkInterface, wsClient, ) // Create the apollo client with the new network interface const apolloClient = new ApolloClient({ networkInterface: networkInterfaceWithSubscriptions, connectToDevTools: true, }) // Install the plugin like before Vue.use(VueApollo) ``` After: ```js const httpLink = new HttpLink({ // You should use an absolute URL here uri: 'http://localhost:3020/graphql', }) // Create the subscription websocket link const wsLink = new WebSocketLink({ uri: 'ws://localhost:3000/subscriptions', options: { reconnect: true, }, }) // using the ability to split links, you can send data to each link // depending on what kind of operation is being sent const link = split( // split based on operation type ({ query }) => { const { kind, operation } = getMainDefinition(query) return kind === 'OperationDefinition' && operation === 'subscription' }, wsLink, httpLink ) // Create the apollo client const apolloClient = new ApolloClient({ link, cache: new InMemoryCache(), connectToDevTools: true, }) // Install the vue plugin like before Vue.use(VueApollo) ```
Learn more at the [official apollo documentation](https://www.apollographql.com/docs/react/2.0-migration.html). --- # API Reference WIP (PR welcome!) ## ApolloProvider ### Constructor ```javascript const apolloProvider = new VueApollo({ // Multiple clients support // Use the 'client' option inside queries // or '$client' on the apollo definition clients: { a: apolloClientA, b: apolloClientB, }, // Default client defaultClient: apolloClient, // Default 'apollo' definition defaultOptions: { // See 'apollo' definition // For example: default loading key $loadingKey: 'loading', }, // Watch loading state for all queries // See the 'watchLoading' advanced option watchLoading (state, mod) { loading += mod console.log('Global loading', loading, mod) }, // Global error handler for all smart queries and subscriptions errorHandler (error) { console.log('Global error handler') console.error(error) }, }) ``` Use the apollo provider into your Vue app: ```javascript new Vue({ el: '#app', apolloProvider, render: h => h(App), }) ``` ### Methods #### prefetchAll (SSR) Prefetch all queued component definitions and returns a promise resolved when all corresponding apollo data is ready. ```javascript await apolloProvider.prefetchAll (context, componentDefs, options) ``` `context` is passed as the argument to the `prefetch` options inside the smart queries. It may contain the route and the store. `options` defaults to: ```javascript { // Include components outside of the routes // that are registered with `willPrefetch` includeGlobal: true, } ``` #### getStates (SSR) Returns the apollo stores states as JavaScript objects. ```JavaScript const states = apolloProvider.getStates(options) ``` `options` defaults to: ```javascript { // Prefix for the keys of each apollo client state exportNamespace: '', } ``` #### exportStates (SSR) Returns the apollo stores states as JavaScript code inside a String. This code can be directly injected to the page HTML inside a `