good1492
good1492

Reputation: 251

Apollo-Server-Express not receiving Upload Object

I'm trying to upload a file using apollo-server-express and apollo-client. However, when the file object is passed to the resolver it is always empty. I can see the file on the client, but not the server side. How can I resolve this ?

My Schema

  type File {
    id: ID
    path: String
    filename: String
    mimetype: String
  }

  extend type Query {
    getFiles: [File]
  }

  extend type Mutation {
    uploadSingleFile(file: Upload!): File
  }

My Resolver

  Mutation: {

    uploadSingleFile: combineResolvers(
      isAuthenticated,
      async (parent, { file }, { models, user, storeUpload }, info) => {
        console.log('Resolver-> uploadSingleFile')
        console.log(file) // Will return empty, { }
        const x = await file
        console.log(x) // Will also return empty, { }
        const storedFile = storeUpload(file)
        return storedFile
      }
    ),

  },

My Client-side queries file

export const UPLOAD_SINGLE_FILE = gql`
  mutation uploadSingleFile($file: Upload!) {
    uploadSingleFile(file: $file) {
      id
    }
  }
`

My Client-side interface

import React from 'react'

// GQL
import { useApolloClient, useMutation } from '@apollo/react-hooks'
import { UPLOAD_SINGLE_FILE } from '../../queries'

const FileUpload = props => {

  const [uploadSingleFile, uploadSingleFileResult] = useMutation(UPLOAD_SINGLE_FILE, {
    onCompleted(uploadSingleFile) {
      console.log('Completed uploadSingleFile')
    }
  })

  const apolloClient = useApolloClient()

  const handleUploadFile = ({
    target: {
      validity,
      files: [file]
    }
  }) => {
    console.log('Uploading file...')
    if(validity.valid) {
      console.log('Valid')
      console.log(file.name)
      uploadSingleFile({ variables: { file } })
      .then(() => {
        apolloClient.resetStore()
      })
    }
    else console.log('Invalid file')      
  }

  return(
        <input type="file" required onChange={handleUploadFile} />
  )
}

export default FileUpload

UPDATED

My front-end set-up is:

const httpLink = createHttpLink({
  uri: 'http://localhost:4000/graphql',
})

const authLink = setContext((_, { headers }) => {
  const token = localStorage.getItem('token')
  return {
    headers: {
      ...headers,
      authorization: token ? `Bearer ${token}` : "",
    }
  }
})

const client = new ApolloClient({
  link: authLink.concat(httpLink),
  cache: new InMemoryCache(),
})

Upvotes: 0

Views: 1693

Answers (2)

Richard Albayaty
Richard Albayaty

Reputation: 131

Assuming you have the proper link set up and in use (using createUploadLink as Daniel mentions in his post), you should be able to destructure the props from file once you await the promise in your resolver on the server.

const { filename, mimetype, createReadStream } = await file.promise;
console.log(filename, mimetype);

// to get a stream to use of the data
const stream = createReadStream();

UPDATE: in more recent versions of graphql-upload you can just await the file like you do in the OP, rather than the file.promise. I was using an older version of the lib it seems.

Upvotes: 0

Daniel Rearden
Daniel Rearden

Reputation: 84867

You need to utilize the appropriate Link with your Apollo Client in order to enable file uploads. The easiest way to do that is by using createUploadLink from apollo-upload-client. It functions as a drop-in replacement for createHttpLink, so just swap out the functions and you'll be good to go.

const httpLink = createUploadLink({
  uri: 'http://localhost:4000/graphql',
})
const authLink = ...
const client = new ApolloClient({
  link: authLink.concat(httpLink),
  cache: new InMemoryCache(),
})

Upvotes: 2

Related Questions