Skip to main content

REST Usage

npm install --save @data-client/rest
TypeScript 4

When using TypeScript, version 4.0 or above is required.

Define the API

Resources are a collection of methods for a given data model. Entities and Schemas are the declarative data model. RestEndpoint are the methods on that data.

import { Entity } from '@data-client/rest';

export class User extends Entity {
  id = '';
  username = '';

  pk() {
import { Entity, createResource } from '@data-client/rest';
import { User } from './User';

export class Article extends Entity {
  id = '';
  title = '';
  content = '';
  author = User.fromJS();
  tags: string[] = [];
  createdAt = Temporal.Instant.fromEpochSeconds(0);

  pk() {

  static schema = {
    author: User,
    createdAt: Temporal.Instant.from,

  static key = 'Article';

export const ArticleResource = createResource({
  urlPrefix: '',
  path: '/article/:id',
  searchParams: {} as { userId?: string } | undefined,
  schema: Article,
  paginationField: 'page',

Entity is a kind of schema that has a primary key (pk). This is what allows us to avoid state duplication, which is one of the core design choices that enable such high safety and performance characteristics.

static schema lets us specify declarative transformations like auto field deserialization with createdAt and nesting the author field.

Urls are constructed by combining the urlPrefix with path templating. TypeScript enforces the arguments specified with a prefixed colon like :id in this example.

// GET
ArticleResource.get({ id: 5 });

Bind the data with Suspense

import { useSuspense } from '@data-client/react';
import { ArticleResource } from 'api/article';

export default function ArticleDetail({ id }: { id: number }) {
const article = useSuspense(ArticleResource.get, { id });
return (

useSuspense() acts like await, ensuring the data is available before returning. Learn how to be declare your data dependencies

Mutate the data

import { useController } from '@data-client/react';
import { ArticleResource } from 'api/article';

export default function NewArticleForm() {
const ctrl = useController();
return (
onSubmit={e =>
ctrl.fetch(ArticleResource.getList.push, new FormData(
<FormField name="title" />
<FormField name="content" type="textarea" />
<FormField name="tags" type="tag" />

getList.push then takes any keyable body to send as the payload and then returns a promise that resolves to the new Resource created by the API. It will automatically be added in the cache for any consumers to display.

We use FormData in the example since it doesn't require any opinionated form state management solution. Feel free to use whichever one you prefer.

Mutations automatically updates all usages without the need for additional requests.