File Upload | 🍓 Strawberry GraphQL

All Strawberry integrations support multipart uploads as described in the
GraphQL multipart request specification.
This includes support for uploading single files as well as lists of files.

Uploads can be used in mutations via the Upload scalar.
The type passed at runtime depends on the integration:

Since these integrations use asyncio for communication, the resolver must be async.

Additionally, these servers rely on the python-multipart package, which is not included by Strawberry by default. It can be installed directly, or, for convenience, it is included in extras: strawberry[asgi] (for ASGI/Starlette) or strawberry[fastapi] (for FastAPI). For example:

  • if using Pip, pip install 'strawberry[fastapi]'
  • if using Poetry, strawberry = { version = "...", extras = ["fastapi"] } in pyproject.toml.

Example:

import

typing

import

strawberry

from

strawberry

.

file_uploads

import

Upload

@strawberry

.

input

class

FolderInput

:

files

:

typing

.

List

[

Upload

]

@strawberry

.

type

class

Mutation

:

@strawberry

.

mutation

async

def

read_file

(

self

,

file

:

Upload

)

-

>

str

:

return

(

await

file

.

read

(

)

)

.

decode

(

"utf-8"

)

@strawberry

.

mutation

async

def

read_files

(

self

,

files

:

typing

.

List

[

Upload

]

)

-

>

typing

.

List

[

str

]

:

contents

=

[

]

for

file

in

files

:

content

=

(

await

file

.

read

(

)

)

.

decode

(

"utf-8"

)

contents

.

append

(

content

)

return

contents

@strawberry

.

mutation

async

def

read_folder

(

self

,

folder

:

FolderInput

)

-

>

typing

.

List

[

str

]

:

contents

=

[

]

for

file

in

folder

.

files

:

content

=

(

await

file

.

read

(

)

)

.

decode

(

"utf-8"

)

contents

.

append

(

content

)

return

contents

Example:

import

typing

import

strawberry

from

strawberry

.

file_uploads

import

Upload

@strawberry

.

input

class

FolderInput

:

files

:

typing

.

List

[

Upload

]

@strawberry

.

type

class

Mutation

:

@strawberry

.

mutation

def

read_file

(

self

,

file

:

Upload

)

-

>

str

:

return

file

.

read

(

)

.

decode

(

"utf-8"

)

@strawberry

.

mutation

def

read_files

(

self

,

files

:

typing

.

List

[

Upload

]

)

-

>

typing

.

List

[

str

]

:

contents

=

[

]

for

file

in

files

:

content

=

file

.

read

(

)

.

decode

(

"utf-8"

)

contents

.

append

(

content

)

return

contents

@strawberry

.

mutation

def

read_folder

(

self

,

folder

:

FolderInput

)

-

>

typing

.

List

[

str

]

:

contents

=

[

]

for

file

in

folder

.

files

:

contents

.

append

(

file

.

read

(

)

.

decode

(

"utf-8"

)

)

return

contents

The tricky part is sending the HTTP request from the client because it must follow the GraphQL multipart request specifications mentioned above.

The multipart/form-data POST request’s data must include:

  • operations key for GraphQL request with query and variables
  • map key with mapping some multipart-data to exact GraphQL variable
  • and other keys for multipart-data which contains binary data of files

Assuming you have your schema up and running, here there are some requests examples:

curl

localhost:8000/graphql

\

-F

operations

=

'{ "query": "mutation($file: Upload!){ readFile(file: $file) }", "variables": { "file": null } }'

\

-F

map

=

'{ "file": ["variables.file"] }'

\

-F

file

=

@a.txt

curl

localhost:8000/graphql

\

-F

operations

=

'{ "query": "mutation($files: [Upload!]!) { readFiles(files: $files) }", "variables": { "files": [null, null] } }'

\

-F

map

=

'{"file1": ["variables.files.0"], "file2": ["variables.files.1"]}'

\

-F

file1

=

@b.txt

\

-F

file2

=

@c.txt

curl

localhost:8000/graphql

\

-F

operations

=

'{ "query": "mutation($folder: FolderInput!) { readFolder(folder: $folder) }", "variables": {"folder": {"files": [null, null]}} }'

\

-F

map

=

'{"file1": ["variables.folder.files.0"], "file2": ["variables.folder.files.1"]}'

\

-F

file1

=

@b.txt

\

-F

file2

=

@c.txt

Was this helpful? What can we improve?

  • 😭

  • 😕

  • 😃

  • 🤩

  • Edit on Github