I am working on vue-apollo and was able to use mutations and queries, but am facing issue using subscriptions. The apollo-express server with subscriptions is working fine. When a new event is created on the backend I want the front end to update as well but this does not seem to be happening with the current code i have written.
Vue-cli version:5.0.4
Vue version:[email protected].
Any help would be appreciated.
My Code:
main.js
import { createApp, provide, h } from "vue";
import { ApolloClient, InMemoryCache } from "@apollo/client/core";
import { DefaultApolloClient } from "@vue/apollo-composable";
import App from "./App.vue";
import { splitLink } from "./uri-utility";
const defaultClient = new ApolloClient({
// uri: "http://localhost:4000/graphql",
link: splitLink,
cache: new InMemoryCache(),
});
createApp({
setup() {
provide(DefaultApolloClient, defaultClient);
},
render: () => h(App),
}).mount("#app");
uri-utility.js
import { split, HttpLink } from "@apollo/client";
import { getMainDefinition } from "@apollo/client/utilities";
import { GraphQLWsLink } from "@apollo/client/link/subscriptions";
import { createClient } from "graphql-ws";
const httpLink = new HttpLink({
uri: "http://localhost:4000/graphql",
});
const wsLink = new GraphQLWsLink(
createClient({
url: "ws://localhost:4000/graphql",
options: {
reconnect: true,
},
})
);
export const splitLink = split(
({ query }) => {
const definition = getMainDefinition(query);
return (
definition.kind === "OperationDefinition" &&
definition.operation === "subscription"
);
},
wsLink,
httpLink
);
App.vue
<template>
<div>
<div>
<HelloWorld />
</div>
<!-- <div>
<div v-if="loading">Loading..</div>
<div v-else-if="error">Error: {{ error.message }}</div>
<div v-else-if="events">
<ul v-for="event in events" :key="event.id">
<li>{{ event.title }}</li>
</ul>
</div>
</div> -->
<div>
<input
type="text"
placeholder="find by title"
v-model="searchItem"
/><br />
<button @click="getSearchEvent" class="btn btn-primary">
Search event
</button>
<div v-if="searchRes">
{{ searchRes.title }}<br />
{{ searchRes.start }}<br />
{{ searchRes.end }}
</div>
</div>
</div>
</template>
<script>
import { useQuery, useResult } from "@vue/apollo-composable";
import gql from "graphql-tag";
import { ref } from "vue";
import HelloWorld from "./components/HelloWorld.vue";
export default {
components: {
HelloWorld,
},
setup() {
const searchItem = ref("");
const variables = ref({
title: "skull2",
});
const { result, loading, error } = useQuery(
gql`
query ($title: String!) {
getAllEvents {
id
title
start
end
}
getEvent(title: $title) {
title
start
end
}
}
`,
variables,
{
notifyOnNetworkStatusChange: true,
}
);
function getSearchEvent() {
variables.value.title = searchItem.value;
console.log(variables.value.title);
}
const events = useResult(result, null, (data) => data.getAllEvents);
const searchRes = useResult(result, null, (data) => data.getEvent);
return {
searchItem,
getSearchEvent,
variables,
events,
searchRes,
loading,
error,
};
},
};
</script>
<style>
div {
margin: 1em;
}
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
HelloWorld.vue
<script>
import {
useMutation,
useQuery,
useResult,
useSubscription,
} from "@vue/apollo-composable";
import gql from "graphql-tag";
import { reactive, ref } from "vue-demi";
import { watch } from "vue";
const GET_ALL_EVENTS = gql`
query {
getAllEvents {
title
start
end
}
}
`;
const CREATE_EVENT = gql`
mutation ($event: EventType!) {
createEvent(event: $event) {
title
start
end
}
}
`;
const EVENT_CREATED_SUB = gql`
subscription {
eventCreated {
id
title
start
end
}
}
`;
export default {
setup() {
const messages = ref([]);
const { result, loading, error } = useQuery(GET_ALL_EVENTS);
const events = useResult(result, []);
const event = reactive({
title: "",
start: "",
end: "",
});
//console.log(event.event);
//mutation
const {
mutate: createEvent,
loading: createEventLoading,
error: createEventError,
} = useMutation(CREATE_EVENT, () => ({
variables: {
event: event,
},
update: (cache, { data: { createEvent } }) => {
const data = cache.readQuery({ query: GET_ALL_EVENTS });
data.events.push(createEvent);
cache.writeQuery({ query: GET_ALL_EVENTS, data });
},
}));
//Subscription
const { sub_result } = ref(useSubscription(EVENT_CREATED_SUB));
console.log(sub_result);
watch(
sub_result,
(data) => {
messages.value.push(data.eventCreated);
console.log("New message received:", data.eventCreated);
},
{
lazy: true, // Don't immediately execute handler
}
);
return {
createEvent,
createEventError,
createEventLoading,
event,
events,
messages,
loading,
error,
};
},
};
</script>
<template>
<div class="container">
<div>
{{ messages }}
</div>
<div>
<form @submit="createEvent()" class="card text-white bg-secondary mb-3">
<div class="card-header">Create Event</div>
<div class="card-body">
<input
type="text"
v-model="event.title"
placeholder="enter event name"
class="form-control"
required
/>
<br />
<input
type="datetime-local"
v-model="event.start"
class="form-control"
required
/>
<br />
<input
type="datetime-local"
v-model="event.end"
class="form-control"
required
/><br />
<button
v-if="event.title && event.start && event.end"
class="btn btn-primary"
>
create event
</button>
</div>
</form>
<div>
<div v-if="loading">Loading..</div>
<div v-else-if="error">Error: {{ error.message }}</div>
<div v-else-if="events">
<!-- <ul v-for="event in events" :key="event.id">
<li>{{ event.title }}</li>
</ul> -->
<table class="tableFixHead">
<thead>
<th>Title</th>
<th>Start Time</th>
<th>End Time</th>
</thead>
<tbody>
<tr v-for="event in events" :key="event.id">
<td>{{ event.title }}</td>
<td>{{ event.start }}</td>
<td>{{ event.end }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</template>
<style scoped>
.tableFixHead {
overflow-y: auto;
height: 106px;
}
.tableFixHead thead th {
position: sticky;
top: 0;
}
table {
border-collapse: collapse;
width: 100%;
}
th,
td {
padding: 8px 16px;
border: 1px solid #ccc;
}
th {
background: #eee;
}
input {
margin: 1em;
}
</style>
Please let me know if you need any other detail. Thank you
Fixed it. I didn't need to watch the result. onResult() subscription event hook gets triggered whenever there is a new event created on server side and I only need to call refetch() query method to refresh the query. Rookie mistake. Thanks!