Latest news about Bitcoin and all cryptocurrencies. Your daily crypto news habit.
Getting started with Vue & GraphQL using AWS AppSync.
To view the final project or to keep it as a reference, check out this repo.
This project will be using the vue-apollo and aws-appsync packages.
I am not a Vue developer. I’ve tried it when I’ve had the time to be learning something new, and I’ve really loved it so far, but really I specialize in React & React Native.
I was looking for some documentation about how to get up and running with AppSync & Vue & I realized there was none, so I decided to write a blog post to show others how it’s done!
If you haven’t already used it, AppSync is a service that allows you to quickly configure and deploy scalable serverless GraphQL APIs that also have features such as subscriptions (real time data) & multiple data sources among other things.
In this post, we’ll walk through wiring up a new AppSync API, attaching it to, and using it in a Vue application.
The application we will be building will need to demonstrate basic create, read, update, and delete operations.
Creating the API
The AppSync schema will need to have the ability to create, read, update, and delete a list of items.
Here is a glance at the Schema we will be using:
input CreateTaskInput { id: ID! name: String! completed: Boolean!}
input DeleteTaskInput { id: ID!}
type Mutation { createTask(input: CreateTaskInput!): Task updateTask(input: UpdateTaskInput!): Task deleteTask(input: DeleteTaskInput!): Task}
type Query { getTask(id: ID!): Task listTasks(first: Int, after: String): TaskConnection}
type Subscription { onCreateTask(id: ID, name: String, completed: Boolean): Task@aws_subscribe(mutations: ["createTask"]) onUpdateTask(id: ID, name: String, completed: Boolean): Task@aws_subscribe(mutations: ["updateTask"]) onDeleteTask(id: ID, name: String, completed: Boolean): Task@aws_subscribe(mutations: ["deleteTask"])}
type Task { id: ID! name: String! completed: Boolean!}
type TaskConnection { items: [Task] nextToken: String}
input UpdateTaskInput { id: ID! name: String completed: Boolean}
schema { query: Query mutation: Mutation subscription: Subscription}
If you already know how to set up a schema and endpoint using AppSync, go ahead and do so with the above schema. If not, this video will walk you through how to do it (just replace Todo with Task, and replace fetchTodos with fetchTasks when creating the schema):
Once you’ve created your API, download your AWS AppSync.json file, we will need it to configure the API client. You can download it by scrolling to the bottom of the main page in your API and clicking on Download next to “2. Download the AWS AppSync.js config file”:
Save this file, we will need it once we have created our Vue app.
Creating the client
Next, we will use the Vue cli to create a new application using the vue router:
vue init webpack vue-graphql
Here are the exact options I chose:
Next, we need to change into the directory and install the dependencies we will need:
yarn add aws-appsync vue-apollo graphql-tag
Now, move the AppSync.js file you downloaded from the AppSync dashboard, and save it in the src directory of your project.
Next, we need to initialize the AppSync client. In src/main.js, update the entrypoint to the following:
// main.jsimport Vue from 'vue'import App from './App'import router from './router'
import AWSAppSyncClient from "aws-appsync"import VueApollo from 'vue-apollo'import appSyncConfig from './AppSync'
const client = new AWSAppSyncClient({ url: appSyncConfig.graphqlEndpoint, region: appSyncConfig.region, auth: { type: appSyncConfig.authType, apiKey: appSyncConfig.apiKey, }},{ defaultOptions: { watchQuery: { fetchPolicy: 'cache-and-network', } }})
const appsyncProvider = new VueApollo({ defaultClient: client})
Vue.config.productionTip = falseVue.use(VueApollo)
new Vue({ el: '#app', router, components: { App }, provide: appsyncProvider.provide(), template: '<App/>'})
Finally, we need to change our App.vue recognize that we do not want to render until the client has been rehydrated from local storage.
Because the AppSync client stores our data locally, making it available offline, we need to be sure the data is available to the application when it loads. To do this, we can wait for a promise called apollo.provider.defaultClient.hydrated, waiting for it to be fulfilled, before rendering our app. We provide a v-if statement that will show and hide the main entrypoint of our app depending on if the client is hydrated:
// App.js<template> <div id="app" v-if="hydrated"> <router-view/> </div></template>
<script>export default { name: 'App', data: () => ({ hydrated: false }), async mounted() { await this.$apollo.provider.defaultClient.hydrated(); this.hydrated = true; }}</script>
Once this is set up, run npm run dev and you should be ready to move to the next step!
Creating queries & mutations
Now that we have the base app up and running and our GraphQL backend ready to go, the next logical step may be to go ahead and begin writing the GraphQL queries that will correlate with our AppSync schema.
In the src directory, let’s go ahead and create a mutations and queries folder:
cd srcmkdir queries mutations
Next, let’s go ahead and create the following queries:
- ListTasks.js
// queries/ListTasks.js
import gql from 'graphql-tag' export default gql` query listTasks { listTasks { items { id name completed } } }`
And the following mutations:
- AddTask.js
- UpdateTask.js
- DeleteTask.js
// mutations/AddTask.js
import gql from 'graphql-tag' export default gql` mutation addTask($id: ID!, $name: String!, $completed: Boolean!) { createTask( input: { id: $id, name: $name, completed: $completed } ) { id name completed } }`
// mutations/UpdateTask.js
import gql from 'graphql-tag'
export default gql` mutation updateTask($id: ID!, $name: String!, $completed: Boolean!) { updateTask( input: { id: $id name: $name completed: $completed } ) { id name completed } }`
// mutations/DeleteTask.js
import gql from 'graphql-tag'
export default gql` mutation deleteTask($id: ID!) { deleteTask( input: { id: $id } ) { id } }`
Now, go ahead and create a new file in the components folder called Tasks.vue.
Finally, we will update the router to use the new Tasks.vue file as the entrypoint.
Update index.js to the following:
import Vue from 'vue'import Router from 'vue-router'import Tasks from '@/components/Tasks'
Vue.use(Router) export default new Router({ routes: [ { path: '/', name: 'Tasks', component: Tasks } ]})
Now that we have our application configured with AppSync, our queries and mutations created, and our folder structure configured, the only thing we need to do is create our Task component and wire everything together.
There will be a lot going on in the Tasks.vue component, and if you understand how Vue works, then the Template will make a lot of sense. If you are new to Vue, it is actually extremely straight forward for newcomers, as this is the first Vue app I’ve ever created and the first time I’ve ever used Vue!
Here is the final Tasks.vue component. (also embedded below)
We will be breaking apart the functionality in this component and going into each area to describe what is going on.
Template
<template> <div class="tasks"> <h1>Task Manager</h1> <input v-model="taskname" placeholder="Task Name" class="input"> <button @click="createTask()" class="taskButton" >Create Task</button> <ul> <li class="task" v-for="(task, index) in tasks" :key="index" > <p class="text">{{ task.name }}</p> <p @click="toggleComplete(task)" class="text button" > {{ task.completed ? 'completed' : 'not completed' }} </p> <p @click="deleteTask(task)" class="text button delete" >Delete task</p> </li> </ul> </div></template>
Our template is pretty basic. We are looping through an array of tasks, showing the name, whether the task is completed or not, and a delete button.
We also have a text input with a button that will submit to the API to create a new task.
Imports
At the top of our script declaration, we import the queries & mutations that we will be needing to interact with our AppSync API. We also import uuid so that we can generate unique ids on the fly:
import ListTasks from '../queries/ListTasks'import CreateTask from '../mutations/CreateTask'import DeleteTask from '../mutations/DeleteTask'import UpdateTask from '../mutations/UpdateTask'import uuidV4 from 'uuid/v4'
Initial state
Let’s now take a look at our initial data:
data () { return { taskname: '', tasks: [] }}
We set an initial value of tasks to an empty array, and taskname to an empty string. tasks will hold the task list array after it is fetched from AppSync, and taskname will hold the value of our text input.
Apollo configuration
Next, we will look at our apollo configuration for the tasks array:
apollo: { tasks: { query: () => ListTasks, update: data => return data.listTasks.items }}
We have set an initial query of ListTasks, and an update function that returns just the array of items from the data and sets it as the value for tasks.
CreateTask mutation
Now, let’s see how we can perform a mutation to add a new task to our API:
createTask() { const taskname = this.taskname if ((taskname) === '') { alert('please create a task') return } this.taskname = '' const id = uuidV4() const task = { name: taskname, id, completed: false }
this.$apollo.mutate({ mutation: CreateTask, variables: task, update: (store, { data: { createTask } }) => { const data = store.readQuery({ query: ListTasks }) data.listTasks.items.push(createTask) store.writeQuery({ query: ListTasks, data }) }, optimisticResponse: { __typename: 'Mutation', createTask: { __typename: 'Task', ...task } }, }) .then(data => console.log(data)) .catch(error => console.error("error!!!: ", error))
We take the taskName along with a newly generated ID and create a new object called task, setting completed to false. We then call $this.apollo.mutate, setting the variables as the task object we just created.
We also supply both an optimisticResponse & update function to implement a snappy optimistic UI. This will basically assume the API call was successful and update our cache and UI, and if not it will then revert back to the state before the mutation was called.
DeleteTask mutation
deleteTask(task) { this.$apollo.mutate({ mutation: DeleteTask, variables: { id: task.id }, update: (store, { data: { deleteTask } }) => { const data = store.readQuery({ query: ListTasks }) data.listTasks.items = data.listTasks.items.filter(task => task.id !== deleteTask.id) store.writeQuery({ query: ListTasks, data }) }, optimisticResponse: { __typename: 'Mutation', deleteTask: { __typename: 'Task', ...task } }, }) .then(data => console.log(data)) .catch(error => console.error(error))}
This is very similar to the CreateTask mutation in the sense that we are not only updating our API, but also providing an optimistic response as well as update function for optimistic UI.
UpdateTask mutation
toggleComplete(task) { const updatedTask = { ...task, completed: !task.completed } this.$apollo.mutate({ mutation: UpdateTask, variables: updatedTask, update: (store, { data: { updateTask } }) => { const data = store.readQuery({ query: ListTasks }) const index = data.listTasks.items.findIndex(item => item.id === updateTask.id) data.listTasks.items[index] = updateTask store.writeQuery({ query: ListTasks, data }) }, optimisticResponse: { __typename: 'Mutation', updateTask: { __typename: 'Task', ...updatedTask } }, }) .then(data => console.log(data)) .catch(error => console.error(error))}
We use the UpdateTask mutation to toggle whether or not the task was completed. To do that, we take in the task as an argument, toggle the completed value to its opposite, then resubmit the task object to our API.
The final Tasks.vue should look like the following:
Conclusion
To view the final project, go here.
With this being my first time to use Vue, I was very surprised to see how easy it was to get up and running with everything.
The API for Vue is very intuitive and I did not run into anything that wasn’t just a search away either on Google or within their documentation.
I think for Vue developers looking to get up and running with GraphQL, this is a powerful and easy way to do so without having to deal with creating and maintaining your own backend & API.
My Name is Nader Dabit . I am a Developer Advocate at AWS Mobile working with projects like AppSync and Amplify, and the founder of React Native Training.If you like React and React Native, check out out our podcast — React Native Radio on Devchat.tv.Also, check out my book, React Native in Action now available from Manning Publications.If you enjoyed this article, please recommend and share it! Thanks for your time.
Full Stack Vue with GraphQL & AWS AppSync was originally published in Hacker Noon on Medium, where people are continuing the conversation by highlighting and responding to this story.
Disclaimer
The views and opinions expressed in this article are solely those of the authors and do not reflect the views of Bitcoin Insider. Every investment and trading move involves risk - this is especially true for cryptocurrencies given their volatility. We strongly advise our readers to conduct their own research when making a decision.