Releases: f/graphql.js
0.6.7
0.6.5
graphql.js: lightweight graphql client
Lightest GraphQL client with intelligent features.
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.
Download for Browser
Using Bower
bower install graphql.js --save
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: "john@doe.com",
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: "john@doe.com",
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: "john@doe.com", // 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 mod...