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
آخر نشاط في المصدر
٢٩ أغسطس ٢٠٢٦ في ١٧:٠٥
لغة SKILL.md المكتشفة
الإنجليزية
النجوم
٦٣١
التفرعات
٢٠

خيارات التثبيت

يُحدَّد 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