Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

File Upload with Relay and graphql-dotnet

I'm trying to build a mutation within relay that includes a file. As soon as I implement the getFiles() method referenced here: https://facebook.github.io/relay/docs/api-reference-relay-mutation.html#getfiles

Relay sends a multipart request causing a 415 error from ASP.NET Core MVC.

I'm looking for a working example, similar to "How would you do file uploads in a React-Relay app?" with the graphql-dotnet library.

like image 808
Jon Lebensold Avatar asked Nov 11 '16 11:11

Jon Lebensold


People also ask

Can you upload files using GraphQL?

GraphQL request and responses are typically in JSON format even though the GraphQL Spec doesn't mandate any format. All data fetching and uploading can be done easily with GraphQL and responses can also use GZIP for compression. One thing GraphQL lacks (or rather doesn't have a standard implementation for) is File Uploads. Why/Why not?

Is GraphQL relay compatible with GraphQL DotNet?

While quite poorly documented in the GraphQL.NET docs (at the time of writing), functionality for Relay compatible endpoints has been implemented. Some of this logic resides in the main graphql-dotnet package, while other tools and helpers reside in the relay package.

What is the difference between GraphQL and field based connection?

The GraphQL connection endpoints can be compared with traditional Field based ones. They show that the api offers access to a certain type of data. The major difference is that a Connection graph types comes with a bit more complexity to handle the added complexity of pagination.

Why is GraphQL so popular?

GraphQL has become quite popular due to its various features fixing under/over fetching issues. It also allows for easy caching, federation, non-versioning APIs, subscriptions etc,.


1 Answers

The GraphQL Endpoint wasn't accepting the multi-part form mimetype because it wasn't JSON. I was able to work with the files once I got them into graphql-dotnet via the RootObject and the context that's available in the mutation. What was throwing me off was MVC.

So I wrote a simple Filter:

public class RelayResourceFilter : IResourceFilter
{
    private readonly string jsonMediaType = "application/json";

    public void OnResourceExecuted(ResourceExecutedContext context)
    {
    }

    public void OnResourceExecuting(ResourceExecutingContext context)
    {


        if (!string.Equals(MediaTypeHeaderValue.Parse(context.HttpContext.Request.ContentType).MediaType,
            this.jsonMediaType, StringComparison.OrdinalIgnoreCase))
        {
            var encoder = JavaScriptEncoder.Create();
            var variables = encoder.Encode(context.HttpContext.Request.Form["variables"]);
            var query = encoder.Encode(context.HttpContext.Request.Form["query"]);
            var body = $"{{\"query\":\"{query}\", \"variables\":\"{variables}\"}}";

            byte[] requestData = Encoding.UTF8.GetBytes(body);
            context.HttpContext.Request.Body = new MemoryStream(requestData);
            context.HttpContext.Request.ContentType = this.jsonMediaType;
        }
    }
}

registered it:

services.AddScoped<RelayResourceFilter>();

and then applied it like so in the Controller:

[ServiceFilter(typeof(RelayResourceFilter))]
    public async Task<ExecutionResult> Post([FromBody]GraphQLQuery query, bool? useErrorCode)
{
var files = this.Request.HasFormContentType ? this.Request.Form.Files : null;
// ... assignment to Root Object
}
like image 195
Jon Lebensold Avatar answered Oct 06 '22 07:10

Jon Lebensold