Skip to main content

stacks-http

Use when working with HTTP utilities in a Stacks application - HTTP status codes, making outbound HTTP requests via HttxClient, reactive fetch composables (useFetch/createFetch), or HTTP-related helpers. Covers @stacksjs/http, @stacksjs/httx, and the fetch composables in @stacksjs/composables.

ソース情報

リポジトリ
stacksjs/stacks
ソースの最終更新活動
2026年8月29日 17:05
検出された SKILL.md の言語
英語
スター
631
フォーク
20

インストール方法

デフォルトでは、最初にソースを確認する Prompt が選択されています。直接コマンドに切り替えるか、ローカルコピーをダウンロードすることもできます。

ソースファイルを確認

インストールを決める前に、SKILL.md と SkillsMP に表示されている付属ファイルをお読みください。

SKILL.md を表示中

SKILL.md
ソースの指示 · 読み取り専用プレビュー
name
stacks-http
description
Use when working with HTTP utilities in a Stacks application - HTTP status codes, making outbound HTTP requests via HttxClient, reactive fetch composables (useFetch/createFetch), or HTTP-related helpers. Covers @stacksjs/http, @stacksjs/httx, and the fetch composables in @stacksjs/composables.
license
MIT
compatibility
Bun >= 1.3.0, TypeScript
allowed-tools
Read Edit Write Bash Grep Glob
# Stacks HTTP ## Key Paths - Core package: `storage/framework/core/http/src/` - Composables (useFetch/createFetch): `storage/framework/core/composables/src/useFetch.ts`, `storage/framework/core/composables/src/createFetch.ts` - Buddy CLI command: `storage/framework/core/buddy/src/commands/http.ts` - httx dependency (installed): `node_modules/@stacksjs/httx/` - Package: `@stacksjs/http` (status codes), `@stacksjs/httx` (HTTP client), `@stacksjs/composables` (useFetch/createFetch) ## Source Files ``` http/src/ └── index.ts # Response enum — all HTTP status codes (100-511) composables/src/ ├── useFetch.ts # Chainable reactive fetch composable (builder pattern) └── createFetch.ts # Factory for pre-configured useFetch with baseUrl buddy/src/commands/ └── http.ts # `buddy http [domain]` CLI command using HttxClient ``` ## Response Enum — HTTP Status Codes (index.ts) The `@stacksjs/http` package exports a single `Response` enum with all standard HTTP status codes: ```typescript import { Response } from '@stacksjs/http' Response.HTTP_OK // 200 Response.HTTP_CREATED // 201 Response.HTTP_NO_CONTENT // 204 Response.HTTP_MOVED_PERMANENTLY // 301 Response.HTTP_NOT_MODIFIED // 304 Response.HTTP_BAD_REQUEST // 400 Response.HTTP_UNAUTHORIZED // 401 Response.HTTP_FORBIDDEN // 403 Response.HTTP_NOT_FOUND // 404 Response.HTTP_UNPROCESSABLE_ENTITY // 422 Response.HTTP_TOO_MANY_REQUESTS // 429 Response.HTTP_INTERNAL_SERVER_ERROR // 500 Response.HTTP_SERVICE_UNAVAILABLE // 503 ``` ### Full Status Code Categories - **1xx Informational**: `HTTP_CONTINUE` (100), `HTTP_SWITCHING_PROTOCOLS` (101) - **2xx Success**: `HTTP_OK` (200), `HTTP_CREATED` (201), `HTTP_ACCEPTED` (202), `HTTP_NON_AUTHORITATIVE_INFORMATION` (203), `HTTP_NO_CONTENT` (204), `HTTP_RESET_CONTENT` (205), `HTTP_PARTIAL_CONTENT` (206) - **3xx Redirection**: `HTTP_MULTIPLE_CHOICES` (300), `HTTP_MOVED_PERMANENTLY` (301), `HTTP_FOUND` (302), `HTTP_SEE_OTHER` (303), `HTTP_NOT_MODIFIED` (304), `HTTP_USE_PROXY` (305), `HTTP_UNUSED` (306), `HTTP_TEMPORARY_REDIRECT` (307), `HTTP_PERMANENT_REDIRECT` (308) - **4xx Client Error**: `HTTP_BAD_REQUEST` (400) through `HTTP_UNAVAILABLE_FOR_LEGAL_REASONS` (451) — includes `HTTP_I_AM_A_TEAPOT` (418) - **5xx Server Error**: `HTTP_INTERNAL_SERVER_ERROR` (500) through `HTTP_NETWORK_AUTHENTICATION_REQUIRED` (511) ## HttxClient — Outbound HTTP Client (@stacksjs/httx) The `@stacksjs/httx` package provides the `HttxClient` class for making outbound HTTP requests. It wraps Bun's `fetch()` with retry logic, timeouts, timing data, and Result-based error handling via `ts-error-handling`. ```typescript import { HttxClient } from '@stacksjs/httx' const client = new HttxClient(config?: Partial<HttxConfig>) const result = await client.request<T>(url: string, options: RequestOptions): Promise<Result<HttxResponse<T>, Error>> ``` ### HttxConfig ```typescript interface HttxConfig { verbose?: boolean | string[] // Enable debug logging (or filter by category) defaultHeaders?: Record<string, string> baseUrl?: string // Prepended to all request URLs timeout?: number // Request timeout in ms retry?: RetryOptions // Default retry configuration } ``` ### RequestOptions ```typescript interface RequestOptions { method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS' query?: Record<string, string> // URL query parameters form?: boolean // Send body as form-encoded multipart?: boolean // Send body as multipart/form-data json?: boolean // Send body as JSON unix?: string // Unix socket path proxy?: string // Proxy URL downloadProgress?: (progress: number) => void retry?: RetryOptions // Per-request retry override stream?: boolean // Stream response acceptHeader?: string // Override Accept header verbose?: boolean timeout?: number body?: BodyInit | Record<string, string> headers?: HeadersInit } ``` ### RetryOptions ```typescript interface RetryOptions { retries?: number // Max retry attempts retryDelay?: number // Base delay between retries (ms) retryOn?: number[] // HTTP status codes that trigger retry shouldRetry?: (error: Error, attempt: number) => boolean // Custom retry predicate } ``` ### HttxResponse ```typescript interface HttxResponse<T = unknown> { status: number statusText: string headers: Headers data: T timings: { start: number end: number duration: number // Total request duration in ms } } ``` ### Result Pattern (from ts-error-handling) `client.request()` returns a `Result<HttxResponse<T>, Error>`. Use `.match()` to handle success/failure: ```typescript const result = await client.request('https://api.example.com/users', { method: 'GET', }) result.match({ ok: (response) => { console.log(response.status) // 200 console.log(response.timings.duration) // e.g. 142 (ms) console.log(response.data) // parsed response body }, err: (error) => { console.error(error.message) }, }) ``` Or use `result.isOk` / `result.value` / `result.error` directly. ### Error Classes ```typescript class HttxError extends Error { readonly context?: Record<string, unknown> } class HttxRequestError extends HttxError { readonly method: string readonly url: string readonly statusCode?: number } class HttxTimeoutError extends HttxError { readonly method: string readonly url: string readonly timeout: number } class HttxNetworkError extends HttxError { readonly method: string readonly url: string readonly originalError?: Error } class HttxResponseError extends HttxRequestError { readonly statusText: string readonly responseBody?: unknown } ``` ### Utility Exports ```typescript import { debugLog, sleep } from '@stacksjs/httx' debugLog(category: string, message: string | (() => string), verbose?: boolean | string[]): void sleep(ms: number): Promise<void> ``` ### Config Exports ```typescript import { config, defaultConfig, getConfig } from '@stacksjs/httx' const config: HttxConfig // Synchronous access with default fallback const defaultConfig: HttxConfig // Default configuration object function getConfig(): Promise<HttxConfig> // Async config loader ``` ## useFetch — Reactive Fetch Composable (@stacksjs/composables) A chainable, reactive fetch composable using the builder pattern. Uses native `fetch()` under the hood (not HttxClient). Returns reactive `Ref` values from `@stacksjs/stx`. ```typescript import { useFetch } from '@stacksjs/composables' // Builder pattern: method -> format -> result const { data, error, isFetching } = await useFetch('/api/posts').get().json() ``` ### FetchBuilder Interface ```typescript interface FetchBuilder { get: () => FetchBuilder post: (body?: string) => FetchBuilder patch: (body?: string) => FetchBuilder put: (body?: string) => FetchBuilder delete: () => FetchBuilder json: () => UseFetchResult } ``` ### UseFetchResult Interface ```typescript interface UseFetchResult { data: Ref<any> // Reactive ref — populated with parsed JSON on success error: Ref<any> // Reactive ref — populated with error JSON or Error object isFetching: Ref<boolean> // Starts as true, becomes false when request completes then: (resolve: (value: { data: Ref<any>, error: Ref<any> }) => void) => Promise<void> } ``` ### Usage Examples ```typescript import { useFetch } from '@stacksjs/composables' // GET request const { data, error } = await useFetch('/api/users').get().json() // POST with JSON body const { data, error } = await useFetch('/api/users') .post(JSON.stringify({ name: 'Alice', email: 'alice@example.com' })) .json() // PATCH with body const { data, error } = await useFetch('/api/users/1') .patch(JSON.stringify({ name: 'Updated' })) .json() // DELETE const { data, error } = await useFetch('/api/users/1').delete().json() // Synchronous access (reactive — updates when request completes) const result = useFetch('/api/items').get().json() result.isFetching.value // true (synchronously, before microtask resolves) // ... later ... result.data.value // parsed JSON response result.isFetching.value // false ``` ### How json() Works Internally 1. Builds a `RequestInit` with the configured method and `Accept: application/json` header 2. For POST/PATCH/PUT with a body, also sets `Content-Type: application/json` 3. Calls native `fetch(url, init)` 4. On success (`response.ok`), parses JSON into `data.value` 5. On HTTP error, parses JSON into `error.value` 6. On network error, sets `error.value` to the caught Error 7. Always sets `isFetching.value = false` in `finally` ## createFetch — Pre-configured Fetch Factory (@stacksjs/composables) Creates a reusable `useFetch` instance with a pre-configured base URL. ```typescript import { createFetch } from '@stacksjs/composables' interface CreateFetchOptions { baseUrl?: string // Base URL prepended to all requests options?: RequestInit // Default options (currently unused in implementation) } const useApi = createFetch({ baseUrl: 'https://api.example.com' }) // URL becomes: https://api.example.com/users const { data, error } = await useApi('/users').get().json() ``` ### URL Resolution `createFetch` joins the base URL and path by stripping trailing/leading slashes: - `baseUrl: 'https://api.example.com/'` + `'/users'` → `'https://api.example.com/users'` - `baseUrl: 'https://api.example.com'` + `'users'` → `'https://api.example.com/users'` ## CLI Command — buddy http ```bash buddy http [domain] # Send a GET request (defaults to config.app.url) buddy http example.com # GET https://example.com buddy http -v example.com # Verbose output ``` The CLI command uses `HttxClient` from `@stacksjs/httx`: - Prepends `https://` if the URL does not start with `http` - Logs status code, status text, and duration - Prints response body (string or pretty-printed JSON) ## Gotchas - **`@stacksjs/http` is status codes only** — it exports a single `Response` enum with HTTP status code constants. It does NOT contain an HTTP client, request helpers, or fetch utilities - **The actual HTTP client is `@stacksjs/httx`** — a separate package (not part of `@stacksjs/http`). It is used by `buddy http` and available for general use - **`useFetch` uses native `fetch()`, not HttxClient** — despite `@stacksjs/httx` being available, the `useFetch` composable calls the global `fetch()` directly - **`useFetch` only supports `.json()` format** — there is no `.text()`, `.blob()`, or `.arrayBuffer()` method. The only terminal method is `.json()` - **Method chaining overrides previous method** — calling `.post('body').get()` will send a GET request (last method wins, body is cleared for GET/DELETE) - **`createFetch` ignores the `options` parameter** — the `CreateFetchOptions.options` field is declared in the interface but not used in the implementation - **`useFetch` body is only sent for POST/PATCH/PUT** — GET and DELETE requests strip the body even if one was set - **The `Response` enum name conflicts with the global `Response`** — importing `Response` from `@stacksjs/http` shadows the native Fetch API `Response` type. Use a named import alias if both are needed: `import { Response as HttpStatus } from '@stacksjs/http'` - **HttxClient uses Result pattern** — `request()` returns `Result<HttxResponse, Error>` from `ts-error-handling`, not a plain Promise. Use `.match()`, `.isOk`, or `.value`/`.error` to unwrap - **`useFetch` returns reactive Refs** — `data`, `error`, and `isFetching` are `Ref<T>` from `@stacksjs/stx`. Access values via `.value` property - **No request interceptors or middleware** — neither `useFetch` nor `HttxClient` support request/response interceptors - **For API route handling, use `@stacksjs/router`** — `@stacksjs/http` and `@stacksjs/httx` are for outbound requests only, not for defining API endpoints
GitHubで見る