AV
HomeAboutProjectBlog

© 2026 Ave syah Shina. All rights reserved.

  1. Home
  2. Blog
  3. 11 — Consuming APIs and Styling CLI Output

11 — Consuming APIs and Styling CLI Output

August 13, 20266 min read
Download as Markdown

"Just fetch it and console.log the result" was my API-consuming strategy, and it worked until the ergonomics mattered. The model that splits cleanly: there is a built-in fetch good enough for most calls, and the libraries (Axios, Got, Ky) exist to add the ergonomics — retries, interceptors, automatic JSON parsing — that raw fetch leaves to me. On the output side, every "pretty terminal" library is just writing ANSI escape codes to stdout [1].

The framing that landed for me is the layers on both sides: fetch on the input, stdout sugar on the output.

fetch: the built-in baseline

Since Node 18, fetch is a global, the same API browsers use [2][3]. It is promise-based, returns a Response object, and covers the common cases — GET, POST with JSON, custom headers — without any dependency.

const res = await fetch('https://api.example.com/users/42');
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const user = await res.json();

Two things about fetch that I had to internalize. First, fetch does not throw on HTTP errors — a 404 or 500 is a resolved promise with res.ok === false. I check res.ok (or res.status) myself and throw. Second, the body is a stream — res.json() parses it as JSON, res.text() reads it as a string, but the body is consumed once. These two traits are why the libraries exist: they add the "throw on bad status" and "parse the body" steps that fetch leaves explicit.

For a one-off call, fetch is enough. The libraries earn their keep when the calls multiply.

Axios: the full-featured client

Axios is the most popular HTTP client for both browser and Node, and its appeal over raw fetch is the ergonomics pile [4]: it throws on non-2xx statuses, automatically parses JSON, supports request/response interceptors (for adding auth tokens globally), and handles timeouts cleanly.

import axios from 'axios';

const client = axios.create({ baseURL: 'https://api.example.com', timeout: 5000 });
client.interceptors.request.use(config => {
config.headers.Authorization = `Bearer ${getToken()}`;
return config;
});

const { data } = await client.get('/users/42'); // data is already parsed JSON

The interceptor pattern is the part I use most — one place to attach auth headers, log requests, or retry on failure, applied to every call through that client instance. For an app talking to one API with auth, that centralization is the reason to pick Axios over fetch.

Got: the Node-native, human-friendly client

Got is designed specifically for Node.js (it does not run in browsers) and is more tuned to Node's strengths — it supports pagination, RFC-compliant caching, retries with backoff, and streaming out of the box [5]. Where Axios aims for "familiar everywhere," Got aims for "powerful on the server."

import got from 'got';

const data = await got('https://api.example.com/users/42', {
timeout: { request: 5000 },
retry: { limit: 2 }
}).json();

The .json() method both fetches and parses in one call, and the retry option means transient failures get retried automatically. Got is what I reach for in server-side scripts and APIs where retry and streaming matter.

Ky: the tiny Fetch-based client

Ky is a small, modern client built on top of the Fetch API itself [6]. It is essentially "fetch with the ergonomics added" — it throws on errors, parses JSON, supports retries, and weighs about 1KB. Because it is built on fetch, the same code runs in modern browsers, Deno, and Node. Ky is the choice when I want the fetch ergonomics but not Axios's weight or Got's Node-only scope.

Picking between them

The decision is mostly about how much I want on top of fetch:

  • fetch (built-in) — one-off calls, no dependencies, I am willing to handle errors and parsing myself.
  • Axios — many calls to one API, interceptors for auth/logging, familiar API across teams.
  • Got — server-side work that needs retries, streaming, pagination, or caching.
  • Ky — fetch ergonomics in a tiny package, multi-runtime.

The model I keep is "fetch is the baseline; the libraries are conveniences." Knowing exactly which convenience I am paying for keeps the choice deliberate.

Styling CLI output: chalk, figlet, cli-progress

On the output side, three libraries cover the everyday CLI presentation needs, and all of them work by writing ANSI escape codes to stdout that the terminal interprets as styling [7].

chalk is the color-and-style library — red for errors, green for success, bold for emphasis, underline for links. It works across different terminals and degrades gracefully where a style is unsupported.

import chalk from 'chalk';
console.log(chalk.green('✓ build succeeded'));
console.log(chalk.red.bold('✗ tests failed'));

figlet renders text as large ASCII art — the stylized banner a CLI prints on startup. cli-progress draws progress bars for long-running operations like downloads, migrations, or batch processing, where seeing completion percentage matters.

The discipline with all three: styling is decoration on the streams, never a substitute for correct exit codes and good stderr messages. A red error message still needs a non-zero exit code, because CI and shell scripts do not read colors — they read exit codes. I keep styling for the human-facing layer and make sure the machine-facing layer (exit codes, structured stderr) is correct regardless.

How I use this

The model I keep is "fetch is the baseline; libraries add ergonomics; styling is decoration." For outbound HTTP, I start with fetch and reach for Axios (or Got) the moment I find myself repeating interceptor-style logic or retry logic by hand. For CLI output, I reach for chalk the moment I want errors to read differently from normal output, but I never let styling carry information that the exit code does not also carry. Keeping the two layers separate — the data-fetching layer and the presentation layer — is what keeps both legible.

References

[1] "chalk," GitHub. [Online]. Available: https://github.com/chalk/chalk#readme

[2] Mozilla, "Using the Fetch API," MDN Web Docs. [Online]. Available: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch

[3] OpenJS Foundation, "Globals: fetch," Node.js API Docs. [Online]. Available: https://nodejs.org/api/globals.html#fetch

[4] "Axios Documentation," axios-http.com. [Online]. Available: https://axios-http.com/docs/intro

[5] "Got," npm. [Online]. Available: https://www.npmjs.com/package/got

[6] "Ky," GitHub. [Online]. Available: https://github.com/sindresorhus/ky

[7] "figlet," GitHub. [Online]. Available: https://github.com/patorjk/figlet.js

Knowledge check · Question 1 of 5

Raw fetch does NOT throw on which of these?

Comments

Leave a Comment

You must be signed in to comment

0 Comments

No comments yet. Be the first to comment!