How to Upload Files With GraphQL and Apollo Server 2.0

Learn how to upload files with Apollo Server and set it up on the client.

In web applications, one of the most common requirements is file uploads. For a while now, the question being asked around the GraphQL community is “How does one perform file uploads in GraphQL itself?” The community turned to the GraphQL specification and reference implementation but found no answers because the specification has no provisions for file uploads.

There are several ways you could upload files when using a GraphQL API, for example sending the file to Cloudinary or S3 directly and passing the returned URL through a GraphQL mutation. However, coordinating these multiple requests might be hard to manage.

One of the simplest ways of achieving file uploads in a single request is to base64-encode a file and send as a string variable in a mutation. Another option is to send file(s) in the same request as your mutation, which I’ll cover in this post!
. . .

How it works

The upload functionality follows the GraphQL multipart form requests specification. Two parts are needed to make the upload work correctly. The server and the client:

The Client: On the client, file objects are mapped into a mutation and sent to the server in a multipart request.

The Server: The multipart request is received. The server processes it and provides an upload argument to a resolver. In the resolver function, the upload promise resolves an object.

Stay with me! The resolver section will reveal what the object contains and how you can use it.

Server Configuration

The default option for enabling file uploads in Apollo Server 2.0 involves creating a schema and using the Upload type like so:

The first question you’re most likely to ask from the schema observation above is “Where does Upload scalar type come from?” Don’t fret. It is added automatically by Apollo Server.

Apollo Server 2.0 automatically adds the Upload scalar to the schema, when you are not setting the schema manually. It’s available out of the box in apollo-server-express too!

Resolver implementation

Earlier on, I mentioned that the server returns an upload promise that resolves an object. The object contains the following:
  1. stream: The upload stream manages streaming the file(s) to a filesystem or any storage location of your choice. e.g. S3, Azure, Cloudinary, e.t.c.
  2. filename: The name of the uploaded file(s).
  3. mimetype: The MIME type of the file(s) such as text/plain, application/octet-stream, etc.
  4. encoding: The file encoding such as UTF-8.

In the code above, the file can be validated after the promise resolves. If the file size or type is right (depending on the validation technique), it can be streamed into cloud storage like Cloudinary and the returned link can be stored in a database. Otherwise an Apollo error can be thrown within the resolver.

Client Setup

If the user is expected to upload files from an interactive client UI, then you need to install the apollo-upload-client package from npm. And deal with single and multiple files from the client.

Single file upload example from the client:

Use FileList, File, Blob or ReactNativeFile instances anywhere within query or mutation input variables to send a GraphQL multipart request. In the example above, we operated a single file upload, thus using File.

The user selects a file, the client validates the file and immediately sends a multipart request in the same request as the uploadFile mutation to the server.

Try it out

A full-stack working example created by Jayden is available on GitHub. Clone and try it out. Feel free to provide feedback.

On a mission to build Next-Gen Community Platform for Developers