Skip to content

Instantly share code, notes, and snippets.

@nolawill
Last active April 3, 2020 08:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save nolawill/9ec158b6c1f2478ed3cfd8637d7d1ac5 to your computer and use it in GitHub Desktop.
Save nolawill/9ec158b6c1f2478ed3cfd8637d7d1ac5 to your computer and use it in GitHub Desktop.
8Base Vue Apollo Client Config
<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>
// 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);
});
// All the graphql queries Im using in this example
export const NOTES_LIST = gql`
query {
notesList {
items {
id
title
body
users {
email
id
}
}
}
}
`;
export const NOTES_SUBSCRIPTION = gql`
subscription {
Notes(filter: {mutation_in: [create, update, delete]}) {
mutation
previousValues {
id
title
body
users {
email
id
}
}
node {
id
title
body
users {
email
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 {
email
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 {
email
id
}
}
}
`;
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");
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