ftched
v1.0.2
Published
Next generation zero config data engine for JS/TS with SWR and AES-GCM encryption.
Maintainers
Readme
ftched
A next-generation, zero-configuration data orchestration engine for JavaScript and TypeScript.
ftched handles the entire lifecycle of server-side data in a client application: fetching, intelligent caching, background revalidation, request deduplication, optimistic mutations, encrypted persistence, CSRF protection, schema validation, and React integration — with zero configuration and zero production dependencies.
Why ftched
| Capability | fetch | axios | ftched |
| :----------------------------------------------- | :-----: | :-----: | :------: |
| Returns data directly (no .json(), no .data) | No | Yes | Yes |
| Global SWR cache | No | No | Yes |
| Request deduplication | No | No | Yes |
| Race condition prevention | No | No | Yes |
| Automatic retry with exponential backoff | No | No | Yes |
| Tag-based cache invalidation | No | No | Yes |
| Optimistic mutations with rollback | No | No | Yes |
| AES-GCM encrypted local persistence | No | No | Yes |
| Automated CSRF token injection | No | Partial | Yes |
| Sensitive header cache protection | No | No | Yes |
| Schema validation (Zod / TypeBox) | No | No | Yes |
| React hooks synchronized with the cache | No | No | Yes |
| Zero production dependencies | Yes | No | Yes |
Installation
npm install ftchedReact hooks (optional):
npm install reactQuick Start
import { ftched } from "ftched";
// GET — returns your data directly
const user = await ftched("/api/users/1");
// POST
const created = await ftched.post("/api/posts", { title: "Hello" });
// With caching, retries, and tags
const feed = await ftched("/api/feed", {
staleTime: "1m",
cacheTime: "24h",
tags: ["feed"],
retry: 3,
});Core Methods
ftched(url, options); // GET by default
ftched.get(url, options);
ftched.post(url, body, options);
ftched.put(url, body, options);
ftched.patch(url, body, options);
ftched.delete(url, options);
ftched.stream(url, options); // Returns ReadableStream
ftched.mutate(url, variables, options); // Full mutation engineAll methods return the parsed JSON body. Pass full: true for the complete response object including status, headers, and fromCache.
Options
| Option | Type | Default | Description |
| :--------------- | :----------------------- | :--------------- | :----------------------------------------------------------------------------------- |
| key | string | Auto-derived | Override the cache key. |
| staleTime | number \| string | 0 | Time before background revalidation. Accepts '30s', '5m', '2h', '1d', or ms. |
| cacheTime | number \| string | '5m' | Time before full cache expiry. |
| tags | string[] | [] | Group invalidation tags. |
| retry | number | 0 | Retry attempts on failure with exponential backoff. |
| retryDelay | number | 1000 | Base retry delay in ms. |
| schema | Zod / TypeBox | — | Runtime response validation. |
| full | boolean | false | Return FtchedResponse object instead of raw data. |
| xsrfCookieName | string | 'XSRF-TOKEN' | Cookie name to read the CSRF token from. |
| xsrfHeaderName | string | 'X-XSRF-TOKEN' | Header name to inject the CSRF token into. |
| signal | AbortSignal | — | External cancellation signal. |
| headers | Record<string, string> | — | Additional request headers. |
Caching
The SWR pattern serves cached data immediately while refreshing in the background.
const posts = await ftched("/api/posts", {
staleTime: "30s",
cacheTime: "10m",
tags: ["posts"],
});See docs/core/caching.md for keys, time formats, and tag invalidation.
Mutations
Full write lifecycle with optimistic updates and automatic rollback.
await ftched.mutate(
"/api/posts",
{ title: "New" },
{
key: "posts",
onMutate: (vars) => [{ id: 99, ...vars }],
onError: (err, vars, rollback) => rollback(),
onSuccess: (data) => console.log("Saved", data.id),
invalidateTags: ["posts"],
},
);See docs/core/mutations.md for the complete lifecycle API.
Security
Three built-in security mechanisms — all automatic:
- CSRF tokens injected into all write requests from browser cookies
- Sensitive header shield prevents caching authenticated responses
- AES-GCM encryption for localStorage persistence via
EncryptedLocalStorageAdapter
See docs/security.md.
Interceptors
Globally transform requests and responses.
ftched.interceptors.useRequest(async (options) => ({
...options,
headers: {
...options.headers,
Authorization: `Bearer ${getToken()}`,
},
}));See docs/interceptors.md.
React Hooks
import { useQuery, useMutation } from 'ftched';
function Posts() {
const { data, loading } = useQuery('/api/posts', { staleTime: '1m' });
const { mutate } = useMutation('/api/posts', { invalidateTags: ['posts'] });
return (
<>
{data?.map(p => <div key={p.id}>{p.title}</div>)}
<button onClick={() => mutate({ title: 'New' })}>Add</button>
</>
);
}See docs/react.md.
Encrypted Persistence
import { ftched, EncryptedLocalStorageAdapter } from "ftched";
const client = ftched.create({
...ftched.context,
adapter: new EncryptedLocalStorageAdapter("my-32-char-secret"),
});See docs/adapters.md.
Full Documentation
| Document | Topic | | :----------------------------------------------------------------- | :--------------------------------------------- | | docs/introduction.md | Architecture, philosophy, feature overview | | docs/getting-started.md | Installation and first steps | | docs/core/caching.md | SWR cache, keys, tags | | docs/core/mutations.md | Mutations, optimistic updates, lifecycle | | docs/core/networking.md | Retry, deduplication, abort pooling, streaming | | docs/core/schema-validation.md | Zod and TypeBox validation | | docs/security.md | CSRF, header shield, AES-GCM encryption | | docs/interceptors.md | Request and response interceptors | | docs/adapters.md | Storage adapters and custom adapter API | | docs/react.md | useQuery and useMutation hooks | | docs/instances.md | Isolated instances with ftched.create |
License
MIT
