├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── index.js ├── package.json └── test └── graphql-fetch.unit.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 18 | .grunt 19 | 20 | # node-waf configuration 21 | .lock-wscript 22 | 23 | # Compiled binary addons (http://nodejs.org/api/addons.html) 24 | build/Release 25 | 26 | # Dependency directory 27 | node_modules 28 | 29 | # Optional npm cache directory 30 | .npm 31 | 32 | # Optional REPL history 33 | .node_repl_history 34 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.10" 4 | - "0.12" 5 | - "4" 6 | - "5" 7 | sudo: false -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Tejesh Mehta 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # graphql-fetch [![Build Status](https://travis-ci.org/tjmehta/graphql-fetch.svg?branch=master)](https://travis-ci.org/tjmehta/graphql-fetch) [![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat)](http://standardjs.com/) 2 | Thin GraphQL client powered by fetch. 3 | 4 | # Installation 5 | ```bash 6 | npm i --save graphql-fetch 7 | ``` 8 | 9 | # Usage 10 | ```js 11 | var fetch = require('graphql-fetch')('http://domain.com/graphql') 12 | 13 | var query = ` 14 | query q (id: String!) { 15 | user(id: $id) { 16 | id, 17 | email, 18 | name 19 | } 20 | } 21 | ` 22 | var queryVars = { 23 | id: 'abcdef' 24 | } 25 | var opts = { 26 | // custom fetch options 27 | } 28 | 29 | /** 30 | * @param {Query} query graphql query 31 | * @param {Object} [vars] graphql query args, optional 32 | * @param {Object} [opts] fetch options, optional 33 | */ 34 | fetch(query, queryVars, opts).then(function (results) { 35 | if (results.errors) { 36 | //... 37 | return 38 | } 39 | var user = result.data.user 40 | //... 41 | }) 42 | ``` 43 | 44 | # Notes 45 | * Uses [isomorphic-fetch](https://github.com/matthew-andrews/isomorphic-fetch) under the hood, which makes `fetch`, `Headers`, `Request`, and `Response` globally available. 46 | 47 | # License 48 | MIT -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | require('isomorphic-fetch') // injects globals: fetch, Headers, Request, Response 3 | 4 | var assert = require('assert') 5 | var defaults = require('101/defaults') 6 | 7 | /** 8 | * create a graphql-fetch bound to a specific graphql url 9 | * @param {String} graphqlUrl 10 | * @return {Function} graphqlFetch 11 | */ 12 | module.exports = function factory (graphqlUrl) { 13 | /** 14 | * graphql fetch - fetch w/ smart defaults for graphql requests 15 | * @param {Query} query graphql query 16 | * @param {Object} vars graphql query args 17 | * @param {Object} opts fetch options 18 | * @return {FetchPromise} fetch promise 19 | */ 20 | return function graphqlFetch (query, vars, opts) { 21 | assert(query, 'query is required') 22 | vars = vars || {} 23 | opts = opts || {} 24 | opts.body = JSON.stringify({ 25 | query: query, 26 | variables: vars 27 | }) 28 | // default opts 29 | defaults(opts, { 30 | method: 'POST', 31 | headers: new Headers() 32 | }) 33 | // default headers 34 | var headers = opts.headers 35 | if (!headers.get('content-type')) { 36 | opts.headers.append('content-type', 'application/json') 37 | } 38 | return fetch(graphqlUrl, opts).then(function (res) { 39 | return res.json() 40 | }) 41 | } 42 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "graphql-fetch", 3 | "version": "1.0.1", 4 | "description": "Thin GraphQL client powered by fetch", 5 | "main": "index.js", 6 | "dependencies": { 7 | "101": "^1.5.0", 8 | "isomorphic-fetch": "^2.2.1" 9 | }, 10 | "devDependencies": { 11 | "code": "^1.0.0", 12 | "istanbul": "^0.4.3", 13 | "mocha": "^2.5.3", 14 | "sinon": "^1.17.4", 15 | "sinon-as-promised": "^4.0.0" 16 | }, 17 | "scripts": { 18 | "test": "istanbul cover _mocha ./test && istanbul --text check-coverage --statements 100 --functions 100 --branches 100 --lines 100" 19 | }, 20 | "repository": { 21 | "type": "git", 22 | "url": "https://github.com/tjmehta/graphql-fetch.git" 23 | }, 24 | "keywords": [ 25 | "graphql", 26 | "client", 27 | "fetch", 28 | "request", 29 | "query", 30 | "mutation" 31 | ], 32 | "author": "Tejesh Mehta", 33 | "license": "MIT", 34 | "bugs": { 35 | "url": "https://github.com/tjmehta/graphql-fetch/issues" 36 | }, 37 | "homepage": "https://github.com/tjmehta/graphql-fetch" 38 | } 39 | -------------------------------------------------------------------------------- /test/graphql-fetch.unit.js: -------------------------------------------------------------------------------- 1 | var afterEach = global.afterEach 2 | var before = global.before 3 | var beforeEach = global.beforeEach 4 | var describe = global.describe 5 | var it = global.it 6 | 7 | var expect = require('code').expect 8 | var sinon = require('sinon') 9 | require('sinon-as-promised') 10 | 11 | var graphqlUrl = 'http://host/graphql' 12 | var graphqlFetch = require('../index.js')(graphqlUrl) 13 | 14 | describe('graphql-fetch', function () { 15 | before(function () { 16 | expect(global.fetch).to.exist() 17 | expect(global.Headers).to.exist() 18 | expect(global.Response).to.exist() 19 | expect(global.Request).to.exist() 20 | }) 21 | beforeEach(function () { 22 | this.json = {} 23 | this.result = { 24 | json: sinon.stub().returns(this.json) 25 | } 26 | sinon.stub(global, 'fetch').resolves(this.result) 27 | this.query = "query { user { username } }" 28 | this.vars = { foo: 1 } 29 | this.headers = new Headers() 30 | this.headers.append('authorization', 'token abcdef') 31 | this.headers.append('content-type', 'text/html') 32 | this.opts = { 33 | credentials: true, 34 | headers: this.headers 35 | } 36 | }) 37 | afterEach(function () { 38 | global.fetch.restore() 39 | }) 40 | 41 | it('should make a graphql request', function () { 42 | var self = this 43 | return graphqlFetch(this.query).then(function (result) { 44 | sinon.assert.calledOnce(global.fetch) 45 | sinon.assert.calledWith(global.fetch, graphqlUrl, sinon.match(assertOpts)) 46 | sinon.assert.calledOnce(self.result.json) 47 | expect(result).to.equal(self.json) 48 | function assertOpts (opts) { 49 | expect(opts.body).to.equal(JSON.stringify({ 50 | query: self.query, 51 | variables: {} 52 | })) 53 | expect(opts.method).to.equal('POST') 54 | expect(opts.headers).to.be.an.instanceOf(Headers) 55 | expect(opts.headers.get('content-type')).to.equal('application/json') 56 | return true 57 | } 58 | }) 59 | }) 60 | it('should make a graphql request w/ vars and fetch options', function () { 61 | var self = this 62 | return graphqlFetch(this.query, this.vars, this.opts).then(function (result) { 63 | sinon.assert.calledOnce(global.fetch) 64 | sinon.assert.calledWith(global.fetch, graphqlUrl, sinon.match(assertOpts)) 65 | sinon.assert.calledOnce(self.result.json) 66 | expect(result).to.equal(self.json) 67 | function assertOpts (opts) { 68 | expect(opts.body).to.equal(JSON.stringify({ 69 | query: self.query, 70 | variables: self.vars 71 | })) 72 | expect(opts.method).to.equal('POST') 73 | expect(opts.headers).to.equal(self.headers) 74 | expect(opts.headers.get('content-type')).to.equal('text/html') 75 | return true 76 | } 77 | }) 78 | }) 79 | }) --------------------------------------------------------------------------------