The most powerful GraphQL Client for the web in just 2kb - WunderGraph
State of GraphQL Federation 2026
CEO & Co-Founder at WunderGraph
May 12, 2021·17min read
Last updated on September 9, 2025
Archive Notice
This article is archived and no longer maintained. It describes an earlier version of WunderGraph, including its client and server architecture, which is no longer part of the current product. The comparisons and implementation details may not reflect the current state of GraphQL tooling or WunderGraph. For current product information, see WunderGraph Cosmo
I claim that WunderGraph is by far the most powerful GraphQL client for the modern web. Actually it's not just a good client for GraphQL but also for REST APIs, but that's not the point here.
How to judge if an API client is any good?
When talking about building for the modern web, I mean frameworks like NextJS, React, Vue, Svelte etc...
So what are the most important capabilities, a modern API client should bring to the table?
Here's a list of categories that should be important to you when building modern web applications.
Developer Productivity
The number one criteria for me is developer productivity. API clients should support developers as best as they can, taking away as much of the heavy lifting as they can. Type safety and easy to use APIs are good examples.Respect the fundamentals of the web
The web is a very powerful platform to build on. A good API client makes good use of existing infrastructure and APIs. Browsers offer some extremely powerful tools like caching which an API client shouldn't dismiss.Integrate well with OIDC & OAuth2
Most if not all applications require some sort of authentication and authorization. A good API client integrates well with both protocols to help developers build login- and authorization flows.Secure
This item should actually be on the first item of the list. An API client should help developers to avoid and combat common threats, e.g. the OWASP top 10.Performant & Lightweight
Last but not least, a client should be as performant as possible without interfering with developer productivity. Page load times are important. For many websites, lower latency means more money earned. An API client should never stay in the way of building high performance applications.
Results
Before diving into the different aspects of what makes a good GraphQL client, let's have a quick look at the raw numbers.
This post is accompanied by a repository with a branch for each client. You can checkout each branch and run the bundle analyzer yourself.
| Client | gzip | difference | Time to Interactive | Largest Contentful Paint |
|---|---|---|---|---|
| NextJS | 102.99kb | - | 1.8s | 1.9s |
| Apollo | 146.28kb | 43.29kb | 2.3s | 2.8s |
| graphql-request | 110.62kb | 7.63kb | 2.1s | 2.6s |
| urql | 119.94kb | 16.95kb | 2.2s | 2.4s |
| WunderGraph | 105.31kb | 2.3kb | 1.9s | 2.1s |
Time to Interactive and Largest Contentful Paint was measured using Chrome Lighthouse test.
1. Developer productivity
Now let's have a look at how different clients affect productivity.
Project setup
Unsurprisingly, all clients make it very easy to get started. You can easily find documentation to help you with the first steps. If you look at the repository, you'll see that setting up all clients is very much straight forward.
Apollo, urql and WunderGraph use the provider pattern for dependency injection. graphql-request doesn't require this step, making it the easiest library to get started.
Hello World - your first Query
Apollo, urql and graphql-request are very intuitive, when it comes to writing your first Query.
You can define the Query side-by-side with your Components and use one of their hooks to make the first request.
Example using urql:
const [result] = useQuery({ query: `
{
posts {
title
content
}
}
` });
WunderGraph takes a different route here. You have to write all Queries in one .graphql file, located in the .wundergraph directory. It's also strictly required to give all operations a name. This name will be used to generate a typesafe React hook.
Example using WunderGraph:
query GetPosts {
posts {
title
content
}
}
TypeScript support
Supporting TypeScript nowadays is essential for a good developer experience. It prevents a lot of bugs and helps developers better understands the code.
Apollo, urql and graphql-request support TypeScript in that you're allowed to attach type definitions to query hooks. How you get those type definitions is up to the developer. You could write them by hand or use an additional code generator. There's a good change this extra step results in inconsistencies. Ideally, you wouldn't have to add extra dependencies for such a basic requirement.
With WunderGraph, I didn't want to accept the status quo and adopted a pattern from Relay, compiling Queries! WunderGraph ships out of the box with a Query Compiler / Code Generator. You can start it with one single command: wunderctl up
All you have to do is write a Query with a name. You'll get a typesafe client, hooks, type definitions, all without any extra work or adding extra dependencies.
Authentication aware data fetching
It's a very common use case that some API Endpoints (Queries) require the user to be authenticated. Usually, the client is not aware of this, leaving it up the to frontend-developer to properly circumvent situations, where a login is required.
Apollo, urql and graphql-request ignore this problem. As a frontend-developer, it's on you to know when a user needs to be authenticated. You have to catch the cases where this might happen and present the right UI components to the user.
I think this is not a good developer experience. This should be handled elegantly.
Can you guess what WunderGraph does?
When writing a Query, we're aware if it requires authentication or not. That is, we can define a Query to require authentication or not.
With this information, we instruct the server-side component to validate if the user is authenticated. At the same time, we generate some extra code in the client to "only" fire when the user is authenticated. If they're not, the Query will simply wait.
Here's an example:
const { status } = useQuery(GetPosts);
if (status === "authenticated") {
// Fetch data
}
Subscriptions
graphql-request, being a simple client, doesn't support Subscriptions. urql wants us to do some extra setup to be able to use Subscriptions. A similar step is required when using Apollo.
Guess what you have to do when using WunderGraph? Nothing. Subscriptions in WunderGraph just work. If you write a subscription Operation, we'll compile to on the server-side component. We setup an Apollo compliant GraphQL client (on the server) and stream responses to the client using HTTP/2. On the client side, we generate a typesafe client + hook for the operation.
2. Respect the fundamentals of the web
A fundamental concept of the web is that every resource needs to have its own unique identifier, a URL or URI. It's important to respect this concept when building APIs for the web.
When we're talking about the elephant in the room, caching, it's important to note that Browsers rely on the concept of the URI.
Browsers bring very powerful tools for efficient data fetching out of the box. However, they can only unleash their full potential if you play by the rules.
The first rule is, you have to use the verb GET for Queries. Sending requests with the verb POST will disable caching.
The second rule is, you should always return an ETag header alongside the response. The Browser will automatically send a If-None-Match Header alongside subsequent requests. If the ETag didn't change, the server can respond with a 304 status code, indicating to the client that the data is still fresh, no data has to be sent to the client.
The third rule is, whenever you can, you should use Cache-Control directives to instruct the client how to cache, in- and revalidate content.
Out of the box, Apollo, urql and graphql-request disrespect the fundamentals of the web in every possible way. There are no unique URIs, no use of the GET verb for Queries, no ETags, no Cache-Control headers. There's obviously an endless list of packages, extensions, etc. to improve the situation. However, we're looking at functionality that should be part of the core, not an extension.
WunderGraph on the other hand doesn't just respect the web, it understands it.
5. Performant and Lightweight
We've looked at the numbers already.
With graphql-request, you get a good entry level GraphQL client that's also the size of an entry level client. It's a good choice for simpler use cases.
urql comes with a lot more functionality than graphql-request. It's more than double the size, but you'll get a lot more functionality.
I'd say that both graphql-request and urql have a very good functionality to JavaScript size ratio.
Apollo Client on the other hand seems to offer less functionality than urql while being almost three times the size of it. This might not be accurate as I only used the documentation in my analysis. At the same time, if it's not documented, it doesn't exist.
Now let's have a look at the WunderGraph client. The filesize is just ridiculous. That's because it's generated and has no dependencies. Compiled queries on the server-side component add almost no latency. The generated RPC client in the client-side component is not just very slim, it's also a lot more performant than other clients which you can see from the benchmarks.
Conclusion
When I was younger, I always wanted to use open sources tools everywhere. It was simply unacceptable for me to use cloud services. I always wanted to run my services on prem. I've even ran my Postgres database on my own virtual machine and figured out backups etc...
What I didn't realize is two things. My home-grown solutions like self-hosting a Postgres database were very immature compared to other services. I usually fell for the trap of not solving business problems. It was just more exciting to me to figure out how to "tune" a Postgres database than getting users. I could have bought a ready to use database service, but this would mean I would have to focus on getting users or solve real use cases. Instead, I just wanted to tinker with technologies.
If you're the open-source tinkerer, I hope I gave you some inspiration how I solve problems with WunderGraph. Some problems described above might be new to you. You could take inspiration from the patterns we use and implement them in your own stack.
If you're more like the older me, the business problem solver type of person, you should give WunderGraph a try.