GraphQL made easy with Apollo and Vue
@Akryum
github.com/Akryum
Guillaume Chau
Apollo
GraphQL
Vue
Meteor Development Group
meteor.io
github.com/apollographql
- GraphQL Client
- JavaScript
- Android
- iOS
- Server Tools
- express
- koa
- hapi
- restify
- AWS lambda
Apollo Optics
apollodata.com/optics
Apollo Dev Tools!
Apollo Client
Query
Mutation
Subscription (Web socket)
.gql
Observable
query
Normalized Cache
Getting Started
import { ApolloClient, createNetworkInterface }
from 'apollo-client'
// Create the apollo client
const apolloClient = new ApolloClient({
networkInterface: createNetworkInterface({
uri: 'http://localhost:3000/graphql',
}),
connectToDevTools: true,
})
vue-apollo
Install
npm i -S apollo-client vue-apollo
yarn add apollo-client vue-apollo
Enable
import Vue from 'vue'
import VueApollo from 'vue-apollo'
// Install the vue plugin
Vue.use(VueApollo)
Create a provider
const apolloProvider = new VueApollo({
defaultClient: apolloClient,
})
// Add it to the root instance
new Vue({
el: '#app',
apolloProvider,
render: h => h(App),
})
Inside your Component
<template>
<nav>
<a v-for="post of posts">{{post.title}}</a>
</nav>
</template>
<script>
export default {
}
</script>
Write a GraphQL document
import gql from 'graphql-tag'
const POSTS_QUERY = gql`
query Posts {
posts {
title
}
}
`
Use the new apollo option
export default {
data () {
return {
posts: [],
}
},
// Apollo-specific options
apollo: {
posts: POSTS_QUERY,
},
}
GraphQL Webpack Loader
export default {
rules: [
// ...
{
test: /\.(graphql|gql)$/,
exclude: /node_modules/,
loader: 'graphql-tag/loader',
},
]
}
Documents in separate files
import POSTS_QUERY from '../graphql/Posts.gql'
export default {
// ...
apollo: {
posts: POSTS_QUERY,
},
}
Another document
query Post ($id: ID!) {
post (id: $id) {
title
contents
publishedDate
tags
author {
name
company
}
}
}
Variables & Loading
export default {
props: ['id'],
data () {
return {
post: null,
loading: 0,
}
},
apollo: { /* ... */ },
}
Variables & Loading
apollo: {
post: {
query: POST_QUERY,
variables () {
return { id: this.id }
},
loadingKey: 'loading',
},
},
More Features
- Mutations
- Pagination (Fetch more)
- Real-time Subscriptions
- Multiple Apollo clients
- SSR support
vue-supply
Helping you
consume Reactive Data
Efficiently
How does it work?
Reactive
Data
Component
Created /
Activated
consumers++
consumers > 0
Now Active
Supply
Subscribe
How does it work?
Reactive
Data
Component
Destroyed /
Deactivated
consumers--
consumers === 0
Now Inactive
Supply
Unsubscribe
Install
npm i -S vue-supply
yarn add vue-supply
Enable
import Vue from 'vue'
import VueSupply from 'vue-supply'
Vue.use(VueSupply)
Create a base Supply Definition
import { Supply } from 'vue-supply'
import apolloProvider from '../apollo-provider'
export default {
extends: Supply,
apolloProvider,
apollo: {
$skipAll () {
return !this.active
},
},
}
Extend it in another
import Base from './BaseApolloSupply.js'
export default {
extends: Base,
data () {
return { posts: [] },
},
apollo: {
posts: {
query: POSTS_QUERY,
loadingKey: 'loading',
},
},
}
GraphQL Subscription
apollo: {
posts: {
query: POSTS_QUERY,
subscribeToMore: {
document: POST_ADDED_QUERY,
updateQuery (previousResult, { sub }) {
return {
posts: [
...previousResult.posts,
sub.data.newPost,
],
}
Register the supply
import { register } from 'vue-supply'
import Posts from './PostsSupply.js'
register('Posts', Posts)
Consume in Component
import { use } from 'vue-supply'
export default {
// This component now uses Posts supply
mixins: [use('Posts')],
// Use the values in computed properties
computed: {
filteredPosts () {
return this.$supply.Posts.posts.filter(...)
},
},
}
Consume in Component
<template>
<section>
<div v-if="!$supply.Posts.ready">
Loading...
</div>
<div v-for="tag of $supply.Posts.posts">
{{ post.title }}
</div>
</section>
</template>
Inject the supply in the Vuex Store
export default {
supply: {
use: ['Posts'],
inject: ({ Posts }) => ({
getters: {
'all-posts': () => Posts.items,
},
}),
},
getters: {
'posts-count': (state, getters) => {
return getters['all-posts'].length
},
},
}
Augment the Vuex Store
import { injectSupply } from 'vue-supply'
import storeOptions from './vuex/store.js'
// Prevent duplicate supplies!
const supplyCache = {}
const o = injectSupply(storeOptions, supplyCache)
const store = new Vuex.Store(o)
new Vue({
// ...
store,
supplyCache,
}),
Consume in Components
import { use } from 'vue-supply'
import { mapGetters } from 'vuex'
export default {
// This component now uses Posts supply
mixins: [use('Posts')],
// Getter that uses the supply
computed: mapGetters({
items: 'all-posts',
}),
}
Consume in the store
supply: {
use: ['Posts'],
inject: ({ Posts }) => ({
actions: {
'subscribe-action' () {
Posts.grasp()
return Posts.ensureReady()
},
'unsubscribe-action' () {
Posts.release()
},
}
}),
}
Consume in the store
export default {
actions: {
async foo ({ dispatch, getters }) {
await dispatch('subscribe-action')
const data = getters['all-posts']
dispatch('unsubscribe-action')
},
},
}
SSR-friendly!
Resources
Thank you!
@Akryum
github.com/Akryum
Guillaume Chau
[EN] Vue + Apollo + GraphQL
By Guillaume Chau
[EN] Vue + Apollo + GraphQL
Talk from Vue Conf 2017. Demo app source code: https://github.com/Akryum/vueconf-2017-demo
- 34,757