UNPKG

9.42 kBMarkdownView Raw
1# graphql.js
2
3> GitHub GraphQL API client for browsers and Node
4
5[![@latest](https://img.shields.io/npm/v/@octokit/graphql.svg)](https://www.npmjs.com/package/@octokit/graphql)
6[![Build Status](https://github.com/octokit/graphql.js/workflows/Test/badge.svg)](https://github.com/octokit/graphql.js/actions?query=workflow%3ATest+branch%3Amain)
7
8<!-- toc -->
9
10- [Usage](#usage)
11 - [Send a simple query](#send-a-simple-query)
12 - [Authentication](#authentication)
13 - [Variables](#variables)
14 - [Pass query together with headers and variables](#pass-query-together-with-headers-and-variables)
15 - [Use with GitHub Enterprise](#use-with-github-enterprise)
16 - [Use custom `@octokit/request` instance](#use-custom-octokitrequest-instance)
17- [TypeScript](#typescript)
18 - [Additional Types](#additional-types)
19- [Errors](#errors)
20- [Partial responses](#partial-responses)
21- [Writing tests](#writing-tests)
22- [License](#license)
23
24<!-- tocstop -->
25
26## Usage
27
28<table>
29<tbody valign=top align=left>
30<tr><th>
31Browsers
32</th><td width=100%>
33
34Load `@octokit/graphql` directly from [esm.sh](https://esm.sh)
35
36```html
37<script type="module">
38 import { graphql } from "https://esm.sh/@octokit/graphql";
39</script>
40```
41
42</td></tr>
43<tr><th>
44Node
45</th><td>
46
47Install with <code>npm install @octokit/graphql</code>
48
49```js
50import { graphql } from "@octokit/graphql";
51```
52
53</td></tr>
54</tbody>
55</table>
56
57### Send a simple query
58
59```js
60const { repository } = await graphql(
61 `
62 {
63 repository(owner: "octokit", name: "graphql.js") {
64 issues(last: 3) {
65 edges {
66 node {
67 title
68 }
69 }
70 }
71 }
72 }
73 `,
74 {
75 headers: {
76 authorization: `token secret123`,
77 },
78 },
79);
80```
81
82### Authentication
83
84The simplest way to authenticate a request is to set the `Authorization` header, e.g. to a [personal access token](https://github.com/settings/tokens/).
85
86```js
87const graphqlWithAuth = graphql.defaults({
88 headers: {
89 authorization: `token secret123`,
90 },
91});
92const { repository } = await graphqlWithAuth(`
93 {
94 repository(owner: "octokit", name: "graphql.js") {
95 issues(last: 3) {
96 edges {
97 node {
98 title
99 }
100 }
101 }
102 }
103 }
104`);
105```
106
107For more complex authentication strategies such as GitHub Apps or Basic, we recommend the according authentication library exported by [`@octokit/auth`](https://github.com/octokit/auth.js).
108
109```js
110const { createAppAuth } = await import("@octokit/auth-app");
111const auth = createAppAuth({
112 appId: process.env.APP_ID,
113 privateKey: process.env.PRIVATE_KEY,
114 installationId: 123,
115});
116const graphqlWithAuth = graphql.defaults({
117 request: {
118 hook: auth.hook,
119 },
120});
121
122const { repository } = await graphqlWithAuth(
123 `{
124 repository(owner: "octokit", name: "graphql.js") {
125 issues(last: 3) {
126 edges {
127 node {
128 title
129 }
130 }
131 }
132 }
133 }`,
134);
135```
136
137### Variables
138
139⚠️ Do not use [template literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals) in the query strings as they make your code vulnerable to query injection attacks (see [#2](https://github.com/octokit/graphql.js/issues/2)). Use variables instead:
140
141```js
142const { repository } = await graphql(
143 `
144 query lastIssues($owner: String!, $repo: String!, $num: Int = 3) {
145 repository(owner: $owner, name: $repo) {
146 issues(last: $num) {
147 edges {
148 node {
149 title
150 }
151 }
152 }
153 }
154 }
155 `,
156 {
157 owner: "octokit",
158 repo: "graphql.js",
159 headers: {
160 authorization: `token secret123`,
161 },
162 },
163);
164```
165
166### Pass query together with headers and variables
167
168```js
169import { graphql } from("@octokit/graphql");
170const { repository } = await graphql({
171 query: `query lastIssues($owner: String!, $repo: String!, $num: Int = 3) {
172 repository(owner: $owner, name: $repo) {
173 issues(last: $num) {
174 edges {
175 node {
176 title
177 }
178 }
179 }
180 }
181 }`,
182 owner: "octokit",
183 repo: "graphql.js",
184 headers: {
185 authorization: `token secret123`,
186 },
187});
188```
189
190### Use with GitHub Enterprise
191
192```js
193import { graphql } from "@octokit/graphql";
194graphql = graphql.defaults({
195 baseUrl: "https://github-enterprise.acme-inc.com/api",
196 headers: {
197 authorization: `token secret123`,
198 },
199});
200const { repository } = await graphql(`
201 {
202 repository(owner: "acme-project", name: "acme-repo") {
203 issues(last: 3) {
204 edges {
205 node {
206 title
207 }
208 }
209 }
210 }
211 }
212`);
213```
214
215### Use custom `@octokit/request` instance
216
217```js
218import { request } from "@octokit/request";
219import { withCustomRequest } from "@octokit/graphql";
220
221let requestCounter = 0;
222const myRequest = request.defaults({
223 headers: {
224 authorization: "bearer secret123",
225 },
226 request: {
227 hook(request, options) {
228 requestCounter++;
229 return request(options);
230 },
231 },
232});
233const myGraphql = withCustomRequest(myRequest);
234await request("/");
235await myGraphql(`
236 {
237 repository(owner: "acme-project", name: "acme-repo") {
238 issues(last: 3) {
239 edges {
240 node {
241 title
242 }
243 }
244 }
245 }
246 }
247`);
248// requestCounter is now 2
249```
250
251## TypeScript
252
253`@octokit/graphql` is exposing proper types for its usage with TypeScript projects.
254
255### Additional Types
256
257Additionally, `GraphQlQueryResponseData` has been exposed to users:
258
259```ts
260import type { GraphQlQueryResponseData } from "@octokit/graphql";
261```
262
263## Errors
264
265In case of a GraphQL error, `error.message` is set to a combined message describing all errors returned by the endpoint.
266All errors can be accessed at `error.errors`. `error.request` has the request options such as query, variables and headers set for easier debugging.
267
268```js
269import { graphql, GraphqlResponseError } from "@octokit/graphql";
270graphql = graphql.defaults({
271 headers: {
272 authorization: `token secret123`,
273 },
274});
275const query = `{
276 viewer {
277 bioHtml
278 }
279}`;
280
281try {
282 const result = await graphql(query);
283} catch (error) {
284 if (error instanceof GraphqlResponseError) {
285 // do something with the error, allowing you to detect a graphql response error,
286 // compared to accidentally catching unrelated errors.
287
288 // server responds with an object like the following (as an example)
289 // class GraphqlResponseError {
290 // "headers": {
291 // "status": "403",
292 // },
293 // "data": null,
294 // "errors": [{
295 // "message": "Field 'bioHtml' doesn't exist on type 'User'",
296 // "locations": [{
297 // "line": 3,
298 // "column": 5
299 // }]
300 // }]
301 // }
302
303 console.log("Request failed:", error.request); // { query, variables: {}, headers: { authorization: 'token secret123' } }
304 console.log(error.message); // Field 'bioHtml' doesn't exist on type 'User'
305 } else {
306 // handle non-GraphQL error
307 }
308}
309```
310
311## Partial responses
312
313A GraphQL query may respond with partial data accompanied by errors. In this case we will throw an error but the partial data will still be accessible through `error.data`
314
315```js
316import { graphql } from "@octokit/graphql";
317graphql = graphql.defaults({
318 headers: {
319 authorization: `token secret123`,
320 },
321});
322const query = `{
323 repository(name: "probot", owner: "probot") {
324 name
325 ref(qualifiedName: "master") {
326 target {
327 ... on Commit {
328 history(first: 25, after: "invalid cursor") {
329 nodes {
330 message
331 }
332 }
333 }
334 }
335 }
336 }
337}`;
338
339try {
340 const result = await graphql(query);
341} catch (error) {
342 // server responds with
343 // {
344 // "data": {
345 // "repository": {
346 // "name": "probot",
347 // "ref": null
348 // }
349 // },
350 // "errors": [
351 // {
352 // "type": "INVALID_CURSOR_ARGUMENTS",
353 // "path": [
354 // "repository",
355 // "ref",
356 // "target",
357 // "history"
358 // ],
359 // "locations": [
360 // {
361 // "line": 7,
362 // "column": 11
363 // }
364 // ],
365 // "message": "`invalid cursor` does not appear to be a valid cursor."
366 // }
367 // ]
368 // }
369
370 console.log("Request failed:", error.request); // { query, variables: {}, headers: { authorization: 'token secret123' } }
371 console.log(error.message); // `invalid cursor` does not appear to be a valid cursor.
372 console.log(error.data); // { repository: { name: 'probot', ref: null } }
373}
374```
375
376## Writing tests
377
378You can pass a replacement for [the built-in fetch implementation](https://github.com/bitinn/node-fetch) as `request.fetch` option. For example, using [fetch-mock](http://www.wheresrhys.co.uk/fetch-mock/) works great to write tests
379
380```js
381import assert from "assert";
382import fetchMock from "fetch-mock";
383
384import { graphql } from "@octokit/graphql";
385
386graphql("{ viewer { login } }", {
387 headers: {
388 authorization: "token secret123",
389 },
390 request: {
391 fetch: fetchMock
392 .sandbox()
393 .post("https://api.github.com/graphql", (url, options) => {
394 assert.strictEqual(options.headers.authorization, "token secret123");
395 assert.strictEqual(
396 options.body,
397 '{"query":"{ viewer { login } }"}',
398 "Sends correct query",
399 );
400 return { data: {} };
401 }),
402 },
403});
404```
405
406## License
407
408[MIT](LICENSE)