GithubHelp home page GithubHelp logo

f / graphql.js Goto Github PK

View Code? Open in Web Editor NEW
2.3K 31.0 89.0 7.21 MB

A Simple and Isomorphic GraphQL Client for JavaScript

License: MIT License

JavaScript 90.01% HTML 9.99%
graphql graphql-client relay

graphql.js's Introduction

graphql.js: lightweight graphql client

Lightest GraphQL client with intelligent features.


Bower version NPM version Build Status

Originally inspired by Robert Mosolgo's blog post

Features

  • Too small, 4k gzipped.
  • No dependencies, plain vanilla JavaScript.
  • Plug & Play.
  • Isomorphic.
  • Runs on most browsers.
  • You don't need to install Node.js ecosystem on your computer.
  • Query merging to reduce request number.

Overview

GraphQL is based on a very simple HTTP transaction, which sends a request to an endpoint with query and variables.

Many libraries require complex stacks to make that simple request. In any project you don't use React, Relay, you'll need a simpler client which manages your query and makes a simple request.

// Connect...
var graph = graphql("/graphql")

// Prepare...
graph.fragment({
  user: `on User {
    id,
    name
  }`
})

const allUsers = graph(`query { allUsers { ...user } }`)

const createUser = graph(`mutation (@autodeclare) {
  createUser($firstName, $lastName) { ...user }
}`)

await createUser({
  firstName: "John",
  lastName: "Doe"
})

const users = await allUsers()

console.log(users)
// {
//   "allUsers": [{ "id": 1, "name": "John Doe" }]
// }

Installation

You can download graphql.js directly, or you can use Bower or NPM.

CDN

<script src="//cdn.jsdelivr.net/npm/[email protected]/graphql.min.js"></script>

Download for Browser

Using NPM

npm install graphql.js --save

# or

yarn add graphql.js

Using with Rails Asset Pipeline

You can use GraphQL.js with Rails Asset Pipeline using graphqljs-rails.

Using

GraphQL.js is isomorphic. You can use it in both browser and Node.js.

Use in Browser

<script src="/path/to/graphql.js"></script>

Use in Node.js

var graphql = require('graphql.js')

Or using import

import graphql from 'graphql.js'

Connection

Create a simple connection to your GraphQL endpoint.

var graph = graphql("http://localhost:3000/graphql", {
  method: "POST", // POST by default.
  headers: {
    // headers
    "Access-Token": "some-access-token"
    // OR "Access-Token": () => "some-access-token"
  },
  fragments: {
    // fragments, you don't need to say `fragment name`.
    auth: "on User { token }",
    error: "on Error { messages }"
  }
})

Executing Queries and Mutations

graph will be a simple function that accepts query and variables as parameters.

graph(`query ($email: String!, $password: String!) {
  auth(email: $email, password: $password) {
    ... auth # if you use any fragment, it will be added to the query.
    ... error
  }
}`, {
  email: "[email protected]",
  password: "my-super-password"
}).then(function (response) {
  // response is originally response.data of query result
  console.log(response)
}).catch(function (error) {
  // response is originally response.errors of query result
  console.log(error)
})

Prepare Query for Lazy Execution

You can prepare queries for lazy execution. This will allow you to reuse your queries with different variables without any hassle.

var login = graph(`query ($email: String!, $password: String!) {
  auth(email: $email, password: $password) {
    ... on User {
      token
    }
  }
}`)

// Call it later...
login({
  email: "[email protected]",
  password: "my-super-password"
})

Direct Execution with .run and ES6 Template Tag

If your query doesn't need any variables, it will generate a lazy execution query by default. If you want to run your query immediately, you have three following options:

// 1st option. create and run function.
graph(`...`)()
graph.query(`...`)()
graph.mutate(`...`)()
//...

// 2nd option. create and run function with `run` method.
graph.run(`...`)
graph.query.run(`...`)
graph.mutate.run(`...`)

// 3rd option. create and run function with template tag.
graph`...`
graph.query`...`
graph.mutate`...`

I don't recommend using this. Using it too much may break DRY. Use lazy execution as much as possible.

Prefix Helpers

You can prefix your queries by simply calling helper methods: .query, .mutate or .subscribe

var login = graph.query(`($email: String!, $password: String!) {
  auth(email: $email, password: $password) {
    ... on User {
      token
    }
  }
}`)

var increment = graph.mutate`increment { state }`
var onIncrement = graph.subscribe`onIncrement { state }`

Automatic Declaring with @autodeclare or {declare: true}

Declaring primitive-typed (String, Int, Float, Boolean) variables in query were a little bothering to me. That's why I added an @autodeclare keyword or {declare: true} setting to the processor. It detects types from the variables and declares them in query automatically.

var login = graph.query(`(@autodeclare) {
  auth(email: $email, password: $password) {
    ... on User {
      token
    }
  }
}`)

login({
  email: "[email protected]", // It's String! obviously.
  password: "my-super-password" // It is, too.
})

This will create following query:

query ($email: String!, $password: String!) {
  auth(email: $email, password: $password) {
    ... on User {
      token
    }
  }
}

You can also pass {declare: true} option to the .query, .mutate and .subscribe helper:

var login = graph.query(`auth(email: $email, password: $password) {
  ... on User {
    token
  }
}`, {declare: true})

This will also create the same query above.

Detecting IDs

Variable names with matching /_id/i pattern will be declared as ID type. Following examples will be declared as IDs:

  • id: 1 will be declared as $id: ID!
  • post_id: "123af" will be declared as $post_id: ID!
  • postID: 3 will be declared as $postID: ID!
  • postId: 4 will be declared as $postId: ID!

You can disable auto ID declaration by adding an ! to the end of the variable name:

  • id!: 1 will be declared as $id: Int!
  • post_id!: "123af" will be declared as $post_id: String!

And, explicitly given types are prioritized.

  • postID!CustomId: 3 will be declared as $postID: CustomId!
  • postId!UUID: 4 will be declared as $postId: UUID!
var userById = graph.query(`(@autodeclare) {
  user(id: $id) {
    email
  }
}`)

userById({
  id: 15
})

The example above will generate following query:

query ($id: ID!) {
  user(id: $id) {
    email
  }
}

Solving Integer and Float Problem

Let's say you have a rating query that accepts an argument with a Float argument named rating. GraphQL.js will declare 10 value as Integer since it casts using value % 1 === 0 ? 'Int' : 'Float' check.

var rate = graph.query(`(@autodeclare) {
  rating(rating: $rating) {
    rating
  }
}`)

rate({
  rating: 10
})

In this case, you must use ! mark to force your type to be Float as below:

rate({
  "rating!Float": 10
})

This will bypass the casting and declare rating as Float.

Advanced Auto Declaring

Beside you can pass {declare: true} to helpers:

graph.query("auth(email: $email, password: $password) { token }", {declare: true})

Also you can enable auto declaration to run by default using alwaysAutodeclare setting.

var graph = graphql("http://localhost:3000/graphql", {
  alwaysAutodeclare: true
})

After you enable alwaysAutodeclare option, your methods will try to detect types of variables and declare them.

// When alwaysAutodeclare is true, you don't have to pass {declare: true} option.

graph.query("auth(email: $email, password: $password) { token }")

Auto Declaring Custom Types

You can define custom types when defining variables by using a simple "variable!Type" notation. It will help you to make more complex variables:

var register = graph.mutate(`(@autodeclare) {
  userRegister(input: $input) { ... }
}`)

register({
  // variable name and type.
  "input!UserRegisterInput": { ... }
})

This will generate following query:

mutation ($input: UserRegisterInput!) {
  userRegister(input: $input) { ... }
}

Fragments

Fragments make your GraphQL more DRY and improves reusability. With .fragment method, you'll manage your fragments easily.

Simple Fragments

While constructing your endpoint, you can predefine all of your fragments.

var graph = graphql("/graphql", {
  fragments: {
    userInfo: `on User { id, name, surname, avatar }`
  }
})

And you can use your fragments in your queries. The query will pick your fragments and will add them to the bottom of your query.

graph.query(`{ allUsers { ...userInfo } }`)

Nested Fragments

You can nest your fragments to keep them organized/namespaced.

var graph = graphql("/graphql", {
  fragments: {
    user: {
      info: `on User { id, name, surname, avatar }`
    }
  }
})

Accessing them is also intuitive:

graph.query(`{ allUsers { ...user.info } }`)

Using Fragments in Fragments

You can reuse fragments in your fragments.

graph.fragment({
  user: "on User {name, surname}",
  login: {
    auth: "on User {token, ...user}"
  }
})

Lazy Fragments

You can also add fragments lazily. So you can use your fragments more modular.

// Adds a profile fragment
graph.fragment({
  profile: `on User {
    id
    name(full: true)
    avatar
  }`
})

var allUsers = graph.query(`{
  allUsers {
    ... profile
  }
}`)

allUsers().then(...)

Also you can add nested fragments lazily, too:

graph.fragment({
  login: {
    error: `on LoginError {
      reason
    }`
  }
})

graph.fragment({
  something: {
    error: `on SomeError {
      messages
    }`
  }
})

graph.query(`{ login {... login.error } }`)
graph.query(`{ something {... something.error } }`)

Getting Fragments by Path

You can call fragment string by using .fragment method. You have to pass path string to get the fragment.

graph.fragment('login.error')

This will give you the matching fragment code:

fragment login_error on LoginError {
  reason
}

Using Fragments in Tag Query

You can use fragments lazily using ES6 template tag queries.

var userProfileToShow = graph.fragment('user.profile')

graph`query { ... ${userProfileToShow} }`

Query Building

You can create queries using .ql ES6 template tag.

// Add some fragments...
graph.fragment({
  username: {
    user: `on User {
      username
    }`,
    admin: `on AdminUser {
      username,
      administrationLevel
    }`
  }
})

// Get any fragment with its path...
var admin = graph.fragment('username.admin')

// Build your query with using fragment paths or dynamic template variables.
var query = graph.ql`query {
  ...username.user
  ...${admin}
}`

// Use query anywhere...
$.post("/graphql", {query: query}, function (response) { ... })

graph.ql will generate this query string:

query {
  ... username_user
  ... username_admin
}

fragment username_user on User {
  username
}

fragment username_admin on AdminUser {
  username,
  administrationLevel
}

Query Merging: Merge Multiple Queries into One Request

merge

This GIF shows a before/after case to make an example how query merging changes the performance.

graphql.js supports query merging that allows you to collect all the requests into one request.

Assume we've these queries on server, define them just like before we do:

var fetchPost = graph.query(`{
  post(id: $id) {
    id
    title
    text
  }
}`)

var fetchComments = graph.query(`{
  commentsOfPost: comments(postId: $postId) {
    comment
    owner {
      name
    }
  }
}`)

Normally, we make requests as following:

var postId = 123

// This will send a request.
fetchPost({ id: postId }).then(function (response) {
  console.log(response.post)
})

// This also will send a request.
fetchComments({ postId: postId }).then(function (response) {
  console.log(response.commentsOfPost)
})

This will make two requests:

ss1

Use .merge(mergeName, variables) command to put them into a merge buffer:

var postId = 123

// This won't send a request.
fetchPost.merge('buildPage', { id: postId }).then(function (response) {
  console.log(response.post)
})

// This also won't send a request.
fetchComments.merge('buildPage', { postId: postId }).then(function (response) {
  console.log(response.commentsOfPost)
})

These will create a buffer with buildPage name, and append the queries to that buffer. You need to use commit(mergeName) to merge the buffer and send to the server, the response will be consolidated:

// This will send a merged request:
graph.commit('buildPage').then(function (response) {
  // All base fields will be in response return.
  console.log(response.post)
  console.log(response.commentsOfPost)
})

And this will create only one request:

ss2

This will create the following merged query generated by graphql.js:

query ($merge024533__id: ID!, $merge141499__postId: ID!) {
  merge024533_post: {
    post(id: $merge024533__id) {
      id
      title
      text
    }
  }
  merge141499_commentsOfPost: {
    comments(postId: $merge141499__postId) {
      comment
      owner {
        name
      }
    }
  }
}

And variables will be generated, too:

{
  "merge024533__id": 123,
  "merge141499__postId": 123
}

The merge{number} aliases won't be passed into your responses, since they will be used for initial seperation.

โš ๏ธ Important Restriction: You cannot use multiple root fields using query merging. โš ๏ธ Important Restriction: Autodeclaration is on by default, do not use alwaysAutodeclare: true.

Debugging

You can pass debug: true to options parameter to get a console output looks like following:

[graphql]: POST http://localhost:3000/graphql
  QUERY: query ($email: String!, $password: String!) {
    auth(email: $email, password: $password) {
      .. login_auth
    }
  }

  fragment info on User { hasPin }
  fragment login_auth on User { token, ...info }

  VARIABLES: {
    "email": "[email protected]",
    "password": "123123"
  }

  sending as form url-data

Advanced

Using with Vue.js

Create a GraphQLProvider.js.

import graphql from 'graphql.js';

/* eslint-disable no-underscore-dangle */
export default {
  install(Vue, url, options) {
    Vue.mixin({
      created() {
        this._graph = graphql(url, options);
      },
    });
    Object.defineProperty(Vue.prototype, '$graph', {
      get() {
        return this._graph;
      },
    });
  },
};

And then you can use this with your Vue app:

import Vue from 'vue';
import GraphQLProvider from './GraphQLProvider';

Vue.use(GraphQLProvider, 'http://localhost:3000/graphql', {
  headers: {
    // headers...
  },
});

// ... in your Vue VM
data() {
  return {
    hello: '',
  };
},
methods: {
  makeSomeQuery() {
    this.$graph.query(`{hello}`).then(response => {
      this.hello = response.hello;
    });
  },
}

Change POST Method

As default, GraphQL.js makes a POST request. But you can change the behavior by setting asJSON.

var graph = graphql("http://localhost:3000/graphql", {
  asJSON: true
});

Using with graphql-tag

graphql-tag converts GraphQL query strings to AST. You can use graphql-tag with GraphQL.js

graph.query(gql`query { name }`)

Using graphql-tag will not allow you to use auto declaration and nested fragments syntaxes since these are not valid query syntax for GraphQL but only for this library.

Change Url Anywhere

You can change url anywhere with setUrl method.

var graph = graphql("http://localhost:3000/graphql", {
  asJSON: true
});

// Change url
graph.setUrl('http://www.example.com')

// Run query
graph.query(`{ name }`)

Todo App Example

A CRUD ToDo app example code to show how to use GraphQL.js. An implementation can be found at f/graphql.js-demo

var graph = graphql("/graphql", {
  alwaysAutodeclare: true,
  fragments: {
    todo: `on Todo {
      id
      text
      isCompleted
    }`
  }
})

function getTodos() {
  return graph.query.run(`allTodos {
    ...todo
  }`)
}

function addTodo(text) {
  return graph.mutate(`todoAdd(text: $text) {
    ...todo
  }`, {
    text: text
  })
}

function setTodo(id, isCompleted) {
  return graph.mutate(`todoComplete(
    id: $id,
    status: $isCompleted
  ) {
    ...todo
  }`, {
    id: id,
    isCompleted: isCompleted
  })
}

function removeTodo(id) {
  return graph.mutate(`todoRemove(
    id: $id
  ) {
    ...todo
  }`, {
    id: id
  })
}

License

MIT License

Copyright (c) 2018 Fatih Kadir Akฤฑn

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

graphql.js's People

Contributors

aldeed avatar benjamingr avatar danieldiekmeier avatar f avatar gbxl avatar halostatue avatar rmosolgo avatar sinanmtl avatar urkle avatar ustun avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

graphql.js's Issues

Declaring with template tag and running later

I love this package! We've been using graphql with React for a while now and we have a project where React isn't an option so to connect to our graphql server this is perfect. However we usually first declare our query with the template tag:

const Query = gql`
   query Stuff()...
`

And then run it by calling it.

It would be awesome if we could do the same here, is this something you plan to support? Maybe I misread the doc but from what I saw using this syntax will run the query straight away.

Thanks

Es-module variant

Is it possible to make a es-module variant where we can just do import graph from "graphql.js"?

It seems the "http.js" and "https.js" and "url.js" is needed?

It seems the "http.js" and "https.js" abd "url.js" is needed? where can i find these js files?

--- graphql.js ----

xhr.send(body)
} else if (typeof require == 'function') {
var http = require('http'), https = require('https'), URL = require('url'), uri = URL.parse(url);
var req = (uri.protocol === 'https:' ? https : http).request({
protocol: uri.protocol,
hostname: uri.hostname,

Body is not defined / Must provide a query string

I am creating this lines of code and it throws me an error saying body is not defined, although I debug and before sending the xmlrequest the body variable is defined.

` const graph = graphql(HOST);

  const signIn = graph(`query SignIn($options: UserInput!){
    signIn(options: $options)
  }`);

  try {
    const auth = await signIn({ options: { token }});

    return auth.data;
  } catch(e) {
    console.log(e.message)
  }
}`

Disclaimer: the query is tested and is working as it is.

Direct query execution using async await

According to documentation it is possible to execute queries using the following approach:

graph(`query ($email: String!, $password: String!) {
  auth(email: $email, password: $password) {
    ... auth # if you use any fragment, it will be added to the query.
    ... error
  }
}`, {
  email: "[email protected]",
  password: "my-super-password"
}).then(function (response) {
  // response is originally response.data of query result
  console.log(response)
}).catch(function (error) {
  // response is originally response.errors of query result
  console.log(error)
})

I have been using this approach in the following way using the async await syntax.

await graph(`query`, { })

However, while this worked in version 0.4.20, I haven't been able to get it to work using version 0.6.5.

Is there something I'm overlooking, or has this approach been deprecated?

Webpack breaks isomorphic code - graphql.js adds 25kb gzipped!

I've discovered that Webpack's default production build minifies graphql.js in a strange way, and breaks the isomorphic nature of this library. It might be a webpack problem but I wanted to see if others had encountered this issue. It seems that the problem relates to a specific part of graphql.js's code.

When Webpack's minifier processes this condition starting line 34:

if (typeof XMLHttpRequest !== 'undefined') {
// browser compatible __doRequest function
} else if (typeof require === 'function') {
// node compatible __doRequest function
}

It turns it into:

if ("undefined" != typeof XMLHttpRequest);
else {}

And it removes the browser-compatible definition of __doRequest, so we're only left with the Node-compatible one that depends on the native libraries http, https, and url. It doesn't actually break a build because Webpack automatically pulls in node polyfills. But this means that graphql.js adds far more than 4kb to a bundle, in fact it bloats the bundle by about 25kb gzipped!

Would you have any idea why it's doing this? This only happens when minifying.

To reproduce:

  1. Clone this repo
  2. Run npm install
  3. Run npm build-prod
  4. Open dist/main.beautified.js and go to line 2296.

You should see this code:

        if ("undefined" != typeof XMLHttpRequest); // if condition appears here, empty.
        else {}

        function o(t, e, n, o, s, a, u, f) {
            if (n) {
                if (a) var h = JSON.stringify({
                    query: s.query,
                    variables: s.variables
                });
                else h = "query=" + encodeURIComponent(s.query) + "&variables=" + encodeURIComponent(JSON.stringify(s.variables));
                for (var c in t && (console.groupCollapsed("[graphql]: " + e.toUpperCase() + " " + n + ": " + s.query.split(/\n/)[0].substr(0, 50) + "... with " + JSON.stringify(s.variables).substr(0, 50) + "..."), console.log("QUERY: %c%s", "font-weight: bold", s.query), console.log("VARIABLES: %c%s\n\nsending as " + (a ? "json" : "form url-data"), "font-weight: bold", JSON.stringify(s.variables, null, 2), s.variables), console.groupEnd()), o) "function" == typeof o[c] && (o[c] = o[c]());
                ! function(t, e, n, o, s, a, u, f) { // this is the node-compatible __doRequest function
                    var h = r(9), // http node library
                        c = r(42), // https node library
                        l = r(8).parse(e), // url node library
                        p = ("https:" === l.protocol ? c : h).request({
                            protocol: l.protocol,
                            hostname: l.hostname,
                            port: l.port,
                            path: l.path,
                            method: t.toUpperCase(),
                            headers: i({
                                "Content-type": n,
                                Accept: o
                            }, s)
                        }, (function(t) {
                            var e = "";
                            t.setEncoding("utf8"), t.on("data", (function(t) {
                                e += t
                            })), t.on("end", (function() {
                                f(JSON.parse(e), t.statusCode)
                            }))
                        }));
                    "function" == typeof u && p.on("error", (function(t) {
                        u(t)
                    })), p.write(a), p.end()
                }(e, n, a ? "application/json" : "application/x-www-form-urlencoded", "application/json", o, h, u, f)
            }
        }

.mutate() does not autodeclare variables

When I use the .query() helper method with autodeclare, it works perfectly. However, .mutate() does not autodeclare the variables for input.

I can still use the main function and enter the full mutation syntax, with manual declarations. But having the helper method work properly would be nice.

Aborting pending requests

Hello!

I'm not sure if that's a responsibility of this lib or not but i'm building a search widget based on graphql server and graphql.js lib and was wondering - is there any way to abort/cancel pending request done by graphql.js?

Sometimes user changes his query during load of a previous query which generates two parallel requests and leads to weird results. It would be great to be able to run some abort() method on mutate() or query() instance to kill the request.

Thanks!

Does "Independant" mean decoupled from facebook's libraries and tech

After the recent licensing issues with Facebook's BSD + Patents license the company I work for is very hesitant to use any of their libraries. However we still wish to make use of the GraphQL standard as it seems to be the way of the future. So my question is, does "independent" mean that this repository has no dependencies on Facebook libraries?

Query merging does not work correctly.

I have the following setup

export const graph = graphql('/graphql', {
  method: 'POST',
  asJSON: true,
  debug: true,
});

graph.fragment({
  video: `on Video {
    id
    description
    duration
    videoType
  }`,
  category: `on Category {
    id
    name
    categoryType
  }`,
});

export const videos = graph.query('{ videos { ...video } }');

export const categories = graph.query('{ categories { ...category } }');

I try to use the "merge" functionality like so.

queries.videos.merge('test');
queries.categories.merge('test');
queries.graph.commit('test').then((r) => console.log('all', r));

Now the query sent to my server is as follows.

query  {
merge993382_videos :{ videos { ... video } }
merge278951_categories :{ categories { ... category } }
 }

fragment video on Video {
    id
    description
    duration
    videoType
  }

fragment category on Category {
    id
    name
    categoryType
  }

Which is, of course, invalid as the extra {} shouldn't be there around the query pieces.

I'm using graphql.js version 0.6.7

v0.6.3 cannot make requests from node (__doRequest is not a function)

When making requests in node with 0.6.3, they always fail with:

TypeError: __doRequest is not a function
  at __request (node_modules/graphql.js/graphql.js:109:5)
  at .../node_modules/graphql.js/graphql.js:320:11

This worked fine in v0.6.2. I'm testing specifically with node 10.15.3, but I suspect all versions are affected.

It looks like this is happening because https://github.com/f/graphql.js/pull/38/files flipped the test to define __doRequest for node.js from:

typeof require == 'function'

to:

typeof require !== 'function'

See the resulting code here: https://github.com/f/graphql.js/blob/master/graphql.js#L54-L82

This is never true in node, and neither is the XMLHttpRequest test above, so __doRequest is always undefined, and no requests can ever be made.

Headers not working

Hi, I am doing something like this

		let client =  graphql("http://localhost:5000/graphql", {
		  headers: {
		    "Access-Control-Allow-Origin": "*"
		  }
		})

but I am getting the following error
Access to XMLHttpRequest at 'http://localhost:5000/graphql' from origin 'http://localhost:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Can you please let me know what I am doing wrong here ?
Thanks

Unable to get any query to run on GitHub's API - `.then` is not a function error

I must admit that I am a beginner with JavaScript and web based technologies.
I followed issues #2 , #21 and #40 along with the code taken straight from this SO answer (corresponding #21), but I get the following error in console -

TypeError: graph(...).then is not a functiongraphql.html:59:4

I made a html file with the following content and all I do is open it in the browser and see the console.
Following is the content of the html file -

<!DOCTYPE html>
<html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <meta content="text/html;charset=utf-8" http-equiv="Content-Type">
</head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="graphql.min.js"></script>
<script>
var graph = graphql("https://api.github.com/graphql", {
  headers: {
    "Authorization": "Bearer <my id>"
  },
  asJSON: true
});
graph(`
    query repo($name: String!, $owner: String!){
        repository(name:$name, owner:$owner){
            id      
        }
    }
`, {
  name: "freeCodeCamp",
  owner: "freeCodeCamp"
}).then(function(response) {
  console.log(response);
}).catch(function(error) {
  console.log(error);
});
</html>

Could you please tell me how should I make it display the response json in the console ?

JSON parse error when using OAuth token

I've got this to work using Postman, however the following code generates a JSON parse error?

Code

const graphql = require('graphql.js');
const graph = graphql('https://api.github.com/graphql', {
  method: "POST",
  headers: {
    "User-Agent": "node",
    "Authorization": "Bearer <github-token>"
  }
});

const query = graph(`query { viewer { login name } }`);

query().then(
  res => console.log(JSON.stringify(res)),
  err => console.error(err)
);

Error

{ message: 'Problems parsing JSON',
  documentation_url: 'https://developer.github.com/v4' }

`client.query().then` is not a function in 0.6.2 version

This code doesn't work in v0.6.2 but works in v0.4.0:

const client = graphql('/api')
client.query(`query { me }`, { var1: '42' }).then(/* ... */ )

It seems like once number of arguments to returning function of GraphQLClient.prototype.createSenderFunction had changed from one to three, it became not working.

I had managed to fix it in three ways:

  1. Add two extra arguments to query
- client.query(`query { me }`, { var1: '42' }).then(/* ... */ )
+ client.query(`query { me }`, '', '', { var1: '42' }).then(/* ... */ )
  1. Remove query type from GraphQL query string
- client.query(`query { me }`, { var1: '42' }).then(/* ... */ )
+ client.query(`{ me }`, { var1: '42' }).then(/* ... */ )
  1. Add caller call to graphql.js itself to behave like 0.4.20 version before changes in GraphQLClient.prototype.createSenderFunction
if (arguments.length > 3) {
    return caller.apply(null, Array.prototype.slice.call(arguments, 3))
}
+if (arguments.length > 1) {
+    return caller.apply(null, Array.prototype.slice.call(arguments, 1))
+}
return caller

@f what is the correct way to fix this problem?

Error with mutation that returns true

I can't understand what I'm doing wrong, but all mutations calls fail with this error in the browser console:

Uncaught TypeError: that [m.method] (...) is not a function
at GraphQLClient.that.(/Users/.../anonymous function).run (file:///Users/.../vendor/graphql.min.js:1:8351)
at deleteAuthor (app.js:82)
at HTMLButtonElement. (app.js:15)
at HTMLButtonElement.dispatch (jquery.min.js:2)
at HTMLButtonElement.y.handle (jquery.min.js:2)
that.(anonymous function).run @ graphql.min.js:1
deleteAuthor @ app.js:82
(anonymous) @ app.js:15
dispatch @ jquery.min.js:2
y.handle @ jquery.min.js:2

Here's how I run the migration:

var q = `DeleteAuthor($id: ID) {DeleteAuthor(id: $id)}`;
var vars = {id: id};

graph.mutate.run(q, vars).then(function (r) {
    alert(`Author ${id} deleted.`);
}).catch(function (error) {
    alert(JSON.stringify(error))
})

I'm trying this in Chrome 66. Any ideas?

Sending cookies

How to set withCredentials: true to send cross-domain request?

Update fragmentPath method to not use Function constructor (CSP 'unsafe-eval' issue)

I'm testing out this module in a browser extension and get a CSP error because of the Function constructor used in the fragmentPath method here (trips the 'unsafe-eval' source expression for the 'script-src' directive; more info). This prevents me from using the module as-is with my extension's CSP.

In my testing, I was able to just replace that Function constructor:

var getter = new Function("fragments", "return fragments." + path.replace(/\./g, FRAGMENT_SEPERATOR));

with this:

var obj = fragments[path.replace(/\./g, FRAGMENT_SEPERATOR)];

Without issue.

Is this a change that would be considered for the module? Or am I missing something that requires the current usage?

Thanks!

.then is not a function

Context

var graphql = require('graphql.js')

var graph = graphql('someurl, {
  headers: {
    'Authorization': `Bearer XXX`,
    'Content-Type': 'application/json'
  }
}

function getX() {
  return graph('query me { name }')
}

getX().then(me => {
  console.log(me)
})

All bundled with webpack, errors on .then call and add the graph.query('...') does not seem to affect a change.

Expected Result

  • Console logs data

Actual Result

  • Error on .then
  • No network requests made

POST body missing. Did you forget use body-parser middleware?

Hi,

Has anyone faced this issue

POST body missing. Did you forget use body-parser middleware?

SyntaxError: Unexpected token P in JSON at position 0
    at JSON.parse (<anonymous>)
    at IncomingMessage.<anonymous> (/home/abc/server/node_modules/graphql.js/graphql.js:71:25)

this is my code

const query = graph(`mutation(
    $username: String!, 
    $password: String! ) {
      authentication {
      login(
        username: $username,
        password: $password,
        strategy: "local"
      ) {
        responseResult {
          succeeded
          errorCode
          message
        }
        jwt
      }
    }
  }`);

  query({
    username: req.body.username,
    password: req.body.password
  }).then(
    resData => {
      res.send(resData);
    },
    err => {
      console.error(err)
      res.status(500).send(err)
    }
  );

am running graphql.js in express server and not using 'body-parser' but rather uses express.json(). I am sure that I am able to read 'req.body.username' and 'req.body.password' values successfully, so why is it suggesting to use body-parser ?

Weird thing is this request runs fine on my local machine but gives this error on deployed server.

Could someone let me know, how to console.log body, before we query?

Regards

Better Subscription

Subscriptions need to be implemented.

They can be that easy:

var onCommentsAdded = graph.subscribe`{ commendAdded { user {name}, text } }`

onCommentsAdded(function (data) {
  console.log(data.user.name)
})

And can be unsubscribed.

var onCommentsAdded = graph.subscribe`{ commendAdded { user {name}, text } }`

onCommentsAdded.unsubscribe()

jquery is needed?

Hello,

On your README, there is this line:

No dependencies, plain vanilla JavaScript.

And in your bower.json there is this:

"dependencies": {
    "jquery": "^3.1.1"
  }

So, do your project depend on jQuery?

I can't use it because when I install your lib I must break the dependency of another lib that use jQuery 2.

Browser version giving error 404

I couldn't get this to work in the browser, I wrote a small test program that just runs a query and ran it with node and then in the browser.

This is probably just me using it wrong, if so I apologize

Thanks in advance

Recursive fragments

Hi, why i can't to use recursive fragments?
Error: Recursive fragment usage detected on folderFragment.
Recursive fragments are working in ApolloClient, original GraphQL and i have added pull request in lokka.

Query is returning data in debugger, but .then() is never called

I'm successfully sending the GraphQL queries to my server, the data is returning, Chrome debugger shows the response data in the network view, but my invocation of .then() never gets called. My function that I pass in to call console.log does not run, and the promise always shows as "pending" even after the call returns.

What could be the issue?

Protocol "https:" not supported. Expected "http:"

When trying to access the GitHub GraphQL API I'm getting the following error:

Error: Protocol "https:" not supported. Expected "http:"
    at new ClientRequest (_http_client.js:55:11)
    at Object.exports.request (http.js:31:10)
    at __request (/Users/elliotblackburn/code/life-bot/node_modules/graphql.js/graphql.js:51:22)
    at /Users/elliotblackburn/code/life-bot/node_modules/graphql.js/graphql.js:245:11
    at caller (/Users/elliotblackburn/code/life-bot/node_modules/graphql.js/graphql.js:244:16)
    at Object.<anonymous> (/Users/elliotblackburn/code/life-bot/scratch.js:44:1)
    at Module._compile (module.js:570:32)
    at Object.Module._extensions..js (module.js:579:10)
    at Module.load (module.js:487:32)
    at tryModuleLoad (module.js:446:12)

Are you able to enable https for the module?

Headers not working (credentials: 'included'?)

Hello,

The backend is attempting to set a cookie by using a cookie request. Typically, in front end applications one uses credentials: true or credentials: "included" which then sets the cookie, but this does not seem to be working. In the network tab, I can see the cookie request come in, yet it never actually sticks as a cookie. What setting should I use in my headers to enable credentials?

    var graph = graphql("http://localhost:3000/api/graphql", {
        headers: {
            credentials: "include",
            "Authorization": "",
        },
        asJSON: true,
    });

Thank you

Determine Int's and Float's

Hi!
Does this mean that if I pass value 10 it will be declared as Int in my query, even though on server the field is declared as Float?

    var typeMap = {
      string: "String",
      number: function (value) {
        return value % 1 === 0 ? "Int" : "Float";
      },
      boolean: "Boolean"
    }

Thanks.

Request payload not compatible with Github API

I have tried using this repository to query Github's GraphQL API. But no matter what I do, I keep getting a "Problems parsing JSON" message.
An extracted piece of the code below:

var graph = graphql("https://api.github.com/graphql", {
  method: "POST",
  headers: {
    "Authorization": "Bearer <my-token-here>",
    "Content-Type": "application/json"
  },
  fragments: {
    rateLimitInfo: "on RateLimit {cost,remaining,resetAt}"
  }
});
graph(`
    query repo($name: String!, $owner: String!){
        repository(name:$name, owner:$owner){
            id      
        }
    }
`,{
    name: "freeCodeCamp",
    owner: "freeCodeCamp"
}).then(function(response){
    console.log(response);
}).catch(function(error){
    console.log(error);
});

With this code I always get a HTTP 400 (Bad Request) response. With the following data:

{
    message: "Problems parsing JSON", 
    documentation_url: "https://developer.github.com/v3"
}

Looking at Chrome's Network tab, I see that the request payload that is being sent is:
query=query%20repo(%24name%3A%20String!%2C%20%24owner%3A%20String!)%7Brepository(name%3A%24name%2C%20owner%3A%24owner)%7Bid%7D%7D&variables=%7B%22name%22%3A%22freeCodeCamp%22%2C%22owner%22%3A%22freeCodeCamp%22%7D
I have the same query running on Insomnia and it works. When I convert Insomnia's request to curl, the payload looks like this:
--data '{"query":"query repo($name: String!, $owner: String!){\n\trepository(name:$name, owner:$owner){\n\t\tid\t\t\n\t}\n}","variables":{"name":"freeCodeCamp","owner":"freeCodeCamp"},"operationName":"repo"}'

When decoding both strings, it is visible that both payloads have different formats.

Is this an issue with how the repository is creating the payload, is this a mistake on my code, or is this something particular to how Github's API accept the query string?

Same question posted on stackoverflow.

headers not working?

The header option seems to be used in a weird way. Here's how I build my graphql object:

var graph = graphql("http://localhost:3000/", {
    headers: {
        'Authorization': 'abcdef'
    }
});

And here's the HTTP request that it produces:

OPTIONS / HTTP/1.1
Host: localhost:3000
Connection: keep-alive
Access-Control-Request-Method: POST
Origin: null
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36
Access-Control-Request-Headers: authorization
Accept: /
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9,ro;q=0.8,sv;q=0.7,zh-TW;q=0.6,zh;q=0.5

As you can see, the request is changed from POST to OPTIONS, with a number of consequences. Rather than being a header in its own right, my header appears inside the 'Access-Control-Request-Headers' header, and its value is lost. No POST request is ever being made.

Is jquery dropped?

Hi

Can you confirm if jquery is dropped, because it still appears at top of github and in package.json.

Thanks

cut larger jsons

In my case I have over 10000 entries in json (ca. 500kB). It will be cut by the lib. I see it on network console of firefox

Option to build and return 'body' parsed

Hi,

Is there a way to build/parse 'body' of the request based on query and variable instead of direct issue XMLHttpRequest to server?
This would allow uses of other 'request/fetch' option and provide more flexibility.

TypeError: query.trim is not a function

Hi,

I am trying to use graphql.js inside the Google Cloud Functions to query my database through a Graphql interface.

However when I try to set the URL, I get this error:

textPayload: "TypeError: query.trim is not a function
    at Object.helper (/srv/node_modules/graphql.js/graphql.js:425:80)
    at GraphQLClient.that.(anonymous function) [as query] (/srv/node_modules/graphql.js/graphql.js:442:23)
    at listAggregators (/srv/lib/intents/location-permission.js:17:54)

Here is my code:

const graphql = require('graphql.js');

function listAggregators(conv: DialogflowConversation) {
  const graph = graphql('https://graphql.staging.mygraphq.com/graphql');
 // [...]
}

Should I set some extra parameters here?

Note: I am using typescript, instead of javascript. Maybe there is some compiler option that I should set, because I also get a compile error when I try to use import instead of require in import graphql from 'graphql.js';

Thanks,
Komyg

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.