apollo-fetch-upload

Allows files to be used in GraphQL requests with apollo-fetch.

Usage no npm install needed!

<script type="module">
  import apolloFetchUpload from 'https://cdn.skypack.dev/apollo-fetch-upload';
</script>

README

apollo-fetch-upload

npm version

Enables the use of File, FileList and ReactNativeFile instances anywhere within mutation or query input variables. With the apollo-upload-server middleware setup on the GraphQL server, files upload to a configurable temp directory. Upload input type metadata replaces the original files in the arguments received by the resolver.

Checkout the example API and client.

Use apollo-upload-client for earlier versions of apollo-client that do not support an apollo-fetch network interface.

Setup

Install with npm:

npm install apollo-fetch-upload

To setup an ApolloClient network interface:

import ApolloClient from 'apollo-client'
import { createApolloFetchUpload } from 'apollo-fetch-upload'
import { print } from 'graphql/language/printer'

const apolloFetchUpload = createApolloFetchUpload({
  uri: 'https://api.githunt.com/graphql'
})

const ApolloClient = new ApolloClient({
  networkInterface: {
    query: request => apolloFetchUpload({
      ...request,
      query: print(request.query)
    })
  }
})

Alternatively use query batching:

import ApolloClient from 'apollo-client'
import BatchHttpLink from 'apollo-link-batch-http'
import { createApolloFetchUpload } from 'apollo-fetch-upload'

const ApolloClient = new ApolloClient({
  networkInterface: new BatchHttpLink({
    fetch: createApolloFetchUpload({
      uri: 'https://api.githunt.com/graphql'
    })
  })
})

createApolloFetchUpload and constructUploadOptions have the same API as createApolloFetch and constructDefaultOptions in apollo-fetch.

See also the setup instructions for the apollo-upload-server middleware.

Usage

Use File, FileList or ReactNativeFile instances anywhere within mutation or query input variables. For server instructions see apollo-upload-server. Checkout the example API and client.

File example

import { graphql, gql } from 'react-apollo'

const UploadFile = ({ mutate }) => {
  const handleChange = ({ target }) =>
    target.validity.valid &&
    mutate({
      variables: {
        file: target.files[0]
      }
    })

  return <input type="file" required onChange={handleChange} />
}

export default graphql(gql`
  mutation($file: Upload!) {
    uploadFile(file: $file) {
      id
    }
  }
`)(UploadFile)

FileList example

import { graphql, gql } from 'react-apollo'

const UploadFiles = ({ mutate }) => {
  const handleChange = ({ target }) =>
    target.validity.valid &&
    mutate({
      variables: {
        files: target.files
      }
    })

  return <input type="file" multiple required onChange={handleChange} />
}

export default graphql(gql`
  mutation($files: [Upload!]!) {
    uploadFiles(files: $files) {
      id
    }
  }
`)(UploadFiles)

React Native

Substitute File with ReactNativeFile from extract-files:

import { ReactNativeFile } from 'apollo-fetch-upload'

const variables = {
  file: new ReactNativeFile({
    uri: /* Camera roll URI */,
    type: 'image/jpeg',
    name: 'photo.jpg'
  }),
  files: ReactNativeFile.list([{
    uri: /* Camera roll URI */,
    type: 'image/jpeg',
    name: 'photo-1.jpg'
  }, {
    uri: /* Camera roll URI */,
    type: 'image/jpeg',
    name: 'photo-2.jpg'
  }])
}

How it works

An ‘operations object’ is a GraphQL request (or array of requests if batching). A ‘file’ is a File or ReactNativeFile instance.

When an operations object is to be sent to the GraphQL server, any files within are extracted using extract-files, remembering their object paths within request variables.

If no files are extracted a normal fetch with default options happens; the operations object is converted to JSON and sent in the fetch body.

Files must upload as individual multipart form fields. A new FormData form is created and each extracted file is appended as a field named after the file's original operations object path; for example variables.files.0 or 0.variables.files.0 if batching. The operations object (now without files) is converted to JSON and appended as a field named operations. The form is sent in the fetch body.

Multipart GraphQL server requests are handled by apollo-upload-server middleware. The files upload to a temp directory, the operations field is JSON decoded and object-path is used to insert metadata about each of the uploads (including the temp path) in place of the original files in the resolver arguments.