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.
storage/framework/core/http/src/storage/framework/core/composables/src/useFetch.ts, storage/framework/core/composables/src/createFetch.tsstorage/framework/core/buddy/src/commands/http.tsnode_modules/@stacksjs/httx/@stacksjs/http (status codes), @stacksjs/httx (HTTP client), @stacksjs/composables (useFetch/createFetch)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
The @stacksjs/http package exports a single Response enum with all standard HTTP status codes:
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
HTTP_CONTINUE (100), HTTP_SWITCHING_PROTOCOLS (101)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)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)HTTP_BAD_REQUEST (400) through HTTP_UNAVAILABLE_FOR_LEGAL_REASONS (451) — includes HTTP_I_AM_A_TEAPOT (418)HTTP_INTERNAL_SERVER_ERROR (500) through HTTP_NETWORK_AUTHENTICATION_REQUIRED (511)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.
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>>
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
}
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
}
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
}
interface HttxResponse<T = unknown> {
status: number
statusText: string
headers: Headers
data: T
timings: {
start: number
end: number
duration: number // Total request duration in ms
}
}
client.request() returns a Result<HttxResponse<T>, Error>. Use .match() to handle success/failure:
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.
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
}
import { debugLog, sleep } from '@stacksjs/httx'
debugLog(category: string, message: string | (() => string), verbose?: boolean | string[]): void
sleep(ms: number): Promise<void>
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
A chainable, reactive fetch composable using the builder pattern. Uses native fetch() under the hood (not HttxClient). Returns reactive Ref values from @stacksjs/stx.
import { useFetch } from '@stacksjs/composables'
// Builder pattern: method -> format -> result
const { data, error, isFetching } = await useFetch('/api/posts').get().json()
interface FetchBuilder {
get: () => FetchBuilder
post: (body?: string) => FetchBuilder
patch: (body?: string) => FetchBuilder
put: (body?: string) => FetchBuilder
delete: () => FetchBuilder
json: () => UseFetchResult
}
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>
}
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: '[email protected]' }))
.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
RequestInit with the configured method and Accept: application/json headerContent-Type: application/jsonfetch(url, init)response.ok), parses JSON into data.valueerror.valueerror.value to the caught ErrorisFetching.value = false in finallyCreates a reusable useFetch instance with a pre-configured base URL.
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()
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'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:
https:// if the URL does not start with http@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@stacksjs/httx — a separate package (not part of @stacksjs/http). It is used by buddy http and available for general useuseFetch uses native fetch(), not HttxClient — despite @stacksjs/httx being available, the useFetch composable calls the global fetch() directlyuseFetch only supports .json() format — there is no .text(), .blob(), or .arrayBuffer() method. The only terminal method is .json().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 implementationuseFetch body is only sent for POST/PATCH/PUT — GET and DELETE requests strip the body even if one was setResponse 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'request() returns Result<HttxResponse, Error> from ts-error-handling, not a plain Promise. Use .match(), .isOk, or .value/.error to unwrapuseFetch returns reactive Refs — data, error, and isFetching are Ref<T> from @stacksjs/stx. Access values via .value propertyuseFetch nor HttxClient support request/response interceptors@stacksjs/router — @stacksjs/http and @stacksjs/httx are for outbound requests only, not for defining API endpoints