DEV Community

Anastasiia Lysenko
Anastasiia Lysenko

Posted on

GraphQL: Files, upload more files!

This article is a part of my 'graphql' articles series, so be free to check out other as well in case you are interested in 'three years long graphql adventure in real-life projects'.


Well, what is the one of the most needed and wide-use web features? Surely, it is file upload.
Pff, what’s a fuss, you may think of. Standard multipart/form-data, FILES array|FileInputStream|etc., attach validation and dunk. Various implementations in all kinds of languages, frameworks or vendors. At this moment you are possibly thinking and what's wrong with GraphQL, why can we implement it as usual? You can! And nothing will stop you from doing it the standard way. Well, wait, except, one thing. If you upload files via from an PHP Files array (or any other backend language solution), your query handler (resolver) will know literally nothing about the uploaded files. It’s simply a query without files in it. You are going to handle it as usual, but on the Application layer: obtain the file structure and work with it. We have this type of uploading on my first DDD with GraphQL project and it has nothing good with it. To mix layers logic is always a bad decision in any project architecture. It was no good then and I always want to change that and know that in any project from scratch I’ll do the right thing. And in the current project it has been successfully implemented by me and my fellow teammates.


GraphQL enthusiasts and developers have ready and working solution for you. Let’s meet an UploadType - the custom type for uploaded files, that has been implemented by Ecodev developers.
Arguments, and as to be more specific, one parameter to load file with this type looks pretty simple:

FileUploaderResolver:

'args' => [ 'file' => new UploadType()]
Enter fullscreen mode Exit fullscreen mode

FileUpload Mutation Example:

mutation(
   $file: Upload
) {
   uploadYourFileHere(
       file: $file
   ) {
      File {
          path,        
          name
      }
}
Enter fullscreen mode Exit fullscreen mode

More to say, after you create a resolver and use this object type in it, your additional application logic actions with this file will be identical to the version with standard file uploading. Frontend developers should download a file and send it in map schema.

For example, request payload:

--------------------------cec8e8123c05ba25
Content-Disposition: form-data; name="operations"

{ "query": "mutation ($file: Upload!) { uploadYourFileHere(file: $file) { id } }", "variables": { "file": null } }
--------------------------cec8e8123c05ba25
Content-Disposition: form-data; name="map"

{ "0": ["variables.file"] }
--------------------------cec8e8123c05ba25
Content-Disposition: form-data; name="0"; filename="a.txt"
Content-Type: text/plain
Alpha file content.

--------------------------cec8e8123c05ba25--
Enter fullscreen mode Exit fullscreen mode

And list of files (difference will be only in 'map' here and List(Upload) as mutation param accordingly):

Content-Disposition: form-data; name="map"

{ "0": ["variables.files.0"], "1": ["variables.files.1"] }
Enter fullscreen mode Exit fullscreen mode

You can explore all the details in the GraphQl Multipart Request documentation or in tests in the Ecodev library.

After uploading, you can store files in any preferable way. As Cloud adept, I prefer to store them in AWS S3 with links in DB.

Keep your code clean and yourself safe and sound.

Top comments (0)