Search code examples
c#asp.net-web-apigraphqlmicroserviceshotchocolate

Get original GraphQL query string from HTTP request


I am working on a GraphQL backend service in C# (.NET8 Web Api), which is a proxy for an another GraphQL service (An existing black box application with a GraphQL API, unknown implementation). I have to proxy the payload to the remote black box GraphQL endpoint, adding additional authentication headers, etc. Also, the service developed by me will be visible on a proxy service (Technically, a proxy frontend service), using GraphQL schema stitching. The Proxy frontend service wont have any graphql capabilities apart from schema-stitching, no Query/Mutation implementations. It will show only, what my service exposes on its GraphQL endpoint.

I have two ideas in mind: 1, Catch every HTTP request, filter out the real GraphQL calls, extract the original HTTP Request Body payload, then send it by HttpClient for the original GraphQL service.

I wasn't able to do this with the interceptor, the original HTTP Request body payload will be deleted, after Hotchocolate GraphQL finishes parsing it. RequestBuilder will have the original payload, in pieces, and I am not 100% sure, that I will be able to assemble the original one (pretty sure not).

public class HttpRequestInterceptor : DefaultHttpRequestInterceptor
{
    public override ValueTask OnCreateAsync(HttpContext context,
        IRequestExecutor requestExecutor, OperationRequestBuilder requestBuilder,
        CancellationToken cancellationToken)
    {

        //Extract original query from context and send it by HttpClient -> context.Request.Body is empty            after GraphQL processing

        return base.OnCreateAsync(context, requestExecutor, requestBuilder,
            cancellationToken);
    }
}

I might be able to catch it earlier with a .NET middleware, but then I have to filter out health checks and other non-GraphQL requests, which would not be optimal.

2, Get the partial query from RequestBuilder for each Query:

I set up my GraphQL query endpoint as the following:

public IEnumerable<FooType> GetFoo([Service] IQueryRequestBuilder requestBuilder)
{
  //If this GraphQL query endpoint is being hit, extract the query for this endpoint only from requestBuilder, then forward it by HttpClient
}

public IEnumerable<FooAlternativeType> GetFooAlternative([Service] IQueryRequestBuilder requestBuilder)
{
  //If this GraphQL query endpoint is being hit, extract the query for this endpoint only from requestBuilder, then forward it by HttpClient
}

What should be the better solution? If it possible at all. Or am I off-track, and should try a different approach?


Solution

  • When injecting an Interceptor, you can access the request Body through the context (this is based on the docs):

    @Injectable()
    export class CustomInterceptor implements NestInterceptor {
      constructor() {}
    
      intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
        // Graphql
        if (context.getType<GqlContextType>() === 'graphql') {
          const gqlContext = GqlExecutionContext.create(context);
      
          const gqlRequest = gqlContext.getContext().req;
    
          // Get Body
          const body = gqlRequest.body
          ...
        }
    }