Feature light client library for fetching resources via GraphQL.
With Yarn:
$ yarn add graphql-js-client
With NPM:
$ npm install graphql-js-client
GraphQLClient
requires a "type bundle" which is a set of ES6 modules generated by graphql-js-schema
that represent your GraphQL schema.
import GraphQLClient from 'graphql-js-client';
// This is the generated type bundle from graphql-js-schema
import types from './types.js';
const client = new GraphQLClient(types, {
url: 'https://graphql.myshopify.com/api/graphql',
fetcherOptions: {
headers: { Authorization: 'Basic aGV5LXRoZXJlLWZyZWluZCA=' }
}
});
const query = client.query((root) => {
root.add('shop', (shop) => {
shop.add('name');
shop.addConnection('products', {args: {first: 10}}, (product) => {
product.add('title');
});
});
});
/* Will generate the following query:
query {
shop {
name
products (first: 10) {
pageInfo {
hasNextPage
hasPreviousPage
}
edges {
cursor
node {
id
title
}
}
}
}
}
Note: things that implement Node will automatically have the id added to the
query. `addConnection` will automatically build out the connection query with
all information necessary for pagination.
*/
let objects;
client.send(query).then(({model, data}) => {
objects = model;
console.log(model); // The serialized model with rich features
console.log(data); // The raw data returned from the API endpoint
});
client.refetch(objects.shop.products[0]).then((product) => {
console.log(product); // This is a fresh instance of the product[0]
});
In the above example, objects.shop.products[0]
implements the Node interface.
The client is smart enough to understand this, and generate the following query:
query {
node (id: 'abc123') {
__typename
... on Product {
id
title
}
}
}
It then resolves directly with the node
field, which is a product in this
case.
const query = client.query((root) => {
root.add('shop', (shop) => {
shop.add('name');
shop.addConnection('products', {args: {first: 10}}, (product) => {
product.add('title');
product.addConnection('variants', {args: {first: 50}}, (variant) => {
variant.add('title');
variant.add('price');
});
});
});
});
client.send(query).then(({model}) => {
client.fetchNextPage(model.shop.products).then((products) => {
console.log(products); // resolves with the next 10 products.
});
client.fetchNextPage(model.shop.products[0].variants).then((variants) => {
console.log(variants); // resolves with the next 50 variants. Page size is
// taken from the previous `first` argument.
});
});
The client understands the Relay
specification, and will send the following query for the call
client.fetchNextPage(model.shop.products)
:
query {
shop {
name
products (first: 10, after: 'abc123') {
pageInfo {
hasNextPage
hasPreviousPage
}
edges {
cursor
node {
id
title
variants (first: 50) {
pageInfo {
hasNextPage
hasPreviousPage
}
edges {
cursor
node {
id
title
price
}
}
}
}
}
}
}
}
The client can also use the Node interface to optimize queries for nested
paginated sets. fetchNextPage
in the second case
client.fetchNextPage(model.shop.products[0].variants)
will generate the
following query:
query {
node (id: '1') {
__typename
... on Product {
id
variants (first: 50, after: 'abc123') {
id
title
price
}
}
}
}
In both cases, fetchNextPage
resolves with the models you're paginating, since
the object graph to those models may not be obvious due to the query generation
algorithm. Page size and fields on the paginated object are retained, while
fields not in the paginated set are pruned.
const query = client.query((root) => {
root.add('shop', (shop) => {
shop.add('name');
shop.addConnection('products', {args: {first: 10}}, (product) => {
product.add('title', {directives: {include: {if: true}}});
});
});
});
/* Will generate the following query:
query {
shop {
name
products (first: 10) {
pageInfo {
hasNextPage
hasPreviousPage
}
edges {
cursor
node {
id
title @include(if: true)
}
}
}
}
}
*/
For full API documentation, check out the API docs.
$ git clone [email protected]:Shopify/graphql-js-client.git
$ cd graphql-js-client
$ yarn install
$ yarn start
Then visit http://localhost:4200
$ yarn test
MIT, see LICENSE.md for details.