The final episode of this series covers the alternative data fetching ecosystem: TanStack Query vs SWR, RTK Query, Apollo GraphQL, and manual useEffect, when to choose each, a recap of the journey from episodes 0 to 21, as well as the evolution direction of TanStack Start and v6.

Every episode in this series compared TanStack Query with other approaches, but the full map has never been examined. This final episode presents that map: SWR, RTK Query, Apollo GraphQL, and manual useEffect. You'll see the strengths and weaknesses of each, then decide when to choose which.
Episode 22 also closes your journey. After the comparison, we recap the entire journey from episodes 0 to 21, summarize the 2026 production server state stack, and look at the evolution direction of TanStack Query: TanStack Start and Query v6. This isn't the end of learning, but the foundation for building real applications.
SWR is a data fetching library from Vercel with the "stale-while-revalidate" philosophy: show old data as fast as possible, then revalidate in the background. This concept is nearly the same as TanStack Query, but with a leaner feature scope. Install all three in your experiment project to compare directly:
npm i swr
npm i @reduxjs/toolkit
npm i @apollo/client graphqlimport useSWR from "swr"
const fetcher = (url) => fetch(url).then((res) => res.json())
function Todos() {
const { data, isLoading, error } = useSWR("/api/todos", fetcher)
return <p>Jumlah todos: {data ? data.length : 0}</p>
}useSWR creates a hook without a separate queryFn — the fetcher is called with the key as an argument. For small applications that need simple caching, SWR is a legitimate, easy-to-learn choice. But as requirements grow — mutations with optimistic updates, dependent queries, infinite queries, persistence, devtools — TanStack Query wins with more complete features managed in one ecosystem.
A practical difference you'll notice: TanStack Query has a first-class useMutation with the onMutate, onError, and onSettled lifecycle; SWR leaves write operations to manual code. Infinite queries in TanStack Query have battle-tested getNextPageParam and fetchNextPage; SWR uses the useSWRInfinite pattern, which is simpler but less rich. For teams seriously building data-heavy applications, these features are often the deciding factor.
RTK Query integrates directly with Redux Toolkit: its cache lives inside the Redux store, and endpoints are described in createApi. Its strength is access to Redux DevTools and one place for all state — no second paradigm the team has to learn.
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react"
export const todosApi = createApi({
reducerPath: "todosApi",
baseQuery: fetchBaseQuery({ baseUrl: "https://jsonplaceholder.typicode.com" }),
endpoints: (builder) => ({
getTodos: builder.query({ query: () => "/todos" }),
}),
})
export const { useGetTodosQuery } = todosApiThe weakness of RTK Query: server data and client state are mixed in the same store, and its API is more verbose than TanStack Query hooks. createApi requires describing endpoints in one large entity, while TanStack Query gives you the freedom to organize queries per domain. Choose RTK Query if you're fully committed to Redux and don't want to add a second paradigm.
Apollo Client is the dominant choice for GraphQL applications. It provides type-based normalized caching, schema-aware queries, and fragment colocation with no direct equivalent in TanStack Query. TanStack Query, by contrast, works with REST or any endpoint that returns a Promise.
For REST APIs, TanStack Query is lighter and more flexible — no schema, no codegen, just a query key and a query function. For complex GraphQL with type normalization and many fragments, Apollo is the right choice. There's also a hybrid path: using TanStack Query for REST and Apollo for GraphQL in the same application, though ideally you'd simplify to avoid a double cache.
The useEffect plus manual fetch pattern — the starting point of this series — isn't without its place. It's still relevant in these scenarios:
But once data is shared, mutable, or read repeatedly, the manual cost balloons: repetitive loading states in every component, no deduplication, no retry, no invalidation. That seemingly simple code turns into a series of useEffect calls that are hard to read and prone to race conditions. This comparison is the main reason TanStack Query exists — all those costs are eliminated automatically.
Here are practical decisions you can use as a guide when starting a new project:
useEffect for once-loaded data, static server-rendered pages, or experiments that are quickly discarded.The most important rule: don't combine several data fetching solutions without a reason. Two different caches mean two sources of truth that can get out of sync. Stay consistent with one library, and leave an exit path for migration if requirements change.
Let's draw an outline of your journey. Episodes 0 to 2 built the foundation: environment setup, history, and the architecture of queries on top of a global cache. Episodes 3 to 7 covered the core operations: QueryClient, useQuery, query keys, mutations, and staleness rules. Episodes 8 to 12 moved up to real workloads: parallel and dependent queries, pagination and infinite queries, prefetching, optimistic updates, and offline persistence.
Episodes 13 to 15 touched performance, SSR, and security: cache tuning, Suspense, Next.js hydration, and 401/403 handling patterns. Episodes 16 to 18 kept code quality high: state management integration, devtools, and testing. Episodes 19 to 21 completed it: framework adapters, the latest v5 features, and production architecture. At this point, you hold the entire server state map.
The best practices that most often save production: hierarchical and centralized query keys, deliberately set staleTime, careful invalidation, and separating server state from client state. If you carry these four things into your next project, most common cache bugs in the field will never touch your code.
The ecosystem keeps moving. TanStack Start — a full-stack framework from the same team — brings data fetching, routing, and server functions into one place with TanStack Query as a natural part of it. Server functions written in one file can be called from the client without manual HTTP endpoints, and TanStack Query keeps the cache consistent around them.
Meanwhile, Query v6 is under development and has started appearing as beta releases in the Solid ecosystem. These beta releases give a glimpse of the next-generation API without changing the stable v5 behavior. Both show the direction: server state will become increasingly integrated with frameworks, without sacrificing the query keys and cache philosophy you've mastered from episodes 0 to 21.
Episode 22 is the complete map and the closing at once. You can now compare TanStack Query with SWR, RTK Query, Apollo, and manual useEffect, choose what fits your needs, and place TanStack Query in its position: a mature server state library for growing applications.
Key takeaways:
useEffect is still relevant for once-loaded data and prototypes.And so the Learning TanStack Query journey from episodes 0 to 22 comes to an end. This series closes the server state material collection on this blog — from manual patterns to a production-ready architecture. Everything you've learned is now ready to be applied to your own projects. A complete summary of this series can be found on the series meta page as a reference map. Thank you for completing twenty-three episodes; keep building, and happy writing applications whose data is always in sync.