Graphql-flutter: How do you retrieve headers from the response?

Created on 31 Jul 2020  Â·  10Comments  Â·  Source: zino-app/graphql-flutter

I'm using your GraphQLClient and was wondering how to get the headers from the http response that fetched the information. We have cookies in our header that needs to be written out and re-used in some of our pure http calls outside of the graphQL data requests.

Do you support this functionality? if not can you point me in the direction of where I can look to expose this, I'll fork and use that for now and request a PR as well if you'd like the functionality into the main package.

Most helpful comment

Wanted to chime in here as I found a way to accomplish this by pulling out the response object we stuff into the operation:

https://github.com/zino-app/graphql-flutter/blob/f12aae525b1bf7fd5c3120a3a62bccd87cabad68/packages/graphql/lib/src/link/http/link_http.dart#L90-L100

I have a custom link I created to get access to the operation and fetchResult like:

class GraphQLLink extends Link {
  final Future<void> Function(Operation) onRequest;
  final Future<void> Function(Operation, FetchResult) onResponse;
  final Future<void> Function(Operation, FetchResult) onError;

  GraphQLLink({
    this.onRequest,
    this.onResponse,
    this.onError,
  }) : super(request: (Operation operation, [NextLink forward]) {
          StreamController<FetchResult> controller;

          Future<void> onListen() async {
            if (onRequest != null) {
              await onRequest(operation);
            }

            final stream = forward(operation).asyncMap((fetchResult) async {
              if (fetchResult.errors != null &&
                  fetchResult.errors.isNotEmpty &&
                  onError != null) {
                await onError(operation, fetchResult);
              }

              if (onResponse != null) {
                await onResponse(operation, fetchResult);
              }

              return fetchResult;
            });

            await controller.addStream(stream);
            await controller.close();
          }

          controller = StreamController<FetchResult>(onListen: onListen);
          return controller.stream;
        });
}

Which then enables me to do something like:

...
}, onResponse: (operation, fetchResult) async {
    // Pull the context off of the operation (not the FetchResult as we don't populate it)
    final operationsContext =
        operation.getContext() ?? <String, dynamic>{};

    // Grab the `StreamedResponse` object that was stuffed into the operation.
    final StreamedResponse response = operationsContext['response'];

    // Now we can pull the response headers off that `StreamedResponse`
    final responseHeaders = response?.headers ?? <String, String>{};
...

Hope this helps!

@micimize you may find interest in this solution as it also might be an easy win to include the headers somehow in the parseResponse function over in https://github.com/zino-app/graphql-flutter/blob/master/packages/graphql/lib/src/link/http/link_http.dart#L296.

All 10 comments

HttpLink supports overriding client

HttpLink(uri: uri, httpClient: httpClient);

Making your custom client is pretty esasy
just extend
abstract class OOClient extends BaseClient and implement send method.

Thanks, just to confirm I'm not going in the wrong direction, overriding the send will allow me to access the response of that http request?

Yes. In send you’ll have

final response = await _client.send(request);
Return response;

So you’ll can do whatever you like with response

v4 will probably have response headers in a gql link context: https://github.com/gql-dart/gql/issues/119

371 was the previous main thread on this but I think the conclusion is just not pragmatic. The people need their headers. Anyhow I'll probably still leave it for v4

Wanted to chime in here as I found a way to accomplish this by pulling out the response object we stuff into the operation:

https://github.com/zino-app/graphql-flutter/blob/f12aae525b1bf7fd5c3120a3a62bccd87cabad68/packages/graphql/lib/src/link/http/link_http.dart#L90-L100

I have a custom link I created to get access to the operation and fetchResult like:

class GraphQLLink extends Link {
  final Future<void> Function(Operation) onRequest;
  final Future<void> Function(Operation, FetchResult) onResponse;
  final Future<void> Function(Operation, FetchResult) onError;

  GraphQLLink({
    this.onRequest,
    this.onResponse,
    this.onError,
  }) : super(request: (Operation operation, [NextLink forward]) {
          StreamController<FetchResult> controller;

          Future<void> onListen() async {
            if (onRequest != null) {
              await onRequest(operation);
            }

            final stream = forward(operation).asyncMap((fetchResult) async {
              if (fetchResult.errors != null &&
                  fetchResult.errors.isNotEmpty &&
                  onError != null) {
                await onError(operation, fetchResult);
              }

              if (onResponse != null) {
                await onResponse(operation, fetchResult);
              }

              return fetchResult;
            });

            await controller.addStream(stream);
            await controller.close();
          }

          controller = StreamController<FetchResult>(onListen: onListen);
          return controller.stream;
        });
}

Which then enables me to do something like:

...
}, onResponse: (operation, fetchResult) async {
    // Pull the context off of the operation (not the FetchResult as we don't populate it)
    final operationsContext =
        operation.getContext() ?? <String, dynamic>{};

    // Grab the `StreamedResponse` object that was stuffed into the operation.
    final StreamedResponse response = operationsContext['response'];

    // Now we can pull the response headers off that `StreamedResponse`
    final responseHeaders = response?.headers ?? <String, String>{};
...

Hope this helps!

@micimize you may find interest in this solution as it also might be an easy win to include the headers somehow in the parseResponse function over in https://github.com/zino-app/graphql-flutter/blob/master/packages/graphql/lib/src/link/http/link_http.dart#L296.

@jeffscaturro-aka woah I had forgotten we add the response to the context. Will try and keep that in mind for https://github.com/gql-dart/gql/issues/119 – maybe it really should be the entire response after all

@jeffscaturro-aka is there any way to grab anything from the response headers where I make my query/mutation? For instance in the repository where I do my request (using your suggested approach). Something like this:

// repository / service
{
  // ...
  final result = await graphqlClient.mutate(options);

  // here I'd like to store the cookie sent in the response
  // ...
}

If this is not currently possible, would you know if this will be possible in v4? I can't find a way to do so in the v4 beta source, but I might as well be blind.

Or can I somehow use the example you suggested above to add that data (response headers) back in the result? I don't think so, right?

Thanks!

@emme1444 I think you can get it in the link chain from Context with response.context.entry<HttpLinkResponseContext>().

QueryResult doesn't contain context, but we should add it #793

@micimize Thanks for the reply! Yeah okay, so I ended up just forking, adding the context to QueryResult, and overriding that dependency. Thanks for the tip! I'll be looking forward to a new 4 release, hopefully in the near future.

Thanks @jeffscaturro-aka , simple enough solution and it is working great !

Was this page helpful?
0 / 5 - 0 ratings