├── .github └── workflows │ └── node.js.yml ├── README.md ├── package.json ├── test.js ├── index.js └── .gitignore /.github/workflows/node.js.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | strategy: 15 | matrix: 16 | node-version: [10.x, 12.x, 14.x] 17 | 18 | steps: 19 | - uses: actions/checkout@v2 20 | - name: Use Node.js ${{ matrix.node-version }} 21 | uses: actions/setup-node@v1 22 | with: 23 | node-version: ${{ matrix.node-version }} 24 | - run: npm ci 25 | - run: npm test 26 | env: 27 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![CI](https://github.com/imsky/github-graphql-client/workflows/CI/badge.svg) 2 | 3 | # GitHub GraphQL Client 4 | 5 | Node.js client for the GitHub GraphQL API with zero dependencies. 6 | 7 | ## Usage 8 | 9 | ```js 10 | var client = require('github-graphql-client'); 11 | 12 | var request = client({ 13 | token: 'your GitHub token', 14 | query: 'your GraphQL query' 15 | }, function (err, res) { 16 | if (err) { 17 | // handle errors 18 | } else { 19 | // handle results 20 | } 21 | }); 22 | 23 | ``` 24 | 25 | ## License 26 | 27 | [MIT](http://opensource.org/licenses/MIT) 28 | 29 | ## Credits 30 | 31 | Made by [Ivan Malopinsky](http://imsky.co). 32 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "github-graphql-client", 3 | "version": "1.0.0", 4 | "description": "Node.js client for the GitHub GraphQL API with zero dependencies", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "ava" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+ssh://git@github.com/imsky/github-graphql-client.git" 12 | }, 13 | "keywords": [ 14 | "github", 15 | "graphql", 16 | "client" 17 | ], 18 | "author": "Ivan Malopinsky", 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/imsky/github-graphql-client/issues" 22 | }, 23 | "homepage": "https://github.com/imsky/github-graphql-client#readme", 24 | "devDependencies": { 25 | "ava": "^3.10.1" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | // Require modules 2 | const test = require('ava'); 3 | const client = require('./'); 4 | 5 | // Define test queries 6 | const queryWorking = ` 7 | query { 8 | repository(name: "github-graphql-client", owner: "imsky") { 9 | name 10 | } 11 | } 12 | `; 13 | 14 | const queryBroken = ` 15 | qery { 16 | repository(name: "github-graphql-client", owner: "imsky") { 17 | name 18 | } 19 | } 20 | `; 21 | 22 | test('fails without token', t => { 23 | const error = t.throws(() => { 24 | client({ query: '' }); 25 | }, { instanceOf: Error }); 26 | 27 | t.is(error.message, 'Missing GitHub token'); 28 | }); 29 | 30 | test('fails without query', t => { 31 | const error = t.throws(() => { 32 | client({ token: process.env.GITHUB_TOKEN }); 33 | }, { instanceOf: Error }); 34 | 35 | t.is(error.message, 'Missing query'); 36 | }); 37 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var https = require('https'); 2 | 3 | function Request (options, cb) { 4 | var token = options.token; 5 | var query = options.query; 6 | var variables = options.variables || {}; 7 | 8 | if (!token) { 9 | throw Error('Missing GitHub token'); 10 | } else if (!query) { 11 | throw Error('Missing query'); 12 | } 13 | 14 | var payload = { 15 | 'query': query, 16 | 'variables': variables 17 | }; 18 | 19 | var payloadString = JSON.stringify(payload); 20 | 21 | var req = https.request({ 22 | 'hostname': 'api.github.com', 23 | 'path': '/graphql', 24 | 'method': 'POST', 25 | 'headers': { 26 | 'Content-Type': 'application/json', 27 | 'Content-Length': payloadString.length, 28 | 'Authorization': 'bearer ' + token, 29 | 'User-Agent': 'GitHub GraphQL Client' 30 | } 31 | }, function (res) { 32 | var chunks = []; 33 | 34 | res.on('data', function (chunk) { 35 | chunks.push(chunk.toString('utf8')); 36 | }); 37 | 38 | res.on('end', function () { 39 | if (res.statusCode !== 200) { 40 | cb(res.statusMessage); 41 | return; 42 | } 43 | 44 | var response = chunks.join(''); 45 | var json; 46 | 47 | try { 48 | json = JSON.parse(response); 49 | } catch (e) { 50 | cb('GitHub GraphQL API response is not able to be parsed as JSON'); 51 | return; 52 | } 53 | 54 | if (!json.data) { 55 | if (json.errors) { 56 | cb(json.errors); 57 | return; 58 | } else { 59 | cb('Unknown GraphQL error'); 60 | return; 61 | } 62 | } 63 | 64 | cb(null, json); 65 | }); 66 | }); 67 | 68 | req.on('error', function (err) { cb(err); }); 69 | req.write(payloadString); 70 | req.end(); 71 | } 72 | 73 | module.exports = Request; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.toptal.com/developers/gitignore/api/node 3 | # Edit at https://www.toptal.com/developers/gitignore?templates=node 4 | 5 | ### Node ### 6 | # Logs 7 | logs 8 | *.log 9 | npm-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* 12 | lerna-debug.log* 13 | 14 | # Diagnostic reports (https://nodejs.org/api/report.html) 15 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 16 | 17 | # Runtime data 18 | pids 19 | *.pid 20 | *.seed 21 | *.pid.lock 22 | 23 | # Directory for instrumented libs generated by jscoverage/JSCover 24 | lib-cov 25 | 26 | # Coverage directory used by tools like istanbul 27 | coverage 28 | *.lcov 29 | 30 | # nyc test coverage 31 | .nyc_output 32 | 33 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 34 | .grunt 35 | 36 | # Bower dependency directory (https://bower.io/) 37 | bower_components 38 | 39 | # node-waf configuration 40 | .lock-wscript 41 | 42 | # Compiled binary addons (https://nodejs.org/api/addons.html) 43 | build/Release 44 | 45 | # Dependency directories 46 | node_modules/ 47 | jspm_packages/ 48 | 49 | # TypeScript v1 declaration files 50 | typings/ 51 | 52 | # TypeScript cache 53 | *.tsbuildinfo 54 | 55 | # Optional npm cache directory 56 | .npm 57 | 58 | # Optional eslint cache 59 | .eslintcache 60 | 61 | # Microbundle cache 62 | .rpt2_cache/ 63 | .rts2_cache_cjs/ 64 | .rts2_cache_es/ 65 | .rts2_cache_umd/ 66 | 67 | # Optional REPL history 68 | .node_repl_history 69 | 70 | # Output of 'npm pack' 71 | *.tgz 72 | 73 | # Yarn Integrity file 74 | .yarn-integrity 75 | 76 | # dotenv environment variables file 77 | .env 78 | .env.test 79 | 80 | # parcel-bundler cache (https://parceljs.org/) 81 | .cache 82 | 83 | # Next.js build output 84 | .next 85 | 86 | # Nuxt.js build / generate output 87 | .nuxt 88 | dist 89 | 90 | # Gatsby files 91 | .cache/ 92 | # Comment in the public line in if your project uses Gatsby and not Next.js 93 | # https://nextjs.org/blog/next-9-1#public-directory-support 94 | # public 95 | 96 | # vuepress build output 97 | .vuepress/dist 98 | 99 | # Serverless directories 100 | .serverless/ 101 | 102 | # FuseBox cache 103 | .fusebox/ 104 | 105 | # DynamoDB Local files 106 | .dynamodb/ 107 | 108 | # TernJS port file 109 | .tern-port 110 | 111 | # Stores VSCode versions used for testing VSCode extensions 112 | .vscode-test 113 | 114 | # End of https://www.toptal.com/developers/gitignore/api/node 115 | --------------------------------------------------------------------------------