demo · v132

Which Body method should I use?

Body has six ways to drain. .bytes() is the new one, and it's the right default for binary data. Here's the cheat sheet, plus recipes for the common cases.

method matrix

methodreturnscopies / wrapstypical use
.text()stringUTF-8 decodeHTML, JSON, CSS
.json()parsed JS valuedecode + parseJSON responses
.blob()Blobwrapsimages, video, downloads
.formData()FormDatamultipart parseform posts
.arrayBuffer()ArrayBufferraw bytes (typed-array wrap needed)WebAssembly, ZIP, parsing
.bytes() v132Uint8Arrayraw bytes (already a view)same as arrayBuffer() but one fewer line

recipes

upload a Uint8Array

Build the payload in JS (WebAssembly memory, image manipulation) and POST it.

const view = new Uint8Array(2048);
crypto.getRandomValues(view);
await fetch("/upload", {
  method: "POST",
  body: view
});
// inside the server handler:
//   const view = await request.bytes(); // v132+
//   process(view);

decode an image header

Read the first 8 bytes of a PNG without ever materialising the entire image.

const res = await fetch("/icon.png");
const view = await res.bytes();
const sig = view.subarray(0, 8);
const isPNG = sig[0] === 0x89 && sig[1] === 0x50;

stream + bytes()

For large responses, pipe the body and use bytes() on each chunk's Response view.

const reader = res.body.getReader();
while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  // value is already Uint8Array
  process(value);
}

service worker rewrite

Intercept a request, swap a few bytes, re-emit. Was awkward with arrayBuffer; one line shorter now.

self.addEventListener("fetch", (e) => {
  e.respondWith((async () => {
    const r = await fetch(e.request);
    const v = await r.bytes();
    v[0] = 0x42; // rewrite first byte
    return new Response(v, r);
  })());
});

see also