Skip to content
Back to skills

Fetcher Decorator Service

ASecurity

Declare HTTP endpoints as a typed class with `@ahoo-wang/fetcher-decorator`: `@api`, `@get`/`@post`/`@put`/`@patch`/`@del`, `@path`/`@query`/`@header`/`@body`, return types, result extractors, `beforeExecute`/`afterExecute`. Use when writing or debugging a decorated service class. Direct `fetcher.get/post` calls, URLs built at runtime, client setup or interceptors (even for decorated services): fetcher-integration; React state: fetcher-react-hooks.

  • 3 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 2, 2026
ai-agentsgoreactdebuggingapi

Works with

  • cli
  • api

Security analysis

A100/100

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

Scanned October 7, 2026

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

Installs into .claude/skills of the current project.

Are you the author of Fetcher Decorator Service?

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

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

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-decorator-service
description: >
  Declare HTTP endpoints as a typed class with `@ahoo-wang/fetcher-decorator`: `@api`, `@get`/`@post`/`@put`/`@patch`/`@del`, `@path`/`@query`/`@header`/`@body`, return types, result extractors, `beforeExecute`/`afterExecute`. Use when writing or debugging a decorated service class. Direct `fetcher.get/post` calls, URLs built at runtime, client setup or interceptors (even for decorated services): fetcher-integration; React state: fetcher-react-hooks.
---

# fetcher-decorator-service

## Decisions

- **Decorator service vs direct calls**: use a class when endpoints are stable and discoverable; call the fetcher directly (`$fetcher-integration`) when URLs or bodies are built dynamically.
- **Which fetcher**: `@api(basePath, { fetcher: 'api' })` resolves a `NamedFetcher` by name (or takes a `Fetcher` instance) on every call, falling back to `fetcherRegistrar.default`, which throws if nothing is named `'default'`. An instance `apiMetadata` field is shallow-merged over the class options on every call, so replacing or mutating it applies to the next call.
- **Cross-cutting behaviour** (auth, retry, 401 redirects) stays in fetcher interceptors (`$fetcher-integration`). `afterExecute` runs only after a successful (or interceptor-recovered) exchange — it is not a `finally`, and under the default status validation a 401 rejects with `HttpStatusValidationError` before it.

## Gotchas a capable model gets wrong

- Legacy decorators: the app needs `experimentalDecorators: true` (not `emitDecoratorMetadata`); compiled as standard decorators, `@api` throws a TypeError saying so. `reflect-metadata` ships with the package and is imported by it; no entry-point import is needed.
- Method bodies are placeholders: `throw autoGeneratedError(...args)`. If that error is actually thrown at runtime, `@api` or the method decorator is missing.
- DELETE is `@del` (not `@delete`); there is no `@trace` — use `@endpoint(HttpMethod.TRACE, path)`.
- Decorated methods default to `JsonResultExtractor` and `EndpointReturnType.RESULT`, unlike core `fetcher.get()` which returns a `Response`. Use `{ returnType: EndpointReturnType.EXCHANGE }` or a `resultExtractor` per endpoint to change it.
- Parameter names are read from the function source when omitted, which minifiers break: always pass names (`@path('id')`). `null`/`undefined` arguments are skipped. A plain object passed to `@query`/`@path`/`@header` is spread into keys; an array, `Date` or other value is bound to the parameter name (`@query('ids') ids: number[]` → `ids=1&ids=2`; an array header is comma-joined).
- An `AbortSignal` or `AbortController` argument is detected automatically; it applies together with the fetcher timeout (whichever fires first aborts).
- A subclass inherits endpoints but uses its own `@api` base path — inherited paths must still resolve there. Overriding an inherited endpoint without an endpoint decorator keeps the override's own code.
- A method called without its instance (`const { getUser } = service`) throws a TypeError; bind it or call it on the service.

## Minimal example

```ts
import { NamedFetcher } from '@ahoo-wang/fetcher';
import {
  api,
  autoGeneratedError,
  body,
  get,
  path,
  post,
  query,
} from '@ahoo-wang/fetcher-decorator';

new NamedFetcher('api', { baseURL: 'https://api.example.com' });

@api('/users', { fetcher: 'api' })
export class UserService {
  @get('/{id}')
  getUser(@path('id') id: string): Promise<User> {
    throw autoGeneratedError(id);
  }
  @get('')
  list(@query('page') page?: number): Promise<User[]> {
    throw autoGeneratedError(page);
  }
  @post('')
  create(@body() user: Omit<User, 'id'>): Promise<User> {
    throw autoGeneratedError(user);
  }
}
```

## References

- `references/api.md`: decorator signatures, parameter binding rules, execution flow, lifecycle hooks, inheritance and CRUD examples. Load it for anything beyond the example above.

## Related Skills

- $fetcher-integration: the `Fetcher`/`NamedFetcher` and interceptors underneath.
- $fetcher-llm-streaming: endpoints that return SSE streams.
- $fetcher-react-hooks: `createExecuteApiHooks` / `createQueryApiHooks` turn a service into hooks.

Files in this skill

  • SKILL.md1.7 KB
  • agents/openai.yaml209 B
  • references/api.md13.9 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.