Getting Started
λ Query is a lightweight library for managing asynchronous data. It implements a stale-while-revalidate (SWR) caching strategy, where cached data is returned immediately while a background refetch ensures freshness. The core is framework-agnostic, with first-class React 19 bindings that leverage use() for Suspense integration.
λ Query has no dependencies and is packed at around 1.7KB compressed.
Installation
Section titled “Installation”npm i @studiolambda/queryλ Query’s React bindings require React 19 as a peer dependency.
Quick Start (Vanilla)
Section titled “Quick Start (Vanilla)”At its core, λ Query is a cache-backed data fetcher. Create an instance and start querying:
import { createQuery } from "@studiolambda/query";
const query = createQuery();
// Uses the key as the URL, fetches with the global fetch().const posts = await query.query("/api/posts");console.log(posts);The first call to query.query("/api/posts") fetches the data and caches it. Subsequent calls within the expiration window return the cached value without a network request. After expiration, a stale value is returned immediately while a background refetch runs.
Quick Start (React)
Section titled “Quick Start (React)”Wrap your application with QueryProvider and use the useQuery hook in your components:
import { QueryProvider, useQuery } from "@studiolambda/query/react";import { createRoot } from "react-dom/client";import { Suspense } from "react";
interface User { name: string; email: string;}
function App() { return ( <QueryProvider> <Suspense fallback={<p>Loading...</p>}> <UserCard /> </Suspense> </QueryProvider> );}
function UserCard() { const { data } = useQuery<User>("/api/user");
return ( <div> <h1>{data.name}</h1> <p>{data.email}</p> </div> );}
createRoot(document.getElementById("root")!).render(<App />);The useQuery hook suspends the component until data is available. Once resolved, data is always the fully loaded value — no null checks or loading states inside the component. React’s Suspense boundary handles the loading UI.
How It Works
Section titled “How It Works”λ Query uses a two-cache system:
- Items cache — stores resolved data with an expiration timestamp. This is your “results” cache.
- Resolvers cache — stores in-flight fetch promises with abort controllers. This provides request deduplication — multiple
query()calls for the same key while a fetch is in-flight share the same promise.
When you call query(key):
- If the key is not cached → fetch, cache the result, return it.
- If the key is cached and not expired → return the cached value.
- If the key is cached but expired → return the stale value immediately, trigger a background refetch. When the refetch completes, the cache updates and an event fires.
- If
fresh: true→ always refetch regardless of cache state.
This is the SWR (stale-while-revalidate) pattern. Your UI always has data to show, and it stays fresh automatically.
Next Steps
Section titled “Next Steps”- Configuration — Customize expiration, fetchers, staleness, and caches.
- Data Fetching — The
query()method in depth, aborting, and request deduplication. - Cache — Mutations, hydration, invalidation, and inspection.
- Events — Subscribe to cache lifecycle events.
- React — Components and hooks for React 19.