<?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: Thanos Korakas</title>
    <description>The latest articles on DEV Community by Thanos Korakas (@tkorakas).</description>
    <link>https://dev.to/tkorakas</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%2F412593%2F8326feca-fe3d-4bed-9c9d-c46ac11f8f41.jpeg</url>
      <title>DEV Community: Thanos Korakas</title>
      <link>https://dev.to/tkorakas</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tkorakas"/>
    <language>en</language>
    <item>
      <title>React template: Tanstack Query</title>
      <dc:creator>Thanos Korakas</dc:creator>
      <pubDate>Tue, 18 Nov 2025 17:05:06 +0000</pubDate>
      <link>https://dev.to/tkorakas/tanstack-query-5d56</link>
      <guid>https://dev.to/tkorakas/tanstack-query-5d56</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In my &lt;a href="https://dev.to/tkorakas/my-react-template-ho4"&gt;React template&lt;/a&gt;, I mentioned that Tanstack Query is one of my core libraries for handling HTTP requests and caching. In this post, I'll dive deeper into how I use it and why it's become an essential part of my development workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Before Tanstack Query
&lt;/h2&gt;

&lt;p&gt;Fetching data is a crucial part of any frontend application. I can't remember building a web app that didn't need to fetch and display data from the server.&lt;/p&gt;

&lt;p&gt;I've tried GraphQL with Apollo and SWR, but nothing felt quite right until I discovered Tanstack Query. Before that, like most developers, I used React state (or Redux and other libraries) to handle server state.&lt;/p&gt;

&lt;p&gt;Below is a simple example of fetching data using the fetch API and useEffect.&lt;/p&gt;

&lt;p&gt;When the page renders for the first time, we fire a request, and when we get the data, we update the users state. React will then re-render to display the updated state.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setUsers&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;

&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://jsonplaceholder.typicode.com/users&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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;setUsers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&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;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Error fetching users:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&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;span class="p"&gt;[]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This seems simple but lacks important features like loading and error handling.&lt;/p&gt;

&lt;p&gt;Adding these features makes the code more complex:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setUsers&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://jsonplaceholder.typicode.com/users&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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;setUsers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&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;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Error fetching users:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&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;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&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;span class="p"&gt;[]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We could improve this by using useReducer to manage state, but it doesn't solve the fundamental challenges.&lt;/p&gt;

&lt;p&gt;I've also tried building custom hooks to reuse this logic, but new problems arise. What if we need user data in multiple components? We might end up making the same request multiple times. Moving the query higher in the component tree and using React Context helps, but creates other issues: what if the query depends on component state? The entire app re-renders every time we fetch data since the Provider is typically at the top level.&lt;/p&gt;

&lt;p&gt;And with Tanstack Query we can simply do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const { data: user, isLoading, error } = useQuery({
  queryKey: 'users',
  queryFn: getUsersFn
  staleTime: 5000,
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;useQuery&lt;/code&gt; functions returns multiple properties like isLoading or error.&lt;/p&gt;

&lt;p&gt;What I find more interesting though is the queryKey and stale time. Tanstack Query gives us out of the box caching.&lt;/p&gt;

&lt;h2&gt;
  
  
  Caching
&lt;/h2&gt;

&lt;p&gt;Tanstack Query solves many of these problems: loading states, error handling, pagination, infinite queries, dependent queries, polling, and more. But what really clicked for me was caching.&lt;/p&gt;

&lt;p&gt;With Tanstack Query, you specify how long data remains valid. After that period, it automatically refetches. Even better, if two components request the same data, the library makes only one request and provides the result to both components.&lt;/p&gt;

&lt;p&gt;This works through cache keys.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isLoading&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;queryKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;// cache keys&lt;/span&gt;
  &lt;span class="na"&gt;queryFn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;getCurrentUser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;gcTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;Infinity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;staleTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;Infinity&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;In my React template, I use &lt;a href="https://github.com/tkorakas/react-template/blob/main/src/common/auth/use-auth.ts" rel="noopener noreferrer"&gt;this approach&lt;/a&gt; for authentication. Instead of maintaining an &lt;code&gt;isAuthenticated&lt;/code&gt; boolean in context or Zustand, I rely on caching. Each component that needs user data calls the &lt;code&gt;useAuth&lt;/code&gt; hook, which makes an HTTP request to the &lt;code&gt;/me&lt;/code&gt; endpoint. Since I cache indefinitely using &lt;code&gt;staleTime: Infinity&lt;/code&gt; and &lt;code&gt;gcTime: Infinity&lt;/code&gt;, it won't make another request until we invalidate the &lt;code&gt;USER_QUERY_KEY&lt;/code&gt; cache.&lt;/p&gt;

&lt;p&gt;When logging out, after calling the &lt;code&gt;/logout&lt;/code&gt; endpoint, we clear the cache:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;clearUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;queryClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setQueryData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;USER_QUERY_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;queryClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeQueries&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;queryKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;USER_QUERY_KEY&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;The &lt;a href="https://github.com/tkorakas/react-template/blob/main/src/common/auth/private-route.tsx" rel="noopener noreferrer"&gt;PrivateRoute&lt;/a&gt; component uses the &lt;code&gt;useAuth&lt;/code&gt; hook, so it automatically re-renders and navigates the user to login.&lt;/p&gt;

&lt;p&gt;Similarly, I listen for 401 responses using a &lt;a href="https://github.com/tkorakas/react-template/blob/main/src/common/auth/auth-hooks.ts" rel="noopener noreferrer"&gt;Ky hook&lt;/a&gt; and clear the user cache, which redirects the user to login:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;beforeErrorHook&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BeforeErrorHook&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&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;requestUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;||&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;isAuthMeEndpoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;requestUrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/auth/me&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isAuthMeEndpoint&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;queryClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setQueryData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;USER_QUERY_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;queryClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeQueries&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;queryKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;USER_QUERY_KEY&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;error&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;You can apply this pattern to any data that's used across the app but changes infrequently, like a list of countries for a dropdown. Cache it indefinitely, and it's available everywhere. When a user adds a new country, simply invalidate the cache after a successful response, and Tanstack Query will fetch the updated list including the newly created entry.&lt;/p&gt;

&lt;p&gt;Using Tanstack Query helps separate server state from UI state, letting React focus on what it does best: managing the interface.&lt;/p&gt;

&lt;p&gt;Tanstack Query is an incredibly powerful library with many more features. To learn more, I highly recommend this &lt;a href="https://tkdodo.eu/blog/practical-react-query" rel="noopener noreferrer"&gt;series&lt;/a&gt; by one of the core contributors. &lt;/p&gt;

</description>
      <category>react</category>
      <category>frontend</category>
      <category>javascript</category>
      <category>tooling</category>
    </item>
    <item>
      <title>React template: Introduction</title>
      <dc:creator>Thanos Korakas</dc:creator>
      <pubDate>Mon, 17 Nov 2025 22:07:22 +0000</pubDate>
      <link>https://dev.to/tkorakas/my-react-template-ho4</link>
      <guid>https://dev.to/tkorakas/my-react-template-ho4</guid>
      <description>&lt;p&gt;You can find the original post on my &lt;a href="https://tkorakas.dev/blog/react-template/" rel="noopener noreferrer"&gt;blog&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;Lately I've found myself wanting to work on some side project ideas. Every time I had to implement the same stuff again and again: basic library configuration, routing, layout, forms, authentication. Most of the time I would start a project using Vite and copy-paste parts from other apps I'd worked on.&lt;/p&gt;

&lt;p&gt;That alone could take a couple of hours or days without making any progress on my idea.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Tech Stack
&lt;/h2&gt;

&lt;p&gt;The things I usually need are simple. I like working with Tanstack Query for handling HTTP requests and caching. I implement authentication using sessions where an HTTP-only cookie is set by the backend, and on the frontend I make a request against a &lt;code&gt;/me&lt;/code&gt; or &lt;code&gt;/user&lt;/code&gt; endpoint to check if the user has a session. I set up some interceptors to log the user out on 401 requests. Finally, I like to wire basic input fields so I can build forms without having to care about error handling or wiring the field every time.&lt;/p&gt;

&lt;p&gt;Here is a more detailed list of the libraries that I use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React Router&lt;/li&gt;
&lt;li&gt;Tanstack Query&lt;/li&gt;
&lt;li&gt;React Hook Form&lt;/li&gt;
&lt;li&gt;Zod&lt;/li&gt;
&lt;li&gt;Ky (previously Axios)&lt;/li&gt;
&lt;li&gt;Chakra UI&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;p&gt;It was time for my side project to be a React template to help me build my next ideas faster. I published the code on &lt;a href="https://github.com/tkorakas/react-template" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Architecture
&lt;/h2&gt;

&lt;p&gt;In my project, I prefer separating files by logic/context rather than file type. So you won't find components and hooks folders in the project.&lt;/p&gt;

&lt;p&gt;It follows a DDD-like approach where each feature goes into the features folder. Features are composed of a template file and a view model where I wire stuff like forms or HTTP requests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/
├── features/
│   ├── login/
│   │   ├── index.tsx
│   │   └── use-handler.ts
│   └── register/
│       ├── index.tsx
│       └── use-hander.ts
├── data-access/
│   ├── api.ts
│   └── users.schema.ts
└── commong/
    └── auth/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I keep features agnostic of storage or external dependencies by putting them in the data-access folder. So data-access describes and validates API requests using Ky and Zod.&lt;/p&gt;

&lt;p&gt;Then in the view model, I use Tanstack Query to call the API and get a response.&lt;/p&gt;

&lt;p&gt;In the past, I used to create custom hooks inside data-access so I didn't expose Tanstack Query into my features. Although this helped a lot with testing and keeping features agnostic of implementation details, it required a lot of boilerplate and honestly, I depend a lot on Tanstack Query for many things, so I decided to simplify the setup.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mock Server
&lt;/h2&gt;

&lt;p&gt;I also created a mock server to help me prototype fast without touching the backend yet. The mock server supports authentication using an HTTP-only cookie, MFA by printing an OTP to the console, and OAuth2 providers for login—currently it implements GitHub.&lt;/p&gt;

&lt;p&gt;The mock server can read JSON files from the filesystem and use them as a database. It also supports all CRUD operations. That way I can build CRUD APIs fast to verify my ideas. Usually it helps me a lot to build a POC of the UI and improve it by using it, instead of spending more time designing and thinking about how it should work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Future Plans
&lt;/h2&gt;

&lt;p&gt;I plan to continue working on the template by adding support for more OAuth providers like Google and enhancing security by implementing XSRF and other best practices.&lt;/p&gt;

&lt;p&gt;Further improvements include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Support for HOTP/TOTP&lt;/li&gt;
&lt;li&gt;More features regarding user management like profile management and password changes&lt;/li&gt;
&lt;li&gt;Roles &amp;amp; permissions, perhaps payments&lt;/li&gt;
&lt;li&gt;Helpers for handling tables by integrating Tanstack Table and custom helpers all wired with Chakra components&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you have any issues with the template, please create an &lt;a href="https://github.com/tkorakas/react-template/issues" rel="noopener noreferrer"&gt;issue&lt;/a&gt; or share any other ideas.&lt;/p&gt;

&lt;p&gt;Thanks for reading.&lt;/p&gt;

</description>
      <category>react</category>
      <category>vite</category>
    </item>
    <item>
      <title>Circuit breaker pattern in Laravel applications</title>
      <dc:creator>Thanos Korakas</dc:creator>
      <pubDate>Mon, 20 Jul 2020 07:05:35 +0000</pubDate>
      <link>https://dev.to/tkorakas/circuit-breaker-pattern-in-laravel-applications-5ap2</link>
      <guid>https://dev.to/tkorakas/circuit-breaker-pattern-in-laravel-applications-5ap2</guid>
      <description>&lt;p&gt;Let's start by framing the problem. A lot of applications make external requests to get or sent data. As we already know by the &lt;a href="https://en.wikipedia.org/wiki/Fallacies_of_distributed_computing" rel="noopener noreferrer"&gt;Fallacies of distributed computing&lt;br&gt;
&lt;/a&gt; the network is not reliable and is one of those things that we can’t control in our applications. A failing network request happening repeatedly may drive your application to be unresponsive.&lt;/p&gt;

&lt;p&gt;For example, you are building an application and you are using an external search service. Like we mentioned before the network is not our friend in this and one day the service starts having big delays or stops responding completely.&lt;/p&gt;

&lt;p&gt;As you can easily understand, these are bad news for our application. Allowing an application to make requests in a situation like this will result in it underperforming or stop working entirely.&lt;/p&gt;
&lt;h2&gt;
  
  
  Circuit breaker pattern to the rescue
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;The circuit breaker is a design pattern used in modern software development. It is used to detect failures and encapsulates the logic of preventing a failure from constantly recurring, during maintenance, temporary external system failure or unexpected system difficulties. - &lt;a href="https://en.wikipedia.org/wiki/Circuit_breaker_design_pattern" rel="noopener noreferrer"&gt;Wikipedia&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Circuit breaker is a pattern that prevents request execution against unresponsive services.&lt;/p&gt;

&lt;p&gt;If you looking for more information on the topic I can highly recommend this &lt;a href="https://martinfowler.com/bliki/CircuitBreaker.html" rel="noopener noreferrer"&gt;article&lt;/a&gt; by Martin Fowler.&lt;/p&gt;

&lt;p&gt;Laravel 7 ships with all the ingredients needed to build a circuit breaker:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An HTTP client&lt;/li&gt;
&lt;li&gt;The RateLimiter (A good explanation &lt;a href="https://medium.com/@DarkGhostHunter/laravel-there-is-a-rate-limiter-and-you-didnt-know-eb443b1bedc" rel="noopener noreferrer"&gt;https://medium.com/@DarkGhostHunter/laravel-there-is-a-rate-limiter-and-you-didnt-know-eb443b1bedc&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’ll try to keep the code as simple as possible and focus more on the pattern itself to better explain the concept.&lt;/p&gt;

&lt;p&gt;Before the circuit breaker pattern our application looked something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Http&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'https://fake-search-service.com/api/v1/search?q=Laravel'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'articles.index'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'articles'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;body&lt;/span&gt;&lt;span class="p"&gt;()];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The HTTP client will wait for a couple of seconds before throwing an exception so it’s a good practice to always define timeout on our requests.&lt;/p&gt;

&lt;p&gt;Laravel's client comes with a handy method to define the timeout of our request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Http&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'https://fake-search-service.com/api/v1/search?q=Laravel'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'articles.index'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'articles'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;body&lt;/span&gt;&lt;span class="p"&gt;()];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ok, but we are still not preventing our service to make a request to the unresponsive API.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$limiter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;app&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RateLimiter&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$actionKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'search_service'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$threshold&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$limiter&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;tooManyAttempts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$actionKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$threshold&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Exceeded the maximum number of failed attempts.&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;failOrFallback&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Http&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'https://fake-search-service.com/api/v1/search?q=Laravel'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'articles.index'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'articles'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;body&lt;/span&gt;&lt;span class="p"&gt;()]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;\Exception&lt;/span&gt; &lt;span class="nv"&gt;$exception&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$limiter&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;hit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$actionKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Carbon&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;addMinutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;failOrFallback&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;Using Laravel's RateLimiter we can count the failed attempts performed to the search API &lt;code&gt;$limiter-&amp;gt;hit($actionKey, Carbon::now()-&amp;gt;addMinutes(15))&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;By counting those attempts we can prevent our application from making any more requests to this API for a certain amount of time.&lt;/p&gt;

&lt;p&gt;Before every request, we check if there are too many failed attempts &lt;code&gt;$limiter-&amp;gt;tooManyAttempts($actionKey, $threshold)&lt;/code&gt; and if the failed attempts have passed our threshold we will prevent our service to request the search API for 15 minutes.&lt;/p&gt;

&lt;p&gt;With the &lt;code&gt;failOrFallback&lt;/code&gt; method we can provide a fallback implementation like search in the database instead of the external service or return an appropriate error message to the user.&lt;/p&gt;

&lt;p&gt;Having a lot of requests waiting to be answered by the unresponsive service will slow down our application. Using the circuit breaker pattern we can prevent our whole application from going down. The search page is down but without affecting the performance of the other pages. &lt;/p&gt;

&lt;p&gt;If we are building an application using the microservices architecture, we can use this pattern for our services as well.&lt;/p&gt;

&lt;p&gt;Moreover, it would be good to use shared storage (like Redis) so all the services will have a common way to learn if another service is down without requesting N times. Using this pattern will help the unresponsive service to recover faster instead of adding more load on top.&lt;/p&gt;

&lt;p&gt;Like I said this is a simplistic implementation of the circuit breaker pattern. You can implement it in a more sophisticated way and handle many things more appropriately.&lt;/p&gt;

&lt;p&gt;If you have implemented this pattern in Laravel in a different way I would love to read it so feel free to leave your feedback.&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>php</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
