fate is a modern data client for React inspired by Relay and GraphQL. It combines view composition, normalized caching, data masking, Async React features, and type-safe data fetching.
Features
- View Composition: Components declare their data requirements using co-located "views". Views are composed into a single request per screen, minimizing network requests and eliminating waterfalls.
- Normalized Cache: fate maintains a normalized cache for all fetched data. This enables efficient data updates through actions and mutations and avoids stale or duplicated data.
- Data Masking & Strict Selection: fate enforces strict data selection for each view, and masks (hides) data that components did not request. This prevents accidental coupling between components and reduces overfetching.
- Async React: fate uses modern Async React features like Actions, Suspense, and
useto support concurrent rendering and enable a seamless user experience. - Lists & Pagination: fate provides built-in support for connection-style lists with cursor-based pagination, making it easy to implement infinite scrolling and "load-more" functionality.
- Optimistic Updates: fate supports declarative optimistic updates for mutations, allowing the UI to update immediately while the server request is in-flight. If the request fails, the cache and its associated views are rolled back to their previous state.
- Live Views: fate can keep individual view refs up to date through a single native Server-Sent Events stream, merging updates into the normalized cache.
- AI-Ready: fate's minimal, predictable API and explicit data selection enable local reasoning, enabling humans and AI tools to generate stable, type-safe data-fetching code.
A modern data client for React
fate is designed to make data fetching and state management in React applications more composable, declarative, and predictable. The framework has a minimal API, no DSL, and no magic—it's just JavaScript.
GraphQL and Relay introduced several novel ideas: fragments co‑located with components, a normalized cache keyed by global identifiers, and a compiler that hoists fragments into a single network request. These innovations made it possible to build large applications where data requirements are modular and self‑contained.
Nakazawa Tech builds apps and games primarily with GraphQL and Relay. We advocate for these technologies in talks and provide templates (server, client) to help developers get started quickly.
However, GraphQL comes with its own type system and query language. If you are already using tRPC or another type‑safe RPC framework, it's a significant investment to adopt and implement GraphQL on the backend. This investment often prevents teams from adopting Relay on the frontend.
Many React data frameworks lack Relay's ergonomics, especially fragment composition, co-located data requirements, predictable caching, and deep integration with modern React features. Optimistic updates usually require manually managing keys and imperative data updates, which is error-prone and tedious.
fate takes the great ideas from Relay and applies them to plain TypeScript data fetching. You get type safety between the client and server, a native protocol with optional adapters such as tRPC, and GraphQL-like ergonomics for data fetching. Using fate usually looks like this:
export const PostView = view<Post>()({
author: UserView,
content: true,
id: true,
title: true,
});
export const PostCard = ({ post: postRef }: { post: ViewRef<'Post'> }) => {
const post = useView(PostView, postRef);
return (
<Card>
<h2>{post.title}</h2>
<p>{post.content}</p>
<UserCard user={post.author} />
</Card>
);
};Learn more about fate's core concepts or create an app from one of the templates.