Graphene File Upload
graphene-file-upload is a Python library that extends Graphene, Graphene-Django, and Flask-GraphQL to enable robust file upload functionality in GraphQL mutations. It acts as a drop-in replacement for the standard GraphQL view and adheres to the GraphQL Multipart Request Specification. The current version is 1.3.0, with releases occurring periodically but not on a fixed cadence, the last significant update being in early 2021.
Common errors
-
django.http.multipartparser.MultiPartParserError: Invalid boundary in multipart: None
cause The backend failed to parse the multipart/form-data request, often due to an incorrectly formatted `Content-Type` header or missing boundary information, or the wrong GraphQL view being used.fixFirst, verify you are using `FileUploadGraphQLView` in your Django `urls.py`. Second, ensure your client-side code (e.g., JavaScript `fetch`) is constructing the `FormData` object correctly and is *not* explicitly setting the `Content-Type` header, allowing the browser to set it with the correct boundary. -
info.context.FILES.items is empty / file argument in mutation is None
cause The uploaded file is not being correctly passed to the Graphene mutation. This typically happens if the `GraphQLView` is not correctly configured or the client-side request is malformed.fixDouble-check that `FileUploadGraphQLView` is used in your Django/Flask `urls.py`/routing configuration. Also, review the client-side request; ensure the `file` is properly attached to the `FormData` and that `operations` and `map` fields in the multipart request are correctly defined if not using a dedicated upload client. -
{"errors":[{"message":"Must provide query string."}]}cause This error occurs when the GraphQL server (specifically the standard Graphene `GraphQLView`) expects a JSON body with a `query` field, but it receives a `multipart/form-data` request that it cannot parse as a standard GraphQL query. This implies `FileUploadGraphQLView` is not active.fixEnsure you have correctly replaced the default `GraphQLView` with `FileUploadGraphQLView` in your application's URL routing (e.g., `urls.py` for Django or `app.add_url_rule` for Flask).
Warnings
- gotcha It is critical to replace your standard `GraphQLView` (from `graphene_django.views` or `flask_graphql`) with `FileUploadGraphQLView` from `graphene_file_upload.django` or `graphene_file_upload.flask`. Failure to do so will result in the backend not correctly parsing multipart form data, leading to errors like 'Must provide query string.' or an empty `file` argument in your mutation.
- deprecated When using Flask-GraphQL, versions prior to 2.0 are not supported by `graphene-file-upload`. Ensure you are using `flask-graphql` version 2.0 or higher.
- gotcha Client-side `multipart/form-data` requests for file uploads can be tricky. If manually constructing a `fetch` request, explicitly setting the `Content-Type: multipart/form-data` header might cause issues with automatic boundary generation, leading to `MultiPartParserError` or the `file` argument being empty on the server.
Install
-
pip install graphene-file-upload
Imports
- Upload
from graphene.types.scalars import Upload
from graphene_file_upload.scalars import Upload
- FileUploadGraphQLView (Django)
from graphene_django.views import GraphQLView
from graphene_file_upload.django import FileUploadGraphQLView
- FileUploadGraphQLView (Flask)
from flask_graphql import GraphQLView
from graphene_file_upload.flask import FileUploadGraphQLView
Quickstart
import graphene
from graphene_file_upload.scalars import Upload
from graphene_file_upload.django import FileUploadGraphQLView
import os # For example file handling
class UploadFileMutation(graphene.Mutation):
class Arguments:
file = Upload(required=True)
success = graphene.Boolean()
file_name = graphene.String()
file_size = graphene.Int()
def mutate(self, info, file, **kwargs):
# `file` is a Django InMemoryUploadedFile or TemporaryUploadedFile object
# `info.context.FILES` also contains the uploaded files
# Example: Save the file to a temporary location or process it
upload_dir = 'uploads'
os.makedirs(upload_dir, exist_ok=True)
file_path = os.path.join(upload_dir, file.name)
with open(file_path, 'wb+') as destination:
for chunk in file.chunks():
destination.write(chunk)
return UploadFileMutation(
success=True,
file_name=file.name,
file_size=file.size
)
class Query(graphene.ObjectType):
hello = graphene.String(name=graphene.String(default_value="stranger"))
def resolve_hello(self, info, name):
return "Hello " + name
class Mutation(graphene.ObjectType):
upload_file = UploadFileMutation.Field()
schema = graphene.Schema(query=Query, mutation=Mutation)
# --- Django urls.py example (assuming you have a Django project setup) ---
# from django.urls import path
# from .schema import schema
#
# urlpatterns = [
# path(
# 'graphql/',
# FileUploadGraphQLView.as_view(schema=schema, graphiql=True)
# ),
# ]
#
# --- Flask app.py example (assuming you have a Flask app setup) ---
# from flask import Flask
# from .schema import schema # assuming schema is in a schema.py file
#
# app = Flask(__name__)
# app.add_url_rule(
# '/graphql',
# view_func=FileUploadGraphQLView.as_view(schema=schema, graphiql=True)
# )