npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@nlite/logger-hapi

v1.0.2

Published

> Drop-in Hapi.js plugin that turns your server into a fully observable NLite logger client. Captures every request, response, error, 404, validation failure, and auth challenge with zero boilerplate.

Readme

@nlite/logger-hapi

Drop-in Hapi.js plugin that turns your server into a fully observable NLite logger client. Captures every request, response, error, 404, validation failure, and auth challenge with zero boilerplate.

Built on top of @nlite/logger-core and intended to be used with the @nlite/logger-server ingestion API.


Table of Contents

  1. Highlights
  2. Installation
  3. Quick Start
  4. Plugin Options
  5. Programmatic API
  6. Workflow & Request Lifecycle
  7. Architecture Diagrams
  8. Advanced Examples
  9. Environment Variables
  10. Scripts
  11. Compatibility
  12. License & Author

Highlights

  • Zero-config request/response/error capture via Hapi lifecycle extensions.
  • Trace IDs auto-generated or pulled from x-trace-id / trace-id headers.
  • Header sanitization — Authorization, Cookie, x-api-key, etc. are redacted automatically.
  • Hooks for everything — pass getUserId, getSessionId, getTraceId to integrate with your auth/session middleware.
  • Configurable ignore paths so /health, /ready, /metrics don't flood your dashboard.
  • Graceful shutdown — final flush on server.events.on('stop').

Installation

# npm
npm install @nlite/logger-hapi

# pnpm
pnpm add @nlite/logger-hapi

# yarn
yarn add @nlite/logger-hapi

Requirements

| Tool | Version | |------|---------| | @hapi/hapi | >=20.0.0 (peer) | | @nlite/logger-core | installed automatically (workspace dep) | | Node.js | >=18.0.0 |


Quick Start

import Hapi from '@hapi/hapi';
import { createHapiMiddleware, getLogger } from '@nlite/logger-hapi';

async function bootstrap() {
  const server = Hapi.server({ port: 3000 });

  await server.register({
    plugin: createHapiMiddleware({
      loggerConfig: {
        apiKey: process.env.NLITE_API_KEY!,
        appName: 'orders-api',
        platform: 'backend',
        environment: (process.env.NODE_ENV as any) ?? 'development',
        endpoint: process.env.NLITE_ENDPOINT ?? 'http://localhost:3000',
      },
      // Capture only what you need (all default to true)
      captureRequest: true,
      captureResponse: true,
      captureError: true,
      ignorePaths: ['/health', '/ready', '/metrics', '/favicon.ico'],
      // Optional context extractors
      getUserId: (req) => (req.auth.credentials?.user?.id as string | undefined),
      getSessionId: (req) => req.headers['x-session-id'],
      getTraceId: (req) => req.headers['x-trace-id'],
    }),
  });

  server.route({
    method: 'GET',
    path: '/orders/{id}',
    handler: async (request, h) => {
      // Access the logger from inside any handler
      const logger = getLogger(request);
      logger?.info('Loading order', { orderId: request.params.id });
      return { id: request.params.id };
    },
  });

  await server.start();
}

bootstrap();

Plugin Options

createHapiMiddleware(options: HapiMiddlewareOptions) accepts:

| Option | Type | Default | Description | |--------|------|---------|-------------| | loggerConfig | SdkConfig | — | Required. Forwarded to @nlite/logger-core. | | captureRequest | boolean | true | Log incoming requests + add navigation breadcrumbs. | | captureResponse | boolean | true | Log outgoing responses with status, duration, headers. | | captureError | boolean | true | Log internal errors (5xx, thrown exceptions, request errors). | | ignorePaths | string[] | ['/health', '/ready', '/metrics', '/favicon.ico'] | Prefix-matched paths that skip request/response logging. | | customTags | Record<string,string> | {} | Tags merged into every log emitted by this plugin. | | getUserId | (req) => string \| undefined | — | Extract the authenticated user id. | | getSessionId | (req) => string \| undefined | — | Extract a session id. | | getTraceId | (req) => string \| undefined | — | Extract / generate a trace id. |

A ready-made helper is also exported:

import { createDefaultHapiConfig } from '@nlite/logger-hapi';

const options = createDefaultHapiConfig('API_KEY', 'orders-api', {
  captureResponse: false,
  ignorePaths: ['/_internal/*'],
});

Programmatic API

createHapiMiddleware(options)

Returns a Hapi plugin object. Register it with server.register({ plugin }).

getLogger(request)

Inside any route handler:

server.route({
  method: 'POST',
  path: '/checkout',
  handler: async (request) => {
    const logger = getLogger(request)!;
    logger.setUser('user_42', { plan: 'pro' });
    logger.info('Checkout started');
    // ...
  },
});

The plugin decorates each request with:

request.nLiteLogger      // LoggerSdk
request.nLiteLogContext  // { startTime, traceId, spanId }

hapiMiddlewareOptionsSchema

Static schema description (useful for Hapi's server.register({ options, plugin }) validation pattern). Note: this is a documentation object, not a Joi schema — wire it through your own validator if you need strict runtime checks.


Workflow & Request Lifecycle

Every request that is not on an ignore path flows through these stages:

              ┌────────────────────────────────────────────────────┐
              │ Hapi request arrives                                │
              └─────────────────────┬──────────────────────────────┘
                                    │
                                    ▼
        ┌──────────────────────────────────────────────────────────┐
        │ onRequest                                                │
        │  - record startTime, traceId, spanId                     │
        │  - store context on request.nLiteLogContext              │
        │  - build sanitized LogRequest (headers redacted)         │
        │  - add 'http' breadcrumb                                 │
        │  - call getUserId / getSessionId, attach user/session    │
        │  - if payload > 10KB → log 'large payload' debug        │
        └──────────────────────────┬───────────────────────────────┘
                                   │
                                   ▼
                              route handler
                                   │
                                   ▼
        ┌──────────────────────────────────────────────────────────┐
        │ onPreResponse (response branch)                          │
        │  - compute durationMs                                    │
        │  - build LogResponse                                     │
        │  - emit single log with level by status (5xx=error,      │
        │    4xx=warn, else=info)                                  │
        └──────────────────────────┬───────────────────────────────┘
                                   │
                                   ▼
        ┌──────────────────────────────────────────────────────────┐
        │ onPreResponse (error branch — captureError: true)        │
        │  - 404 → warn 'Route not found'                          │
        │  - 400 → warn 'Validation error'                         │
        │  - 401/403 → warn 'Auth error'                           │
        │  - 5xx → error with thrown Boom                          │
        └──────────────────────────┬───────────────────────────────┘
                                   │
                                   ▼
        ┌──────────────────────────────────────────────────────────┐
        │ request event tagged error                               │
        │  - emits 'Request error' with thrown error context       │
        └──────────────────────────┬───────────────────────────────┘
                                   │
                                   ▼
              ┌─────────────────────────────────────┐
              │ Enqueue → @nlite/logger-core        │
              │ (batched, retried, persisted)       │
              └─────────────────────────────────────┘

Level mapping

| HTTP status | Log level | |-------------|-----------| | 5xx | error | | 4xx | warn | | 2xx/3xx | info |


Architecture Diagrams

Component view

                    ┌──────────────────────────┐
                    │       Hapi Server       │
                    └────────────┬─────────────┘
                                 │
                                 ▼
        ┌─────────────────────────────────────────────────────────┐
        │ createHapiMiddleware()                                │
        │                                                       │
        │  onRequest ─► build LogRequest (sanitize headers)     │
        │              store request.nLiteLogContext            │
        │              add 'http' breadcrumb                    │
        │                                                       │
        │  onPreResponse ─► build LogResponse                   │
        │                   emit level-mapped log               │
        │                   handle 4xx/5xx branches             │
        │                                                       │
        │  request 'error' event ─► logger.error()              │
        │                                                       │
        │  server 'stop' ─► logger.destroy() (flush)            │
        └────────────────────────────┬──────────────────────────┘
                                     │
                                     ▼
                ┌────────────────────────────────────────┐
                │  @nlite/logger-core                    │
                │  queue → batch → retry → transport     │
                └────────────────────┬───────────────────┘
                                     │
                                     ▼
                       POST {endpoint}/api/logs/batch
                       @nlite/logger-server → SQLite / Redis

Sequence diagram

Client    Hapi    HapiLoggerPlugin    CoreLogger    Transport   Server
  |         |          |                  |             |          |
  |  POST / |          |                  |             |          |
  |-------->|          |                  |             |          |
  |         | onRequest|                  |             |          |
  |         |--------->|                  |             |          |
  |         |          | breadcrumb       |             |          |
  |         |          |----------------->|             |          |
  |         |  handler |                  |             |          |
  |         |--------->|                  |             |          |
  |         |          | info('hi')       |             |          |
  |         |          |----------------->|             |          |
  |         | onPreResponse               |             |          |
  |         |<---------| log('POST / 200')|             |          |
  |         |          |----------------->|             |          |
  |         |          |                  | batch POST  |          |
  |         |          |                  |------------>|          |
  |         |          |                  |             | 200 OK   |
  |         |          |                  |             |<-------->|
  |   200   |          |                  |             |          |
  |<--------|          |                  |             |          |

Advanced Examples

Selective capture (ignore noisy paths)

createHapiMiddleware({
  loggerConfig: { /* ... */ },
  captureResponse: true,
  captureError: true,
  captureRequest: true,
  ignorePaths: ['/health', '/ready', '/metrics', '/static', '/_next'],
});

Per-route user binding

const server = Hapi.server({ port: 3000 });

await server.register({
  plugin: createHapiMiddleware({
    loggerConfig: { /* ... */ },
    getUserId: (req) => (req.auth.isAuthenticated ? req.auth.credentials.user.id : undefined),
  }),
});

server.route({
  method: 'GET',
  path: '/me',
  options: { auth: 'jwt' },
  handler: (request) => {
    const logger = getLogger(request)!;
    logger.setUser(request.auth.credentials.user.id, request.auth.credentials.user);
    logger.info('Profile viewed');
    return request.auth.credentials.user;
  },
});

Custom tags via setTags

createHapiMiddleware({
  loggerConfig: { /* ... */ },
  customTags: { service: 'orders', team: 'checkout' },
});

Tracing integration

createHapiMiddleware({
  loggerConfig: { /* ... */ },
  getTraceId: (req) => req.headers['x-b3-traceid'] as string | undefined,
});

Environment Variables

| Variable | Description | |----------|-------------| | NLITE_ENDPOINT | Override the ingestion endpoint. Defaults to http://localhost:3000. | | NLITE_API_KEY | API key used by createDefaultHapiConfig. | | NODE_ENV | Mapped to environment when using the default helper. | | npm_package_version | Used as appVersion by the default helper. |


Scripts

| Script | Description | |--------|-------------| | npm run build | tsc + copy dist/index.js to dist/index.cjs. | | npm run dev | Watch-mode build. | | npm test | Run Vitest. | | npm run test:watch | Vitest watch. | | npm run lint | ESLint over src. | | npm run typecheck | tsc --noEmit. |


Compatibility

  • Hapi 20.x and 21.x.
  • Node.js 18, 20, 22.
  • TypeScript 5.3+.
  • Works alongside other Hapi plugins (e.g. @hapi/auth-jwt2, @hapi/inert).

License & Author

MIT — © Debanjan Dasgupta. See the root README.