1 package GraphQL
::Client
::http
;
2 # ABSTRACT: GraphQL over HTTP
8 use HTTP
::AnyUA
::Util
qw(www_form_urlencode);
12 our $VERSION = '0.600'; # VERSION
14 sub _croak
{ require Carp
; goto &Carp
::croak
}
18 my $self = @_ % 2 == 0 ? {@_} : $_[0];
24 my ($request, $options) = @_;
26 my $url = delete $options->{url
} || $self->url;
27 my $method = delete $options->{method} || $self->method;
29 $request && ref($request) eq 'HASH' or _croak
q{Usage: $http->execute(\%request)};
30 $request->{query
} or _croak
q{Request must have a query};
31 $url or _croak
q{URL must be provided};
33 my $data = {%$request};
35 if ($method eq 'GET' || $method eq 'HEAD') {
36 $data->{variables
} = $self->json->encode($data->{variables
}) if $data->{variables
};
37 my $params = www_form_urlencode
($data);
38 my $sep = $url =~ /^[^#]+\?/ ? '&' : '?';
39 $url =~ s/#/${sep}${params}#/ or $url .= "${sep}${params}";
42 my $encoded_data = $self->json->encode($data);
43 $options->{content
} = $encoded_data;
44 $options->{headers
}{'content-length'} = length $encoded_data;
45 $options->{headers
}{'content-type'} = 'application/json';
48 return $self->_handle_response($self->any_ua->request($method, $url, $options));
51 sub _handle_response
{
55 if (eval { $resp->isa('Future') }) {
56 return $resp->followed_by(sub {
59 if (my ($exception, $category, @other) = $f->failure) {
60 if (ref $exception eq 'HASH') {
61 my $resp = $exception;
62 return Future-
>done($self->_handle_error($resp));
69 exception_details
=> [$category, @other],
75 return Future-
>done($self->_handle_success($resp));
79 return $self->_handle_error($resp) if !$resp->{success
};
80 return $self->_handle_success($resp);
88 my $data = eval { $self->json->decode($resp->{content
}) };
89 my $content = $resp->{content
} // 'No content';
90 my $reason = $resp->{reason
} // '';
91 my $message = "HTTP transport returned $resp->{status} ($reason): $content";
97 http_response
=> $resp,
102 sub _handle_success
{
106 my $data = eval { $self->json->decode($resp->{content
}) };
107 if (my $exception = $@) {
109 error
=> "HTTP transport failed to decode response: $exception",
112 http_response
=> $resp,
120 http_response
=> $resp,
130 agent
=> $ENV{GRAPHQL_CLIENT_HTTP_USER_AGENT
} // "perl-graphql-client/$VERSION",
137 $self->{any_ua
} //= HTTP
::AnyUA-
>new(ua
=> $self->ua);
147 $self->{method} // 'POST';
152 $self->{json
} //= do {
153 require JSON
::MaybeXS
;
154 JSON
::MaybeXS-
>new(utf8
=> 1);
168 GraphQL::Client::http - GraphQL over HTTP
176 my $transport = GraphQL::Client::http->new(
177 url => 'http://localhost:5000/graphql',
182 query => 'query Greet($name: String) { hello(name: $name) }',
183 operationName => 'Greet',
184 variables => { name => 'Bob' },
188 authorization => 'Bearer s3cr3t',
191 my $response = $client->execute($request, $options);
195 You probably shouldn't use this directly. Instead use L<GraphQL::Client>.
197 C<GraphQL::Client::http> is a GraphQL transport for HTTP. GraphQL is not required to be transported
198 via HTTP, but this is definitely the most common way.
200 This also serves as a reference implementation for C<GraphQL::Client> transports.
206 A user agent, such as:
212 instance of a L<HTTP::Tiny> (this is the default if no user agent is provided)
216 instance of a L<Mojo::UserAgent>
220 the string C<"AnyEvent::HTTP">
228 See L<HTTP::AnyUA/"SUPPORTED USER AGENTS">.
232 The L<HTTP::AnyUA> instance. Can be used to apply middleware if desired.
236 The http URL of a GraphQL endpoint, e.g. C<"http://myapiserver/graphql">.
240 The HTTP method to use when querying the GraphQL server. Can be one of:
254 GraphQL servers should be able to handle both, but you can set this explicitly to one or the other
255 if you're dealing with a server that is opinionated. You can also provide a different HTTP method,
256 but anything other than C<GET> and C<POST> are less likely to work.
260 The L<JSON::XS> (or compatible) object used for encoding and decoding data structures to and from
263 Defaults to a L<JSON::MaybeXS>.
269 $transport = GraphQL::Client::http->new(%attributes);
271 Construct a new GraphQL HTTP transport.
277 $response = $client->execute(\%request);
278 $response = $client->execute(\%request, \%options);
280 Get a response from the GraphQL server.
282 The C<%data> structure must have a C<query> key whose value is the query or mutation string. It may
283 optionally have a C<variables> hashref and an C<operationName> string.
285 The C<%options> structure is optional and may contain options passed through to the user agent. The
286 only useful options are C<headers> (which should have a hashref value) and C<method> and C<url> to
287 override the attributes of the same names.
289 The response will either be a hashref with the following structure or a L<Future> that resolves to
293 response => { # decoded response (may be undef if an error occurred)
297 error => 'Something happened', # may be ommitted if no error occurred
298 details => { # optional information which may aide troubleshooting
304 L<https://graphql.org/learn/serving-over-http/>
308 Please report any bugs or feature requests on the bugtracker website
309 L<https://github.com/chazmcgarvey/graphql-client/issues>
311 When submitting a bug or request, please include a test-file or a
312 patch to an existing test-file that illustrates the bug or desired
317 Charles McGarvey <chazmcgarvey@brokenzipper.com>
319 =head1 COPYRIGHT AND LICENSE
321 This software is copyright (c) 2020 by Charles McGarvey.
323 This is free software; you can redistribute it and/or modify it under
324 the same terms as the Perl 5 programming language system itself.