Fc200
fetchium
v0.1.0TypeScript

The missing element
in data fetching

Type-safe queries with entity normalization, fine-grained reactivity, and streaming — for REST APIs and beyond.

queries/GetUser.ts
1class GetUser extends RESTQuery {
2 params = {
3 id: t.number,
4 };
5
6 path = `/users/${this.params.id}`;
7
8 result = {
9 user: t.entity(User),
10 };
11}
components/Profile.tsx
1const UserProfile = component(async () => {
2 const { user } = await fetchQuery(
3 GetUser, { id: 42 }
4 );
5
6 return (
7 <div>
8 <h1>{user.name}</h1>
9 <p>{user.email}</p>
10 </div>
11 );
12});
$ npm install fetchium
Get started

REST-first. Protocol-agnostic. Built on Signalium. Works with React, Vue, Svelte, and more.

Entity normalizationEnd-to-end type safetyFine-grained reactivityStreaming updatesSmart cachingProtocol agnostic

The elements of Fetchium

Everything you need for production data fetching, nothing you don't.

1QrQUERY
Queries

Class-based templates with type-safe params. Define once, use everywhere. Protocol-agnostic — switch from REST to GraphQL without changing usage sites.

2MuMUTATION
Mutations

Optimistic updates, cache invalidation, and rollback. Mutations share the same class-based pattern as queries for a consistent API.

3TsTYPES
Type DSL

A zero-alloc DSL that is both a runtime validator and TypeScript type. Number masks, not objects. Designed for resilience — optional fields fall back, arrays filter unknown items.

4EnENTITY
Entities

Normalized, deduplicated, identity-stable proxy objects. The same user across 10 queries is the exact same reference. Update once, see it everywhere.

5SgSIGNAL
Fine-grained reactivity

Built on Signalium. Lazy entanglement means you only pay for what you read. No wasted renders, no stale closures. Works outside React too.

6StSTREAM
Streaming

First-class real-time subscriptions on entities. WebSocket, SSE, or any source — define __subscribe and Fetchium handles the lifecycle.

7CkCACHE
Cache

Normalized entity store with configurable GC, deduplication, and stale-while-revalidate. Smart enough to skip the network when data is fresh.

8RfREFETCH
Refetching

Polling, window focus refetch, network reconnect, and manual invalidation. Configure per-query or globally. Dynamic intervals based on response state.

Entities that stay in sync

When the same user appears in a post and a profile, Fetchium returns the exact same object. Update it anywhere, see it everywhere — no manual cache invalidation.

Identity stable

Entity proxies backed by signals. Lazy entanglement means you only pay for what you read.

Works with React

Deep cloning with structural sharing at the React boundary. Compatible with memo, compiler, and Suspense.

entities/User.ts
1class User extends Entity {
2 __typename = t.typename('User');
3 id = t.id;
4 name = t.string;
5 email = t.string;
6 avatar = t.optional(t.string);
7 createdAt = t.format('date-time');
8
9 get fullName() {
10 return this.name;
11 }
12}
The same object, everywhere
1const { user } = await fetchQuery(GetUser, { id: '1' });
2const { post } = await fetchQuery(GetPost, { id: '5' });
3
4// If post #5's author is user #1:
5post.author === user; // true
Resilient by default
1// Optional fields fall back gracefully
2result = {
3 name: t.string,
4 bio: t.optional(t.string), // unknown? -> undefined
5};
6
7// Arrays filter out unparseable items
8result = {
9 items: t.array(
10 t.union(TextItem, ImageItem) // unknown? -> skipped
11 ),
12};

APIs change. Your app shouldn't break.

Fetchium's type system is designed for resilience. Optional fields fall back to undefined. Arrays silently filter unknown types. Your app keeps running while the API evolves.

The type DSL uses number masks under the hood — not objects. A type like t.optional(t.string) creates zero allocations.

What you get

FetchiumTanStack QueryApollo Client
REST supportFirst classBYO fetch fn
GraphQL supportAdapterFirst class
Entity normalization
End-to-end type safetyPartialPartial
Fine-grained reactivity
Streaming / real-timeSubscriptions
Protocol agnostic
Zero-alloc type DSL

Start fetching

Add Fetchium to your project and define your first query in minutes.

$ npm install fetchium
Read the docs