WP REST API vs GraphQL Performance

A client had recently pointed out to me a post detailing performance differences between the REST API that ships in WordPress Core, and WPGraphQL —
> <https://www.wpgraphql.com/docs/wpgraphql-vs-wp-rest-api#performance>
>
>
In it, it asserts that the difference between two similar queries running the REST API and WPGraphQL yields vastly different performance impacts:
> Below are screenshots of the same WordPress site asking for 100 posts from the WP REST API and 100 posts from WPGraphQL with the Chrome network tab open.
>
> **REST:**
>
> * Download size: 335 kb
> * Time: 7.91s
>
> **WPGraphQL**
>
> * Download size: 6.4 kb
> * Time: 67 ms
>
>
That seemed like an unrealistic difference from an initial glance — the REST API takes **over 100x longer** to return data than WPGraphQL? I wanted to check and see if I could duplicate the test and find out what accounted for the orders of magnitude difference between the two.
So I reached out to [Jason Bahl](https://jasonbahl.com/about/) who gave me some more details of the original source data.
The two URLs tested are both on [twincities.com](https://www.twincities.com/) — a WPVIP hosted site.
So I ran those tests through HTTPSTAT (more on this later) and got the following results:
At this point, it’s looking like the HTTP requests being compared on the WPGraphQL site may be comparing the network cached version of the WPGraphQL output versus the uncached version of the REST API?
Certainly the uncached numbers are significantly higher for the REST API — `7437ms` to `680ms` results in about a `10.94x` longer processing duration, but that’s an *order of magnitude different* from the over `100x` difference cited earlier, and without the ability to profile requests, it’s difficult to know their specific reasons for such a disparity.
#### Duplicating Results
Next, I set to trying to reproduce locally — I did a quick local test using [Studio](https://developer.wordpress.com/studio/) (my preferred local dev environment).
The only two plugins installed are FakerPress for generating the content and WPGraphQL.
I used [FakerPress](https://wordpress.org/plugins/fakerpress/) to generate the following amounts of content in the DB:
* 5 Users
* 420 Posts
* 33 Categories
* 30 Tags
* 439 Media Items
* and 286 Comments
I saved this as an export as well as xml-wxr file, if anyone would like a copy to reproduce findings on similar datasets.
#### HTTPSTAT
For the performance tests, I’m running `[httpstat](https://github.com/reorx/httpstat)` to determine how long requests are taking. When possible to get value from it, I’m running multiple requests to see averages of data and to track outliers. Full output is in accordion format, so click things you want more data on.
#### Running Locally – WPGraphQL
Now, keeping in mind that these are run on a local machine running Sqlite DB instead of a full MySQL or MariaDB. The content-length for all results is consistent at `25805`, and seemed to trend to about `0.84 seconds`.
To try and clean any local object caching for consistency between tests, I restarted the Studio site in the ui before running the following tests.
#### Running Locally – WP REST API
Running the test again versus the REST API results in the following:
Response data was `583193` for the REST API (22.6x larger) and the duration of the requests trended towards about `1.1s` (about 1.3x the duration of the GraphQL queries) — due to GraphQL’s explicit reduction of data that it would like returned (and thereby reducing the processing time to deliver them), GraphQL’s pattern of batching queries, as well as not pulling any postmeta fields unless explicitly requested (whereas the REST API delivers it all in a standardized bundle).
#### The Data Returned by each
#### Running on “Production” Sites
To do this, I pushed the content up to a free WP.cloud hosted preview site — <https://georgestephanis-qylme-studio.wp.build/> — (probably expired by the time you read this — they only last about a week)
For performance, we’re seeing a similar difference especially when examining the “server processing time” portions of the duration — when not hitting the static cache, GraphQL took `698 ms`, versus `821 ms` for the REST API — about `1.18x` the duration. Similar scale — GraphQL, due to not pulling in any postmeta in these tests, and some optimized batch queries for authors and the like that entail keeping more data in memory rather than sequentially processing records, it takes roughly 80-85% the amount of time to process than the REST API takes.
#### What does this tell us?
GraphQL and the REST API behave differently. If they didn’t, it would be somewhat pointless for both to exist!
Custom REST API Endpoints, or additional custom parameters limiting the data returned by REST API endpoints could likely also serve to accelerate the data transfer and performance of REST API endpoints, although it may not help with how WPGraphQL tends to evaluate requests (querying all the posts at once, and then finding out which authors are used, and querying those all in one go — rather than packaging up each individual entry sequentially, which can be more memory intensive, but also more efficient on queries)
Also, the base uncached response times for WPGraphQL on the WordPress.com hosted test site is `698 ms`, with a total time of `729 ms` — far more than the cited `67 ms` for a production site running WPGraphQL and a lot more plugins and functionality. My suspicion is that a great deal of that `67 ms` fast performance is due to persistent object caching accelerating the response data, and having been already primed, whereas the REST API is not optimized to have such an advantage in the comparison.
#### Real-World Conditions
But, let’s face it, a stock WordPress install with a core theme and just FakerPress and WPGraphQL installed is hardly indicative of real-world performance. So let’s set up a different environment, with a lot more going on.
For the purpose of this test, I’m installing the Astra theme, and the following plugins:
* Advanced Custom Fields
* Akismet
* All-in-One WP Migration and Backup
* bbPress
* Contact Form 7
* Elementor
* Hello Dolly
* Jetpack (offline mode)
* MailPoet
* WooCommerce
+ Google for WooCommerce
+ Pinterest for WooCommerce
* Yoast Duplicate Post
* Yoast SEO
Where a plugin launched me into a setup wizard, I followed it through and tended toward defaults — with the sole exception of not setting up linked accounts like Elementor defaults to prompting.
Running the same queries again, I get the following load times:
* 3953ms
* 4762ms
* 4815ms
* 8223ms
* 4788ms
* 4730ms
* 3022ms — min
* 8527ms — max
* 6843ms
* 4553ms
Firing them off versus the WP REST API, we get the following ten durations:
* 14890ms
* 9937ms
* 24694ms — max
* 5393ms — min
* 8838ms
* 16168ms
* 16345ms
* 6515ms
* 9634ms
* 7154ms
If we discount the min/max as potential outliers, the average load time for WPGraphQL comes to **5,333ms**, and the WP REST API comes to **11,185ms** — which is 2.097x the average duration of WPGraphQL.
Now, if I wanted to also get information on the posts taxonomies — tags and categories — and postmeta, the query would look something like this:
This is also pulling in a postmeta field that I had registered in ACF, and tied into GraphQL via Jason’s WPGraphQL for ACF plugin.
Running this, I get response times as follows:
* 4566ms
* 4095ms
* 3555ms
* 4678ms
* 7222ms — max
* 3552ms
* 3464ms
* 3445ms — min
* 3478ms
* 3979ms
Again, removing the min and max, we get an average of **3921ms** — which is somewhat odd being lower than the prior queries where we were asking for less, but that’s potentially down to running it on my local machine which could be doing other sync or background tasks at the time, impacting load time.
#### Conclusions
Using the default WP REST API has a lot going for it — it has a bunch of native tooling built into WordPress Core’s React libraries, and ships in WordPress itself. However, if you’re trying to squeeze out every bit of performance and you’re comfortable bringing your own frameworks, it’s possible to get better results from the WPGraphQL — just expect something probably closer to a 1.2-2x speed increase, rather than the 100x that the WPGraphQL website asserts.
There’s always advanced tuning, caching, and optimizations that can be done to improve performance of any given system — it’s important, though, that when comparing them, we’re comparing like to like, optimized to optimized, rather than souping up one car and comparing it to a default configuration of another brand, and touting those results as accurate.
*Special thanks to Jason Bahl for taking the time to talk through his methodology and reviewing an in-progress draft of this article.*, WP REST API vs GraphQL Performance, George Stephanis, 01kdkpemz30gjdst92ksbhss3j.