docs: Vuepress-powered docs (#296)

* * created vuepress docs

* chore: upgrade deps

* docs: patreon link

* Docs changes
This commit is contained in:
Natalia Tepluhina
2018-06-18 16:38:46 +03:00
committed by Guillaume Chau
parent 76671c37b6
commit 2759825251
31 changed files with 6708 additions and 2245 deletions
+1 -2027
View File
File diff suppressed because it is too large Load Diff
+13
View File
@@ -0,0 +1,13 @@
set -e
npm run docs:build
cd docs/.vuepress/dist
git init
git add -A
git commit -m 'deploy'
git push -f git@github.com:Akryum/vue-apollo.git master:gh-pages
cd -
+63
View File
@@ -0,0 +1,63 @@
module.exports = {
base: '/vue-apollo/',
themeConfig: {
repo: 'Akryum/vue-apollo',
docsDir: 'docs',
editLinks: true,
nav: [
{
text: 'Guide',
link: '/guide/'
},
{
text: 'API Reference',
link: '/api/'
},
{
text: 'Migration',
link: '/migration/'
},
{
text: 'CLI plugin',
link: 'https://github.com/Akryum/vue-cli-plugin-apollo'
},
{
text: 'Patreon',
link: 'https://www.patreon.com/akryum'
}
],
sidebarDepth: 3,
sidebar: {
'/guide/': [
'',
'installation',
'create-provider',
'usage-in-components',
'queries',
'mutations',
'subscriptions',
'pagination',
'special-options',
'skip-all',
'multiple-clients',
'components',
'ssr',
'local-state'
],
'/api/': [
'apollo-provider',
'apollo-query',
'apollo-subscribe-to-more',
'apollo-mutation',
'dollar-apollo',
'smart-query',
'smart-subscription',
],
'/migration/': [
''
]
}
},
title: 'Apollo and GraphQL for Vue.js',
description: '🚀 Apollo and GraphQL plugin for Vue.js framework'
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

+13
View File
@@ -0,0 +1,13 @@
---
home: true
heroImage: /logo.png
actionText: Get Started →
actionLink: /guide/
footer: LICENCE ISC - Created by Guillaume CHAU (@Akryum)
---
<p style="text-align: center;">
<a href="https://www.patreon.com/akryum" target="_blank">
<img src="https://c5.patreon.com/external/logo/become_a_patron_button.png" alt="Become a Patreon">
</a>
</p>
+5
View File
@@ -0,0 +1,5 @@
# API Reference
::: warning WIP
PR welcome!
:::
+1
View File
@@ -0,0 +1 @@
# ApolloMutation component
+104
View File
@@ -0,0 +1,104 @@
# 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 `<script>` tag.
```javascript
const js = apolloProvider.exportStates(options)
```
`options` 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: '',
}
```
+1
View File
@@ -0,0 +1 @@
# ApolloQuery component
+1
View File
@@ -0,0 +1 @@
# ApolloSubscribeToMore component
+3
View File
@@ -0,0 +1,3 @@
# Dollar Apollo
This is the apollo manager added to any component that uses apollo. It can be accessed inside a component with `this.$apollo`.
+3
View File
@@ -0,0 +1,3 @@
# Smart Query
Each query declared in the `apollo` definition (that is, which doesn't start with a `$` char) in a component results in the creation of a smart query object.
+3
View File
@@ -0,0 +1,3 @@
# Smart Subscription
Each subscription declared in the `apollo.$subscribe` option in a component results in the creation of a smart subscription object.
+29
View File
@@ -0,0 +1,29 @@
# Introduction
::: danger
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).
:::
This library 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/)
## What is GraphQL?
[GraphQL](https://graphql.org/) is a specification that aims at easing the communication between frontends and backends. It mainly consists of a Schema Language for the server and a Query Language for the client.
## What is Apollo?
[Apollo](https://www.apollographql.com/) is a set of tools and community effort to help you use GraphQL in your apps. It's well known for its [client](https://www.apollographql.com/client) and its [server](https://www.apollographql.com/server). Apollo is developped and supported by the [Meteor Development Group](https://www.meteor.io/).
## Links
[<img src="https://assets-cdn.github.com/favicon.ico" alt="icon" width="16" height="16"/> Vue-cli plugin](https://github.com/Akryum/vue-cli-plugin-apollo)
[<img src="https://assets-cdn.github.com/favicon.ico" alt="icon" width="16" height="16"/> More vue-apollo examples](https://github.com/Akryum/vue-apollo-example)
[<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://www.howtographql.com/static/howtographql.d1a2e5b4.svg" alt="icon" width="16" height="16"/> How to GraphQL](https://www.howtographql.com/vue-apollo/0-introduction/)
[<img src="https://conf.vuejs.org/img/logo-48.png" alt="icon" width="16" height="16"/> VueConf 2017 demo](https://github.com/Akryum/vueconf-2017-demo) &amp; [slides](http://slides.com/akryum/graphql#/)
[<img src="https://assets-cdn.github.com/favicon.ico" alt="icon" width="16" height="16"/> Devfest Summit Example](https://github.com/Akryum/devfest-nantes-2017) (with lots of features like SSR, OAuth, Realtime updates, Apollo Optics...)
+143
View File
@@ -0,0 +1,143 @@
# Components
## Query components
::: warning WIP
You can use the `ApolloQuery` (or `apollo-query`) component to make watched Apollo queries directly in your template:
:::
```html
<ApolloQuery
:query="require('../graphql/HelloWorld.gql')"
:variables="{ name }"
>
<template slot-scope="{ result: { loading, error, data } }">
<!-- Loading -->
<div v-if="loading" class="loading apollo">Loading...</div>
<!-- Error -->
<div v-else-if="error" class="error apollo">An error occured</div>
<!-- Result -->
<div v-else-if="data" class="result apollo">{{ data.hello }}</div>
<!-- No result -->
<div v-else class="no-result apollo">No result :(</div>
</template>
</ApolloQuery>
```
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
- `isLoading`: Smart Query loading state
- `gqlError`: first GraphQL error if any
Events:
- `result(resultObject)`
- `error(errorObject)`
::: warning WIP
You can subscribe to more data with the `ApolloSubscribeToMore` (or `apollo-subscribe-to-more`) component:
:::
```html
<template>
<ApolloQuery :query="...">
<ApolloSubscribeToMore
:document="require('../gql/MessageAdded.gql')"
:variables="{ channel }"
:updateQuery="onMessageAdded"
/>
<!-- ... -->
</ApolloQuery>
</template>
<script>
export default {
data () {
return {
channel: 'general',
}
},
methods: {
onMessageAdded (previousResult, { subscriptionData }) {
// The previous result is immutable
const newResult = {
messages: [...previousResult.messages],
}
// Add the question to the list
newResult.messages.push(subscriptionData.data.messageAdded)
return newResult
},
},
}
</script>
```
*You can put as many of those as you want inside a `<ApolloQuery>` component.*
## Mutation component
::: warning WIP
You can use the `ApolloMutation` (or `apollo-mutation`) component to call Apollo mutations directly in your template:
:::
```html
<ApolloMutation
:mutation="require('@/graphql/userLogin.gql')"
:variables="{
email,
password,
}"
@done="onDone"
>
<template slot-scope="{ mutate, loading, error }">
<button :disabled="loading" @click="mutate()">Click me</button>
<p v-if="error">An error occured: {{ error }}</p>
</template>
</ApolloMutation>
```
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
- `gqlError`: first GraphQL error if any
Events:
- `done(resultObject)`
- `error(errorObject)`
+15
View File
@@ -0,0 +1,15 @@
# 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),
})
```
+32
View File
@@ -0,0 +1,32 @@
# 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)
```
+35
View File
@@ -0,0 +1,35 @@
# 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)
---
+32
View File
@@ -0,0 +1,32 @@
# 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',
}
```
+117
View File
@@ -0,0 +1,117 @@
# 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)
},
},
}
```
+87
View File
@@ -0,0 +1,87 @@
# 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
<template>
<div id="app">
<h2>Pagination</h2>
<div class="tag-list" v-if="tagsPage">
<div class="tag-list-item" v-for="tag in tagsPage.tags">
{{ tag.id }} - {{ tag.label }} - {{ tag.type }}
</div>
<div class="actions">
<button v-if="showMoreEnabled" @click="showMore">Show more</button>
</div>
</div>
</div>
</template>
<script>
import gql from 'graphql-tag'
const pageSize = 10
export default {
name: 'app',
data: () => ({
page: 0,
showMoreEnabled: true,
}),
apollo: {
// Pages
tagsPage: {
// GraphQL Query
query: gql`query tagsPage ($page: Int!, $pageSize: Int!) {
tagsPage(page: $page, size: $pageSize) {
tags {
id
label
type
}
hasMore
}
}`,
// Initial variables
variables: {
page: 0,
pageSize,
},
},
},
methods: {
showMore() {
this.page ++
// Fetch more data and transform the original result
this.$apollo.queries.tagsPage.fetchMore({
// New variables
variables: {
page: this.page,
pageSize,
},
// Transform the previous result with new data
updateQuery: (previousResult, { fetchMoreResult }) => {
const newTags = fetchMoreResult.tagsPage.tags
const hasMore = fetchMoreResult.tagsPage.hasMore
this.showMoreEnabled = hasMore
return {
tagsPage: {
__typename: previousResult.tagsPage.__typename,
// Merging the tag list
tags: [...previousResult.tagsPage.tags, ...newTags],
hasMore,
},
}
},
})
},
},
}
</script>
```
**Don't forget to include the `__typename` to the new result.**
+477
View File
@@ -0,0 +1,477 @@
# 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.<name>`.
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
<template>
<div class="apollo">
<h3>Hello</h3>
<p>
{{hello}}
</p>
</div>
</template>
```
## 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
<template>
<div class="apollo">
<h3>Ping</h3>
<p>
{{ ping }}
</p>
</div>
</template>
```
## Loading state
You can display a loading state thanks to the `$apollo.loading` prop:
```html
<div v-if="$apollo.loading">Loading...</div>
```
Or for this specific `ping` query:
```html
<div v-if="$apollo.queries.ping.loading">Loading...</div>
```
## 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
<template>
<div class="apollo">
<h3>Ping</h3>
<input v-model="pingInput" placeholder="Enter a message" />
<p>
{{ping}}
</p>
</div>
</template>
```
## 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.*
+27
View File
@@ -0,0 +1,27 @@
# 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
}
}
```
+46
View File
@@ -0,0 +1,46 @@
# 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
<script>
export default {
data () {
return {
loading: 0,
}
},
apollo: {
$query: {
loadingKey: 'loading',
},
query1: { ... },
query2: { ... },
},
}
</script>
```
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',
},
},
})
```
+322
View File
@@ -0,0 +1,322 @@
# 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.
::: danger
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.<client_id>`
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)
})
```
+301
View File
@@ -0,0 +1,301 @@
# 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.<name>`, 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
::: danger
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.<name>`.
*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.*
+13
View File
@@ -0,0 +1,13 @@
# 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.<key>` (for [Multiple clients](#multiple-clients)) in all your vue components.
+196
View File
@@ -0,0 +1,196 @@
# From vue-apollo 2 and Apollo 1
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'
```
### 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,
})
```
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,
})
```
### 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, {
apolloClient,
})
new Vue({
// ...
})
```
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)
// Create a provider
const apolloProvider = new VueApollo({
defaultClient: apolloClient,
})
// Use the provider
new Vue({
provide: apolloProvider.provide(),
// ...
})
```
## 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'
```
Learn more at the [official apollo documentation](https://www.apollographql.com/docs/react/2.0-migration.html).
+1 -11
View File
@@ -1,6 +1,6 @@
{
"name": "vue-apollo",
"version": "3.0.0-beta.5",
"version": "3.0.0-beta.16",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -4191,16 +4191,6 @@
"integrity": "sha512-svL3uiZf1RwhH+cWrfZn3A4+U58wbP0tGVTLQPbjplZxZ8ROD9VLuNgsRniTlLe7OlSqR79RUehXgpBW/s0IQw==",
"dev": true
},
"lodash.debounce": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
"integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168="
},
"lodash.throttle": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
"integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ="
},
"loose-envify": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz",
+5 -2
View File
@@ -15,7 +15,9 @@
"dev": "nodemon --exec 'npm run build:es' --watch src",
"test": "npm run test:eslint && npm run test:types",
"test:eslint": "eslint --ext .js src",
"test:types": "tsc -p types/test"
"test:types": "tsc -p types/test",
"docs:dev": "vuepress dev docs",
"docs:build": "vuepress build docs"
},
"repository": {
"type": "git",
@@ -67,6 +69,7 @@
"rollup-plugin-uglify": "^3.0.0",
"typescript": "^2.9.2",
"uglify-es": "^3.1.6",
"vue": "^2.5.9"
"vue": "^2.5.16",
"vuepress": "^0.10.0"
}
}
+4619 -205
View File
File diff suppressed because it is too large Load Diff