{"id":15628010,"url":"https://github.com/abhiaiyer91/graphql-client-exploration","last_synced_at":"2025-04-28T19:43:17.392Z","repository":{"id":75592823,"uuid":"121566138","full_name":"abhiaiyer91/GraphQL-Client-Exploration","owner":"abhiaiyer91","description":"Simple exploration of GraphQL Clients","archived":false,"fork":false,"pushed_at":"2023-12-15T14:58:02.000Z","size":6587,"stargazers_count":18,"open_issues_count":1,"forks_count":3,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-01-09T18:24:09.064Z","etag":null,"topics":["apollo-client","apollo-fetch","fetchql","graphql","graphql-request","lokka","micro-graphql-react","relay-modern","urql"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/abhiaiyer91.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2018-02-14T21:58:55.000Z","updated_at":"2022-02-06T15:52:28.000Z","dependencies_parsed_at":"2023-03-03T11:45:50.988Z","dependency_job_id":null,"html_url":"https://github.com/abhiaiyer91/GraphQL-Client-Exploration","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abhiaiyer91%2FGraphQL-Client-Exploration","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abhiaiyer91%2FGraphQL-Client-Exploration/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abhiaiyer91%2FGraphQL-Client-Exploration/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abhiaiyer91%2FGraphQL-Client-Exploration/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/abhiaiyer91","download_url":"https://codeload.github.com/abhiaiyer91/GraphQL-Client-Exploration/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":234138174,"owners_count":18785368,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["apollo-client","apollo-fetch","fetchql","graphql","graphql-request","lokka","micro-graphql-react","relay-modern","urql"],"created_at":"2024-10-03T10:20:31.546Z","updated_at":"2025-01-16T02:06:58.777Z","avatar_url":"https://github.com/abhiaiyer91.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# GraphQL Client Exploration\n\nCheckout this in detail on https://medium.com/open-graphql/exploring-different-graphql-clients-d1bc69de305f\n\n## Getting Started\n\n### Starting the server\n\n`cd packages/server`\n\n`yarn start`\n\n\n### Starting the client\n\n`cd packages/client`\n\n`yarn start`\n\n# Musings\n\nOver the past few weeks a lot of new GraphQL clients were released aiming to give Relay and Apollo a run for their money! It's true, there is still plenty of areas to explore when it comes to GraphQL clients and I hope the result of all this is a \"melting pot\" of advances in developer experience.\n\nI wanted to study how these clients work, so I aimed to implement the same query using different clients and share my experience.\n\nThe GraphQL Clients we explored are:\n\n* FetchQL\n* GraphQL Request\n* Apollo Fetch\n* Lokka\n* Micro GraphQL React\n* URQL\n* Apollo Client\n* Relay Modern\n\nAll were using React as the view layer.\n\nBefore we get into the different clients I'd like to explain what exactly a GraphQL client is.\n\nIf you boil it down to the basics, a GraphQL client is code that makes a POST request to a GraphQL Server. In the body of the request we send a GraphQL query or mutation as well as some variables and we expect to get some JSON back.\n\n```graphql\nquery example($someVariable: String) {\n  someField(someVariable: $someVariable) {\n    field1\n    field2\n  }\n}\n```\n\n```bash\ncurl -XPOST -H \"Content-Type:application/json\"  -d 'query hello { helloWorld }' http://localhost:3000/graphql\n```\n\nFrom my experience there are 2 main types of GraphQL Clients.\n\n### Fetch Client\nA fetch client handles sending GraphQL queries/mutation and variables to the GraphQL server in an ergonomic way for the developer.\n\n### Caching Client\nA caching client does the same thing as a fetch client, but includes a way for the application to store data in memory. These clients are built to reduce network trips the application makes and provides a helping hand in managing application state. With a caching client you can keep your data layer concerns separate from your view layer.\n\n\nYou should use a GraphQL client for work that sits at an agnostic layer of your application. You shouldn't have to worry about networking details or roll your own cache for the query results.\n\n\nNow that we had a brief rundown on what a GraphQL Client is, lets start exploring some clients.\n\nOur goal is make this query:\n\n```\nquery hello {\n  helloWorld\n}\n```\n\nand discuss our experience.\n\n// put github link\n\n## FetchQL\n\nThe first client I took a look at was FetchQL. This client is a super basic fetch client!\n\n```js\nimport FetchQL from \"fetchql\";\n\nconst client = new FetchQL({ url: \"/graphql\" });\n\nclient.query({ operationName: 'hello', query: helloWorldQuery }).then(result =\u003e {\n  // do something with the result\n});\n```\n\nHooking requests in a React components `componentDidMount`, you can set state to your component pretty easily!\nWhat I really did not like about this client was the friction with adding an `operationName` to the query. Ideally\nyou should be able to read that from the query itself, but thats okay. Still very easy to get up and running.\n\n## GraphQL Request\n\nNext we took a look at another fetch client. `graphql-request` from my friends at graphcool is just a convenient interface over the `fetch` api.\n\n```js\nimport { GraphQLClient } from \"graphql-request\";\n\nconst client = new GraphQLClient(\"/graphql\");\n\nthis.client.request(helloWorldQuery).then(data =\u003e {\n  // do something\n});\n```\n\nNothing else to it! Pretty simple. I usually use this library for server to server graphql communication! Only critique on `graphql-request` is that it wasn't immediately obvious that I can pass any options `fetch` supports (it does). So all this needs is a little documentation upgrade!\n\n## Apollo Fetch\n\nSo when it comes to Apollo I am extremely biased haha. Apollo Fetch is but a small cog in the overall GraphQL client that is Apollo Client. But, you can still use it as a dead simple fetch client!!\n\n```js\nimport { createApolloFetch } from \"apollo-fetch\";\n\nconst uri = \"/graphql\";\n// create a fetcher\nconst fetcher = createApolloFetch({ uri });\n\nfetcher({ query: helloWorldQuery }).then(result =\u003e {\n  // do something\n});\n```\n\nI'm starting to see a trend between all these fetch clients. Most of them use the `fetch` api and operate the same way. I'm starting to wonder why they exist?\n\n## Micro GraphQL React\nMicro GraphQL is created by Adam Rackis with the aim to have a simple client to connect React components to a GraphQL Server. It uses `fetch` with `HTTP Get` for queries and uses `graphql-request` to handle mutations. It has a built in cache stored at the component level.\n\nLet's show an example:\n\n```js\nimport React from \"react\";\nimport { Client, query } from \"micro-graphql-react\";\nimport { helloWorldQuery } from \"../queries\";\n\nconst client = new Client({\n  endpoint: \"/graphql\"\n});\n\nclass MicroGraphQL extends React.Component {\n  render() {\n    const { loading, data } = this.props;\n\n    if (loading) {\n      return \u003cp\u003e Loading your MicroGraphQL data...\u003c/p\u003e;\n    }\n\n    return \u003cp\u003e{data \u0026\u0026 data.helloWorld} from Micro GraphQL React\u003c/p\u003e;\n  }\n}\n\nexport default query(client, props =\u003e ({\n  query: helloWorldQuery\n}))(MicroGraphQL);\n```\n\nInstantiate a client instance like we're used to, then wrap the component in a `query` container to handle the fetch of the query and pass the `data` as props to the component. Under the hood, the component uses a `Map` to set the cache at the component level.\n\nThe caching goal here is to actually use a tool like Google's Workbox, or sw-toolbox to take the response from the HTTP requests and cache results there.\n\nMy critique for this client is the need to pass the client instance into the container component every time I need to make a query. Maybe with the new React Context API this can be passed a lot easier to child components! Also the caching at the component level for me is a little limiting, but this library clearly aims to solve this a certain way and thats okay!\n\n\n## Lokka\nLokka by my friend Arunoda was one of the first clients aside from relay classic back before even Apollo existed! It also heavily inspired some of the clients you see today. I think the distinguishing factor is its separation of the \"transport\" or \"network\" interface e.g. \"over what protocol are these requests going through?\" and the actual mechanism by which results are cached. When we look at caching clients today they are very modular and I give props to Arunoda for being forward thinking. When you separate the network interface from the client code you give engineers the ability to send GraphQL requests over different protocols! Like Websockets or whatever else you want! I think it's also safe to assume that if you're using a fetch client, you're probably speaking HTTP!\n\nLet's setup Lokka, it's super easy\n\n```js\nimport { Lokka } from \"lokka\";\nimport { Transport } from \"lokka-transport-http\";\n\nconst client =  new Lokka({\n  transport: new Transport(\"/graphql\")\n});\n\nclient.query(helloWorldQuery).then(result =\u003e {\n  // do something\n});\n```\n\nLokka has a built in cache when using the `watchQuery` API.\n\n```js\n// watch the query\nconst watchHandler = (err, payload) =\u003e {\n  if (err) {\n    console.error(err.message);\n    return;\n  }\n\n  // do something when the cache updates\n};\n\nclient.watchQuery(helloWorldQuery, {}, watchHandler);\n```\n\nAny time the Lokka cache is updated, the registered handler function is called. This allows you to do a lot of different things in your UI in response to cache updates!\n\n## URQL\nURQL by Formidable Labs is a GraphQL client aiming to make the client side GraphQL workflow as simple as possible. Under the hood this uses a `fetch` api to handle the fetch client.\n\nLet's write a simple example:\n\nFirst we need to setup a Provider to pass the `client` instance to child components.\n```js\nimport React from \"react\";\nimport { Provider, Client } from \"urql\";\nimport { helloWorldQuery } from \"../queries\";\n\nconst client = new Client({\n  url: \"/graphql\"\n});\n\nexport default function Root() {\n  return (\n    \u003cProvider client={client}\u003e\n    \u003c/Provider\u003e\n  );\n}\n```\n\nNext we need to use the `Connect` and `query` components to bind data to a component.\n`Connect` uses the `render prop` pattern.\n\n```js\n\u003cConnect query={query(helloWorldQuery)}\u003e\n  {({ loaded, refetch, data }) =\u003e {\n    // write UI in here\n  }}\n\u003c/Connect\u003e\n```\n Don't like render props? You can use the `ConnectHOC` to do the same thing!\n\n```js\nexport default ConnectHOC({\n  query: query(helloWorldQuery)\n})(MyComponent);\n```\nThats it!\n\nIn regards to caching and control of that cache, URQL does a great job of exposing invalidation apis! The URQL cache is based on the `__typename` field in a GraphQL response. You can invalidate the cache pretty easily by passing a function, `shouldInvalidate`.\n\n```js\nshouldInvalidate={(changedTypenames, typenames, mutationResponse, data) =\u003e {\n  return data.todos.some(d =\u003e d.id === mutationResponse.id);\n}}\n```\n\n## Apollo Client\nApollo Client is a sophisticated caching GraphQL client.\n\nTaken from my `How to GraphQL` course:\n\n\"Apollo Client is a community-driven effort to build an easy-to-understand, flexible and powerful GraphQL client. Apollo has the ambition to build one library for every major development platform that people use to build web and mobile applications. Right now there is a JavaScript client with bindings for popular frameworks like React, Angular, Ember or Vue as well as early versions of iOS and Android clients. Apollo is production-ready and has handy features like caching, optimistic UI, subscription support and many more.\"\n\nNow I think the real reason a lot of these other clients came out was due to the complexity Apollo client was creating in response to bigger engineering teams using their software. Growing complexity is totally fine especially when you need to support tons of use cases, but through cycles of building engineers should come back to simplify and extend what they've built.\n\nSo while in Apollo v1, things were very easy to get up and running with, there have been critiques in the configuration overhead of v2.\n\nv2 introduced concepts of custom cache control and networking layer. Which in my opinion is amazing if you are a large engineering team with custom use cases. But... if you are beginner or just trying to get something up and running, Apollo Client was becoming a big turnoff. Until now...\n\nA couple days ago Peggy Rayzis released `Apollo Boost`.\n\nWhat is Apollo Boost? Zero-config GraphQL state management. Dead simple.\n\nYou don't have to configure anything. the network layer? a `http-link` preconfigured for you. Cache? Apollo's fast `inMemoryCache` already setup. You just need to build now, let's do that:\n\nFirst we import the `ApolloClient` constructor from `apollo-boost`. This has the client you need preconfigured with a cache and network interface. Then we get the `ApolloProvider` component to pass down the `client` to all child components.\n\n```js\nimport React from \"react\";\nimport ApolloClient from \"apollo-boost\";\nimport { ApolloProvider } from \"react-apollo\";\n\n// Pass your GraphQL endpoint to uri\nconst client = new ApolloClient({ uri: \"/graphql\" });\n\nexport default function Root() {\n  return (\n    \u003cApolloProvider client={client}\u003e\n      \u003cApp /\u003e\n    \u003c/ApolloProvider\u003e\n  );\n}\n```\nNext we make a component.\n\nReact Apollo in vNext, uses a `Query` component that provides a `render prop`.\n\n```js\nimport { gql } from \"apollo-boost\";\nimport { Query } from \"react-apollo\";\nimport { helloWorldQuery } from \"../queries\";\n\nconst query = gql(helloWorldQuery);\n\nconst App = () =\u003e (\n  \u003cQuery query={query}\u003e\n    {({ loading, error, data }) =\u003e {\n      if (loading) return \u003cp\u003eLoading data from Apollo Client...\u003c/p\u003e;\n\n      return \u003cp\u003e{data \u0026\u0026 data.helloWorld} from Apollo Client\u003c/p\u003e;\n    }}\n  \u003c/Query\u003e\n);\n```\n\nDon't like render props? You can use the `graphql` HOC:\n\n```js\nexport default graphql(gql(helloWorldQuery))(MyComponent);\n```\n\nWe see something similar here between Apollo and URQL. They both wrap their GraphQL query/mutation with a wrapping function.\n\n`URQL` has `query` that wraps the query\n`Apollo Client` uses the `graphql-tag` library to wrap the query.\n\nWhen you're dealing with a fetch client, all you really need is a GraphQL string and a post request to a server. The server will then parse validate and execute that query. When you're dealing with a sophisticated cache on the client side, working with strings suck. You really want to have a structured object to work with. So these 2 ways take a GraphQL string and turn it in a GraphQL AST representing that string. Then library authors can manipulate them a lot easier!\n\n## Relay Modern\nRelay Modern developed by Facebook is a GraphQL client with performance as it's main objective. Graduating from it's previous iteration, Relay Classic, Relay Modern aims to improve on it's API and reduce the overall size.\n\nTo get started though, you do have to jump through a few hoops:\n\nYou'll need 3 libraries:\n\n`react-relay` for the React integration!\n\n`relay-compiler` and `babel-plugin-relay` to enable a ahead of time compilation of queries/mutations\n\nMuch like the other GraphQL clients, we need to setup our `network` interface and `cache`. In Relay this is expressed as the `Environment`.\n\n```js\nimport { Environment, Network, RecordSource, Store } from \"relay-runtime\";\n\nconst store = new Store(new RecordSource());\n\nconst network = Network.create((operation, variables) =\u003e {\n  return fetch(\"/graphql\", {\n    method: \"POST\",\n    headers: {\n      Accept: \"application/json\",\n      \"Content-Type\": \"application/json\"\n    },\n    body: JSON.stringify({\n      query: operation.text,\n      variables\n    })\n  }).then(response =\u003e {\n    return response.json();\n  });\n});\n\nexport default new Environment({\n  network,\n  store\n});\n```\n\nRelay comes with some out of the box components you can configure. We create a `store` to hold out results and create a network interface using `fetch`. But you could technically insert any fetch client in the Network create function.\n\nOkay now let's render a query:\n\n```js\nimport React from \"react\";\nimport { QueryRenderer, graphql } from \"react-relay\";\nimport environment from \"./environment\";\n\nconst query = graphql`\n  query hello {\n    helloWorld\n  }\n`;\n\nexport default function Root() {\n  return (\n    \u003cQueryRenderer\n      environment={environment}\n      query={query}\n      render={({ error, props }) =\u003e {\n        if (error) {\n          return \u003cp\u003e{error.message}\u003c/p\u003e;\n        } else if (props) {\n          return \u003cp\u003e{props.helloWorld} from Relay Modern\u003c/p\u003e;\n        }\n        return \u003cp\u003eLoading your Relay Modern data...\u003c/p\u003e;\n      }}\n    /\u003e\n  );\n}\n```\n\nWe can see come commonalities here:\n\n1. The use of `graphql` to take a query string and use a GraphQL AST under the hood.\n2. The `QueryRenderer`, much like `Query` or `Connect` that allows you to render a component based on data.\n\nThing that felt off:\n\nBefore starting the application I needed to run the relay compiler:\n\n`relay-compiler --src ./src --schema ./schema.graphql` pointing to my schema in the server folder. If building a Relay app, you should probably keep your schema accessible in a shared place!\n\nAfter running this compiler I got an error!\n\n```\nOperation names in graphql tags must be prefixed with the module name and end in \"Mutation\", \"Query\", or \"Subscription\". Got `hello` in module `relayModern`.\n```\n\nI stared this error and was really taken a back. Seems pretty annoying to have to do this, but meh, let's keep going:\n\nChange up my query to this:\n\n```js\n\nconst query = graphql`\n  query relayModernhelloQuery {\n    helloWorld\n  }\n`;\n```\n\nRun the compiler now, run the app, all is good! Relay is a powerful tool but I think the least approachable out of all the GraphQL Clients out there. The client has different concerns and tons of use cases to back it up, so I have no problem with its design!\n\n# Conclusion\n\nSo having explored these clients I've come to realize a few things:\n\n1. We have too many fetch clients out there. Many of the clients that just help you do POST requests via `fetch` aren't really providing any extra value. I'd probably stick to recommending `graphql-request`, or if you're more familiar with Apollo using `apollo-fetch`.\n\n2. Clients that do support caching are relatively doing things similarly. Between Apollo, and Relay you can clearly see a modular separation of concerns between the `networking` and the `cache` details.\n\n3. Users of clients like `URQL` and `Micro GraphQL` are looking for a client that is easy to configure and work with right away.\n\n4. The key for new user adoption is a client that handles these concerns for you with escape hatches for customization when needed. I'm super excited for `Apollo Boost` and there's a project that is similar for relay https://github.com/releasy/react-releasy\n\nI'm so happy the community is coming together around different ideas and making things easier for engineers going forward!\n\nCheers.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fabhiaiyer91%2Fgraphql-client-exploration","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fabhiaiyer91%2Fgraphql-client-exploration","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fabhiaiyer91%2Fgraphql-client-exploration/lists"}