NestJS Profiler
API Reference

Template reference

Color tokens, utility classes and EJS helpers for panel templates.

All profiler templates share a single design system based on Tailwind CSS 4, compiled at build time into a static stylesheet and served same-origin by the module (no external CDN). The semantic CSS custom properties and utility classes live in src/public/profiler.css. Any .ejs template registered through a custom collector automatically has access to every class and helper described here.

The stylesheet is compiled from the profiler's own templates, so it contains the classes listed here and nothing more. A Tailwind utility that appears in no shipped panel — gap-x-6, tracking-tighter — is simply absent from it and does nothing in yours. Build your layout out of the classes documented below, or from ones you can see a built-in panel using.

Building your first custom collector? Follow the Build a custom collector tutorial first, then come back here as a reference while writing your EJS template.

Color tokens

Tokens adapt automatically to the light/dark theme. Use the Tailwind utility name (e.g. text-foreground) in your template - never hardcode hex values.

Neutral / surface

Tailwind classPurpose
bg-backgroundPage background
bg-surfaceCard / panel background
bg-surface-mutedDimmed surface (table header, code block background)
bg-surface-activeActive / selected surface

Text

Tailwind classPurpose
text-foregroundPrimary content text
text-foreground-secondarySecondary content (metadata, values)
text-foreground-mutedLabels, column headers
text-foreground-faintTimestamps, disabled states, placeholders

Borders

Tailwind classPurpose
border-lineDefault border (cards, tables)
border-line-subtleRow divider inside a table
border-line-activeFocused / highlighted border

Status

Tailwind classPurpose
text-danger / bg-danger-bg / border-danger-lineError state - text, background, border
border-danger-line-subtleSubtle danger border (stack trace separator)
text-successSuccess state
text-code-textMonospace code content

Brand

Tailwind classPurpose
text-nest / bg-nest / border-nestNestJS brand red - active tab indicator, accent
text-nest-lightLighter brand red for hover
text-nest-darkDarker brand red for pressed states

Typography

ClassValuePurpose
text-2xs0.625rem (10px)Smallest label size - column headers, badges
text-xs0.75rem (12px)Primary content in tables and panels
font-mono-Monospace font for code, identifiers, URLs

Badge utility classes

Ready-to-use semantic badges. Apply them on a <span> together with px-1.5 py-0.5 rounded text-2xs font-bold tracking-wide.

HTTP method

ClassColourUse for
badge-getGreenGET / read operations
badge-postBluePOST / insert operations
badge-putAmberPUT / update operations
badge-patchOrangePATCH / partial update
badge-deleteRedDELETE / remove operations
badge-headPurpleHEAD operations
badge-optionsSlateOPTIONS operations
badge-defaultSlateUnknown / fallback

HTTP status

ClassUse for
badge-2xx2xx success responses
badge-3xx3xx redirects
badge-4xx4xx client errors
badge-5xx5xx server errors

Log level

ClassUse for
badge-logLOG
badge-warnWARN
badge-errorERROR
badge-debugDEBUG
badge-verboseVERBOSE
badge-fatalFATAL

Duration classes

Colour-code a duration value based on thresholds.

ClassConditionColour
dur-fast< 100 msGreen
dur-medium100 – 500 msAmber
dur-slow> 500 msRed
dur-noneUnknown / undefinedFaint grey
<%
  const cls = dur < 100 ? 'dur-fast' : dur < 500 ? 'dur-medium' : 'dur-slow';
%>
<span class="<%= cls %> text-xl font-bold tabular-nums"><%= dur %>ms</span>

EJS helper functions

These functions are automatically injected into every template.

HelperSignatureDescription
toJson(value) → stringPretty-prints any value as indented JSON
isoDate(timestamp: number) → stringFormats a Unix ms timestamp as YYYY-MM-DD HH:MM:SS, in the display timezone
timeOnly(timestamp: number) → stringFormats a Unix ms timestamp as HH:MM:SS.mmm, in the display timezone
highlightSql(sql: string) → HTML stringWraps SQL keywords in <span class="sql-keyword"> - use with <%-
methodClass(method: string) → stringReturns the correct badge-* class for an HTTP method
statusClass(status: number) → stringReturns the correct badge-* class for an HTTP status code
logLevelClass(level: string) → stringReturns the correct badge-* class for a log level
mb(bytes: number) → stringConverts bytes to a human-readable X.XX MB string
formatBytes(bytes: number) → stringScales a byte count to B / KB / MB, for values spanning several orders
formatDuration(ms: number) → stringRenders a duration without its unit - append ms yourself
kvTable(data: Record<string, unknown>) → HTML stringRenders a key/value table - use with <%-

The display timezone is the one the process runs in, unless the application sets the timezone module option — panels never need to project a timestamp themselves.


Common template patterns

Empty state

<div class="py-8 text-center bg-surface-muted border border-line rounded-lg">
  <p class="text-foreground-muted text-sm">No entries for this request.</p>
</div>

Summary bar

<div class="flex items-center gap-4 text-xs text-foreground-muted mb-4">
  <span><strong class="text-foreground"><%= items.length %></strong> entries</span>
  <span><strong class="text-foreground"><%= totalDuration %></strong>ms total</span>
</div>

Data table

Every table in the profiler leads with Time then Duration — when something happened and what it cost — before the columns specific to it, in the profile lists and in the detail panels alike. Keep that order so your panel reads like the rest of the UI.

<div class="rounded-lg border border-line overflow-hidden">
  <table class="w-full">
    <thead>
      <tr class="bg-surface-muted border-b border-line">
        <th class="text-left py-2.5 px-4 text-foreground-muted font-medium text-2xs uppercase tracking-widest w-20">Time</th>
        <th class="text-left py-2.5 px-4 text-foreground-muted font-medium text-2xs uppercase tracking-widest w-24">Duration</th>
        <th class="text-left py-2.5 px-4 text-foreground-muted font-medium text-2xs uppercase tracking-widest">Name</th>
      </tr>
    </thead>
    <tbody class="divide-y divide-line-subtle">
      <% for (const item of items) { %>
        <tr class="hover:bg-surface-muted transition-colors">
          <td class="py-2 px-4 text-foreground-secondary text-xs tabular-nums"><%= timeOnly(item.startedAt) %></td>
          <td class="py-2 px-4 text-foreground-secondary text-xs tabular-nums"><%= item.duration %>ms</td>
          <td class="py-2 px-4 text-foreground text-xs"><%= item.name %></td>
        </tr>
      <% } %>
    </tbody>
  </table>
</div>

Badge

<span class="px-1.5 py-0.5 rounded text-2xs font-bold tracking-wide badge-get">GET</span>

Code block (JSON)

<pre class="bg-surface-muted border border-line rounded-lg p-4 text-xs overflow-x-auto leading-relaxed">
  <code class="language-json"><%= toJson(value) %></code>
</pre>

Highlight.js runs automatically on DOMContentLoaded - any <code class="language-*"> block is highlighted.

SQL with keyword highlighting

<code class="text-xs text-code-text leading-relaxed break-all">
  <%- highlightSql(query.sql) %>
</code>
<style>.sql-keyword { color: #818cf8; font-weight: 600; }</style>

Error inline

<% if (item.error) { %>
  <div class="mt-1 text-danger text-2xs">Error: <%= item.error %></div>
<% } %>

Section with heading

<section class="mb-6">
  <h2 class="text-foreground-muted text-2xs uppercase tracking-widest font-semibold mb-3">
    My Section
  </h2>
  <!-- content -->
</section>

Dark mode

All semantic token classes (text-foreground, bg-surface, border-line, badge classes, duration classes) adapt automatically - no extra dark: variants needed. When you need Tailwind colour palette classes (e.g. bg-blue-100) always add the corresponding dark: variant:

<span class="bg-blue-100 text-blue-700 dark:bg-blue-900/40 dark:text-blue-300">
  INFO
</span>
Powered & maintained by

On this page