UNPKG

9.54 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%3Amaster)
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 [cdn.skypack.dev](https://cdn.skypack.dev)
35
36```html
37<script type="module">
38 import { graphql } from "https://cdn.skypack.dev/@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
50const { graphql } = require("@octokit/graphql");
51// or: import { graphql } from "@octokit/graphql";
52```
53
54</td></tr>
55</tbody>
56</table>
57
58### Send a simple query
59
60```js
61const { repository } = await graphql(
62 `
63 {
64 repository(owner: "octokit", name: "graphql.js") {
65 issues(last: 3) {
66 edges {
67 node {
68 title
69 }
70 }
71 }
72 }
73 }
74 `,
75 {
76 headers: {
77 authorization: `token secret123`,
78 },
79 }
80);
81```
82
83### Authentication
84
85The simplest way to authenticate a request is to set the `Authorization` header, e.g. to a [personal access token](https://github.com/settings/tokens/).
86
87```js
88const graphqlWithAuth = graphql.defaults({
89 headers: {
90 authorization: `token secret123`,
91 },
92});
93const { repository } = await graphqlWithAuth(`
94 {
95 repository(owner: "octokit", name: "graphql.js") {
96 issues(last: 3) {
97 edges {
98 node {
99 title
100 }
101 }
102 }
103 }
104 }
105`);
106```
107
108For 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).
109
110```js
111const { createAppAuth } = require("@octokit/auth-app");
112const auth = createAppAuth({
113 id: process.env.APP_ID,
114 privateKey: process.env.PRIVATE_KEY,
115 installationId: 123,
116});
117const graphqlWithAuth = graphql.defaults({
118 request: {
119 hook: auth.hook,
120 },
121});
122
123const { repository } = await graphqlWithAuth(
124 `{
125 repository(owner: "octokit", name: "graphql.js") {
126 issues(last: 3) {
127 edges {
128 node {
129 title
130 }
131 }
132 }
133 }
134 }`
135);
136```
137
138### Variables
139
140⚠️ 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:
141
142```js
143const { lastIssues } = await graphql(
144 `
145 query lastIssues($owner: String!, $repo: String!, $num: Int = 3) {
146 repository(owner: $owner, name: $repo) {
147 issues(last: $num) {
148 edges {
149 node {
150 title
151 }
152 }
153 }
154 }
155 }
156 `,
157 {
158 owner: "octokit",
159 repo: "graphql.js",
160 headers: {
161 authorization: `token secret123`,
162 },
163 }
164);
165```
166
167### Pass query together with headers and variables
168
169```js
170const { graphql } = require("@octokit/graphql");
171const { lastIssues } = await graphql({
172 query: `query lastIssues($owner: String!, $repo: String!, $num: Int = 3) {
173 repository(owner:$owner, name:$repo) {
174 issues(last:$num) {
175 edges {
176 node {
177 title
178 }
179 }
180 }
181 }
182 }`,
183 owner: "octokit",
184 repo: "graphql.js",
185 headers: {
186 authorization: `token secret123`,
187 },
188});
189```
190
191### Use with GitHub Enterprise
192
193```js
194let { graphql } = require("@octokit/graphql");
195graphql = graphql.defaults({
196 baseUrl: "https://github-enterprise.acme-inc.com/api",
197 headers: {
198 authorization: `token secret123`,
199 },
200});
201const { repository } = await graphql(`
202 {
203 repository(owner: "acme-project", name: "acme-repo") {
204 issues(last: 3) {
205 edges {
206 node {
207 title
208 }
209 }
210 }
211 }
212 }
213`);
214```
215
216### Use custom `@octokit/request` instance
217
218```js
219const { request } = require("@octokit/request");
220const { withCustomRequest } = require("@octokit/graphql");
221
222let requestCounter = 0;
223const myRequest = request.defaults({
224 headers: {
225 authentication: "token secret123",
226 },
227 request: {
228 hook(request, options) {
229 requestCounter++;
230 return request(options);
231 },
232 },
233});
234const myGraphql = withCustomRequest(myRequest);
235await request("/");
236await myGraphql(`
237 {
238 repository(owner: "acme-project", name: "acme-repo") {
239 issues(last: 3) {
240 edges {
241 node {
242 title
243 }
244 }
245 }
246 }
247 }
248`);
249// requestCounter is now 2
250```
251
252## TypeScript
253
254`@octokit/graphql` is exposing proper types for its usage with TypeScript projects.
255
256### Additional Types
257
258Additionally, `GraphQlQueryResponseData` has been exposed to users:
259
260```ts
261import type { GraphQlQueryResponseData } from "@octokit/graphql";
262```
263
264## Errors
265
266In case of a GraphQL error, `error.message` is set to a combined message describing all errors returned by the endpoint.
267All errors can be accessed at `error.errors`. `error.request` has the request options such as query, variables and headers set for easier debugging.
268
269```js
270let { graphql, GraphqlResponseError } = require("@octokit/graphql");
271graphqlt = graphql.defaults({
272 headers: {
273 authorization: `token secret123`,
274 },
275});
276const query = `{
277 viewer {
278 bioHtml
279 }
280}`;
281
282try {
283 const result = await graphql(query);
284} catch (error) {
285 if (error instanceof GraphqlResponseError) {
286 // do something with the error, allowing you to detect a graphql response error,
287 // compared to accidentally catching unrelated errors.
288
289 // server responds with an object like the following (as an example)
290 // class GraphqlResponseError {
291 // "headers": {
292 // "status": "403",
293 // },
294 // "data": null,
295 // "errors": [{
296 // "message": "Field 'bioHtml' doesn't exist on type 'User'",
297 // "locations": [{
298 // "line": 3,
299 // "column": 5
300 // }]
301 // }]
302 // }
303
304 console.log("Request failed:", error.request); // { query, variables: {}, headers: { authorization: 'token secret123' } }
305 console.log(error.message); // Field 'bioHtml' doesn't exist on type 'User'
306 } else {
307 // handle non-GraphQL error
308 }
309}
310```
311
312## Partial responses
313
314A 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`
315
316```js
317let { graphql } = require("@octokit/graphql");
318graphql = graphql.defaults({
319 headers: {
320 authorization: `token secret123`,
321 },
322});
323const query = `{
324 repository(name: "probot", owner: "probot") {
325 name
326 ref(qualifiedName: "master") {
327 target {
328 ... on Commit {
329 history(first: 25, after: "invalid cursor") {
330 nodes {
331 message
332 }
333 }
334 }
335 }
336 }
337 }
338}`;
339
340try {
341 const result = await graphql(query);
342} catch (error) {
343 // server responds with
344 // {
345 // "data": {
346 // "repository": {
347 // "name": "probot",
348 // "ref": null
349 // }
350 // },
351 // "errors": [
352 // {
353 // "type": "INVALID_CURSOR_ARGUMENTS",
354 // "path": [
355 // "repository",
356 // "ref",
357 // "target",
358 // "history"
359 // ],
360 // "locations": [
361 // {
362 // "line": 7,
363 // "column": 11
364 // }
365 // ],
366 // "message": "`invalid cursor` does not appear to be a valid cursor."
367 // }
368 // ]
369 // }
370
371 console.log("Request failed:", error.request); // { query, variables: {}, headers: { authorization: 'token secret123' } }
372 console.log(error.message); // `invalid cursor` does not appear to be a valid cursor.
373 console.log(error.data); // { repository: { name: 'probot', ref: null } }
374}
375```
376
377## Writing tests
378
379You 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
380
381```js
382const assert = require("assert");
383const fetchMock = require("fetch-mock/es5/server");
384
385const { graphql } = require("@octokit/graphql");
386
387graphql("{ viewer { login } }", {
388 headers: {
389 authorization: "token secret123",
390 },
391 request: {
392 fetch: fetchMock
393 .sandbox()
394 .post("https://api.github.com/graphql", (url, options) => {
395 assert.strictEqual(options.headers.authorization, "token secret123");
396 assert.strictEqual(
397 options.body,
398 '{"query":"{ viewer { login } }"}',
399 "Sends correct query"
400 );
401 return { data: {} };
402 }),
403 },
404});
405```
406
407## License
408
409[MIT](LICENSE)