]> Dogcows Code - chaz/graphql-client/blob - lib/GraphQL/Client/http.pm
5b9b634f8fa53630576ae087debbf168a2d462aa
[chaz/graphql-client] / lib / GraphQL / Client / http.pm
1 package GraphQL::Client::http;
2 # ABSTRACT: GraphQL over HTTP
3
4 use warnings;
5 use strict;
6
7 use HTTP::AnyUA::Util qw(www_form_urlencode);
8 use HTTP::AnyUA;
9
10 our $VERSION = '999.999'; # VERSION
11
12 sub new {
13 my $class = shift;
14 bless {@_}, $class;
15 }
16
17 sub request {
18 my $self = shift;
19 my ($request, $options) = @_;
20
21 my $url = $options->{url} || $self->url;
22 my $method = $options->{method} || $self->method;
23
24 my $data = {%$request};
25
26 if ($method eq 'GET' || $method eq 'HEAD') {
27 $data->{variables} = $self->json->encode($data->{variables}) if $data->{variables};
28 my $params = www_form_urlencode($data);
29 my $sep = $url =~ /\?/ ? '&' : '?';
30 $url .= "${sep}${params}";
31 }
32 else {
33 my $encoded_data = $self->json->encode($data);
34 $options->{content} = $encoded_data;
35 $options->{headers}{'content-length'} = length $encoded_data;
36 $options->{headers}{'content-type'} = 'application/json';
37 }
38
39 return $self->_handle_response($self->_any_ua->request($method, $url, $options));
40 }
41
42 sub _handle_response {
43 my $self = shift;
44 my ($resp) = @_;
45
46 my $handle_error = sub {
47 my $resp = shift;
48
49 return {
50 errors => [
51 {
52 message => "HTTP transport returned $resp->{status}: $resp->{content}",
53 x_transport_response => $resp,
54 },
55 ],
56 };
57 };
58 my $handle_response = sub {
59 my $resp = shift;
60
61 return $handle_error->($resp) if !$resp->{success};
62 return $self->json->decode($resp->{content});
63 };
64
65 if ($self->_any_ua->response_is_future) {
66 return $resp->transform(
67 done => $handle_response,
68 fail => $handle_error,
69 );
70 }
71 else {
72 return $handle_response->($resp);
73 }
74 }
75
76 sub ua {
77 my $self = shift;
78 $self->{ua} //= do {
79 require HTTP::Tiny;
80 HTTP::Tiny->new(
81 agent => "perl-graphql-client/$VERSION",
82 );
83 };
84 }
85
86 sub url {
87 my $self = shift;
88 $self->{url};
89 }
90
91 sub method {
92 my $self = shift;
93 $self->{method} // 'POST';
94 }
95
96 sub json {
97 my $self = shift;
98 $self->{json} //= do {
99 require JSON::MaybeXS;
100 JSON::MaybeXS->new(utf8 => 1);
101 };
102 }
103
104 sub _any_ua {
105 my $self = shift;
106 $self->{_any_ua} //= HTTP::AnyUA->new(ua => $self->ua);
107 }
108
109 1;
110 __END__
111
112 =head1 SYNOPSIS
113
114 my $transport = GraphQL::Client::http->new(
115 url => 'http://localhost:5000/graphql',
116 method => 'POST',
117 );
118
119 my $data = $client->request($query, $variables, $operation_name, $options);
120
121 =head1 DESCRIPTION
122
123 You probably shouldn't use this directly. Instead use L<GraphQL::Client>.
124
125 C<GraphQL::Client::http> is a GraphQL transport for HTTP. GraphQL is not required to be transported
126 via HTTP, but this is definitely the most common way.
127
128 This also serves as a reference implementation for future GraphQL transports.
129
130 =method new
131
132 $transport = GraphQL::Client::http->new(%attributes);
133
134 Construct a new GraphQL HTTP transport.
135
136 =method request
137
138 $response = $client->request(\%data, \%options);
139
140 Get a response from the GraphQL server.
141
142 The C<%data> structure must have a C<query> key whose value is the query or mutation string. It may
143 optionally have a C<variables> hashref an an C<operationName> string.
144
145 The C<%options> structure contains options passed through to the user agent.
146
147 The response will either be a hashref with the following structure or a L<Future> that resolves to
148 such a hashref:
149
150 {
151 data => {...},
152 errors => [...],
153 }
154
155 =attr ua
156
157 A user agent, such as:
158
159 =for :list
160 * instance of a L<HTTP::Tiny> (this is the default if no user agent is provided)
161 * instance of a L<Mojo::UserAgent>
162 * the string C<"AnyEvent::HTTP">
163 * and more...
164
165 See L<HTTP::AnyUA/"SUPPORTED USER AGENTS">.
166
167 =attr method
168
169 The HTTP method to use when querying the GraphQL server. Can be one of:
170
171 =for :list
172 * C<GET>
173 * C<POST> (default)
174
175 =attr json
176
177 The L<JSON::XS> (or compatible) object used for encoding and decoding data structures to and from
178 the GraphQL server.
179
180 Defaults to a L<JSON::MaybeXS>.
181
182 =head1 SEE ALSO
183
184 L<https://graphql.org/learn/serving-over-http/>
185
This page took 0.039645 seconds and 4 git commands to generate.