CtrlK
BlogDocsLog inGet started
Tessl Logo
SkillPublic

data-client-rest-setup

Set up and migrate to @data-client/rest for REST APIs. Detects existing HTTP patterns (axios, fetch, ky, superagent, got) and migrates them. Creates custom RestEndpoint base class with common behaviors. Use when adopting @data-client/rest in a new or existing project.

Quality

91%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide
SecuritybySnyk

Low

Low-risk findings worth noting

SKILL.md
Quality
Evals
Security

REST Protocol Setup & Migration

Setting up @data-client/rest only defines endpoints and schemas, so it needs no Data Client provider: leave how the app loads and stores data alone unless the user asks to change it.

APIs: RestEndpoint, resource. Vue projects: read <name>.vue.md instead of <name>.md when it exists.

Step 1: Install

Install @data-client/rest with the project's package manager. It ships the skills this setup uses next (data-client-rest, and data-client-schema from @data-client/endpoint); link them from the project root:

npx --no data-client-skills   # pnpm exec data-client-skills, yarn data-client-skills
  • If a skill isn't available by name afterwards, read .claude/skills/<skill>/SKILL.md.
  • If the command isn't found, the installed version predates 0.19: run npx skills add reactive/data-client --skill data-client-rest --skill data-client-schema -y instead.
  • Add this line to AGENTS.md (create it if missing; if a CLAUDE.md exists, make sure it has an @AGENTS.md line), with the project's package manager in the command, and commit .agents/skills and .claude/skills:
After adding, removing or upgrading `@data-client/*` packages, run `npx --no data-client-skills` and commit the skill links.

Step 2: Migrate each HTTP library

Check package.json and imports. Several libraries can coexist: migrate each one's files with its reference. If HTTP calls exist but the library is unclear, ask the user.

DetectedFollow
axiosAxios migration, with the steps below
fetch( calls or wrappers around fetchFetch migration
kyKy migration
superagentSuperAgent migration
gotGot migration
noneStep 3

Axios:

  1. Check git status is clean (commit or ask first), so the codemod's changes can be reviewed on their own.
  2. Run the codemod before any manual edits, on the source folders (never the repo root): npx @data-client/codemods axios-to-rest src/ (it reads TypeScript and skips node_modules). It turns each axios call into a call to an endpoint (usually a module-level const when the call is in a function), turns each axios.create() instance into a base endpoint class, and drops .data unless a response interceptor may have unwrapped it.
  3. The codemod doesn't format its output, so run the fixers the project already uses on the changed files: its format or lint --fix script, or the tool its config names (such as prettier --write, eslint --fix or biome check --write).
  4. Search the source for TODO(data-client). Each comment names one thing to finish and links its guide section, which is in the reference under the same heading. Resolve it, then delete the comment.
  5. Follow the reference's remaining steps. Callers keep loading data the way they did; only the call changes.
  6. Run the type checker and tests. When no axios import or TODO(data-client) comment is left, uninstall axios.

Step 3: Base endpoint class

Put the project's shared request behavior in one RestEndpoint subclass and build every endpoint from it. If a migration made one (the axios codemod does, from axios.create()), add to that class instead of creating another.

import { RestEndpoint, RestGenerics } from '@data-client/rest';

export class BaseEndpoint<O extends RestGenerics = any> extends RestEndpoint<O> {
  urlPrefix = 'https://api.example.com';

  getHeaders(headers: HeadersInit): HeadersInit {
    const token = localStorage.getItem('authToken');
    return { ...headers, ...(token && { Authorization: `Bearer ${token}` }) };
  }
}

Override only what the app does today:

BehaviorOverride
Auth, CSRF or other headers (may be async, e.g. token refresh)getHeaders(headers)
Auth token from React context (Okta, Auth0)hookifyResource(); a base class can't read context
Cookies (axios withCredentials), other RequestInitasync getRequestInit(body) returning { ...(await super.getRequestInit(body)), credentials: 'include' }. Without await, the spread drops method, body and headers, so every mutation is sent as a GET
Transform the response: unwrap an envelope like { "data": {...}, "meta": {...} }, convert caseprocess(value), e.g. return value?.data (value is null for a 204). Not axios's response.data: that is axios's wrapper, which the codemod already removed
Date fieldsNot process(): the Entity's static schema, e.g. createdAt: Temporal.Instant.from (skill "data-client-schema")
Status handling, like a 401 redirectasync fetchResponse(input, init): super.fetchResponse() throws a NetworkError for any non-2xx status, so wrap it in try, check error.status in catch, and rethrow (Server error messages shows it)
Arrays or nested objects in the querysearchToString(searchParams), e.g. qs.stringify(searchParams, { arrayFormat: 'brackets' })
Non-JSON responses (files, text)The content option, e.g. content: 'blob'; otherwise parseResponse(response)

Request bodies need no override: a plain object or array is sent as JSON, and anything else (FormData, URLSearchParams, a string) as-is (getRequestInit).

Defining endpoints

resource() for standard CRUD on one path (get, getList, create, update, partialUpdate, delete):

export const TodoResource = resource({
  path: '/todos/:id',
  schema: Todo,
  Endpoint: BaseEndpoint,
});

new BaseEndpoint({ ... }) for anything else (search, auth, custom actions). Type a body with a truthy value, body: {} as { email: string; password: string }, never undefined as unknown as T: body: undefined means the endpoint sends no body. A POST, PUT or PATCH that sends none needs that body: undefined, or its only argument is taken as the body and the path params go missing.

If the codebase validates responses with Zod or Yup, keep that by parsing in process() and still add an Entity schema; see "Coexisting with Zod or Yup" in the Axios migration.

Next steps

  1. Define Entity classes (skill "data-client-schema") and set each endpoint's schema:. Without one, responses aren't normalized or cached.
  2. Apply skill "data-client-rest" for resource and endpoint patterns.
  3. Only when the user asks to move components onto the store: apply skill "data-client-react" or "data-client-vue".

More: Authentication, Django REST Framework.

Repository

reactive/data-client

Path

.agents/skills/data-client-rest-setup/SKILL.md

Last updated
First committed

Is this your skill?

If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.