npm i @hazae41/glacier
Node Package 📦 • Read the docs 📚 • Next.js Example 🪣 • Expo Example 🪣 • Comparison with other libs 🌐
Philosophy 🧠
Glacier uses two new approaches compared to other data fetching libraries like swr or react-query:
- Encapsulating key+fetcher+params in a single abstraction called schema.
- Composing features with very simple hooks instead of having bloated configuration and unexpected behaviors.
Features 🔥
Current features
- 100% TypeScript and ESM
- Composition-based hooks
- Very easy learning curve
- No dependency except React
- Not over-engineered (hello react-query)
- No unexpected behaviour (hello swr)
- Backend agnostic fetching (REST, GraphQL, WebSocket)
- Storage agnostic caching (new Map(), LocalStorage, IndexedDB)
- Automatic refetching
- Dependent and conditional queries
- Request deduplication, cooldown, timeout, and expiration
- Page-based and cursor-based pagination
- Exponential backoff retry
- SSR & ISR support
- Optimistic mutations
- Cancellable requests
- Automatic cancellation
- Automatic garbage collection
- Per-query persistent storage
- Out of the box IndexedDB and LocalStorage
- Out of the box store normalization
- Super natural React Suspense
- React Native support
- Transport agnostic streaming (ethers.js, WebSockets, Socket.io)
- Bidirectional scrolling
Installation 🔧
Just install @hazae41/glacier
using your favorite package manager.
npm i @hazae41/glacier
Then, wrap your app in a CoreProvider
component.
import { CoreProvider } from "@hazae41/glacier"
function MyWrapper() {
return <CoreProvider>
<MyAwesomeApp />
</CoreProvider>
}
Your first mix 🧪
When using Glacier and its composition-based hooks, you create a mix and only include the ingredients you want.
We'll do a request at /api/data
using JSON, display it with a loading, and automatically refetch it.
Create a fetcher ⚡️
It will just take an url, fetch it, and return the data.
async function fetchAsJson<T>(url: string) {
const res = await fetch(url)
const data = await res.json() as T
return { data }
}
Create a mix 🌪
Then create a mix using a query and some blocks.
function useHello() {
const query = useQuery<Hello>(`/api/hello`, fetchAsJson)
useFetch(query)
useVisible(query)
useOnline(query)
return query
}
Use it in your components 🚀
function MyApp() {
const { data, error } = useHello()
if (error)
return <MyError error={error} />
if (!data)
return <MyLoading />
return <MyPage data={data} />
}
Advanced example 🗿
Last example was good, but here is the best way to use Glacier.
Making our fetcher cancellable ⚡️
Our fetcher was good, but this one can be aborted.
async function fetchAsJson<T>(url: string, more: FetcherMore<T>) {
const { signal } = more
const res = await fetch(url, { signal })
if (!res.ok) {
const error = new Error(await res.text())
return { error }
}
const data = await res.json() as T
return { data }
}
It also returns an error if the request failed.
Defining schemas 📐
Using schemas may seems boilerplate, but it will save you a lot of time later.
function getHelloSchema() {
return getSchema<Hello>("/api/hello", fetchAsJson)
}
It allows you to reuse the same set of key+fetcher+params in multiple places, including imperative code.
Creating mixtures 🧪
The mixtures pattern allows you to reuse the same group of blocks.
function useAutoFetchMixture(query: Query) {
useFetch(query)
useVisible(query)
useOnline(query)
}
Mixing it 🌪
Once you got a schema and a mixture, you just have to mix it.
function useHelloMix() {
const query = useSchema(getHelloSchema, [])
useAutoFetchMixture(query)
return query
}
Use it in your app 🚀
function MyApp() {
const { data, error } = useHelloMix()
if (error)
return <MyError error={error} />
if (!data)
return <MyLoading />
return <MyPage data={data} />
}