- NPM
- Yarn
- pnpm
- esm.sh
yarn add @data-client/rest
npm install --save @data-client/rest
pnpm add @data-client/rest
<script type="module">
import * from 'https://esm.sh/@data-client/rest';
</script>
Define los Resources
Los Resources son una colección de methods (métodos) para un data model (modelo de datos) dado. Las Entities y los Schemas son el modelo de datos declarativo.
Los RestEndpoint son los métodos sobre
esos datos.
- Class
- Mixin
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', });
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 es un tipo de schema que tiene una clave primaria (pk). Esto es lo que nos permite evitar la duplicación de estado, que es una de las decisiones de diseño centrales que permiten unas características de seguridad y rendimiento tan altas.
static schema nos permite especificar transformaciones declarativas, como la deserialización automática de campos con createdAt y el anidamiento del campo author.
Las URLs se construyen combinando el urlPrefix con plantillas de rutas.
TypeScript exige los argumentos especificados con dos puntos como prefijo, como :slug en este ejemplo.
// GET http://test.com/article/use-reactive-data-client
ArticleResource.get({ slug: 'use-reactive-data-client' });
Renderiza los datos
- Single
- List
- Server Component
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>
);
}
useSuspense() actúa como await y se asegura de que los datos estén disponibles antes de devolver. Aprende a declarar tus dependencias de datos
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>
);
}
useSuspense() actúa como await y se asegura de que los datos estén disponibles antes de devolver. Aprende a declarar tus dependencias de datos
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>
);
}
Los Server Components hacen que los datos sean estáticos e inmutables.
Muta los datos
- Create
- Update
- Delete
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 toma entonces cualquier cuerpo keyable para enviarlo como payload y devuelve una promesa que
se resuelve con el nuevo Resource creado por la API. Se añadirá automáticamente a la caché para que lo muestre cualquier consumidor.
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 toma entonces cualquier cuerpo keyable para enviarlo como payload y devuelve una promesa que
toma entonces cualquier cuerpo keyable para enviarlo como payload y devuelve una promesa que
se resuelve con el nuevo Resource creado por la API. Se añadirá automáticamente a la caché para que lo muestre cualquier consumidor.
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>
);
}
Usamos FormData en el ejemplo porque no requiere ninguna solución de gestión del estado de formularios con opiniones propias. Siéntete libre de usar la que prefieras.
Las mutaciones actualizan automáticamente todos los usos sin necesidad de peticiones adicionales.
Si usas TypeScript (opcional), se requiere la versión 4.0 o superior.
Agent Skills de REST
- Skills
- OpenSkills
- Claude Code
npx skills add reactive/data-client \
--skill data-client-schema \
--skill data-client-rest-setup \
--skill data-client-rest
npx openskills install reactive/data-client/.agents/skills/data-client-schema
npx openskills install reactive/data-client/.agents/skills/data-client-rest-setup
npx openskills install reactive/data-client/.agents/skills/data-client-rest
claude plugin marketplace add reactive/data-client
claude plugin install core@data-client
Después, llama a /data-client-rest-setup para migrar
Migrar desde Axios
El skill data-client-rest-setup detecta automáticamente el uso de axios y aplica la migración de axios, incluyendo el codemod, la conversión de interceptores y la migración del manejo de errores.
Consulta la guía de migración de Axios completa para ver ejemplos paso a paso, una tabla de referencia rápida y un codemod independiente.