GraphQL: Files, upload more files!
Tags: graphql, fileupload, howto
Published: 2021-01-06 07:56:09

*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](https://github.com/Ecodev/graphql-upload) developers. 
Arguments, and as to be more specific, one parameter to load file with this type looks pretty simple: 

```
FileUploaderResolver:

'args' => [ 'file' => new UploadType()]
```

FileUpload Mutation Example:

```
mutation(
   $file: Upload
) {
   uploadYourFileHere(
       file: $file
   ) {
      File {
          path,        
          name
      }
}
```

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--
```

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"] }
```

You can explore all the details in the [GraphQl Multipart Request documentation](https://github.com/jaydenseric/graphql-multipart-request-spec) or in [tests](https://github.com/Ecodev/graphql-upload/blob/master/tests/UploadMiddlewareTest.php) 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.*