-
-
Save nolawill/9ec158b6c1f2478ed3cfd8637d7d1ac5 to your computer and use it in GitHub Desktop.
8Base Vue Apollo Client Config
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<template> | |
<div> | |
...all the markup | |
</div> | |
</template> | |
<script> | |
import { mapGetters } from "vuex"; | |
import graphqlClient from "@/utils/api"; | |
// I import my queries | |
import { | |
ADD_NOTE, | |
DELETE_NOTE, | |
CURRENT_USER_QUERY, | |
NOTES_LIST, | |
NOTES_SUBSCRIPTION, | |
UPDATE_NOTE | |
} from "@/utils/graphql"; | |
export default { | |
name: "home", | |
// This is the convenience I get from using Vue Apollo | |
// I can just call queries here and they return as data props | |
apollo: { | |
user: { | |
query: CURRENT_USER_QUERY, | |
}, | |
notes: { | |
query: NOTES_LIST, | |
update: (data) => data.notesList.items, | |
subscribeToMore: { | |
document: NOTES_SUBSCRIPTION, | |
// updateQuery really means update the query data in the cache | |
updateQuery: (previousResult, { subscriptionData }) => { | |
const prevList = previousResult.notesList.items; | |
// if theres no new data for any reason, stop here | |
if (!subscriptionData.data) return prevList; | |
// these selectors follow my data structures, make sure you follow how your data returns | |
const newItem = subscriptionData.data.Notes.node; | |
const mutationType = subscriptionData.data.Notes.mutation; | |
// setup the data object to return, in my case I couldn't just return an array | |
// it had to the match the path of notesList.items like my initial query | |
// (it even complained about the __typename field when it was missing) | |
const newResult = { | |
notesList: { | |
__typename: previousResult.notesList.__typename, | |
items: [ ...prevList ], | |
}, | |
}; | |
// If data returned from the mutation has an ID matching anything in the previous list, get that index value | |
const indexOfItem = prevList.findIndex(i => i.id === newItem.id); | |
// If there's a matching ID that returns an index value, its an UPDATE or DELETE mutation | |
if (indexOfItem !== -1) { | |
// For a DELETE mutation, remove the item at the index of the matching ID | |
if (mutationType === 'delete') prevList.splice(indexOfItem, 1); | |
// In an UPDATE, remove the item at the index of the matching ID, and insert the updated item in its place | |
// a faux update if you will | |
else prevList.splice(indexOfItem, 1, newItem); | |
// update the list | |
newResult.notesList.items = [ ...prevList ]; | |
} else { | |
// For CREATE, append the new item to the array | |
newResult.notesList.items = [ ...prevList, newItem ]; | |
} | |
// update the client query cache | |
return newResult; | |
}, | |
}, | |
error (error) { | |
this.error = error.message; | |
}, | |
}, | |
}, | |
data() { | |
return { | |
notes: [], | |
list: [], | |
id: '', | |
title: '', | |
body: '', | |
error: '', | |
subscription: [], | |
}; | |
}, | |
computed: { | |
...mapGetters(["authenticated"]) | |
}, | |
methods: { | |
methods: { | |
fetchList() { | |
this.$apollo.queries.notes.refetch(); | |
}, | |
async addNote() { | |
try { | |
const { data } = await graphqlClient.mutate({ | |
mutation: ADD_NOTE, | |
variables: { | |
title: this.title, | |
body: this.body, | |
email: this.user.email | |
}, | |
}); | |
this.resetFields(); | |
} catch (e) { | |
this.error = e.message; | |
} | |
}, | |
async deleteNote(id) { | |
try { | |
const { data } = await graphqlClient.mutate({ | |
mutation: DELETE_NOTE, | |
variables: { id } | |
}); | |
} catch (e) { | |
this.error = e.message; | |
} | |
}, | |
async updateNote(id) { | |
try { | |
const { data } = await graphqlClient.mutate({ | |
mutation: UPDATE_NOTE, | |
variables: { | |
id: this.id, | |
title: this.title, | |
body: this.body, | |
email: this.user.email | |
}, | |
}); | |
this.resetFields(); | |
} catch (e) { | |
this.error = e.message; | |
} | |
}, | |
// My hokey way to just test an update mutation real quick | |
fillNote(id, title, body) { | |
this.id = id; | |
this.title = title; | |
this.body = body; | |
}, | |
resetFields() { | |
this.id = ''; | |
this.title = ''; | |
this.body = ''; | |
} | |
} | |
}; | |
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Updated example config code sent @vorobeez from 8base. many thanks mate! | |
import { BatchHttpLink } from "apollo-link-batch-http"; | |
import { HttpLink } from "apollo-link-http"; | |
import { ApolloLink } from "apollo-link"; | |
import { onError } from "apollo-link-error"; | |
import { ApolloClient } from "apollo-client"; | |
import { setContext } from "apollo-link-context"; | |
import { InMemoryCache } from "apollo-cache-inmemory"; | |
import { getMainDefinition } from "apollo-utilities"; | |
import gql from "graphql-tag"; | |
import { SubscriptionLink } from "@8base/apollo-links"; | |
// Get these from your 8base account workspace | |
const ENDPOINT_URI = "https://api.8base.com/XXXXXXXXXXXXXXXXXXXXXXXXX"; | |
const WORKSPACE_ID = "XXXXXXXXXXXXXXXXXXXXXXXXX"; | |
const API_TOKEN = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; | |
function createApolloClient({ uri, token, workspaceId }) { | |
const batchHttpLink = new BatchHttpLink({ uri }); | |
const httpLink = new HttpLink({ uri }); | |
const subscriptionLink = new SubscriptionLink({ | |
// the official websocket uri, no workspace ID needs to be appended | |
// (handled just below in the authstate it seems) | |
uri: "wss://ws.8base.com", | |
getAuthState: () => ({ | |
token, | |
workspaceId | |
}), | |
onAuthError: error => { | |
console.log("log", "[Subscription error]:", error); | |
} | |
}); | |
const onErrorLink = onError(({ graphQLErrors, networkError }) => { | |
if (graphQLErrors) { | |
graphQLErrors.map(({ message, locations, path }) => | |
console.log( | |
"log", | |
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}` | |
) | |
); | |
} | |
if (networkError) { | |
console.log("log", "[Network error]:", networkError); | |
} | |
}); | |
const authLink = setContext((_, { headers }) => ({ | |
headers: { | |
...headers, | |
authorization: token ? `Bearer ${token}` : "" | |
} | |
})); | |
const networkLink = ApolloLink.split( | |
({ query }) => { | |
const definition = getMainDefinition(query); | |
return ( | |
definition.kind === "OperationDefinition" && | |
definition.operation === "subscription" | |
); | |
}, | |
subscriptionLink, | |
ApolloLink.split( | |
operation => operation.getContext().important === true, | |
httpLink, | |
batchHttpLink | |
) | |
); | |
const cache = new InMemoryCache(); | |
return new ApolloClient({ | |
link: ApolloLink.from([authLink, onErrorLink, networkLink]), | |
cache | |
}); | |
} | |
const client = createApolloClient({ | |
uri: ENDPOINT_URI, | |
workspaceId: WORKSPACE_ID, | |
token: API_TOKEN | |
}); | |
// he included some working vanilla js code to see it in action in addition to the config code | |
const stubTableObservable = client.subscribe({ | |
query: gql(` | |
subscription { | |
StubTable(filter: {mutation_in: create}) { | |
node { | |
id | |
timestamp | |
} | |
} | |
} | |
`) | |
}); | |
stubTableObservable.subscribe({ | |
next: data => { | |
const instanceListItem = document.createElement("li"); | |
instanceListItem.appendChild( | |
document.createTextNode( | |
`${data.data.StubTable.node.id} ${data.data.StubTable.node.timestamp}` | |
) | |
); | |
console.log(instanceListItem); | |
document.querySelector("#createdInstances").appendChild(instanceListItem); | |
} | |
}); | |
const runMutation = (client, timestamp) => { | |
return client.mutate({ | |
mutation: gql( | |
` | |
mutation { | |
stubTableCreate(data: { timestamp: "${timestamp}" }) { | |
id | |
} | |
} | |
` | |
) | |
}); | |
}; | |
document.querySelector("#mutation").addEventListener("click", () => { | |
const timestamp = Date.now(); | |
runMutation(client, timestamp); | |
console.log("Created instance with timestamp: ", timestamp); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// All the graphql queries Im using in this example | |
export const NOTES_LIST = gql` | |
query { | |
notesList { | |
items { | |
id | |
title | |
body | |
users { | |
id | |
} | |
} | |
} | |
} | |
`; | |
export const NOTES_SUBSCRIPTION = gql` | |
subscription { | |
Notes(filter: {mutation_in: [create, update, delete]}) { | |
mutation | |
previousValues { | |
id | |
title | |
body | |
users { | |
id | |
} | |
} | |
node { | |
id | |
title | |
body | |
users { | |
id | |
} | |
} | |
} | |
} | |
`; | |
export const ADD_NOTE = gql` | |
mutation($title: String!, $body: String!, $email: String!) { | |
noteCreate( | |
data: { | |
title: $title | |
body: $body | |
users: { connect: { email: $email } } | |
} | |
) { | |
id | |
body | |
title | |
users { | |
id | |
} | |
} | |
} | |
`; | |
export const DELETE_NOTE = gql` | |
mutation($id: ID!) { | |
noteDelete(data: { id: $id }) { | |
success | |
} | |
} | |
`; | |
export const UPDATE_NOTE = gql` | |
mutation ($id: ID!, $title: String!, $body: String!, $email: String!) { | |
noteUpdate( | |
data: { | |
id: $id | |
title: $title | |
body: $body | |
users: { connect: { email: $email } } | |
} | |
) { | |
id | |
body | |
title | |
users { | |
id | |
} | |
} | |
} | |
`; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Vue from "vue"; | |
import App from "./App.vue"; | |
import router from "./router"; | |
import store from "./store"; | |
import "./registerServiceWorker"; | |
// Decided to use Vue Apollo for more convenience | |
import VueApollo from 'vue-apollo' | |
import graphqlClient from '@/utils/api' //aka "my_working_config" | |
// setup the plugin and client from our big config :P | |
Vue.use(VueApollo) | |
const apolloProvider = new VueApollo({ | |
defaultClient: graphqlClient, | |
}); | |
new Vue({ | |
router, | |
store, | |
// add it to the Vue instance and we're rolling... | |
apolloProvider, | |
render: h => h(App) | |
}).$mount("#app"); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { BatchHttpLink } from "apollo-link-batch-http"; | |
import { HttpLink } from "apollo-link-http"; | |
import { ApolloLink } from "apollo-link"; | |
import { onError } from "apollo-link-error"; | |
import { ApolloClient } from "apollo-client"; | |
import { setContext } from "apollo-link-context"; | |
import { InMemoryCache } from "apollo-cache-inmemory"; | |
import { getMainDefinition } from "apollo-utilities"; | |
import { SubscriptionLink } from "@8base/apollo-links"; | |
import store from "@/store"; | |
const uri = process.env.VUE_APP_WORKSPACE_ENDPOINT; | |
const workspaceId = "WORKPSACE_ID"; | |
const token = store.getters.idToken; | |
function createApolloClient({ uri, token, workspaceId }) { | |
const batchHttpLink = new BatchHttpLink({ uri }); | |
const httpLink = new HttpLink({ uri }); | |
const subscriptionLink = new SubscriptionLink({ | |
uri: "wss://ws.8base.com", | |
getAuthState: () => ({ | |
token, | |
workspaceId | |
}), | |
onAuthError: error => { | |
console.log("log", "[Subscription error]:", error); | |
} | |
}); | |
const onErrorLink = onError(({ graphQLErrors, networkError }) => { | |
if (graphQLErrors) { | |
graphQLErrors.map(({ message, locations, path }) => | |
console.log( | |
"log", | |
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}` | |
) | |
); | |
} | |
if (networkError) { | |
console.log("log", "[Network error]:", networkError); | |
} | |
}); | |
const authLink = setContext((_, { headers }) => ({ | |
headers: { | |
authorization: `Bearer ${token}`, | |
...headers, | |
} | |
})); | |
const networkLink = ApolloLink.split( | |
({ query }) => { | |
const definition = getMainDefinition(query); | |
return ( | |
definition.kind === "OperationDefinition" && | |
definition.operation === "subscription" | |
); | |
}, | |
subscriptionLink, | |
ApolloLink.split( | |
operation => operation.getContext().important === true, | |
httpLink, | |
batchHttpLink | |
) | |
); | |
return new ApolloClient({ | |
link: ApolloLink.from([authLink, onErrorLink, networkLink]), | |
cache: new InMemoryCache(), | |
// connectToDevTools: true, | |
}) | |
} | |
const client = createApolloClient({ | |
uri: uri, | |
workspaceId: workspaceId, | |
token: token, | |
}); | |
export default client; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment