What is graphql-anywhere?
The graphql-anywhere package is a flexible utility for running GraphQL queries against any arbitrary data structure. It allows you to execute GraphQL queries on client-side data, making it useful for scenarios where you need to filter or transform data based on a GraphQL query.
What are graphql-anywhere's main functionalities?
Executing GraphQL queries on client-side data
This feature allows you to execute GraphQL queries on client-side data. The code sample demonstrates how to filter a data object based on a GraphQL query, returning only the fields specified in the query.
const { graphql } = require('graphql-anywhere');
const { filter } = require('graphql-anywhere/lib/async');
const query = `{
user {
name
age
}
}`;
const resolver = (fieldName, root, args, context, info) => {
return root[fieldName];
};
const data = {
user: {
name: 'John Doe',
age: 30,
email: 'john.doe@example.com'
}
};
filter(resolver, query, data).then(result => {
console.log(result);
// Output: { user: { name: 'John Doe', age: 30 } }
});
Custom resolvers for handling data
This feature allows you to define custom resolvers to handle data in a specific way. The code sample shows how to use a custom resolver to modify the 'age' field before returning the result.
const { graphql } = require('graphql-anywhere');
const { filter } = require('graphql-anywhere/lib/async');
const query = `{
user {
name
age
}
}`;
const customResolver = (fieldName, root, args, context, info) => {
if (fieldName === 'age') {
return root[fieldName] + 1; // Custom logic to modify the age
}
return root[fieldName];
};
const data = {
user: {
name: 'John Doe',
age: 30,
email: 'john.doe@example.com'
}
};
filter(customResolver, query, data).then(result => {
console.log(result);
// Output: { user: { name: 'John Doe', age: 31 } }
});
Other packages similar to graphql-anywhere
graphql-tools
The graphql-tools package provides a set of utilities to build and manipulate GraphQL schemas in JavaScript. It includes features for schema stitching, mocking, and more. While graphql-tools focuses on schema creation and manipulation, graphql-anywhere is more about executing queries on arbitrary data structures.
apollo-client
Apollo Client is a comprehensive state management library for JavaScript that enables you to manage both local and remote data with GraphQL. It includes features for caching, query batching, and more. While Apollo Client is a full-fledged GraphQL client, graphql-anywhere is a lightweight utility for executing queries on arbitrary data.
graphql-anywhere
Run a GraphQL query anywhere, without a GraphQL server or a schema. Just pass in one resolver. Use it together with graphql-tag.
npm install graphql-anywhere graphql-tag
I think there are a lot of potentially exciting use cases for a completely standalone and schema-less GraphQL execution engine. We use it in Apollo Client to read data from a Redux store with GraphQL.
Let's come up with some more ideas - below are some use cases to get you started!
API
import graphql from 'graphql-anywhere'
graphql(resolver, document, rootValue?, context?, variables?)
resolver
: A single resolver, called for every field on the query.
- Signature is:
(fieldName, rootValue, args, context) => any
document
: A GraphQL document, as generated by the template literal from graphql-tag
rootValue
: The root value passed to the resolver when executing the root fieldscontext
: A context object passed to the resolver for every fieldvariables
: A dictionary of variables for the query
Supported GraphQL features
Why do you even need a library for this? Well, running a GraphQL query isn't as simple as just traversing the AST, since there are some pretty neat features that make the language a bit more complex to execute.
If you come across a GraphQL feature not supported here, please file an issue.
Example: Filter a nested object
import gql from 'graphql-tag';
import graphql from 'graphql-anywhere';
const gitHubAPIResponse = {
"url": "https://api.github.com/repos/octocat/Hello-World/issues/1347",
"title": "Found a bug",
"body": "I'm having a problem with this.",
"user": {
"login": "octocat",
"avatar_url": "https://github.com/images/error/octocat_happy.gif",
"url": "https://api.github.com/users/octocat",
},
"labels": [
{
"url": "https://api.github.com/repos/octocat/Hello-World/labels/bug",
"name": "bug",
"color": "f29513"
}
],
};
const query = gql`
{
title
user {
login
}
labels {
name
}
}
`;
const resolver = (fieldName, root) => root[fieldName];
const result = graphql(
resolver,
query,
gitHubAPIResponse
);
assert.deepEqual(result, {
"title": "Found a bug",
"user": {
"login": "octocat",
},
"labels": [
{
"name": "bug",
}
],
});
Example: Generate mock data
const query = gql`
{
author {
name: string
age: int
address {
state: string
}
}
}
`;
const resolver = (fieldName) => ({
string: 'This is a string',
int: 5,
}[fieldName]);
const result = graphql(
resolver,
query
);
assert.deepEqual(result, {
author: {
name: 'This is a string',
age: 5,
address: {
state: 'This is a string',
},
},
});
Example: Read from a Redux store generated with Normalizr
const data = {
result: [1, 2],
entities: {
articles: {
1: { id: 1, title: 'Some Article', author: 1 },
2: { id: 2, title: 'Other Article', author: 1 },
},
users: {
1: { id: 1, name: 'Dan' },
},
},
};
const query = gql`
{
result {
title
author {
name
}
}
}
`;
const schema = {
articles: {
author: 'users',
},
};
const resolver = (fieldName, rootValue, args, context): any => {
if (!rootValue) {
return context.result.map((id) => assign({}, context.entities.articles[id], {
__typename: 'articles',
}));
}
const typename = rootValue.__typename;
if (typename && schema[typename] && schema[typename][fieldName]) {
const targetType: string = schema[typename][fieldName];
return assign({}, context.entities[targetType][rootValue[fieldName]], {
__typename: targetType,
});
}
return rootValue[fieldName];
};
const result = graphql(
resolver,
query,
null,
data
);
assert.deepEqual(result, {
result: [
{
title: 'Some Article',
author: {
name: 'Dan',
},
},
{
title: 'Other Article',
author: {
name: 'Dan',
},
},
],
});
Known limitations:
- The execution engine is synchronous, so you probably shouldn't use this to query your API. Please submit a PR to add promise functionality, and ideally batching support, like
graphql-js
has! Wouldn't it be cool if you could use this to dynamically query a REST API via GraphQL?