Last active
June 28, 2017 10:39
-
-
Save kbredemeier/236ba53b00cbb6e90309b66d0b91708f to your computer and use it in GitHub Desktop.
NetworkInterface for apollo-client and absinthe_plug to handle file uploads
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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