# Using REST APIs with Reactive Data Client

```bash
npm install @data-client/rest
```

## Define the Resources

[Resources](https://dataclient.io/rest/api/resource.md) are a collection of `methods` for a given `data model`. [Entities](https://dataclient.io/rest/api/Entity.md) and [Schemas](https://dataclient.io/rest/api/schema.md) are the declarative _data model_.
[RestEndpoint](https://dataclient.io/rest/api/RestEndpoint.md) are the [_methods_](https://en.wikipedia.org/wiki/Method_\(computer_programming\)) on
that data.

**Class**

```typescript title="User"
import { Entity } from '@data-client/rest';

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

  static key = 'User';
}
```

```typescript title="Article"
import { Entity, resource } from '@data-client/rest';
import { User } from './User';

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

  pk() {
    return this.slug;
  }

  static key = 'Article';

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

export const ArticleResource = resource({
  urlPrefix: 'http://test.com',
  path: '/article/:slug',
  searchParams: {} as { userId?: string } | undefined,
  schema: Article,
  paginationField: 'page',
});
```

**Mixin**

```typescript title="User"
import { EntityMixin } from '@data-client/rest';

export class User {
  id = '';
  username = '';
}
export class UserEntity extends EntityMixin(User) {}
```

```typescript title="Article"
import { EntityMixin, resource } from '@data-client/rest';
import { UserEntity } from './User';

export class Article {
  slug = '';
  title = '';
  content = '';
  author = UserEntity.fromJS();
  tags: string[] = [];
  createdAt = Temporal.Instant.fromEpochMilliseconds(0);
}

export class ArticleEntity extends EntityMixin(Article, {
  schema: {
    author: UserEntity,
    createdAt: Temporal.Instant.from,
  },
  key: 'Article',
  pk: 'slug',
}) {}

export const ArticleResource = resource({
  urlPrefix: 'http://test.com',
  path: '/article/:slug',
  searchParams: {} as { userId?: string } | undefined,
  schema: ArticleEntity,
  paginationField: 'page',
});
```

[Entity](https://dataclient.io/rest/api/Entity.md) is a kind of schema that [has a primary key (pk)](https://dataclient.io/docs/concepts/normalization.md). This is what allows us
to [avoid state duplication](https://react.dev/learn/choosing-the-state-structure#principles-for-structuring-state), which
is one of the core design choices that enable such high safety and performance characteristics.

[static schema](https://dataclient.io/rest/api/Entity.md#schema) lets us specify declarative transformations like auto [field deserialization](https://dataclient.io/rest/guides/network-transform.md#deserializing-fields) with `createdAt` and [nesting the author field](https://dataclient.io/rest/guides/relational-data.md).

[Urls are constructed](https://dataclient.io/rest/api/RestEndpoint.md#url) by combining the urlPrefix with [path templating](https://github.com/pillarjs/path-to-regexp).
TypeScript enforces the arguments specified with a prefixed colon like `:slug` in this example.

```ts
// GET http://test.com/article/use-reactive-data-client
ArticleResource.get({ slug: 'use-reactive-data-client' });
```

## Render the data

**Single**

```tsx
import { useSuspense } from '@data-client/react';
import { ArticleResource } from '@/resources/Article';

export default function ArticleDetail({ slug }: { slug: string }) {
  const article = useSuspense(ArticleResource.get, { slug });
  return (
    <article>
      <h2>{article.title}</h2>
      <div>{article.content}</div>
    </article>
  );
}
```

> **Info**
>
> [useSuspense()](https://dataclient.io/docs/api/useSuspense.md) acts like [await](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await), ensuring the data is available before returning. [Learn how to be declare your data dependencies](https://dataclient.io/docs/getting-started/data-dependency.md)

**List**

```tsx
import { useSuspense } from '@data-client/react';
import { ArticleResource } from '@/resources/Article';
import ArticleSummary from './ArticleSummary';

export default function ArticleList({ userId }: { userId?: number }) {
  const articles = useSuspense(ArticleResource.getList, { userId });
  return (
    <section>
      {articles.map(article => (
        <ArticleSummary key={article.pk()} article={article} />
      ))}
    </section>
  );
}
```

> **Info**
>
> [useSuspense()](https://dataclient.io/docs/api/useSuspense.md) acts like [await](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await), ensuring the data is available before returning. [Learn how to be declare your data dependencies](https://dataclient.io/docs/getting-started/data-dependency.md)

**Server Component**

```tsx title="app/articles/[userId]/page.tsx"
import { useSuspense } from '@data-client/react';
import { ArticleResource } from '@/resources/Article';
import ArticleSummary from './ArticleSummary';

export default async function ArticleList({ params }: { params: { userId: number } }) {
  const articles = await ArticleResource.getList(params);
  return (
    <section>
      {articles.map(article => (
        <ArticleSummary key={article.pk()} article={article} />
      ))}
    </section>
  );
}
```

> **Warning**
>
> [Server Components](https://dataclient.io/docs/guides/ssr.md#server-components) makes the data static and un-mutable.

## Mutate the data

**Create**

```tsx title="NewArticleForm.tsx"
import { useController } from '@data-client/react';
import { ArticleResource } from '@/resources/Article';

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

[getList.push](https://dataclient.io/rest/api/resource.md#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.

**Update**

```tsx title="UpdateArticleForm.tsx"
import { useController, useSuspense } from '@data-client/react';
import { ArticleResource } from '@/resources/Article';

export default function UpdateArticleForm({ slug }: { slug: string }) {
  const article = useSuspense(ArticleResource.get, { slug });
  const ctrl = useController();
  return (
    <Form
      onSubmit={e =>
        ctrl.fetch(ArticleResource.update, { slug }, new FormData(e.target))
      }
      initialValues={article}
    >
      <FormField name="title" />
      <FormField name="content" type="textarea" />
      <FormField name="tags" type="tag" />
    </Form>
  );
}
```

[update](https://dataclient.io/rest/api/resource.md#update) then takes any `keyable` body to send as the payload and then returns a promise that
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.

**Delete**

```tsx title="ArticleWithDelete.tsx"
import { useController } from '@data-client/react';
import { Article, ArticleResource } from '@/resources/Article';

export default function ArticleWithDelete({
  article,
}: {
  article: Article;
}) {
  const ctrl = useController();
  return (
    <article>
      <h2>{article.title}</h2>
      <div>{article.content}</div>
      <button
        onClick={() =>
          ctrl.fetch(ArticleResource.delete, { slug: article.slug })
        }
      >
        Delete
      </button>
    </article>
  );
}
```

We use [FormData](https://developer.mozilla.org/en-US/docs/Web/API/FormData/FormData) in
the example since it doesn't require any opinionated form state management solution.
Feel free to use whichever one you prefer.

[Mutations](https://dataclient.io/docs/getting-started/mutations.md) automatically updates _all_ usages without the need for
additional requests.

> **Tip: TypeScript 4**
>
> When using TypeScript (optional), version 4.0 or above is required.

## REST Agent Skills

Then call `/data-client-rest-setup` to migrate

[ REST Codegen Skill](https://skills.sh/reactive/data-client/data-client-rest)

### Migrating from Axios

The `data-client-rest-setup` skill automatically detects axios usage and applies the axios migration — including the [codemod](https://dataclient.io/rest/guides/axios-migration.md#codemod), interceptor conversion, and error handling migration.

See the full [Axios Migration Guide](https://dataclient.io/rest/guides/axios-migration.md) for step-by-step examples, a quick reference table, and a standalone codemod.
