<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Abhinay Bathina</title>
    <description>The latest articles on DEV Community by Abhinay Bathina (@abhinay_bathina).</description>
    <link>https://dev.to/abhinay_bathina</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3844111%2F6657abe3-0cac-4fe5-856b-64581a69ddec.png</url>
      <title>DEV Community: Abhinay Bathina</title>
      <link>https://dev.to/abhinay_bathina</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/abhinay_bathina"/>
    <language>en</language>
    <item>
      <title>Why I Extended Apollo Cache Persist with Lazy Per-Query Loading (Supports Apollo Client v3 and v4)</title>
      <dc:creator>Abhinay Bathina</dc:creator>
      <pubDate>Thu, 26 Mar 2026 07:49:41 +0000</pubDate>
      <link>https://dev.to/abhinay_bathina/why-i-extended-apollo-cache-persist-with-lazy-per-query-loading-4d80</link>
      <guid>https://dev.to/abhinay_bathina/why-i-extended-apollo-cache-persist-with-lazy-per-query-loading-4d80</guid>
      <description>&lt;p&gt;Our app was getting slower on reload. Not dramatically — just that familiar sluggishness we'd always chalked up to network latency. Then one day I actually profiled a page reload and saw the real cause: the entire Apollo cache was being loaded into memory at startup, before a single component had rendered.&lt;/p&gt;

&lt;p&gt;Data from a dozen different sections of the app — all of it dumped into memory at time zero. The user had just logged in. The homepage needed none of it.&lt;/p&gt;

&lt;p&gt;I was already using &lt;a href="https://github.com/apollographql/apollo-cache-persist" rel="noopener noreferrer"&gt;&lt;code&gt;apollo-cache-persist&lt;/code&gt;&lt;/a&gt; — a great library that does exactly what it says. The problem wasn't the library, it was the persistence model itself. So I used it as a baseline and built a different approach on top of it: &lt;code&gt;apollo-lazy-cache-persist&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Problem with Traditional Apollo Cache Persistence
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;apollo-cache-persist&lt;/code&gt; works by serializing the entire InMemoryCache and saving it to storage. On startup, it reads that snapshot back and restores everything at once. For small apps, this is fine. For larger apps — SaaS dashboards, enterprise tools, anything with lots of interconnected data — it becomes a silent performance killer.&lt;/p&gt;

&lt;p&gt;Two problems compound each other:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Startup does too much work. Every entity in your cache — regardless of whether the current page needs it — gets hydrated into memory immediately. The bigger your cache grows, the worse your startup gets.&lt;/li&gt;
&lt;li&gt;The storage model keeps growing. The entire cache is stored as a single monolithic blob. Every page you visit, every query that runs, makes that blob larger. Pagination queries are especially bad: every new page of results gets merged in, and the blob keeps growing session after session. Left unchecked, it balloons.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The Lazy Approach: Only Load What's Needed, When It's Needed
&lt;/h3&gt;

&lt;p&gt;The insight was simple: you don't need the whole cache at startup. You need the queries that the current page is actually running.&lt;br&gt;
Instead of restoring a monolithic snapshot, apollo-lazy-cache-persist works as an Apollo Link that intercepts each query as it fires:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Query fires
↓
Check IndexedDB for this specific query key
↓
If found → write result into InMemoryCache immediately (UI renders instantly)
↓
Network request continues in the background
↓
Fresh network response arrives → update InMemoryCache + re-persist this query
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each query is stored as its own independent entry, keyed by operation name + variables. Pagination queries are automatically skipped — variables containing &lt;code&gt;cursor&lt;/code&gt;, &lt;code&gt;offset&lt;/code&gt;, &lt;code&gt;after&lt;/code&gt;, &lt;code&gt;before&lt;/code&gt;, &lt;code&gt;first&lt;/code&gt;, or &lt;code&gt;last&lt;/code&gt; are excluded, so you never accumulate stale partial result sets.&lt;/p&gt;

&lt;p&gt;The result: cache entities load incrementally as queries resolve, not all at once. The app starts with only what the current page needs, and grows from there.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to Use It
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;apollo-lazy-cache-persist
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ApolloClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ApolloLink&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;HttpLink&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;InMemoryCache&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@apollo/client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;localforage&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;localforage&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createLazyCacheStore&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createLazyCacheLink&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;apollo-lazy-cache-persist&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;InMemoryCache&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;storage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;localforage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createInstance&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;apollo-lazy-cache-persist&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;storeName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;query_cache&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createLazyCacheStore&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;ttl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="c1"&gt;// 7 days&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lazyLink&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createLazyCacheLink&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ApolloClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;link&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ApolloLink&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="nx"&gt;lazyLink&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HttpLink&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/graphql&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the entire integration. No changes to your queries, no new hooks, no wrappers around your app.&lt;/p&gt;

&lt;p&gt;For React Native, swap localforage for AsyncStorage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;AsyncStorage&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@react-native-async-storage/async-storage&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createLazyCacheStore&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AsyncStorage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;ttl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What It Doesn't Do (By Design)
&lt;/h3&gt;

&lt;p&gt;This package intentionally does not persist the entire Apollo cache. Manual cache writes like &lt;code&gt;cache.writeQuery&lt;/code&gt;, &lt;code&gt;cache.modify&lt;/code&gt;, or &lt;code&gt;cache.writeFragment&lt;/code&gt; are not persisted.&lt;/p&gt;

&lt;p&gt;This is a deliberate tradeoff: by only persisting network responses, the system stays predictable. You never get stale optimistic updates or inconsistent derived data coming back from storage on reload.&lt;/p&gt;

&lt;p&gt;If you rely heavily on client-side cache mutations that need to survive refreshes, the original &lt;code&gt;apollo-cache-persist&lt;/code&gt; is probably the better fit. But if your app is primarily reading from the network and you want faster, leaner startup — this is what this package was built for.&lt;/p&gt;

&lt;h3&gt;
  
  
  Comparison
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;apollo-lazy-cache-persist&lt;/th&gt;
&lt;th&gt;apollo-cache-persist&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Startup memory usage&lt;/td&gt;
&lt;td&gt;Low — only active queries&lt;/td&gt;
&lt;td&gt;High — entire cache&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cache load pattern&lt;/td&gt;
&lt;td&gt;Incremental&lt;/td&gt;
&lt;td&gt;All at once&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage model&lt;/td&gt;
&lt;td&gt;Per-query entries&lt;/td&gt;
&lt;td&gt;Single blob&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pagination data stored&lt;/td&gt;
&lt;td&gt;No (auto-skipped)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Manual cache writes persisted&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  When Should You Use This?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Your Apollo cache grows large over a session&lt;/li&gt;
&lt;li&gt;Startup performance or time-to-interactive matters to you&lt;/li&gt;
&lt;li&gt;You're building a React Native app where memory pressure is real&lt;/li&gt;
&lt;li&gt;You're on a SaaS dashboard or enterprise app with many query types per session&lt;/li&gt;
&lt;li&gt;If you want apollo cache persist for Apollo Client V4&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Links
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;📦 &lt;a href="https://www.npmjs.com/package/apollo-lazy-cache-persist" rel="noopener noreferrer"&gt;npm: apollo-lazy-cache-persist&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🐙 &lt;a href="https://github.com/abhinaybathina/apollo-lazy-cache-persist" rel="noopener noreferrer"&gt;GitHub: abhinaybathina/apollo-lazy-cache-persist&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Big thanks to the &lt;a href="https://github.com/apollographql/apollo-cache-persist" rel="noopener noreferrer"&gt;&lt;code&gt;apollo-cache-persist&lt;/code&gt;&lt;/a&gt; maintainers whose work this builds on. Feedback and contributions very welcome — especially benchmarks from React Native environments where this pattern tends to shine most.&lt;/p&gt;

</description>
      <category>apollo</category>
      <category>graphql</category>
      <category>performance</category>
      <category>react</category>
    </item>
  </channel>
</rss>
