GraphQL: Is It Hindering Developer Experience?

Recently, when looking at Kris’ “Near Polywrapper and Plugin” write-up, the following code-snippet had me second guessing the “developer friendliness” of GraphQL for dApp developers:

const result = await client.query<{ findAccessKey: AccessKeyInfo }>({
  uri: "w3://ens/near-api.web3api.eth",
  query: `query {
      accountId: $accountId
  variables: {
    accountId: "polywraptest.testnet",

The following code snippet seems a bit more intuitive to me (and it’s already supported):

const result = await client.invoke<AccessKeyInfo>({
  uri: "w3://ens/near-api.web3api.eth",
  module: "query",
  method: "findAccessKey",
  input: {
    accountId: "polywraptest.testnet"

Invoke’s Downsides

  1. With invoke, you cannot perform multiple parallel queries. Instead the user would have to call the function more than once and wait on them in parallel. We could however make the invoke function variadic, where you can pass in multiple invoke option structures to perform in parallel.
  2. Other downsides?

Take Aways

  1. We should not treat GraphQL as a “core” part of the client specification. This will be helpful when implementing the Polywrap Client in other languages like Rust, where GraphQL feels like a needless dependency.
  2. The invoke interface should be the standard. In our documentation, and other materials, we should be showing the invoke interface instead of the query interface.
  3. Treat GraphQL as an added layer on-top of the client that is optional. For example, if I’m a web developer who loves using GraphQL with the ApolloClient, I should be able to do so using a GraphQL wrapper on-top of the Polywrap client. Package names might look like:
    • @polywrap/client-js
    • @polywrap/graphql-client-js
    • @polywrap/graphql-apollo-link
  4. In languages where GraphQL is less popular, we do not have to bother with this dependency.
  5. GraphQL will primarily be used as an IDL (Interface Definition Language), and nothing more. In the future we may roll our own IDL which better conforms to how we want things to work.



I 100% agree that the invoke syntax is better. It’s cleaner and shorter. The graphql syntax can feel verbose and at times I’ve made typos in graphql strings that wouldn’t happen with the invoke syntax.

For the JS/TS client, it would be cool to eventually support something like Web3API Client Type Extensions for plugins, API wrappers · Issue #314 · polywrap/monorepo · GitHub for a syntax like:

const { data, errors } = await client.near.findAccessKey({
  accountId: "polywraptest.testnet",

This sort of syntax is possible in Hardhat but extension code needs to be written or automatically generated. There are similar features in libraries like Ethers where developers can load a smart contract ABI and call the contract’s functions like contract.myFunction(...).