GraphQL & Vue Composition API with Apollo-Composable

Aaron K Saunders - Apr 13 '20 - - Dev Community

🔆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
Enter fullscreen mode Exit fullscreen mode
// 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') }
    ]
}
Enter fullscreen mode Exit fullscreen mode
Aarons-iMac:vue-gql-composition aaronksaunders$ json-graphql-server db.js
GraphQL server running with your data at http://localhost:3000/
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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'
Enter fullscreen mode Exit fullscreen mode

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"
})
Enter fullscreen mode Exit fullscreen mode

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')
Enter fullscreen mode Exit fullscreen mode

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
    }
  }
`;
Enter fullscreen mode Exit fullscreen mode

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"
      },
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

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
    }
  }
`;
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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 }
Enter fullscreen mode Exit fullscreen mode
// 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
    }
  }
`;
Enter fullscreen mode Exit fullscreen mode

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
};
//
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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"
  }
}
Enter fullscreen mode Exit fullscreen mode

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 });
  }
});
Enter fullscreen mode Exit fullscreen mode

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

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .