Recipes

Copy-paste starting points for common transforms. Client setup → Quickstart.

Convert a format

The one-liner. Great for shrinking whatever a browser handed you.

format.ts
const [r] = await tk
  .runQueue([{ bytes, filename: 'photo.jpg' }], 'image')
  .convert({ format: 'webp' });

if (r.ok) console.log(r.outputs[0]!.media.url);

Resize with a dimension cap

maxSizecaps the longest side and preserves aspect ratio. It never upscales, so it's safe to apply to inputs of any size.

resize.ts
const [r] = await tk
  .runQueue([{ bytes, filename: 'photo.png' }], 'image')
  .maxSize(1024)                       // fits inside 1024×1024, keeps ratio, no upscale
  .convert({ format: 'jpg', quality: 80 });

if (r.ok) console.log(r.outputs[0]!.media.url);

Responsive image set (fan-out)

One upload, many sizes. Build a graph on Pipelines and hand it to runPipeline — the output suffix doubles as the width here.

srcset.ts
import { mergePipelineNodeConfig, type Pipeline } from '@transform-kit/sdk';

function srcsetPipeline(widths: number[]): Pipeline {
  const nodes: Pipeline['nodes'] = [{ id: 'in', type: 'pipeline.input' }];
  const edges: Pipeline['edges'] = [];
  for (const w of widths) {
    nodes.push(
      { id: `r${w}`, type: 'image.resize', config: mergePipelineNodeConfig('image.resize', { mode: 'pixels', width: w, height: w, fit: 'inside' }) },
      { id: `c${w}`, type: 'image.convert', config: mergePipelineNodeConfig('image.convert', { format: 'webp' }) },
      { id: `o${w}`, type: 'pipeline.output', config: mergePipelineNodeConfig('pipeline.output', { suffix: String(w) }) },
    );
    edges.push(
      { source: 'in', target: `r${w}` },
      { source: `r${w}`, target: `c${w}` },
      { source: `c${w}`, target: `o${w}` },
    );
  }
  return { nodes, edges };
}

const [photo] = await tk.runPipeline(
  [{ bytes, filename: 'photo.png' }],
  srcsetPipeline([320, 640, 1024, 1600]),
);

// -> "https://… 320w, https://… 640w, …"
const srcset = photo.ok ? photo.outputs.map((o) => `${o.media.url} ${o.output}w`).join(', ') : '';

Video: resize and re-encode

clip.ts
const results = await tk
  .runQueue(files, 'video')
  .maxSize(720)
  .convert({ format: 'mp4', quality: 75 });

for (const r of results) {
  if (r.ok) console.log(r.filename, r.outputs[0]!.media.url);
}

Audio: convert

tone.ts
const results = await tk
  .runQueue(files, 'audio')
  .convert({ format: 'mp3', bitrate: 192 });

for (const r of results) {
  if (r.ok) console.log(r.filename, r.outputs[0]!.media.url);
}

PDF to markdown

brief.ts
import { readFile } from 'node:fs/promises';

const [result] = await tk
  .runQueue([{ bytes: await readFile('brief.pdf'), filename: 'brief.pdf' }], 'document')
  .convert({ format: 'md' });

if (result.ok) {
  const markdown = await fetch(result.outputs[0]!.media.url).then((r) => r.text());
  console.log(markdown);
}

Catch the one thing that throws

A bad file comes back as ok: false (see Core concepts). A malformed pipeline is the exception: it throws a TransformKitError before anything uploads, so wrap the call if you build graphs dynamically.

errors.ts
import { TransformKit, TransformKitError } from '@transform-kit/sdk';

try {
  const results = await tk
    .runQueue(files, 'image')
    .convert({ format: 'webp' })
    .options({ onProgress: (e) => console.log(e.stage, e.filename ?? e.index) });

  for (const r of results) {
    if (r.ok) console.log(r.filename, r.outputs[0]!.media.url);
    else console.error(r.filename, r.error); // e.g. "This request needs 1 credit(s)…"
  }
} catch (err) {
  // Thrown before upload — the pipeline itself was invalid.
  if (err instanceof TransformKitError && err.code === 'invalid_pipeline') {
    console.error(err.message);
  }
  throw err;
}

Next steps

  • Node.js — batch a folder with lazy loaders, and bring your own storage.
  • Next.js — take an upload from the browser and transform it on your server.
  • HTTP & curl — the raw calls behind these recipes.