Skip to content

Getting started

Eugene Lazutkin edited this page Jul 10, 2026 · 3 revisions

Getting started

double-meh is a zero-dependency, ESM-only HTTP I/O library that works in browsers and on the CLI (Node · Bun · Deno).

Install

npm i double-meh

It ships ESM only. Import it in an ES module or a "type": "module" package:

import io from 'double-meh';

CommonJS consumers on modern Node (≥ 20.19 / ≥ 22.12) can require() it directly via require(esm) — the default instance is also a named export, so destructuring works:

// CommonJS, modern Node
const {io} = require('double-meh');

// CommonJS, older Node — dynamic import from async code
const {default: io} = await import('double-meh');

Your first request

The default export io is everything. Bare verbs return the parsed body:

const person = await io.get('https://api.example.com/people/42');
//    ^ already-parsed JSON (or text) — not a Response

Named exports are the same functions, if you prefer them:

import {get, post, full, stream} from 'double-meh';

The four return modes

The method you call fixes what comes back — options never change the return type:

await io.get(url); // → parsed data (the 99% path)
await io.full.get(url); // → the full envelope {data, status, ok, headers, response, ...}
await io.stream.get(url); // → a Web ReadableStream (the raw body)
io.stream.put(url, {as: 'ndjson'}); // → a streaming {writable, readable, response} duplex

The namespaces compose rather than multiply: io.full answers "I need the metadata", io.stream answers "I need the raw body" — and when you need both, ask io.full for a streamed body:

const envelope = await io.full.get(url, null, {stream: true});
envelope.status; // metadata is here...
envelope.data; // ...and data is the raw ReadableStream, undrained

See Core API for the full return model and Streaming for the last two.

Reads and writes

verb(url, data?, options?) — for reads data is the query, for writes it is the body:

await io.get('/search', {q: 'cats', page: 2}); // → /search?q=cats&page=2
await io.post('/things', {name: 'Bob'}); // JSON body {"name":"Bob"}
await io.put('/things/7', updated, {ifMatch: etag}); // conditional write

Errors

A non-2xx response throws, and the error carries the same envelope shape:

try {
  await io.get('/missing');
} catch (error) {
  if (error instanceof io.BadStatus) {
    console.log(error.status, error.data); // e.g. 404 and the parsed problem+json
  }
}

Next

Clone this wiki locally