4.2.8 • Published 2 years ago

graphql-anywhere v4.2.8

Weekly downloads
425,143
License
MIT
Repository
github
Last release
2 years ago

graphql-anywhere

npm version Build Status

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?, options?)
  • resolver: A single resolver, called for every field on the query.
    • Signature is: (fieldName, rootValue, args, context, info) => 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 fields
  • context: A context object passed to the resolver for every field
  • variables: A dictionary of variables for the query
  • options: Options for execution

Options

The last argument to the graphql function is a set of graphql-anywhere-specific options.

  • resultMapper: Transform field results after execution.
    • Signature is: (resultFields, resultRoot) => any
  • fragmentMatcher: Decide whether to execute a fragment. Default is to always execute all fragments.
    • Signature is: (rootValue, typeCondition, context) => boolean

Resolver info

info, the 5th argument to the resolver, is an object with supplementary information about execution. Send a PR or open an issue if you need additional information here.

  • isLeaf: A boolean that is true if this resolver is for a leaf field of the query, i.e. one that doesn't have a sub-selection.
  • resultKey: The key the result of this field will be put under. It's either the field name from the query, or the field alias.
  • directives: An object with information about all directives on this field. It's an object of the format { [directiveName]: { [argumentName]: value }}. So for example a field with @myDirective(hello: "world") will be passed as { myDirective: { hello: 'world' }}. Note that fields can't have multiple directives with the same name, as written in the GraphQL spec.

Utilities

See https://www.apollographql.com/docs/react/advanced/fragments.html for examples of how you might use these.

import { filter } from 'graphql-anywhere'

filter(doc, data);
  • doc: a GraphQL document, as generated by the template literal from graphql-tag, typically either a query or a fragment.
  • data: an object of data to be filtered by the doc

Filter data according to doc.

import { check } from 'graphql-anywhere'

check(doc, data);
  • doc: a GraphQL document, as generated by the template literal from graphql-tag, typically either a query or a fragment.
  • data: an object of data, as may have been filtered by doc.

Check that data is of the form defined by the query or fragment. Throw an exception if not.

import { propType } from 'graphql-anywhere'

X.propTypes = {
  foo: propType(doc),
  bar: propType(doc).isRequired,
}
  • doc: a GraphQL document, as generated by the template literal from graphql-tag, typically either a query or a fragment.

Generate a React propType checking function to ensure that the passed prop is in the right form.

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.

  • Arguments
  • Variables
  • Aliases
  • Fragments, both named and inline
  • @skip and @include directives

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';

// I don't need all this stuff!
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"
    }
  ],
};

// Write a query that gets just the fields we want
const query = gql`
  {
    title
    user {
      login
    }
    labels {
      name
    }
  }
`;

// Define a resolver that just returns a property
const resolver = (fieldName, root) => root[fieldName];

// Filter the data!
const result = graphql(
  resolver,
  query,
  gitHubAPIResponse
);

assert.deepEqual(result, {
  "title": "Found a bug",
  "user": {
    "login": "octocat",
  },
  "labels": [
    {
      "name": "bug",
    }
  ],
});

Example: Generate mock data

// Write a query where the fields are types, but we alias them
const query = gql`
  {
    author {
      name: string
      age: int
      address {
        state: string
      }
    }
  }
`;

// Define a resolver that uses the field name to determine the type
// Note that we get the actual name, not the alias, but the alias
// is used to determine the location in the response
const resolver = (fieldName) => ({
  string: 'This is a string',
  int: 5,
}[fieldName] || 'continue');

// Generate the object!
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',
  },
};

// This resolver is a bit more complex than others, since it has to
// correctly handle the root object, values by ID, and scalar leafs.
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 this field is a reference according to the schema
  if (typename && schema[typename] && schema[typename][fieldName]) {
    // Get the target type, and get it from entities by ID
    const targetType: string = schema[typename][fieldName];
    return assign({}, context.entities[targetType][rootValue[fieldName]], {
      __typename: targetType,
    });
  }

  // This field is just a scalar
  return rootValue[fieldName];
};

const result = graphql(
  resolver,
  query,
  null,
  data // pass data as context since we have to access it all the time
);

// This is the non-normalized data, with only the fields we asked for in our query!
assert.deepEqual(result, {
  result: [
    {
      title: 'Some Article',
      author: {
        name: 'Dan',
      },
    },
    {
      title: 'Other Article',
      author: {
        name: 'Dan',
      },
    },
  ],
});

Example: Generate React components

You can use the resultMapper option to convert your results into anything you like. In this case, we convert the result fields into children for a React component:

const resolver = (fieldName, root, args) => {
  if (fieldName === 'text') {
    return args.value;
  }

  return createElement(fieldName, args);
};

const reactMapper = (childObj, root) => {
  const reactChildren = Object.keys(childObj).map(key => childObj[key]);

  if (root) {
    return cloneElement(root, root.props, ...reactChildren);
  }

  return reactChildren[0];
};

function gqlToReact(query): any {
  return graphql(
    resolver,
    query,
    '',
    null,
    null,
    { resultMapper: reactMapper },
  );
}

const query = gql`
  {
    div {
      s1: span(id: "my-id") {
        text(value: "This is text")
      }
      s2: span
    }
  }
`;

assert.equal(
  renderToStaticMarkup(gqlToReact(query)),
  '<div><span id="my-id">This is text</span><span></span></div>'
);
@preply/video@preply/chat@travelshift/webmortgage-hub-component-libraryfondfolioreact-apollo-boilerplaterags-boilerplate@brudi/brudi-toolbox-graphql@toptal/staff-portalbitcasino-v3pe-layoutsmdlinx-smartestdoc@comet/admin-stories@infinitebrahmanuniverse/nolb-graphql-a@everything-registry/sub-chunk-1784@encoura/apollo-rest-utils@fabien0102/apollo-link-rest@futurelib/graphql-compiler@foxnfork/auth-node@episclera/multipack-weburql-computed-exchangeviat-me@hichroma/chromatic-agentoda-lodashoda-lodash-clientoda-api-graphql@disruptops/apollo-link-state@datadvance/apollo-link-stateredux-conveyorsapien-v2-frontendsourcenet_school@molaux/mui-crudf@gql-conquest/query-selectorcanner-graphql-interface@cornerstone-digital/restqlbnjuilopjhgthtyinext-apollo-examplecra-template-peott-sas-graphql-playgroundps-apollo-clientreact-garden@n1ru4l/apollo-link-rest@ppci/utils@ppci-mock/utils@niama/api@niama/api-clientjsonfarmer@viableone/apollo-link-state@wirelineio/apollo-link-kappa@travelshift/admin@jokio/apollo-link-state@machinemetrics/mm-react-tools@litcrits/web-componentsinferno-apollo@scribalous/react-hookshtv-sdk@tgrx/apollo-link-state@swarm/db@vivid-planet/comet-admin-stories@scottburch/graphql-gunlpdesignsystem@alexlit/apollo-vue-kit@alexlit/vue-apollo-kit@zalastax/nolb-graphql-aapollo-aprovaapollo-cache-core-inmemoryapollo-client-classicapollo-client-cors-hackapollo-cache-inmemory-forkgeoxapollo-link-rest-vmapollo-link-algoliaapollo-contentful-rest-linkapollo-link-computedapollo-link-contentfulapollo-link-ethereum-resolver-ethersjsapollo-link-ethereum-resolver-web3jsapollo-link-firebase-firestoreapollo-fragment-list-linkapollo-link-pouchapollo-link-pouchdbapollo-link-preloadapollo-link-ethereumapollo-link-stateapollo-link-state-lazygroundline-graphql-interface@beardfury/apollo-clientgraphql-query-matchgraphql-gungraphql-mini-koagraphql-fragmentsgraphql-cssgraphql-anywhere-mongodbmoda-web-librarymhymhydmm-react-toolsmonoquerybit-portalmizansen
4.2.8

2 years ago

4.2.7

4 years ago

4.2.6

4 years ago

4.2.4

5 years ago

4.2.3

5 years ago

4.2.2

5 years ago

4.2.2-rc.3

5 years ago

4.2.2-rc.2

5 years ago

4.2.2-rc.1

5 years ago

4.2.2-beta.6

5 years ago

4.2.2-beta.5

5 years ago

4.2.2-beta.1

5 years ago

4.2.2-beta.0

5 years ago

4.2.1

5 years ago

4.2.0

5 years ago

4.2.0-rc.2

5 years ago

4.2.0-rc.1

5 years ago

4.2.0-rc.0

5 years ago

4.1.28

5 years ago

4.2.0-beta.1

5 years ago

4.2.0-beta.0

5 years ago

4.1.27

5 years ago

4.1.26

5 years ago

4.1.25

5 years ago

4.2.0-alpha.9

5 years ago

4.2.0-alpha.8

5 years ago

4.2.0-alpha.7

5 years ago

4.2.0-alpha.5

5 years ago

4.2.0-alpha.3

5 years ago

4.1.24

5 years ago

4.2.0-alpha.2

5 years ago

4.1.23

5 years ago

4.2.0-alpha.1

5 years ago

4.1.22

5 years ago

4.1.21

6 years ago

4.1.20

6 years ago

4.1.19

6 years ago

4.1.18

6 years ago

4.2.0-verify.4

6 years ago

4.2.0-verify.3

6 years ago

4.2.0-verify.2

6 years ago

4.2.0-verify.1

6 years ago

4.1.17

6 years ago

4.1.16

6 years ago

4.1.15-alpha.14

6 years ago

4.1.15-alpha.13

6 years ago

4.1.15-alpha.12

6 years ago

4.1.15-alpha.10

6 years ago

4.1.15-alpha.9

6 years ago

4.1.15-alpha.8

6 years ago

4.1.15-alpha.7

6 years ago

4.1.15-alpha.6

6 years ago

4.1.15-alpha.5

6 years ago

4.1.15-alpha.4

6 years ago

4.1.15-alpha.3

6 years ago

4.1.15

6 years ago

4.1.15-alpha.2

6 years ago

4.1.15-alpha.1

6 years ago

4.1.14

6 years ago

4.1.13

6 years ago

4.1.12

6 years ago

4.1.11

6 years ago

4.1.10

6 years ago

4.1.9

6 years ago

4.1.8

6 years ago

4.1.7

6 years ago

4.1.6

6 years ago

4.1.5

6 years ago

4.1.4

6 years ago

4.1.3

6 years ago

4.1.2

6 years ago

4.1.1

6 years ago

4.1.0

6 years ago

4.1.0-alpha.0

6 years ago

4.0.2

6 years ago

4.0.1

6 years ago

4.0.0

7 years ago

4.0.0-rc.3

7 years ago

4.0.0-rc.2

7 years ago

4.0.0-rc.1

7 years ago

4.0.0-rc.0

7 years ago

4.0.0-beta.5

7 years ago

4.0.0-beta.4

7 years ago

4.0.0-beta.3

7 years ago

4.0.0-beta.2

7 years ago

4.0.0-beta.1

7 years ago

4.0.0-beta.0

7 years ago

4.0.0-alpha.6

7 years ago

4.0.0-alpha.5

7 years ago

4.0.0-alpha.4

7 years ago

4.0.0-alpha.3

7 years ago

4.0.0-alpha.2

7 years ago

4.0.0-0

7 years ago

3.1.0

7 years ago

3.0.1

7 years ago

3.0.0

7 years ago

2.2.0

7 years ago

2.1.0

7 years ago

2.0.0

7 years ago

1.1.2

7 years ago

1.1.1

7 years ago

1.1.0

7 years ago

1.0.0

7 years ago

0.3.0

7 years ago

0.2.4

8 years ago

0.2.3

8 years ago

0.2.2

8 years ago

0.2.1

8 years ago

0.2.0

8 years ago

0.1.14

8 years ago

0.1.13

8 years ago

0.1.12

8 years ago

0.1.11

8 years ago

0.1.10

8 years ago

0.1.9

8 years ago

0.1.8

8 years ago

0.1.7

8 years ago

0.1.6

8 years ago

0.1.5

8 years ago

0.1.4

8 years ago

0.1.3

8 years ago

0.1.2

8 years ago

0.1.1

8 years ago

0.1.0

8 years ago