Skip to content
Back to skills

Fetcher Integration

ASecurity

Call HTTP APIs with the core `@ahoo-wang/fetcher` client: `Fetcher`/`NamedFetcher`, baseURL, timeouts, `{id}` path and query params, request/response/error interceptors, status validation and error classes, result extractors, cancellation, the named registry. Use for direct `fetcher.get/post` calls, client setup, interceptors or request-pipeline bugs. Do not load to write a class with `@api`/`@get`/`@post` decorators (fetcher-decorator-service) or React hooks (fetcher-react-hooks).

  • 3 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 2, 2026
ai-agentsgoreactapi

Works with

  • cli
  • api

Security analysis

A100/100

Pro scans all 4 files and shows the line behind each finding

Scanned October 7, 2026

npx -y skills add Ahoo-Wang/skills --skill fetcher-integration --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Fetcher Integration?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Fetcher Integration
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/ahoo-wang-fetcher-integration/badge)](https://www.skillsdirectory.com/skills/ahoo-wang-fetcher-integration)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: fetcher-integration
description: >
  Call HTTP APIs with the core `@ahoo-wang/fetcher` client: `Fetcher`/`NamedFetcher`, baseURL, timeouts, `{id}` path and query params, request/response/error interceptors, status validation and error classes, result extractors, cancellation, the named registry. Use for direct `fetcher.get/post` calls, client setup, interceptors or request-pipeline bugs. Do not load to write a class with `@api`/`@get`/`@post` decorators (fetcher-decorator-service) or React hooks (fetcher-react-hooks).
---

# fetcher-integration

## Decisions

- **`NamedFetcher` vs `Fetcher`**: use `NamedFetcher('name', options)` whenever decorators, CoSec or hooks will look the client up by name; it registers itself in `fetcherRegistrar` (a reused name silently replaces the earlier one). `fetcherRegistrar.default` throws if nothing is named `'default'`.
- **What a call returns**: `get/post/put/patch/delete/fetch` default to `ResultExtractors.Response`; `request()` defaults to `ResultExtractors.Exchange`. Pass `{ resultExtractor: ResultExtractors.Json }` as the **third** argument for typed data.
- **Where cross-cutting logic goes**: an interceptor, not a wrapper function — auth, tracing, retries and error recovery all belong in `fetcher.interceptors.request/response/error`. This holds for decorated service classes too: they run through the same fetcher's interceptors.
- **Direct calls vs a service class**: call the fetcher directly when URLs or bodies are built at runtime or there are a few one-off calls; stable endpoints declared as a class are `$fetcher-decorator-service`.

## Gotchas a capable model gets wrong

- `intercept(exchange)` **mutates** the exchange and returns nothing; `use()` returns `false` and ignores an interceptor whose `name` is already registered.
- `FetchRequest.headers` is optional: write headers with `setHeader(exchange.ensureRequestHeaders(), 'Authorization', value)`, not `exchange.request.headers.X = …`.
- `name` and `order` are both required on an interceptor object. Built-in order: `REQUEST_BODY_INTERCEPTOR_ORDER` (body → JSON) runs near `Number.MIN_SAFE_INTEGER`, so an object body assigned by a user interceptor with an ordinary order (0, 100) is **not** serialized. `URL_RESOLVE_INTERCEPTOR_ORDER` and `FETCH_INTERCEPTOR_ORDER` come last; response-side `VALIDATE_STATUS_INTERCEPTOR_ORDER` runs near `Number.MAX_SAFE_INTEGER`.
- Non-2xx responses reject with the `HttpStatusValidationError` itself (a subclass of `ExchangeError`, not its `cause`: check `error instanceof HttpStatusValidationError` before `ExchangeError`); timeouts with `ExchangeError` whose `cause` is `FetchTimeoutError`. Inside an error interceptor `exchange.error` is the raw failure (`HttpStatusValidationError`, `FetchTimeoutError`, fetch's `TypeError`), not yet wrapped. An error interceptor that clears `exchange.error` recovers, but the response phase is not re-run; one that throws stops the error phase and the exchange rejects with an `ExchangeError` whose `cause` is the thrown value. Skip validation per call with `{ attributes: new Map([[IGNORE_VALIDATE_STATUS, true]]) }` in the third argument, or per client with `validateStatus`.
- `timeout` defaults to none (`0` also means none). It applies together with a caller `signal`/`abortController` (whichever fires first aborts) and covers only up to the response headers — bound body reads (`response.json()`, streams) with your own signal.
- `fetch` option (`FetchImplementation`): the `fetch` that sends requests (global `fetch`, read at call time, by default), for runtimes or frameworks that supply their own (Tauri, instrumentation) or tests; timeouts and signals still apply. Like `validateStatus`, it is ignored when you pass your own `interceptors` manager. An interceptor that re-sends with `timeoutFetch(exchange.request)` must pass it as the second argument, or it uses the global `fetch`.
- No default `Content-Type`: the body sets it — a plain object is serialized and sent as `application/json`, a string as `application/json` unless one is set, `FormData`/`Blob`/`URLSearchParams` always lose it so fetch sets its own, binary bodies get none. Bodyless requests carry none (no CORS preflight from it).
- `urlParams.path` fills `{id}` / `:id` templates; a placeholder without a value (`undefined`, `null`, or no `path` at all) throws. `urlParams.query` omits `undefined`/`null`, repeats arrays (`ids=1&ids=2`) and sends a `Date` as ISO 8601.

## Minimal example

```ts
import { NamedFetcher, ResultExtractors, setHeader } from '@ahoo-wang/fetcher';

export const api = new NamedFetcher('api', {
  baseURL: 'https://api.example.com',
  timeout: 5000,
});
api.interceptors.request.use({
  name: 'auth',
  order: 100,
  intercept(exchange) {
    setHeader(
      exchange.ensureRequestHeaders(),
      'Authorization',
      `Bearer ${token()}`,
    );
  },
});
const user = await api.get<User>(
  '/users/{id}',
  { urlParams: { path: { id: 1 }, query: { include: 'profile' } } },
  { resultExtractor: ResultExtractors.Json },
);
```

## References

- `references/api.md`: constructor options, every method signature, interceptor order constants, URL params, error classes and full examples. Load it for exact signatures or lifecycle details.

## Related Skills

- $fetcher-decorator-service: stable endpoints declared as a class on top of a named fetcher.
- $fetcher-cosec-auth: JWT, refresh and 401/403 handling as interceptors.
- $fetcher-llm-streaming: SSE and token streams from a Fetcher response.
- $fetcher-react-hooks: request state in React components.

Files in this skill

  • SKILL.md1.7 KB
  • agents/openai.yaml199 B
  • evals/evals.json881 B
  • references/api.md16.5 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.