1 | # request.js
|
2 |
|
3 | > Send parameterized requests to GitHub’s APIs with sensible defaults in browsers and Node
|
4 |
|
5 | [![@latest](https://img.shields.io/npm/v/@octokit/request.svg)](https://www.npmjs.com/package/@octokit/request)
|
6 | [![Build Status](https://travis-ci.org/octokit/request.js.svg?branch=master)](https://travis-ci.org/octokit/request.js)
|
7 | [![Greenkeeper](https://badges.greenkeeper.io/octokit/request.js.svg)](https://greenkeeper.io/)
|
8 |
|
9 | `@octokit/request` is a request library for browsers & node that makes it easier
|
10 | to interact with [GitHub’s REST API](https://developer.github.com/v3/) and
|
11 | [GitHub’s GraphQL API](https://developer.github.com/v4/guides/forming-calls/#the-graphql-endpoint).
|
12 |
|
13 | It uses [`@octokit/endpoint`](https://github.com/octokit/endpoint.js) to parse
|
14 | the passed options and sends the request using [fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API)
|
15 | ([node-fetch](https://github.com/bitinn/node-fetch) in Node).
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 | - [Features](#features)
|
22 | - [Usage](#usage)
|
23 | - [REST API example](#rest-api-example)
|
24 | - [GraphQL example](#graphql-example)
|
25 | - [Alternative: pass `method` & `url` as part of options](#alternative-pass-method--url-as-part-of-options)
|
26 | - [request()](#request)
|
27 | - [`request.defaults()`](#requestdefaults)
|
28 | - [`request.endpoint`](#requestendpoint)
|
29 | - [Special cases](#special-cases)
|
30 | - [The `data` parameter – set request body directly](#the-data-parameter-%E2%80%93-set-request-body-directly)
|
31 | - [Set parameters for both the URL/query and the request body](#set-parameters-for-both-the-urlquery-and-the-request-body)
|
32 | - [LICENSE](#license)
|
33 |
|
34 |
|
35 |
|
36 | ## Features
|
37 |
|
38 | 🤩 1:1 mapping of REST API endpoint documentation, e.g. [Add labels to an issue](https://developer.github.com/v3/issues/labels/#add-labels-to-an-issue) becomes
|
39 |
|
40 | ```js
|
41 | request("POST /repos/:owner/:repo/issues/:number/labels", {
|
42 | mediaType: {
|
43 | previews: ["symmetra"]
|
44 | },
|
45 | owner: "octokit",
|
46 | repo: "request.js",
|
47 | number: 1,
|
48 | labels: ["🐛 bug"]
|
49 | });
|
50 | ```
|
51 |
|
52 | 👶 [Small bundle size](https://bundlephobia.com/result?p=@octokit/request@5.0.3) (\<4kb minified + gzipped)
|
53 |
|
54 | 😎 [Authenticate](#authentication) with any of [GitHubs Authentication Strategies](https://github.com/octokit/auth.js).
|
55 |
|
56 | 👍 Sensible defaults
|
57 |
|
58 | - `baseUrl`: `https://api.github.com`
|
59 | - `headers.accept`: `application/vnd.github.v3+json`
|
60 | - `headers.agent`: `octokit-request.js/<current version> <OS information>`, e.g. `octokit-request.js/1.2.3 Node.js/10.15.0 (macOS Mojave; x64)`
|
61 |
|
62 | 👌 Simple to test: mock requests by passing a custom fetch method.
|
63 |
|
64 | 🧐 Simple to debug: Sets `error.request` to request options causing the error (with redacted credentials).
|
65 |
|
66 | ## Usage
|
67 |
|
68 | <table>
|
69 | <tbody valign=top align=left>
|
70 | <tr><th>
|
71 | Browsers
|
72 | </th><td width=100%>
|
73 | Load <code>@octokit/request</code> directly from <a href="https://cdn.pika.dev">cdn.pika.dev</a>
|
74 |
|
75 | ```html
|
76 | <script type="module">
|
77 | import { request } from "https://cdn.pika.dev/@octokit/request";
|
78 | </script>
|
79 | ```
|
80 |
|
81 | </td></tr>
|
82 | <tr><th>
|
83 | Node
|
84 | </th><td>
|
85 |
|
86 | Install with <code>npm install @octokit/request</code>
|
87 |
|
88 | ```js
|
89 | const { request } = require("@octokit/request");
|
90 | // or: import { request } from "@octokit/request";
|
91 | ```
|
92 |
|
93 | </td></tr>
|
94 | </tbody>
|
95 | </table>
|
96 |
|
97 | ### REST API example
|
98 |
|
99 | ```js
|
100 | // Following GitHub docs formatting:
|
101 | // https://developer.github.com/v3/repos/#list-organization-repositories
|
102 | const result = await request("GET /orgs/:org/repos", {
|
103 | headers: {
|
104 | authorization: "token 0000000000000000000000000000000000000001"
|
105 | },
|
106 | org: "octokit",
|
107 | type: "private"
|
108 | });
|
109 |
|
110 | console.log(`${result.data.length} repos found.`);
|
111 | ```
|
112 |
|
113 | ### GraphQL example
|
114 |
|
115 | For GraphQL request we recommend using [`@octokit/graphql`](https://github.com/octokit/graphql.js#readme)
|
116 |
|
117 | ```js
|
118 | const result = await request("POST /graphql", {
|
119 | headers: {
|
120 | authorization: "token 0000000000000000000000000000000000000001"
|
121 | },
|
122 | query: `query ($login: String!) {
|
123 | organization(login: $login) {
|
124 | repositories(privacy: PRIVATE) {
|
125 | totalCount
|
126 | }
|
127 | }
|
128 | }`,
|
129 | variables: {
|
130 | login: "octokit"
|
131 | }
|
132 | });
|
133 | ```
|
134 |
|
135 | ### Alternative: pass `method` & `url` as part of options
|
136 |
|
137 | Alternatively, pass in a method and a url
|
138 |
|
139 | ```js
|
140 | const result = await request({
|
141 | method: "GET",
|
142 | url: "/orgs/:org/repos",
|
143 | headers: {
|
144 | authorization: "token 0000000000000000000000000000000000000001"
|
145 | },
|
146 | org: "octokit",
|
147 | type: "private"
|
148 | });
|
149 | ```
|
150 |
|
151 | ## Authentication
|
152 |
|
153 | The simplest way to authenticate a request is to set the `Authorization` header directly, e.g. to a [personal access token](https://github.com/settings/tokens/).
|
154 |
|
155 | ```js
|
156 | const requestWithAuth = request.defaults({
|
157 | headers: {
|
158 | authorization: "token 0000000000000000000000000000000000000001"
|
159 | }
|
160 | });
|
161 | const result = await request("GET /user");
|
162 | ```
|
163 |
|
164 | For 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).
|
165 |
|
166 | ```js
|
167 | const { createAppAuth } = require("@octokit/auth-app");
|
168 | const auth = createAppAuth({
|
169 | id: process.env.APP_ID,
|
170 | privateKey: process.env.PRIVATE_KEY,
|
171 | installationId: 123
|
172 | });
|
173 | const requestWithAuth = request.defaults({
|
174 | request: {
|
175 | hook: auth.hook
|
176 | },
|
177 | mediaType: {
|
178 | previews: ["machine-man"]
|
179 | }
|
180 | });
|
181 |
|
182 | const { data: app } = await requestWithAuth("GET /app");
|
183 | const { data: app } = await requestWithAuth("POST /repos/:owner/:repo/issues", {
|
184 | owner: "octocat",
|
185 | repo: "hello-world",
|
186 | title: "Hello from the engine room"
|
187 | });
|
188 | ```
|
189 |
|
190 | ## request()
|
191 |
|
192 | `request(route, options)` or `request(options)`.
|
193 |
|
194 | **Options**
|
195 |
|
196 | <table>
|
197 | <thead>
|
198 | <tr>
|
199 | <th align=left>
|
200 | name
|
201 | </th>
|
202 | <th align=left>
|
203 | type
|
204 | </th>
|
205 | <th align=left>
|
206 | description
|
207 | </th>
|
208 | </tr>
|
209 | </thead>
|
210 | <tr>
|
211 | <th align=left>
|
212 | <code>route</code>
|
213 | </th>
|
214 | <td>
|
215 | String
|
216 | </td>
|
217 | <td>
|
218 | If <code>route</code> is set it has to be a string consisting of the request method and URL, e.g. <code>GET /orgs/:org</code>
|
219 | </td>
|
220 | </tr>
|
221 | <tr>
|
222 | <th align=left>
|
223 | <code>options.baseUrl</code>
|
224 | </th>
|
225 | <td>
|
226 | String
|
227 | </td>
|
228 | <td>
|
229 | <strong>Required.</strong> Any supported <a href="https://developer.github.com/v3/#http-verbs">http verb</a>, case insensitive. <em>Defaults to <code>https://api.github.com</code></em>.
|
230 | </td>
|
231 | </tr>
|
232 | <th align=left>
|
233 | <code>options.headers</code>
|
234 | </th>
|
235 | <td>
|
236 | Object
|
237 | </td>
|
238 | <td>
|
239 | Custom headers. Passed headers are merged with defaults:<br>
|
240 | <em><code>headers['user-agent']</code> defaults to <code>octokit-rest.js/1.2.3</code> (where <code>1.2.3</code> is the released version)</em>.<br>
|
241 | <em><code>headers['accept']</code> defaults to <code>application/vnd.github.v3+json</code>.<br> Use <code>options.mediaType.{format,previews}</code> to request API previews and custom media types.
|
242 | </td>
|
243 | </tr>
|
244 | <tr>
|
245 | <th align=left>
|
246 | <code>options.mediaType.format</code>
|
247 | </th>
|
248 | <td>
|
249 | String
|
250 | </td>
|
251 | <td>
|
252 | Media type param, such as `raw`, `html`, or `full`. See <a href="https://developer.github.com/v3/media/">Media Types</a>.
|
253 | </td>
|
254 | </tr>
|
255 | <tr>
|
256 | <th align=left>
|
257 | <code>options.mediaType.previews</code>
|
258 | </th>
|
259 | <td>
|
260 | Array of strings
|
261 | </td>
|
262 | <td>
|
263 | Name of previews, such as `mercy`, `symmetra`, or `scarlet-witch`. See <a href="https://developer.github.com/v3/previews/">API Previews</a>.
|
264 | </td>
|
265 | </tr>
|
266 | <tr>
|
267 | <th align=left>
|
268 | <code>options.method</code>
|
269 | </th>
|
270 | <td>
|
271 | String
|
272 | </td>
|
273 | <td>
|
274 | <strong>Required.</strong> Any supported <a href="https://developer.github.com/v3/#http-verbs">http verb</a>, case insensitive. <em>Defaults to <code>Get</code></em>.
|
275 | </td>
|
276 | </tr>
|
277 | <tr>
|
278 | <th align=left>
|
279 | <code>options.url</code>
|
280 | </th>
|
281 | <td>
|
282 | String
|
283 | </td>
|
284 | <td>
|
285 | <strong>Required.</strong> A path or full URL which may contain <code>:variable</code> or <code>{variable}</code> placeholders,
|
286 | e.g. <code>/orgs/:org/repos</code>. The <code>url</code> is parsed using <a href="https://github.com/bramstein/url-template">url-template</a>.
|
287 | </td>
|
288 | </tr>
|
289 | <tr>
|
290 | <th align=left>
|
291 | <code>options.data</code>
|
292 | </th>
|
293 | <td>
|
294 | Any
|
295 | </td>
|
296 | <td>
|
297 | Set request body directly instead of setting it to JSON based on additional parameters. See <a href="#data-parameter">"The `data` parameter"</a> below.
|
298 | </td>
|
299 | </tr>
|
300 | <tr>
|
301 | <th align=left>
|
302 | <code>options.request.agent</code>
|
303 | </th>
|
304 | <td>
|
305 | <a href="https://nodejs.org/api/http.html#http_class_http_agent">http(s).Agent</a> instance
|
306 | </td>
|
307 | <td>
|
308 | Node only. Useful for custom proxy, certificate, or dns lookup.
|
309 | </td>
|
310 | </tr>
|
311 | <tr>
|
312 | <th align=left>
|
313 | <code>options.request.fetch</code>
|
314 | </th>
|
315 | <td>
|
316 | Function
|
317 | </td>
|
318 | <td>
|
319 | Custom replacement for <a href="https://github.com/bitinn/node-fetch">built-in fetch method</a>. Useful for testing or request hooks.
|
320 | </td>
|
321 | </tr>
|
322 | <tr>
|
323 | <th align=left>
|
324 | <code>options.request.hook</code>
|
325 | </th>
|
326 | <td>
|
327 | Function
|
328 | </td>
|
329 | <td>
|
330 | Function with the signature <code>hook(request, endpointOptions)</code>, where <code>endpointOptions</code> are the parsed options as returned by <a href="https://github.com/octokit/endpoint.js#endpointmergeroute-options-or-endpointmergeoptions"><code>endpoint.merge()</code></a>, and <code>request</code> is <a href="https://github.com/octokit/request.js#request"><code>request()</code></a>. This option works great in conjuction with <a href="https://github.com/gr2m/before-after-hook">before-after-hook</a>.
|
331 | </td>
|
332 | </tr>
|
333 | <tr>
|
334 | <th align=left>
|
335 | <a name="options-request-signal"></a><code>options.request.signal</code>
|
336 | </th>
|
337 | <td>
|
338 | <a href="https://github.com/bitinn/node-fetch/tree/e996bdab73baf996cf2dbf25643c8fe2698c3249#request-cancellation-with-abortsignal">new AbortController().signal</a>
|
339 | </td>
|
340 | <td>
|
341 | Use an <code>AbortController</code> instance to cancel a request. In node you can only cancel streamed requests.
|
342 | </td>
|
343 | </tr>
|
344 | <tr>
|
345 | <th align=left>
|
346 | <code>options.request.timeout</code>
|
347 | </th>
|
348 | <td>
|
349 | Number
|
350 | </td>
|
351 | <td>
|
352 | Node only. Request/response timeout in ms, it resets on redirect. 0 to disable (OS limit applies). <a href="#options-request-signal">options.request.signal</a> is recommended instead.
|
353 | </td>
|
354 | </tr>
|
355 | </table>
|
356 |
|
357 | All other options except `options.request.*` will be passed depending on the `method` and `url` options.
|
358 |
|
359 | 1. If the option key is a placeholder in the `url`, it will be used as replacement. For example, if the passed options are `{url: '/orgs/:org/repos', org: 'foo'}` the returned `options.url` is `https://api.github.com/orgs/foo/repos`
|
360 | 2. If the `method` is `GET` or `HEAD`, the option is passed as query parameter
|
361 | 3. Otherwise the parameter is passed in the request body as JSON key.
|
362 |
|
363 | **Result**
|
364 |
|
365 | `request` returns a promise and resolves with 4 keys
|
366 |
|
367 | <table>
|
368 | <thead>
|
369 | <tr>
|
370 | <th align=left>
|
371 | key
|
372 | </th>
|
373 | <th align=left>
|
374 | type
|
375 | </th>
|
376 | <th align=left>
|
377 | description
|
378 | </th>
|
379 | </tr>
|
380 | </thead>
|
381 | <tr>
|
382 | <th align=left><code>status</code></th>
|
383 | <td>Integer</td>
|
384 | <td>Response status status</td>
|
385 | </tr>
|
386 | <tr>
|
387 | <th align=left><code>url</code></th>
|
388 | <td>String</td>
|
389 | <td>URL of response. If a request results in redirects, this is the final URL. You can send a <code>HEAD</code> request to retrieve it without loading the full response body.</td>
|
390 | </tr>
|
391 | <tr>
|
392 | <th align=left><code>headers</code></th>
|
393 | <td>Object</td>
|
394 | <td>All response headers</td>
|
395 | </tr>
|
396 | <tr>
|
397 | <th align=left><code>data</code></th>
|
398 | <td>Any</td>
|
399 | <td>The response body as returned from server. If the response is JSON then it will be parsed into an object</td>
|
400 | </tr>
|
401 | </table>
|
402 |
|
403 | If an error occurs, the `error` instance has additional properties to help with debugging
|
404 |
|
405 | - `error.status` The http response status code
|
406 | - `error.headers` The http response headers as an object
|
407 | - `error.request` The request options such as `method`, `url` and `data`
|
408 |
|
409 | ## `request.defaults()`
|
410 |
|
411 | Override or set default options. Example:
|
412 |
|
413 | ```js
|
414 | const myrequest = require("@octokit/request").defaults({
|
415 | baseUrl: "https://github-enterprise.acme-inc.com/api/v3",
|
416 | headers: {
|
417 | "user-agent": "myApp/1.2.3",
|
418 | authorization: `token 0000000000000000000000000000000000000001`
|
419 | },
|
420 | org: "my-project",
|
421 | per_page: 100
|
422 | });
|
423 |
|
424 | myrequest(`GET /orgs/:org/repos`);
|
425 | ```
|
426 |
|
427 | You can call `.defaults()` again on the returned method, the defaults will cascade.
|
428 |
|
429 | ```js
|
430 | const myProjectRequest = request.defaults({
|
431 | baseUrl: "https://github-enterprise.acme-inc.com/api/v3",
|
432 | headers: {
|
433 | "user-agent": "myApp/1.2.3"
|
434 | },
|
435 | org: "my-project"
|
436 | });
|
437 | const myProjectRequestWithAuth = myProjectRequest.defaults({
|
438 | headers: {
|
439 | authorization: `token 0000000000000000000000000000000000000001`
|
440 | }
|
441 | });
|
442 | ```
|
443 |
|
444 | `myProjectRequest` now defaults the `baseUrl`, `headers['user-agent']`,
|
445 | `org` and `headers['authorization']` on top of `headers['accept']` that is set
|
446 | by the global default.
|
447 |
|
448 | ## `request.endpoint`
|
449 |
|
450 | See https://github.com/octokit/endpoint.js. Example
|
451 |
|
452 | ```js
|
453 | const options = request.endpoint("GET /orgs/:org/repos", {
|
454 | org: "my-project",
|
455 | type: "private"
|
456 | });
|
457 |
|
458 | // {
|
459 | // method: 'GET',
|
460 | // url: 'https://api.github.com/orgs/my-project/repos?type=private',
|
461 | // headers: {
|
462 | // accept: 'application/vnd.github.v3+json',
|
463 | // authorization: 'token 0000000000000000000000000000000000000001',
|
464 | // 'user-agent': 'octokit/endpoint.js v1.2.3'
|
465 | // }
|
466 | // }
|
467 | ```
|
468 |
|
469 | All of the [`@octokit/endpoint`](https://github.com/octokit/endpoint.js) API can be used:
|
470 |
|
471 | - [`octokitRequest.endpoint()`](#endpoint)
|
472 | - [`octokitRequest.endpoint.defaults()`](#endpointdefaults)
|
473 | - [`octokitRequest.endpoint.merge()`](#endpointdefaults)
|
474 | - [`octokitRequest.endpoint.parse()`](#endpointmerge)
|
475 |
|
476 | ## Special cases
|
477 |
|
478 | <a name="data-parameter"></a>
|
479 |
|
480 | ### The `data` parameter – set request body directly
|
481 |
|
482 | Some endpoints such as [Render a Markdown document in raw mode](https://developer.github.com/v3/markdown/#render-a-markdown-document-in-raw-mode) don’t have parameters that are sent as request body keys, instead the request body needs to be set directly. In these cases, set the `data` parameter.
|
483 |
|
484 | ```js
|
485 | const response = await request("POST /markdown/raw", {
|
486 | data: "Hello world github/linguist#1 **cool**, and #1!",
|
487 | headers: {
|
488 | accept: "text/html;charset=utf-8",
|
489 | "content-type": "text/plain"
|
490 | }
|
491 | });
|
492 |
|
493 | // Request is sent as
|
494 | //
|
495 | // {
|
496 | // method: 'post',
|
497 | // url: 'https://api.github.com/markdown/raw',
|
498 | // headers: {
|
499 | // accept: 'text/html;charset=utf-8',
|
500 | // 'content-type': 'text/plain',
|
501 | // 'user-agent': userAgent
|
502 | // },
|
503 | // body: 'Hello world github/linguist#1 **cool**, and #1!'
|
504 | // }
|
505 | //
|
506 | // not as
|
507 | //
|
508 | // {
|
509 | // ...
|
510 | // body: '{"data": "Hello world github/linguist#1 **cool**, and #1!"}'
|
511 | // }
|
512 | ```
|
513 |
|
514 | ### Set parameters for both the URL/query and the request body
|
515 |
|
516 | There are API endpoints that accept both query parameters as well as a body. In that case you need to add the query parameters as templates to `options.url`, as defined in the [RFC 6570 URI Template specification](https://tools.ietf.org/html/rfc6570).
|
517 |
|
518 | Example
|
519 |
|
520 | ```js
|
521 | request(
|
522 | "POST https://uploads.github.com/repos/octocat/Hello-World/releases/1/assets{?name,label}",
|
523 | {
|
524 | name: "example.zip",
|
525 | label: "short description",
|
526 | headers: {
|
527 | "content-type": "text/plain",
|
528 | "content-length": 14,
|
529 | authorization: `token 0000000000000000000000000000000000000001`
|
530 | },
|
531 | data: "Hello, world!"
|
532 | }
|
533 | );
|
534 | ```
|
535 |
|
536 | ## LICENSE
|
537 |
|
538 | [MIT](LICENSE)
|