urql vs @octokit/graphql vs apollo-client vs graphql-request vs graphql-tag
GraphQL Client Libraries for Frontend Applications
urql@octokit/graphqlapollo-clientgraphql-requestgraphql-tagSimilar Packages:

GraphQL Client Libraries for Frontend Applications

@octokit/graphql, apollo-client, graphql-request, graphql-tag, and urql are all JavaScript packages that help frontend applications interact with GraphQL APIs, but they serve different scopes and use cases. @octokit/graphql is a specialized client for GitHub’s GraphQL API. apollo-client and urql are full-featured GraphQL clients that handle caching, state management, and real-time updates. graphql-request is a minimal, promise-based HTTP client for making GraphQL queries without extra features. graphql-tag is not a client at all — it’s a utility for parsing GraphQL query strings into AST documents, typically used alongside Apollo or other clients.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
urql583,0088,935135 kB356 months agoMIT
@octokit/graphql049228.8 kB144 months agoMIT
apollo-client019,725-4226 years agoMIT
graphql-request06,116420 kB462 months agoMIT
graphql-tag02,339-1004 years agoMIT

GraphQL Clients Compared: When to Use Which Tool

Frontend developers today have several options for talking to GraphQL APIs. The five packages — @octokit/graphql, apollo-client, graphql-request, graphql-tag, and urql — might seem similar at first glance, but they solve very different problems. Let’s cut through the confusion with real code and clear trade-offs.

🎯 Core Purpose: What Each Package Actually Does

It’s critical to understand that not all of these are full GraphQL clients.

  • @octokit/graphql: A thin wrapper around fetch tuned only for GitHub’s GraphQL API.
  • apollo-client: A complete state management and data fetching system built around GraphQL.
  • graphql-request: A minimal HTTP client that sends POST requests to any GraphQL endpoint.
  • graphql-tag: A parser that turns GraphQL query strings into AST objects — no networking involved.
  • urql: A reactive, stream-based GraphQL client focused on simplicity and performance.

If you try to use graphql-tag to make a network request, it won’t work — it’s just a parser. Similarly, @octokit/graphql won’t help you talk to your company’s internal GraphQL API unless it’s GitHub.

📡 Making a Basic Query: Code Comparison

Here’s how each package (that supports it) sends a simple query.

@octokit/graphql (GitHub-only)

import { graphql } from "@octokit/graphql";

const { repository } = await graphql(
  `
    query {
      repository(owner: "octokit", name: "graphql.js") {
        description
      }
    }
  `,
  {
    headers: {
      authorization: `token ${process.env.GITHUB_TOKEN}`
    }
  }
);

Note: You must provide a GitHub token. This won’t work with other GraphQL servers.

apollo-client

import { ApolloClient, InMemoryCache, gql, HttpLink } from "@apollo/client";

const client = new ApolloClient({
  link: new HttpLink({ uri: "/graphql" }),
  cache: new InMemoryCache()
});

const GET_USER = gql`
  query GetUser($id: ID!) {
    user(id: $id) {
      name
    }
  }
`;

const { data } = await client.query({
  query: GET_USER,
  variables: { id: "123" }
});

Apollo requires setup (cache, link) and uses gql (from graphql-tag) to parse queries.

graphql-request

import { request, gql } from "graphql-request";

const query = gql`
  query GetUser($id: ID!) {
    user(id: $id) {
      name
    }
  }
`;

const data = await request("/graphql", query, { id: "123" });

This is the simplest: one function call, no setup. The gql here is a convenience tag that just returns the string — it doesn’t parse to AST.

urql

import { createClient, gql } from "urql";

const client = createClient({
  url: "/graphql"
});

const GET_USER = gql`
  query GetUser($id: ID!) {
    user(id: $id) {
      name
    }
  }
`;

const result = await client.query(GET_USER, { id: "123" }).toPromise();

Urql uses a similar gql tag (also from graphql-tag under the hood) and returns an observable, so you call .toPromise() for async/await.

graphql-tag (Not a client!)

import gql from "graphql-tag";

const query = gql`
  query GetUser($id: ID!) {
    user(id: $id) {
      name
    }
  }
`;

// query is now a parsed AST object
// You still need a client (like Apollo or urql) to send it

This does nothing on its own. It’s just a build-time or runtime parser.

🧠 Caching and State Management

This is where the big divide appears.

  • apollo-client: Offers normalized caching. If two queries return the same User object, Apollo stores it once by ID and keeps all references in sync. Supports cache updates, optimistic responses, and garbage collection.

  • urql: Provides document caching by default (caches full query results), with optional graph cache (similar to Apollo’s normalization) via @urql/exchange-graphcache. Simpler but less automatic than Apollo.

  • graphql-request, @octokit/graphql, and graphql-tag: No caching at all. Every request hits the network.

If your app shows the same user profile in multiple places and you update it in one, you’ll need Apollo or urql to keep everything consistent without manual refetching.

⚙️ Setup Complexity and Bundle Impact

  • Lowest friction: graphql-request — one import, one function call.
  • Medium: urql — requires client creation but minimal config.
  • Highest: apollo-client — needs cache, link, and often context providers in React.
  • Specialized: @octokit/graphql — trivial setup, but only for GitHub.
  • None: graphql-tag — just a utility.

For a small marketing site that fetches blog posts once, graphql-request is overkill to replace with Apollo. But for a dashboard with live data, Apollo’s complexity pays off.

🔁 Subscriptions and Real-Time Data

  • apollo-client: Full subscription support via WebSocket links.
  • urql: Built-in subscription support using the same stream model.
  • graphql-request, @octokit/graphql, graphql-tag: No subscription support. They’re HTTP-only.

If you need live updates (e.g., chat messages, stock prices), stick with Apollo or urql.

🧩 React Integration

All three full clients (apollo-client, urql, and even graphql-request via custom hooks) work with React, but:

  • Apollo has useQuery, useMutation, and ApolloProvider.
  • Urql has useQuery, useMutation, and Provider.
  • graphql-request requires you to write your own useEffect + useState hook.

Example with graphql-request in React:

function UserProfile({ id }) {
  const [user, setUser] = useState(null);
  useEffect(() => {
    request("/graphql", GET_USER, { id }).then(setUser);
  }, [id]);
  return <div>{user?.name}</div>;
}

Compare that to Apollo:

function UserProfile({ id }) {
  const { data } = useQuery(GET_USER, { variables: { id } });
  return <div>{data?.user.name}</div>;
}

Apollo gives you loading/error states, caching, and refetching out of the box.

🛑 Deprecation and Maintenance Status

As of the latest official sources:

  • All five packages are actively maintained.
  • None are deprecated.
  • graphql-tag is stable and unlikely to change — it’s a small, focused utility.

✅ When to Combine Packages

It’s common to use graphql-tag alongside apollo-client or urql, because both expect parsed AST documents. In fact, Apollo’s gql is just a re-export of graphql-tag.

You would never combine @octokit/graphql with another GraphQL client — it’s a standalone tool for one specific API.

📊 Summary Table

PackageFull Client?CachingSubscriptionsGitHub Only?React Hooks
@octokit/graphql❌ (HTTP only)
apollo-client✅ (normalized)
graphql-request❌ (HTTP only)❌ (manual)
graphql-tag❌ (parser only)
urql✅ (document/graph)

💡 Final Guidance

  • Building a GitHub bot or integration?@octokit/graphql
  • Need enterprise-grade data sync, caching, and tooling?apollo-client
  • Just fetching data once in a script or simple page?graphql-request
  • Writing queries for Apollo or urql?graphql-tag (as a helper)
  • Want something lighter than Apollo but still reactive?urql

Don’t over-engineer: if you don’t need caching or real-time updates, skip the heavy clients. But if your app lives and breathes data, invest in Apollo or urql early — it’s hard to retrofit later.

How to Choose: urql vs @octokit/graphql vs apollo-client vs graphql-request vs graphql-tag

  • urql:

    Choose urql when you want a lightweight, stream-based GraphQL client with good caching and React support but less complexity than Apollo. It’s well-suited for medium-sized apps that need real-time updates (via subscriptions) and cache invalidation without heavy configuration.

  • @octokit/graphql:

    Choose @octokit/graphql only when you’re building integrations specifically with GitHub’s GraphQL API. It handles authentication with GitHub tokens and simplifies requests to their endpoint, but it doesn’t support caching, subscriptions, or general-purpose GraphQL usage. Avoid it for non-GitHub APIs.

  • apollo-client:

    Choose apollo-client when you need a mature, feature-rich GraphQL client with normalized caching, optimistic UI, local state management, and strong React integration. It’s ideal for large applications where data consistency, offline support, and complex querying patterns matter, though it comes with more bundle size and configuration overhead.

  • graphql-request:

    Choose graphql-request when you want the simplest possible way to send GraphQL queries over HTTP without caching, state management, or reactivity. It’s perfect for small apps, scripts, or server-side usage where you just need to fetch data once and move on.

  • graphql-tag:

    Choose graphql-tag only as a companion tool when working with Apollo Client or similar libraries that expect GraphQL queries as parsed AST documents. It should never be used alone — it doesn’t make network requests. Its main role is enabling syntax highlighting and static analysis in tooling.

README for urql

urql

A highly customizable and versatile GraphQL client for React

More documentation is available at formidable.com/open-source/urql.