loading...
Cover image for GraphQL & Vue Composition API with Apollo-Composable

GraphQL & Vue Composition API with Apollo-Composable

aaronksaunders profile image Aaron K Saunders ใƒปUpdated on ใƒป5 min read

๐Ÿ”†Click Here for Ionic Framework ReactJS and VueJS Tips/Tutorials?๐Ÿ”†

In this post we are assuming you understand the basics of GraphQL, but are interested in how to use the Vue Composition API with GraphQL in VueJS

About GraphQL: This is one of the best places to start to get complete overview of GraphQL The Fullstack Tutorial for GraphQL.

Quickly Spinning Up A GraphQL Server

For this to be helpful, you need a graphql server to work with. In the past, that was always a challenge until I found this great package for quickly spinning up a local server, with data based on a json file/

See Documentation for json-graphql-server

npm install -g json-graphql-server
// db.js - in project root directory
module.exports = {
    posts: [
        { id: 1, title: "Lorem Ipsum", views: 254, user_id: 123 },
        { id: 2, title: "Sic Dolor amet", views: 65, user_id: 456 },
    ],
    users: [
        { id: 123, name: "John Doe" },
        { id: 456, name: "Jane Doe" }
    ],
    comments: [
        { id: 987, post_id: 1, body: "Consectetur adipiscing elit", date: new Date('2017-07-03') },
        { id: 995, post_id: 1, body: "Nam molestie pellentesque dui", date: new Date('2017-08-17') }
    ]
}
Aarons-iMac:vue-gql-composition aaronksaunders$ json-graphql-server db.js
GraphQL server running with your data at http://localhost:3000/

You can now point your browser at the server and get the GraphiQL interface to check your data.
Alt Text

Getting The Setup For The VueJS App

See Documentation for apollo-composable

After setting up your base project using vue-cli we need to add the required packages for apollo-composable and graphql.

They are on separate lines for documentation purposes only...

npm install @vue/apollo-composable
npm install @vue/composition-api
npm install apollo-boost
npm install graphql
npm install vue-apollo

Next open main.js to star to add the client information for the graphql support

Add the imports to for the API integration and creating the Apollo client

// GRAPHQL STUFF
import VueCompositionApi, { provide } from '@vue/composition-api'
import { DefaultApolloClient } from '@vue/apollo-composable'
import ApolloClient from 'apollo-boost'

Next lets create the apollo client, the url is from the output when we launched the json-graphql-server.

// client apollo client
const apolloClient = new ApolloClient({
  connectToDevTools: true,
    uri: "http://localhost:3000"
})

And then finally we need to add the VueCompositionApi plugin since we are still not running vue3

Now we us the provide function from the composition api to make the apollo functionality available to the other components in the application.

new Vue({
  // add the client to vue object
  setup () {
    provide(DefaultApolloClient, apolloClient)
  },
  render: h => h(App),
}).$mount('#app')

Starting with Query - Get All Posts

We are not doing a deep dive into GraphQL so I will just briefly explain the query and the expected output.

This query will return the list of all of the posts and include the id of the associated user.

// QUERY
const ALL_POST_QUERY = gql`
  {
    allPosts {
      id
      title
      user_id
    }
  }
`;

The query response object will look similar to this, so when accessing the data in the application it will be data.allPost[]

{
  "data": {
    "allPosts": [
      {
        "id": "1",
        "title": "Lorem Ipsum",
        "user_id": "123"
      },
      {
        "id": "2",
        "title": "Sic Dolor amet",
        "user_id": "456"
      },
      {
        "id": "10",
        "title": "test",
        "user_id": "10"
      },
    ]
  }
}

Now that we have the query set lets get to the component setup.

In the HelloWorld.vue Component, we need to add our query and scaffold out the script section to support the new composition api.

First add the imports, and the query as a constant.

<script>
import { gql } from "apollo-boost";
import { useQuery } from "@vue/apollo-composable";

// QUERY
const ALL_POST_QUERY = gql`
  {
    allPosts {
      id
      title
    }
  }
`;

Next we will add the setup section and include the useQuery function, passing it in the query we want to run.

You can see that the useQuery composible returns the following

  • result - data response from the query
  • loading - true | false indicating the loading state of the query, can be used to provide a visual status of the query
  • error - error information if appropriate
export default {
  name: "HelloWorld",
  setup() {
    // QUERY
    const { result, loading, error } = useQuery(
      ALL_POST_QUERY
    );

    return {
      result,
      loading,
      error
    };
  },
  methods: { }
};
</script>

If you run the application now and look in the vue-dev-tools, you will see the properties returned from the setup function bound to the component as data properties.
Alt Text
Quickly put together some UI to show the query results. We are utilizing the loading property returned from useQuery to determined if we should display a loading messages and the using the result.allPosts to render the objects when the query is completed and finally if there is an error we show the error message.

<template>
  <div>
    <button @click="addPost">ADD POST</button>
    <div v-if="loading">
      <h2>Loading</h2>
    </div>
     <div v-else-if="error">
      <h2>{{error}}</h2>
    </div>
    <div v-else>
      <h2>Query Results</h2>
      <div v-for="p in result.allPosts" :key="p.id">{{p}}</div>
    </div>
  </div>
</template>

Now A Mutation - Adding A Post

This mutation will add a post to the dataset. The way it is constructed require query parameters formatted as follows:

{ title: "New Post Title", id : 100, userId : 10 }
// MUTATION
const ADD_POST_MUTATION = gql`
  mutation createPost($title: String!, $id: ID!, $userId: ID!) {
    createPost(title: $title, views: 0, user_id: $userId, id: $id) {
      id
      title
    }
  }
`;

Next we will include in the existing setup section the useMutation function, passing it in the mutation we want to run.

We are structure this such that we will have access to a function createPost exposed for us to call to execute the query that will be bound to the component.

Note that because we already are returning loading & error from useQuery that we will need to structure the objects that we return a little differently.

// QUERY
const { result, loading, error } = useQuery(
  ALL_POST_QUERY
);

// MUTATION <== NEW
const {
  loading: mLoading,
  error: mError,
  mutate: createPost
} = useMutation(ADD_POST_MUTATION);

return {
  result,
  loading: loading || mLoading, <== NEW
  error: error || mError,       <== NEW
  createPost                    <== NEW
};
//

In the template section of the component we will and an input field and a button for the user to enter the title and the execute the createPost method associated with the useMutation composable.

<template>
  <div>
    <input type="text" v-model="title" placeholder="enter the title" />
    <button @click="addPost">ADD POST</button>

...

  </div>
</template>

Updating the Cache

After the item is added to the list, you will notice that it is not showing up in the list. The client will "update" items if they exist already but will not add new items to cache automatically... you need to do that.

there is an update option on useQuery that we can use to update the local apollo cache which will then update the UI.

when the update function is called the data we get is shaped as the response we defined in the mutation

data: {
  createPost: {
    id: "1586711727281"
    title: "adssad"
    views: 0
    user_id: "200"
    __typename: "Post"
  }
}

we then use that data to update the apollo cache using the following code.

// MUTATION
const {
  loading: mLoading,
  error: mError,
  mutate: createPost
} = useMutation(ADD_POST_MUTATION, {
  update: (cache, { data: { createPost } }) => {
    // get the posts from the cache...
    const data = cache.readQuery({ query: ALL_POST_QUERY });
    // add the new post to the cache
    data.allPosts.push(createPost);
    // write results back to cache
    cache.writeQuery({ query: ALL_POST_QUERY, data });
  }
});

Conclusion

That's it for this post, in the next part I will add update and delete, and then clean up the UI a bit to make it more presentable.

json-graphql-server: https://github.com/marmelab/json-graphql-server
@vue/apollo-composable: https://v4.apollo.vuejs.org/guide-composable

Discussion

pic
Editor guide
Collapse
frnsz profile image
Fransz

Hi Aaron, thanks for sharing this elegant solution. I 've checked out your source code, but in IE it shows a blank page without errors due to the provide in the setup in main.js. Do you have a solution for this?

Collapse
aaronksaunders profile image
Aaron K Saunders Author

I will take a look on my windows machine