Share a Clinic.js report

Clinic.js is three tools and they answer three different questions:

clinic doctor     --dest .clinic -- node app.js   # what kind of problem is this?
clinic flame      --dest .clinic -- node app.js   # which function is burning CPU?
clinic bubbleprof --dest .clinic -- node app.js   # where is the async time going?

doctor is the one to run first, because it classifies: event loop blocked, I/O bound, garbage collection, or genuinely CPU bound. Its recommendation panel is the most useful paragraph in Node performance tooling, and it is the part that gets flattened when the report becomes a screenshot.

The reports are HTML, and they are stubbornly local. Depending on the tool and version, the page pulls parts of its dataset at load time — and a browser blocks those fetches from a file:// origin. So a colleague who double-clicks the file you sent gets a page that renders, and charts that never populate.

Publish it

Point Clinic at a known directory and publish the folder:

clinic doctor --dest .clinic --autocannon [ -c 100 -d 30 http://localhost:3000/ ] \
  -- node app.js

Drag .clinic/ (or a zip of it) into the app:

  • The generated *.html becomes the report body.
  • The data directory beside it uploads as assets and its relative references are rewritten to the uploaded copies — which is the step file:// cannot do.
  • Scripts run inside a sandboxed iframe (allow-scripts, no allow-same-origin), so the charts, the brush-to-zoom on the timeline, and the recommendation panel all work.

Publish the verdict and the follow-up together

A flame report on its own is a picture of some functions. A doctor report that says "the event loop is blocked" followed by the flame graph showing crypto.pbkdf2Sync at the top is a diagnosis anybody can follow.

PATCH both to one report id:

curl -fsS -X PATCH "https://commareports.com/api/v1/reports/$REPORT_ID" \
  -H "Authorization: Bearer $COMMA_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d "$(jq -n --rawfile html "$(ls -t .clinic/*.html | head -1)" \
        --arg t "Clinic flame — /login, sync hashing" '{title: $t, html: $html}')"

One URL, a revision per capture. The revision list is the investigation.

From CI

Event-loop regressions are invisible until latency alarms fire. Run a fixed load in the job:

clinic doctor --dest .clinic --autocannon [ -c 50 -d 20 http://localhost:3000/api ] \
  -- node server.js

then PATCH the HTML to the same report id every run. A stable URL per service turns "is p99 creeping?" into a comparison rather than a hunch.

What review adds

  • Anchored threads on the stall — see commenting on HTML.
  • Revisions, so a fix is verifiable against the run that motivated it.
  • Access per report — private, team, domain-gated or named reviewers. See the sharing model.

Limits

  • Entry HTML: 5 MB. Assets: 25 MB per file, 250 MB and 500 files total. Clinic's data directory is the part that grows with capture length — a 20-30 second load run is the right size for something meant to be read.
  • 60 requests/minute per token.

Try it

Comma is free — unlimited reports, unlimited commenters, unlimited revision history.

Publish a Node profile →

Related