Skip to content

Instantly share code, notes, and snippets.

@kbredemeier
Last active June 28, 2017 10:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kbredemeier/236ba53b00cbb6e90309b66d0b91708f to your computer and use it in GitHub Desktop.
Save kbredemeier/236ba53b00cbb6e90309b66d0b91708f to your computer and use it in GitHub Desktop.
NetworkInterface for apollo-client and absinthe_plug to handle file uploads
import { HTTPFetchNetworkInterface, printAST } from 'apollo-client'
require('isomorphic-fetch')
/*
* Traverses the node recursively and searches for File or FileList
* occurences and replaces theire values with the path to the value inside
* the node. It returns an object mapping the paths to theire File/FileList.
*/
function recurse(node, path = []) {
let files = {}
Object.keys(node).forEach((key) => {
const value = node[key]
// Skip unless the the value isn't traversable
if (typeof value !== 'object' || value === null) return
const currentPath = [...path, key]
const joinedPath = currentPath.join('.')
// Checks if the value is an instance of File/FileList.
// Otherwise traverses the value.
if (value instanceof File) {
files[joinedPath] = value
node[key] = joinedPath
} else if (value instanceof FileList) {
files[joinedPath] = Array.from(value)
node[key] = joinedPath
} else {
files = { ...files, ...recurse(value, currentPath) }
}
})
return files;
}
/*
* Special network interface for handling file uploads with absinthe_plug
*/
export class UploadHTTPFetchNetworkInterface extends HTTPFetchNetworkInterface {
fetchFromRemoteEndpoint({ request, options }) {
// Extracts File/FileLists from the variables
const files = recurse(request.variables)
if (Object.keys(files).length) {
const formData = new FormData()
// Turns the query into a string and adds it to the form data and
// removes it from the request.
const query = printAST(request.query)
formData.append('query', query)
delete request.query
// Adds the files to the form data
Object.entries(files).forEach(([path, file]) => {
formData.append(path, file)
})
// Adds the remaining key/value pairs from the request to the
// form data.
Object.entries(request).forEach(([key, value]) => {
if (typeof value === 'string') {
formData.append(key, value)
} else {
formData.append(key, JSON.stringify(value))
}
})
return fetch(this._uri, {
method: 'POST',
body: formData,
...options,
})
}
return super.fetchFromRemoteEndpoint({ request, options })
}
}
export function createNetworkInterface({ uri, options = {} }) {
return new UploadHTTPFetchNetworkInterface(uri, options)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment