A zero-dependency, axios-style HTTP client for Deno built on native Fetch API. Published to JSR as @anitrend/request-client.
- Deno 2.x only. No Node.js, npm, pnpm, or yarn for development.
- All commands are
deno tasktasks defined indeno.json. - VS Code: install the Deno extension (
denoland.vscode-deno). Workspacesettings.jsonalready enables it withdeno.enable: true.
deno task check # Type-check the entrypoint (packages/client/src/mod.ts)
deno task test:unit # Unit tests with coverage (packages/**/*.test.ts)
deno task test:spec # Spec/integration tests with coverage (packages/**/*.spec.ts)
deno task test:watch # Watch mode
deno task lint # Deno lint
deno task fmt # Format code
deno task fmt:check # Check formatting (no writes — CI style)
deno task coverage # Generate coverage/lcov.infoTests use -P (--allow-read) implicitly plus network restricted to jsonplaceholder.typicode.com (see deno.json test.permissions).
- Monorepo workspace with one active package:
packages/client/. - Entrypoint:
packages/client/src/mod.ts(also the export path injsr.jsonanddeno.json). - Source files:
request.client.ts(main class +createClient),types.ts,interceptors.ts. - Tests live in
packages/client/spec/. Mock helper ismock.helper.tsin the same directory (no separatepackages/utils/— that directory no longer exists). - Examples are in
examples/and import via@scope/packages/client(workspace alias), not the JSR name. - Zero runtime dependencies. Test deps from JSR:
@c4spar/mock-fetch,@std/assert,@std/testing.
User → RequestClient method → Request interceptors → Build URL/headers →
fetch() → Response interceptors → Process response → Return RequestResponse
Error path: fetch() error → RequestError → Error interceptors → throw
- RequestClient (
request.client.ts): Main class with HTTP method wrappers - InterceptorManager (
interceptors.ts): Manages request/response/error interceptors - Types (
types.ts):RequestConfig,RequestResponse<T>,RequestError
Three interceptor types managed by InterceptorManager:
// Request interceptors — modify config before fetch
client.interceptors.request.use((config) => {
return config
})
// Response interceptors — transform response after fetch
client.interceptors.response.use((response) => {
return response
})
// Error interceptors — handle errors (must return error, not throw)
client.interceptors.error.use((error) => {
return error
})Critical: Interceptors execute in registration order. Error interceptors receive RequestError instances.
Enforced by deno fmt and deno lint:
- 2-space indent, single quotes, no semicolons
- Max line width 80 characters
- LF line endings, trailing newline at EOF
- Formatter applies to
packages/only (excludes.github/,coverage/,build/,README.md)
Use workspace aliases from deno.json:
// Library code
import { createClient } from '@anitrend/request-client'
// Examples
import { createClient } from '@scope/packages/client'- Unit tests:
*.test.ts— test individual methods/functions with mocked fetch - Spec tests:
*.spec.ts— integration tests againstjsonplaceholder.typicode.com - Place tests in
packages/client/spec/ - Use
@std/testing/bddfordescribe/itblocks - Mock fetch with
@c4spar/mock-fetchfor unit tests - Test imports use
@anitrend/request-client(JSR name mapped indeno.jsonimports), not relative imports
import { assertEquals } from '@std/assert'
import { describe, it } from '@std/testing/bdd'
describe('Feature', () => {
it('should behave correctly', async () => {
// Arrange, Act, Assert
})
})- Update
RequestConfiginterface intypes.ts - Handle in
RequestClient.request()method - Add unit tests in
packages/client/spec/ - Add spec test in
packages/client/spec/ - Update examples if user-facing
- Document in
README.md
Client supports: json (default), text, blob, arrayBuffer:
const response = await client.get<ArrayBuffer>('/file', {
responseType: 'arrayBuffer',
})Uses AbortController with setTimeout:
const controller = new AbortController()
setTimeout(() => controller.abort(), timeout)
fetch(url, { signal: controller.signal })Always export types explicitly from mod.ts:
export { createClient, RequestClient } from './request.client.ts'
export { RequestError } from './types.ts'
export type { RequestConfig, RequestResponse } from './types.ts'- Don't use
console.login source code — tests verify behavior, not logs - Always await interceptors — they can be async
- Test with mocks AND real API — unit tests use mocks, spec tests use jsonplaceholder
- Respect 80-char limit — enforced by formatter
- Export types separately —
export type { ... }for type-only exports - Use workspace import paths in examples —
@scope/packages/client
- PR CI (
ci.yml): changes filter → lint → format → type-check → unit-test. Test matrix (ubuntu/macos/windows) runs only on pushes tomain. - Quality (
quality.yml): dependency review + TODO/FIXME/XXX detection inpackages/(non-blockingcontinue-on-error). - Publish (
publish.yml): triggers on GitHub Release or manual dispatch. Gated by lint → format → type-check → unit-test →deno publish. - Release Drafter: auto-drafts release notes. Also auto-bumps version in
jsr.jsonanddeno.jsonvia a bot PR on merge tomain.
- Branch naming controls automatic PR labels and semver bumps (via Release Drafter autolabeler):
feat/*→ feature (minor),fix/*→ bug fix (patch),chore/*→ enhancement (minor),refactor/*→ refactor (patch),build/*→ dependencies (patch),test/*→ testing (patch),ci/*→ CI (patch),docs/*→ docs (no version bump),revert/*→ revert (patch) - Breaking changes: Manually add
breakinglabel for major bump. - Version lives in both
jsr.jsonanddeno.json. They must stay in sync. Release Drafter handles this automatically — do not manually bump version in a PR unless you know what you're doing.
- Package name:
@anitrend/request-client - Registry: JSR. Only
packages/client/src/,README.md,LICENSE,jsr.json,deno.jsonare included in the publish. deno.lockis gitignored — never commit it.
"publish": {
"include": [
"packages/client/src/",
"README.md",
"LICENSE",
"jsr.json",
"deno.json"
]
}description: Clear one-linerkeywords: Array of relevant search termsrepository: GitHub URL withgit+https://prefixlicense: "Apache-2.0"bugsandhomepage: GitHub URLs
Reference: https://jsr.io/docs/scoring
- Always use provenance:
deno publish --provenancein CI/CD - No slow types: Export types explicitly:
export type { Foo } from './types.ts' - Complete JSDoc: All public APIs must have JSDoc with
@exampleblocks - Single config source: Only root
deno.jsonshould havenameandversionfields - Test before publish: Run
deno publish --dry-run+ all tests before any release
- Module-level
@moduleJSDoc inmod.ts - Runtime compatibility section in README (Deno, Node.js 18+, Bun)
- Examples for each major feature
- API reference with all public types/functions
When changing public API:
- Update
types.tsJSDoc comments - Update
README.mdAPI Reference section - Update relevant example in
examples/ - Update
ARCHITECTURE.mdif structural change
ARCHITECTURE.mdreferencespackages/utils/but that directory no longer exists.mock.helper.tsis atpackages/client/spec/mock.helper.ts.- The
test:unitandtest:spectasks both write coverage tocoverage/. Runtest:specaftertest:unitonly if you want merged coverage, otherwise the second run overwrites. - Test imports use
@anitrend/request-client(the JSR name mapped indeno.jsonimports), not relative imports.