Vue and wine
Graphql workshop
What is graphql?
Syntax that describes how to ask for data
query {
authors {
name
}
}
{
"data": {
"authors": [
{
"name": "Natalia"
}
]
}
}
query {
authors {
name
age
}
}
{
"data": {
"authors": [
{
"name": "Natalia",
"age": 35
}
]
}
}
No overfetching
no underfetching
Schema and Type System
Basic Example
Type relationship
type User {
name: String!;
age: Int;
}
type Comment {
title: String!;
author: User!;
}
Scalar types
-
Int
-
Float
-
String
-
Boolean
-
ID
Meet our first schema!
-
Go to the graphql-server folder
- Run npm run apollo or yarn apollo
-
Open http://localhost:4000/graphql in the browser
Let's make a query
Moar wines!
Installation
npm install --save vue-apollo graphql apollo-boost
or
yarn add vue-apollo graphql apollo-boost
// main.js
import Vue from 'vue'
import VueApollo from 'vue-apollo'
Vue.use(VueApollo)
// main.js
import { ApolloClient } from 'apollo-client'
import { HttpLink } from 'apollo-link-http'
import { InMemoryCache } from 'apollo-cache-inmemory'
const httpLink = new HttpLink({
uri: 'http://localhost:4000/graphql'
})
const defaultClient = new ApolloClient({
link: httpLink,
cache: new InMemoryCache()
})
// main.js
const apolloProvider = new VueApollo({
defaultClient,
})
new Vue({
el: '#app',
apolloProvider,
render: h => h(App),
})
configuring webpack
module.exports = {
chainWebpack: config => {
config.module
.rule('graphql')
.test(/\.(graphql|gql)$/)
.use('graphql-tag/loader')
.loader('graphql-tag/loader')
.end()
}
}
IDE integration
VS Code: Apollo GraphQL
Webstorm: JS GraphQL
// apollo.config.js
module.exports = {
client: {
service: {
name: 'Wines',
url: 'http://localhost:4000/graphql'
},
includes: ['./src/**/*.gql']
}
}
VS Code
// .graphqlconfig
{
"name": "Wines",
"extensions": {
"endpoints": {
"Default GraphQL Endpoint": {
"url": "http://localhost:4000/graphql",
"headers": {
"user-agent": "JS GraphQL"
},
"introspect": false
}
}
}
}
WEbstorm
dev tools
Finally, let's code
vue app
create query file
import and use your query
import allWinesQuery from '../graphql/allWines.query.gql'
...
apollo: {
allWines: {
query: ...,
}
},
what if we need different name?
import allWinesQuery from '../graphql/allWines.query.gql'
...
wines: {
query: allWinesQuery,
update(data) {
return data.allWines
},
},
What if we have an error 😱
apollo: {
wines: {
query: ...,
update(data) {...},
error(error) {
console.log(error)
}
}
}
handling loading state
// Global
v-if="$apollo.loading"
// For the certain query
v-if="$apollo.queries.wines.loading"
Query component
<ApolloQuery :query="require('../graphql/allWines.query.gql')">
<template v-slot="{ result: { error, data }, isLoading }">
...
</template>
</ApolloQuery>
passing a parameter to query
// getWine.query.gql
query GetWine($id: ID!) {
getWine(id: $id) {
id
...
}
}
write a query to fetch a wine with a given id
using options or component syntax is up to you
Now let's create our own wine
mutation AddWine($wine: WineInput!) {
addWine(wine: $wine) {
}
}
Don't repeat yourself!
// wineShort.fragment.gql
fragment WineShort on Wine {
id
title
year
variety
}
Don't repeat yourself!
#import './wineShort.fragment.gql'
query AllWines {
allWines {
...WineShort
}
}
B-b-but wines are not updated!
this.$apollo
.mutate({
mutation: ...,
variables: ...,
update: (store, {data: {addWine}}) => {}
});
we can read / write queries
update: (store, { data: { addWine } }) => {
const data = store.readQuery({ query: allWinesQuery })
// CHANGE YOUR DATA HERE!
store.writeQuery({ query: allWinesQuery, data })
}
Wine.vue
deleteWine()
mutation components
<ApolloMutation
:mutation="require('../graphql/addWine.mutation.gql')"
:update="updateCache"
@done="closeModal"
>
<template v-slot="{ mutate, loading, error }">
<WineForm
@submit="mutate({ variables: { wine: $event } })"
@cancel="closeModal"
/>
</template>
</ApolloMutation>
subscriptions
const wsLink = new WebSocketLink({
uri: 'ws://localhost:4000/graphql',
options: {
reconnect: true
}
})
subscriptions
//wineSub.subscription.gql
subscription WineSub {
wineSub {
...WineShort
}
}
subscribe to more
subscribeToMore: {
document: wineSubscription,
updateQuery: (
previous,
{ subscriptionData: { data: { wineSub } } }
) => {
// CHANGE PREVIOUS STATE HERE
return previous
}
},
Let's have some favorite wines
Â
cache = new InMemoryCache()
cache.writeData({
data: {
favoriteWines: ['9X9d6n2r7']
}
})
local schema
Â
extend type Query {
favoriteWines: [ID!]!
}
extend type Wine {
isInFavorites: Boolean
}
extend type Mutation {
addOrRemoveFromFavorites(id: ID!): [ID]
}
local query
Â
query FavoriteWines {
favoriteWines @client
}
local query
Â
query AllWines {
allWines {
...WineShort
isInFavorites @client
}
}
local resolvers
Â
export const resolvers = {
Wine: {
isInFavorites: (wine, _, { cache }) => {
const { favoriteWines } = cache.readQuery({ query: getFavoriteWines })
return favoriteWines.includes(wine.id)
}
},
}
local mutation
Â
mutation ToggleFavorites($id: ID!) {
addOrRemoveFromFavorites(id: $id) @client
}
(and its resolver)
Â
Mutation: {
addOrRemoveFromFavorites: (_, { id }, { cache }) => {
const { favoriteWines } = cache.readQuery({ query: getFavoriteWines })
const data = {
favoriteWines: favoriteWines.includes(id)
? favoriteWines.filter(i => i !== id)
: [...favoriteWines, id]
}
cache.writeQuery({ query: getFavoriteWines, data })
return data.favoriteWines
}
}
in component
Â
toggleFavorites() {
this.$apollo.mutate({
mutation: toggleFavoritesMutation,
variables: { id: this.wine.id },
refetchQueries: [{ query: allWinesQuery }]
})
}
VueAndWine GraphQL workshop
By Natalia Tepluhina
VueAndWine GraphQL workshop
Slides to use during GraphQL workshop
- 1,023