File Upload with Vue, Apollo Client and GraphQL

File Upload with Vue, Apollo Client and GraphQL

In this tutorial, I'll be showing you how to hand file uploads in GraphQL by building a fullstack app.

The tutorial will be divided into two main sections: building the GraphQL API and the frontend app. The GraphQL API will be built using Apollo Server and the frontend app will be built with Vue.js and Vue Apollo.

Table of Contents

  • Prerequisites
  • What we'll be building
  • Getting your Cloudinary keys
  • Building the GraphQL server
  • Building the frontend app
  • Fetching all photos
  • Uploading photo
  • Conclusion

Prerequisites

This tutorial assumes you conformatable with GraphQL and using it in a Vue app.

What we'll be building

For the purpose of this tutorial, we'll be building a simple photo album app, where users will be able to upload as well as view their photos. All the photos will be uploaded directly to Cloudinary. Below is a quick demo of the final app:

File Upload with Vue, Apollo Client and GraphQL

Getting your Cloudinary keys

Before we dive into code, let’s make sure we have our Cloudinary key in place. If you don’t already have an account with them, you can signup for free. Otherwise, login to your dashboard where you would see your account details along with your keys.

File Upload with Vue, Apollo Client and GraphQL

Building the GraphQL server

Now, let's start building the GraphQL server. First, let's create our project directory:

$ mkdir graphql-vue-photo-upload && cd graphql-vue-photo-upload
$ mkdir server && cd server
$ npm init -y

All the GraphQL API related code will be inside the server directory. Next, let’s install the necessary dependencies for our GraphQL server:

$ npm install graphql apollo-server cloudinary dotenv

Addition to installing Apollo Server and it dependence, we also install the Cloudinary Node.js library and a package to read environment variables.

Once that’s done installing, we can start building the GraphQL server. Create a new src directory and create a new index.js file inside it, then add the following code in it:

// server/src/index.js

const { ApolloServer } = require('apollo-server')
require('dotenv').config()
const typeDefs = require('./schema')
const resolvers = require('./resolvers')

const server = new ApolloServer({
  typeDefs,
  resolvers
})

server.listen().then(({ url }) => console.log(`Server ready at ${url}`))

Next, we need to create the schema and resolvers which our GraphQL server references. We’ll start with creating the schema. Create a schema.js inside src directory and paste the following code into it:

// server/src/schema.js

const { gql } = require('apollo-server')

const typeDefs = gql`type Photo {
    filename: String!
    path: String!
  }

  type Query {
    allPhotos: [Photo]
  }

  type Mutation {
    uploadPhoto(photo: Upload!): Photo!
  }`

module.exports = typeDefs

Here, we define a Photo type that comprises of two fields: the filename of the photo and the path to the actual photo. Then we define a single query allPhotos for fetching all uploaded photos. Lastly, we have a mutation for uploading a photo. The uploadPhoto mutation accepts a single argument, which is the photo that is to be uploaded. The argument is of the scalar type Upload, which is made available to us my Apollo Server, since it has built-in support of file upload. The mutation will return the uploaded photo.

The next, thing to do is create the resolvers. Still inside src directory, create a resolvers.js and add the code below in it:

// server/src/resolvers.js

const cloudinary = require('cloudinary').v2

cloudinary.config({
  cloud_name: process.env.CLOUD_NAME,
  api_key: process.env.API_KEY,
  api_secret: process.env.API_SECRET
})

const photos = []

const resolvers = {
  Query: {
    allPhotos () {
      return photos
    }
  },
  Mutation: {
    async uploadPhoto (parent, { photo }) {
      const { filename, createReadStream } = await photo

      try {
        const result = await new Promise((resolve, reject) => {
          createReadStream().pipe(
            cloudinary.uploader.upload_stream((error, result) => {
              if (error) {
                reject(error)
              }

              resolve(result)
            })
          )
        })

        const newPhoto = { filename, path: result.secure_url }

        photos.push(newPhoto)

        return newPhoto
      } catch (err) {
        console.log(err)
      }
    }
  }
}

module.exports = resolvers

First, we pull in the Cloudinary library and configured it will our credentials, which are getting from the environment variables. Then we create an empty array, which will hold our photos. Next, we define the resolver for the allPhotos query, which simply returns the photos array.

For the uploadPhoto mutation, Apollo Server would return the selected file as Promise, which contains a bunch of details about the file, such as: createReadStream, filename, mimetype and encoding. In this tutorial, we’ll only be making use of the first two, so we extract them from the object. Using createReadStream, we stream the file directly to Cloudinary. Since it is an asynchronous operation we wrap it in a Promise and awaits it. If the Promise was resolved, that is, the file was uploaded successfully to Cloudinary, we create a new object containing the uploaded file name and the Cloudinary path to the file. Then we push the new object to the photos array and finally return the new object.

Lastly, if there was an error uploading the file to Cloudinary, we simply console log the erorr.

Before we wrap up the GraphQL API, let’s quickly add our environment variables. Create a .env file directly in the server directory and add the code below in it:

// server/.env

CLOUD_NAME=YOUR_CLOUD_NAME
API_KEY=YOUR_API_KEY
API_SECRET=YOUR_API_SECRET

Remember to replace the placeholders with your actual account details.

Finally, let’s start the server:

$ node src/index.js

The server should be running on [[http://localhost:4000](http://localhost:4000)](http://localhost:4000](http://localhost:4000))

Building the frontend app

As already mentioned, the frontend app will be built with Vue.js, so let’s create a new Vue.js app using the Vue CLI:

$ vue create client

When prompted, press enter to select the default preset. Start the app and leave it running while we build on it:

$ cd client
$ yarn serve

The app should be running on http://localhost:8080

Once the Vue app is created, let’s install the necessary dependencies:

$ npm install vue-apollo graphql-tag graphql apollo-cache-inmemory apollo-client apollo-upload-client

Out of these dependencies, the one that is new and that I’d like to point out is [apollo-upload-client]([https://github.com/jaydenseric/apollo-upload-client)](https://github.com/jaydenseric/apollo-upload-client)). It’s a package for Apollo Client that allows us to send GraphQL multipart requests. It will be used in place of apollo-link.

Next, let’s configure Vue Apollo and these dependencies. Update main.js as below:

// client/src/main.js

import { InMemoryCache } from 'apollo-cache-inmemory'
import { ApolloClient } from 'apollo-client'
import { createUploadLink } from 'apollo-upload-client'
import Vue from 'vue'
import VueApollo from 'vue-apollo'
import App from './App.vue'

Vue.config.productionTip = false

Vue.use(VueApollo)

const apolloClient = new ApolloClient({
  link: createUploadLink({ uri: 'http://localhost:4000' }),
  cache: new InMemoryCache()
})

const apolloProvider = new VueApollo({
  defaultClient: apolloClient
})

new Vue({
  apolloProvider,
  render: h => h(App)
}).$mount('#app')

You’ll notice here we are using createUploadLink from apollo-upload-client to create the ApolloClient link, passing to it our GraphQL API endpoint.

To give our app a bit of styling, we’ll be pulling in UIKit. Add the line below to head section of index.html:




Fetching all photos

We’ll start with the GraphQL query for fetching all our photo. Create a graphql directory inside the client/src directory and with in, create a AllPhotos.js file and paste the code below into it:

// client/src/graphql/AllPhotos.js

import gql from 'graphql-tag'

export default gql`query allPhotos {
    allPhotos {
      filename
      path
    }
  }`

For the sake of simplicity, we’ll be making use of just the App.vue component. So let’s update it as below:

// client/src/App.vue




      ## Photo Album






              ![](photo.path)

            {{ photo.filename }}








Within the apollo object, we add the ALL_PHOTOS query to fetch all photos and save it in a allPhotos. Once allPhotos is populated with data from our GraphQL API, we display the photos by looping through them.

If we view our app, we should get something similar to below: File Upload with Vue, Apollo Client and GraphQL

Uploading photo

Of course, we need to have uploaded some photos before we can see them. Let’s implement that now. Still inside the graphql directory, create a UploadPhoto.js and paste the code below into it:

// client/src/graphql/UploadPhoto.js

import gql from 'graphql-tag'

export default gql`mutation uploadPhoto($photo: Upload!) {
    uploadPhoto(photo: $photo) {
      filename
      path
    }
  }`

Next, add the snippet below to the template section of App.vue, just below the Photo Album heading:

// client/src/App.vue



Here, we have a file input field that accepts only images. On change of the input field a uploadPhoto method is triggered.

In the script section, add:

// client/src/App.vue

import UPLOAD_PHOTO from "./graphql/UploadPhoto";

methods: {
  async uploadPhoto({ target }) {
    await this.$apollo.mutate({
      mutation: UPLOAD_PHOTO,
      variables: {
        photo: target.files[0]
      },
      update: (store, { data: { uploadPhoto } }) => {
        const data = store.readQuery({ query: ALL_PHOTOS });

        data.allPhotos.push(uploadPhoto);

        store.writeQuery({ query: ALL_PHOTOS, data });
      }
    });
  }
}

We get extract the target from the input event, then call the mutate method, passing to it the UPLOAD_PHOTO mutation as well as the required argument (through the variables object). We get the selected file from the files on the target object. Once the mutation is executed, we update the cache by adding the newly uploaded photo to the allPhotos array. File Upload with Vue, Apollo Client and GraphQL

File Upload with Vue, Apollo Client and GraphQL

Conclusion

So in this tutorial, we have seen how to handle file uploads in GraphQL using Apollo Server on the server side and Vue and Vue Apollo on the client side. Though we used Cloudinary to store our photos, you can easily wrap it for any other cloud storage service or you can even save directly to your own local filesystem.

graphql vue-js apollo

Bootstrap 5 Complete Course with Examples

Bootstrap 5 Tutorial - Bootstrap 5 Crash Course for Beginners

Nest.JS Tutorial for Beginners

Hello Vue 3: A First Look at Vue 3 and the Composition API

Building a simple Applications with Vue 3

Deno Crash Course: Explore Deno and Create a full REST API with Deno

How to Build a Real-time Chat App with Deno and WebSockets

Convert HTML to Markdown Online

HTML entity encoder decoder Online

8 Popular Websites That Use The Vue.JS Framework

In this article, we are going to list out the most popular websites using Vue JS as their frontend framework. Vue JS is one of those elite progressive JavaScript frameworks that has huge demand in the web development industry. Many popular websites are developed using Vue in their frontend development because of its imperative features.

Building GraphQL APIs with Vue.js and Apollo Client

Learn how to build GraphQL APIs with Vue.js and Apollo Client. Integrate APIs through GraphQL with Vue using Vue-apollo. Install the vue-apollo-client and their dependencies 📦 in your Vue application. GraphQL is the most easygoing way to querying APIs. Apollo client is a community-driven effort to build an easy-to-understand, flexible, and powerful GraphQL client.

Learn Vue Apollo With GraphQL in 15 Minutes

Learn Vue Apollo With GraphQL in 15 Minutes - Vue.js and Apollo go together really well with GraphQL. In this video I discuss how to get client side Vue working using the Vue Apollo plugin!

Leveraging Vue.js & GraphQL with Gridsome (Airtable Database Example)

Vue.js and GraphQL are two powerful modern JavaScript tools. Gridsome finally leverages both to build great websites & apps. We used it to fetch data in Airtable with GraphQL for a neat Vue.js app. Yes, Vue.js finally found its Gatsby, and it’s called Gridsome. It quickly caught the attention of our team and today I’m putting it to the test by building my own Vue & GraphQL-powered e-commerce app.

GraphQL & Vue Composition API with Apollo-Composable

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. GraphQL & Vue Composition API with Apollo-Composable