GraphQL is returning extra information which is slowing down the initial loading

571 views Asked by At

I am getting some extra information within my graphql results. Apart from the data and the end errors I am getting

  • document
  • operation
  • perf
  • extensions

so the result is getting quite bulky. The other think I've noticed is that the initial loading of the documents and the intellisens are taking ages to load.

Any idea how I can get rid of this additional data?

Result of the graphQL query:

Result of the graphQL query

GraphQL Controller

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using ElectronConnectQuery.GraphQL;
using GraphQL;
using GraphQL.DataLoader;
using GraphQL.NewtonsoftJson;
using GraphQL.Types;
using GraphQL.Validation;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;

namespace ElectronConnectQuery.Controllers.v1
{
    [Route("[controller]")]
    public class GraphQLController : Controller
    {
        private readonly IDocumentExecuter _documentExecuter;
        private readonly ISchema _schema;
        private readonly DataLoaderDocumentListener _listener;
        private readonly ILogger<GraphQLController> _logger;

        public GraphQLController(ISchema schema, IDocumentExecuter documentExecuter, DataLoaderDocumentListener listener, ILogger<GraphQLController> logger)
        {
            _schema = schema;
            _documentExecuter = documentExecuter;
            _listener = listener;
            _logger = logger;
        }

        [HttpPost]
        public async Task<IActionResult> Post([FromBody] GraphQLQuery query, [FromServices] IEnumerable<IValidationRule> validationRules)
        {
            if (query == null) { throw new ArgumentNullException(nameof(query)); }
            _logger.LogDebug("GraphQL received query:{Query}", query.Query);
            var inputs = query.Variables.ToInputs();
            var executionOptions = new ExecutionOptions
            {
                Schema = _schema,
                Query = query.Query,
                Inputs = inputs,
                ValidationRules = validationRules,
                EnableMetrics = false
            };

#if (DEBUG)
            executionOptions.EnableMetrics = true;
#endif

            executionOptions.Listeners.Add(_listener);

            var result = await _documentExecuter.ExecuteAsync(executionOptions).ConfigureAwait(false);

            if (result.Errors?.Count > 0)
            {
                return BadRequest(result);
            }

            return Ok(result);
        }
    }
}
2

There are 2 answers

2
Joe McBride On BEST ANSWER

Instead if writing the result yourself, use the IDocumentWriter, which will properly serialize the result.

/// <summary>
/// Serializes an object hierarchy to a stream. Typically this would be serializing an instance of the ExecutionResult class into a JSON stream.
/// </summary>
public interface IDocumentWriter
{
    /// <summary>
    /// Asynchronously serializes the specified object to the specified stream.
    /// </summary>
    Task WriteAsync<T>(Stream stream, T value, CancellationToken cancellationToken = default);
}

There is also an extension method to serialize to a string.

public static async Task<string> WriteToStringAsync<T>(this IDocumentWriter writer, T value)

This example shows using middleware vs. a controller but the idea is the same.

https://github.com/graphql-dotnet/examples/blob/529b530d7a6aad878b2757d776282fdc1cdcb595/src/AspNetCoreCustom/Example/GraphQLMiddleware.cs#L75-L81

private async Task WriteResponseAsync(HttpContext context, ExecutionResult result)
{
    context.Response.ContentType = "application/json";
    context.Response.StatusCode = result.Errors?.Any() == true ? (int)HttpStatusCode.BadRequest : (int)HttpStatusCode.OK;

    await _writer.WriteAsync(context.Response.Body, result);
}

You will need to include GraphQL.SystemTextJson or GraphQL.NewtonSoftJson to choose your implementation of IDocumentWriter.

https://www.nuget.org/packages/GraphQL.SystemTextJson https://www.nuget.org/packages/GraphQL.NewtonsoftJson

0
Emanuil Stoyanov On

The change which I've done to the controller is:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using ElectronConnectQuery.GraphQL;
using GraphQL;
using GraphQL.DataLoader;
using GraphQL.Instrumentation;
using GraphQL.NewtonsoftJson;
using GraphQL.Types;
using GraphQL.Validation;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;

namespace ElectronConnectQuery.Controllers.v1
{
    [Route("[controller]")]
    public class GraphQLController : Controller
    {
        private readonly IDocumentExecuter _documentExecuter;
        private readonly ISchema _schema;
        private readonly DataLoaderDocumentListener _listener;
        private readonly ILogger<GraphQLController> _logger;
        private readonly IDocumentWriter _writer;

        public GraphQLController(ISchema schema, IDocumentExecuter documentExecuter, DataLoaderDocumentListener listener, ILogger<GraphQLController> logger, IDocumentWriter writer)
        {
            _schema = schema;
            _documentExecuter = documentExecuter;
            _listener = listener;
            _logger = logger;
            _writer = writer;
        }

        [HttpPost]
        public async Task Post([FromBody] GraphQLQuery query, [FromServices] IEnumerable<IValidationRule> validationRules)
        {
            if (query == null) { throw new ArgumentNullException(nameof(query)); }
            _logger.LogDebug("GraphQL received query:{Query}", query.Query);
            var inputs = query.Variables.ToInputs();
            var executionOptions = new ExecutionOptions
            {
                Schema = _schema,
                Query = query.Query,
                Inputs = inputs,
                ValidationRules = validationRules,
                EnableMetrics = false,
            };

            executionOptions.Listeners.Add(_listener);

            var result = await _documentExecuter.ExecuteAsync(opts =>
            {   
                opts.Schema = _schema;
                opts.Query = query.Query;
                opts.Inputs = inputs;
                opts.ValidationRules = validationRules;
                opts.FieldMiddleware.Use<InstrumentFieldsMiddleware>();
                opts.EnableMetrics = true;
            }).ConfigureAwait(false);

            result.EnrichWithApolloTracing(DateTime.Now);

            await _writer.WriteAsync(Response.Body, result);
        }


        private async Task WriteResponseAsync(HttpResponse response, ExecutionResult result)
        {
            response.ContentType = "application/json";
            response.StatusCode = result.Errors?.Any() == true ? (int)HttpStatusCode.BadRequest : (int)HttpStatusCode.OK;
            await _writer.WriteAsync(response.Body, result);
        }
    }
}

Startup.cs In ConfigureServices I have added the following lines

  // kestrel
            services.Configure<KestrelServerOptions>(options =>
            {
                options.AllowSynchronousIO = true;
            });

            // IIS
            services.Configure<IISServerOptions>(options =>
            {
                options.AllowSynchronousIO = true;
            });

Also I've registered the DocumentWriter

services.AddScoped<IDocumentWriter, GraphQL.NewtonsoftJson.DocumentWriter>();