mirror of
https://github.com/JamesIves/github-pages-deploy-action.git
synced 2023-12-15 20:03:39 +08:00
399 lines
8.9 KiB
Markdown
399 lines
8.9 KiB
Markdown
# graphql.js
|
||
|
||
> GitHub GraphQL API client for browsers and Node
|
||
|
||
[![@latest](https://img.shields.io/npm/v/@octokit/graphql.svg)](https://www.npmjs.com/package/@octokit/graphql)
|
||
[![Build Status](https://github.com/octokit/graphql.js/workflows/Test/badge.svg)](https://github.com/octokit/graphql.js/actions?query=workflow%3ATest+branch%3Amaster)
|
||
|
||
<!-- toc -->
|
||
|
||
- [Usage](#usage)
|
||
- [Send a simple query](#send-a-simple-query)
|
||
- [Authentication](#authentication)
|
||
- [Variables](#variables)
|
||
- [Pass query together with headers and variables](#pass-query-together-with-headers-and-variables)
|
||
- [Use with GitHub Enterprise](#use-with-github-enterprise)
|
||
- [Use custom `@octokit/request` instance](#use-custom-octokitrequest-instance)
|
||
- [TypeScript](#typescript)
|
||
- [Additional Types](#additional-types)
|
||
- [Errors](#errors)
|
||
- [Partial responses](#partial-responses)
|
||
- [Writing tests](#writing-tests)
|
||
- [License](#license)
|
||
|
||
<!-- tocstop -->
|
||
|
||
## Usage
|
||
|
||
<table>
|
||
<tbody valign=top align=left>
|
||
<tr><th>
|
||
Browsers
|
||
</th><td width=100%>
|
||
|
||
Load `@octokit/graphql` directly from [cdn.skypack.dev](https://cdn.skypack.dev)
|
||
|
||
```html
|
||
<script type="module">
|
||
import { graphql } from "https://cdn.skypack.dev/@octokit/graphql";
|
||
</script>
|
||
```
|
||
|
||
</td></tr>
|
||
<tr><th>
|
||
Node
|
||
</th><td>
|
||
|
||
Install with <code>npm install @octokit/graphql</code>
|
||
|
||
```js
|
||
const { graphql } = require("@octokit/graphql");
|
||
// or: import { graphql } from "@octokit/graphql";
|
||
```
|
||
|
||
</td></tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
### Send a simple query
|
||
|
||
```js
|
||
const { repository } = await graphql(
|
||
`
|
||
{
|
||
repository(owner: "octokit", name: "graphql.js") {
|
||
issues(last: 3) {
|
||
edges {
|
||
node {
|
||
title
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
`,
|
||
{
|
||
headers: {
|
||
authorization: `token secret123`,
|
||
},
|
||
}
|
||
);
|
||
```
|
||
|
||
### Authentication
|
||
|
||
The simplest way to authenticate a request is to set the `Authorization` header, e.g. to a [personal access token](https://github.com/settings/tokens/).
|
||
|
||
```js
|
||
const graphqlWithAuth = graphql.defaults({
|
||
headers: {
|
||
authorization: `token secret123`,
|
||
},
|
||
});
|
||
const { repository } = await graphqlWithAuth(`
|
||
{
|
||
repository(owner: "octokit", name: "graphql.js") {
|
||
issues(last: 3) {
|
||
edges {
|
||
node {
|
||
title
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
`);
|
||
```
|
||
|
||
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).
|
||
|
||
```js
|
||
const { createAppAuth } = require("@octokit/auth-app");
|
||
const auth = createAppAuth({
|
||
id: process.env.APP_ID,
|
||
privateKey: process.env.PRIVATE_KEY,
|
||
installationId: 123,
|
||
});
|
||
const graphqlWithAuth = graphql.defaults({
|
||
request: {
|
||
hook: auth.hook,
|
||
},
|
||
});
|
||
|
||
const { repository } = await graphqlWithAuth(
|
||
`{
|
||
repository(owner: "octokit", name: "graphql.js") {
|
||
issues(last: 3) {
|
||
edges {
|
||
node {
|
||
title
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}`
|
||
);
|
||
```
|
||
|
||
### Variables
|
||
|
||
⚠️ 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:
|
||
|
||
```js
|
||
const { lastIssues } = await graphql(
|
||
`
|
||
query lastIssues($owner: String!, $repo: String!, $num: Int = 3) {
|
||
repository(owner: $owner, name: $repo) {
|
||
issues(last: $num) {
|
||
edges {
|
||
node {
|
||
title
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
`,
|
||
{
|
||
owner: "octokit",
|
||
repo: "graphql.js",
|
||
headers: {
|
||
authorization: `token secret123`,
|
||
},
|
||
}
|
||
);
|
||
```
|
||
|
||
### Pass query together with headers and variables
|
||
|
||
```js
|
||
const { graphql } = require("@octokit/graphql");
|
||
const { lastIssues } = await graphql({
|
||
query: `query lastIssues($owner: String!, $repo: String!, $num: Int = 3) {
|
||
repository(owner:$owner, name:$repo) {
|
||
issues(last:$num) {
|
||
edges {
|
||
node {
|
||
title
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}`,
|
||
owner: "octokit",
|
||
repo: "graphql.js",
|
||
headers: {
|
||
authorization: `token secret123`,
|
||
},
|
||
});
|
||
```
|
||
|
||
### Use with GitHub Enterprise
|
||
|
||
```js
|
||
let { graphql } = require("@octokit/graphql");
|
||
graphql = graphql.defaults({
|
||
baseUrl: "https://github-enterprise.acme-inc.com/api",
|
||
headers: {
|
||
authorization: `token secret123`,
|
||
},
|
||
});
|
||
const { repository } = await graphql(`
|
||
{
|
||
repository(owner: "acme-project", name: "acme-repo") {
|
||
issues(last: 3) {
|
||
edges {
|
||
node {
|
||
title
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
`);
|
||
```
|
||
|
||
### Use custom `@octokit/request` instance
|
||
|
||
```js
|
||
const { request } = require("@octokit/request");
|
||
const { withCustomRequest } = require("@octokit/graphql");
|
||
|
||
let requestCounter = 0;
|
||
const myRequest = request.defaults({
|
||
headers: {
|
||
authentication: "token secret123",
|
||
},
|
||
request: {
|
||
hook(request, options) {
|
||
requestCounter++;
|
||
return request(options);
|
||
},
|
||
},
|
||
});
|
||
const myGraphql = withCustomRequest(myRequest);
|
||
await request("/");
|
||
await myGraphql(`
|
||
{
|
||
repository(owner: "acme-project", name: "acme-repo") {
|
||
issues(last: 3) {
|
||
edges {
|
||
node {
|
||
title
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
`);
|
||
// requestCounter is now 2
|
||
```
|
||
|
||
## TypeScript
|
||
|
||
`@octokit/graphql` is exposing proper types for its usage with TypeScript projects.
|
||
|
||
### Additional Types
|
||
|
||
Additionally, `GraphQlQueryResponseData` has been exposed to users:
|
||
|
||
```ts
|
||
import type { GraphQlQueryResponseData } from "@octokit/graphql";
|
||
```
|
||
|
||
## Errors
|
||
|
||
In case of a GraphQL error, `error.message` is set to the first error from the response’s `errors` array. All errors can be accessed at `error.errors`. `error.request` has the request options such as query, variables and headers set for easier debugging.
|
||
|
||
```js
|
||
let { graphql } = require("@octokit/graphql");
|
||
graphqlt = graphql.defaults({
|
||
headers: {
|
||
authorization: `token secret123`,
|
||
},
|
||
});
|
||
const query = `{
|
||
viewer {
|
||
bioHtml
|
||
}
|
||
}`;
|
||
|
||
try {
|
||
const result = await graphql(query);
|
||
} catch (error) {
|
||
// server responds with
|
||
// {
|
||
// "data": null,
|
||
// "errors": [{
|
||
// "message": "Field 'bioHtml' doesn't exist on type 'User'",
|
||
// "locations": [{
|
||
// "line": 3,
|
||
// "column": 5
|
||
// }]
|
||
// }]
|
||
// }
|
||
|
||
console.log("Request failed:", error.request); // { query, variables: {}, headers: { authorization: 'token secret123' } }
|
||
console.log(error.message); // Field 'bioHtml' doesn't exist on type 'User'
|
||
}
|
||
```
|
||
|
||
## Partial responses
|
||
|
||
A 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`
|
||
|
||
```js
|
||
let { graphql } = require("@octokit/graphql");
|
||
graphql = graphql.defaults({
|
||
headers: {
|
||
authorization: `token secret123`,
|
||
},
|
||
});
|
||
const query = `{
|
||
repository(name: "probot", owner: "probot") {
|
||
name
|
||
ref(qualifiedName: "master") {
|
||
target {
|
||
... on Commit {
|
||
history(first: 25, after: "invalid cursor") {
|
||
nodes {
|
||
message
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}`;
|
||
|
||
try {
|
||
const result = await graphql(query);
|
||
} catch (error) {
|
||
// server responds with
|
||
// {
|
||
// "data": {
|
||
// "repository": {
|
||
// "name": "probot",
|
||
// "ref": null
|
||
// }
|
||
// },
|
||
// "errors": [
|
||
// {
|
||
// "type": "INVALID_CURSOR_ARGUMENTS",
|
||
// "path": [
|
||
// "repository",
|
||
// "ref",
|
||
// "target",
|
||
// "history"
|
||
// ],
|
||
// "locations": [
|
||
// {
|
||
// "line": 7,
|
||
// "column": 11
|
||
// }
|
||
// ],
|
||
// "message": "`invalid cursor` does not appear to be a valid cursor."
|
||
// }
|
||
// ]
|
||
// }
|
||
|
||
console.log("Request failed:", error.request); // { query, variables: {}, headers: { authorization: 'token secret123' } }
|
||
console.log(error.message); // `invalid cursor` does not appear to be a valid cursor.
|
||
console.log(error.data); // { repository: { name: 'probot', ref: null } }
|
||
}
|
||
```
|
||
|
||
## Writing tests
|
||
|
||
You 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
|
||
|
||
```js
|
||
const assert = require("assert");
|
||
const fetchMock = require("fetch-mock/es5/server");
|
||
|
||
const { graphql } = require("@octokit/graphql");
|
||
|
||
graphql("{ viewer { login } }", {
|
||
headers: {
|
||
authorization: "token secret123",
|
||
},
|
||
request: {
|
||
fetch: fetchMock
|
||
.sandbox()
|
||
.post("https://api.github.com/graphql", (url, options) => {
|
||
assert.strictEqual(options.headers.authorization, "token secret123");
|
||
assert.strictEqual(
|
||
options.body,
|
||
'{"query":"{ viewer { login } }"}',
|
||
"Sends correct query"
|
||
);
|
||
return { data: {} };
|
||
}),
|
||
},
|
||
});
|
||
```
|
||
|
||
## License
|
||
|
||
[MIT](LICENSE)
|