Make HTTP requests (GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS) with custom headers, automatic retries, and graceful error handling. Use when the user need...
---
name: simple-http
description: Make HTTP requests (GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS) with custom headers, automatic retries, and graceful error handling. Use when the user needs to call an API, fetch a URL, send webhooks, or make any HTTP request without external dependencies.
---
# Simple HTTP Skill
Make HTTP requests using only Node.js built-in modules. Supports all standard
methods, arbitrary headers, automatic retries with exponential backoff, and
never throws on failure — always resolves with an inspectable response object.
## Required Inputs
- **url** (string): Fully qualified URL to request.
- **method** (string, optional): HTTP method. Default `GET`.
- **headers** (object, optional): Request headers.
- **body** (string | Buffer | object, optional): Request body. Objects are auto-serialized to JSON.
- **maxRetries** (number, optional): Retry attempts for transient failures. Default `3`.
- **timeout** (number, optional): Socket timeout in ms. Default `30000`.
## Step-by-Step Workflow
1. Import the client from `src/http-client.js`:
```js
const { HttpClient } = require("./src/http-client");
```
2. Create a client instance (optionally set default headers shared across calls):
```js
const client = new HttpClient({
defaultHeaders: { Authorization: "Bearer <token>" },
maxRetries: 3,
});
```
3. Make requests using convenience methods or the generic `request()`:
```js
// GET
const resp = await client.get("https://api.example.com/items");
// POST with JSON body
const resp = await client.post("https://api.example.com/items", {
body: { name: "widget" },
});
// PUT with custom headers
const resp = await client.put("https://api.example.com/items/1", {
headers: { "X-Request-Id": "abc123" },
body: { name: "updated" },
});
// DELETE
const resp = await client.delete("https://api.example.com/items/1");
// Generic form — any method
const resp = await client.request("PATCH", "https://api.example.com/items/1", {
body: { qty: 5 },
});
```
4. Inspect the response:
```js
if (resp.ok) {
console.log(resp.body); // parsed JSON or raw string
console.log(resp.status); // e.g. 200
console.log(resp.headers); // response headers object
} else {
console.log(resp.error); // human-readable error (null if HTTP error with status)
console.log(resp.status); // HTTP status code or null for network errors
}
```
## Output Format
Every call resolves with an object containing:
| Key | Type | Description |
|-----------|-------------------|----------------------------------------------------|
| `ok` | `boolean` | `true` if status is 2xx |
| `status` | `number \| null` | HTTP status code; `null` for network-level errors |
| `headers` | `object` | Response headers |
| `body` | `any` | Parsed JSON (if content-type is JSON), else string |
| `error` | `string \| null` | Error description on failure; `null` on success |
## Error Handling & Retry Behavior
- **Retried automatically:** Connection errors, timeouts, and HTTP 429 / 5xx responses.
- **Not retried:** 4xx errors (except 429) — returned immediately.
- **Backoff:** Exponential with jitter (base 500ms, capped at 30s).
- **Graceful failure:** The client never throws. After exhausting retries, it resolves with the last error response so the caller can always inspect `resp.ok` and `resp.error`.
## Configuration Options
All options can be set at the client level (constructor) and overridden per-request:
| Option | Default | Description |
|------------------|----------|--------------------------------------|
| `defaultHeaders` | `{}` | Headers applied to every request |
| `maxRetries` | `3` | Max retry attempts |
| `timeout` | `30000` | Socket timeout in ms |
| `backoffBase` | `500` | Base delay (ms) for exponential backoff |
| `backoffMax` | `30000` | Maximum backoff delay cap (ms) |
## Dependencies
None — uses only Node.js built-in modules (`http`, `https`, `url`).
don't have the plugin yet? install it then click "run inline in claude" again.
added explicit decision points for retry logic, network timeouts, json parsing, header merging, and malformed urls. clarified output contract with response object structure. added outcome signal section with success and failure indicators. preserved all original procedure steps and configuration options.
Make HTTP requests using only Node.js built-in modules without external dependencies. Use this skill when you need to call an API, fetch a URL, send webhooks, or make any HTTP request with automatic retry logic, custom headers, and graceful error handling that never throws. The client always resolves with an inspectable response object so you can handle both success and failure paths predictably.
no external connections required. uses node.js http and https modules only.
const { HttpClient } = require("./src/http-client");
const client = new HttpClient({
defaultHeaders: { Authorization: "Bearer <token>" },
maxRetries: 3,
timeout: 30000
});
const resp = await client.get("https://api.example.com/items");
output: response object with keys ok, status, headers, body, error.
const resp = await client.post("https://api.example.com/items", {
body: { name: "widget" }
});
output: response object. status is 201 or 200 on success, body contains server response.
const resp = await client.put("https://api.example.com/items/1", {
headers: { "X-Request-Id": "abc123" },
body: { name: "updated" }
});
output: response object. custom headers merged with defaultHeaders. body sent as JSON.
const resp = await client.delete("https://api.example.com/items/1");
output: response object. typically 204 or 200 on success, body may be empty.
const resp = await client.request("PATCH", "https://api.example.com/items/1", {
body: { qty: 5 }
});
output: response object. method can be any HTTP verb.
if (resp.ok) {
console.log("success:", resp.status, resp.body);
} else {
console.log("failed:", resp.status, resp.error);
}
output: branching logic based on resp.ok boolean flag.
every call resolves (never rejects) with a response object:
{
ok: boolean, // true if status 200-299, false otherwise
status: number | null, // http status code (e.g. 200, 404, 500) or null on network failure
headers: object, // response headers as key-value pairs (header names lowercase)
body: any, // parsed json (if content-type json), else string, else empty string
error: string | null // human-readable error message on failure, null on success
}
response is always an object. no exceptions thrown. caller must always check resp.ok or resp.error to determine outcome.
if request is retried, only the final attempt response is returned. intermediate retry attempts are not visible to the caller.
you know the skill worked when:
you know the skill failed when:
common success indicators: status 200 (GET, POST, PUT, PATCH), 201 (POST create), 204 (DELETE, no content).
common failure indicators: status 400 (bad request), 401 (unauthorized), 404 (not found), 500 (server error), status null with error "socket timeout" or "connection refused".