Skip to content
Back to skills

Stacks Http

ASecurity

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.

  • 631 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added June 5, 2026
developmenttypescriptgobashreactnodeapi

Works with

  • terminal
  • cli
  • api

Security analysis

A100/100

Scanned September 3, 2026

npx -y skills add stacksjs/stacks --skill stacks-http --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Stacks Http?

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

Security grade badge for Stacks Http
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/stacksjs-stacks-http/badge)](https://www.skillsdirectory.com/skills/stacksjs-stacks-http)

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: 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

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.