{"_id":"@codereb00t/next-request-telemetry","_rev":"2-f7f0798636ed39f65e6ac34d4475c0b9","name":"@codereb00t/next-request-telemetry","dist-tags":{"latest":"1.0.1"},"versions":{"1.0.0":{"name":"@codereb00t/next-request-telemetry","version":"1.0.0","keywords":["nextjs","telemetry","observability","opentelemetry","otel","hyperdx","tracing","monitoring","fetch","xhr","http","instrumentation"],"author":"","license":"MIT","_id":"@codereb00t/next-request-telemetry@1.0.0","maintainers":[{"name":"codreb00t","email":"devanshkg19@gmail.com"}],"homepage":"https://github.com/your-org/next-request-telemetry#readme","bugs":{"url":"https://github.com/your-org/next-request-telemetry/issues"},"dist":{"shasum":"23f43e530ad26264999dd52a42bd0cf59205680f","tarball":"https://registry.npmjs.org/@codereb00t/next-request-telemetry/-/next-request-telemetry-1.0.0.tgz","fileCount":162,"integrity":"sha512-0j/+fGBJk/kCwVv5rC0pbCCA9UcprmDNGpJC99Jc9niKOQrArGLbT2y+aheTGGJ8YEW1mTrSYQWQLiFd7T22Jg==","signatures":[{"sig":"MEYCIQDOxAY5bt+3nf2qO+4E5JN9dpeX1AoaSAiSx+SdA6vxvQIhAPblCvqy1L8axUeDKY9oK7TRywAI1YTmyagYG1ynah1a","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":235875},"main":"./dist/cjs/index.cjs","type":"module","types":"./dist/types/index.d.ts","module":"./dist/esm/index.js","engines":{"node":">=18.0.0"},"exports":{".":{"import":{"types":"./dist/types/index.d.ts","default":"./dist/esm/index.js"},"require":{"types":"./dist/types/index.d.cts","default":"./dist/cjs/index.cjs"}},"./server":{"import":{"types":"./dist/types/server.d.ts","default":"./dist/esm/server.js"},"require":{"types":"./dist/types/server.d.cts","default":"./dist/cjs/server.cjs"}}},"scripts":{"dev":"tsc --watch","lint":"eslint src --ext .ts","test":"vitest run","build":"npm run build:types && npm run build:esm && npm run build:cjs","build:cjs":"tsc --project tsconfig.cjs.json --outDir dist/cjs","build:esm":"tsc --project tsconfig.build.json --outDir dist/esm","typecheck":"tsc --noEmit","test:watch":"vitest","build:types":"tsc --project tsconfig.build.json --declaration --emitDeclarationOnly --outDir dist/types","prepublishOnly":"npm run build && npm run typecheck"},"_npmUser":{"name":"codreb00t","email":"devanshkg19@gmail.com"},"repository":{"url":"git+https://github.com/your-org/next-request-telemetry.git","type":"git"},"_npmVersion":"10.9.4","description":"Framework-agnostic request telemetry SDK for Next.js — captures, enriches, batches, and exports all client and server HTTP requests.","directories":{},"sideEffects":false,"_nodeVersion":"22.22.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^1.6.1","typescript":"^5.9.3","@types/node":"^20.19.42","vite-tsconfig-paths":"^6.1.1"},"peerDependencies":{"next":">=14.0.0"},"peerDependenciesMeta":{"next":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/next-request-telemetry_1.0.0_1784298780451_0.16022090909312925","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@codereb00t/next-request-telemetry","version":"1.0.1","description":"Framework-agnostic request telemetry SDK for Next.js — captures, enriches, batches, and exports all client and server HTTP requests.","author":{},"license":"MIT","type":"module","main":"./dist/cjs/index.cjs","module":"./dist/esm/index.js","types":"./dist/types/index.d.ts","sideEffects":false,"exports":{".":{"import":{"types":"./dist/types/index.d.ts","default":"./dist/esm/index.js"},"require":{"types":"./dist/types/index.d.cts","default":"./dist/cjs/index.cjs"}},"./server":{"import":{"types":"./dist/types/server.d.ts","default":"./dist/esm/server.js"},"require":{"types":"./dist/types/server.d.cts","default":"./dist/cjs/server.cjs"}}},"scripts":{"build":"npm run build:types && npm run build:esm && npm run build:cjs","build:types":"tsc --project tsconfig.build.json --declaration --emitDeclarationOnly --outDir dist/types","build:esm":"tsc --project tsconfig.build.json --outDir dist/esm","build:cjs":"tsc --project tsconfig.cjs.json --outDir dist/cjs","dev":"tsc --watch","typecheck":"tsc --noEmit","test":"vitest run","test:watch":"vitest","lint":"eslint src --ext .ts","prepublishOnly":"npm run build && npm run typecheck"},"devDependencies":{"@types/node":"^20.19.42","typescript":"^5.9.3","vite-tsconfig-paths":"^6.1.1","vitest":"^1.6.1"},"peerDependencies":{"next":">=14.0.0"},"peerDependenciesMeta":{"next":{"optional":true}},"keywords":["nextjs","telemetry","observability","opentelemetry","otel","hyperdx","tracing","monitoring","fetch","xhr","http","instrumentation"],"repository":{"type":"git","url":"https://github.com/codereb00t/next-request-telemetry"},"engines":{"node":">=18.0.0"},"publishConfig":{"access":"public"},"_id":"@codereb00t/next-request-telemetry@1.0.1","dist":{"shasum":"fee0511f800d0fe9876b0d931f1ca05e503f4ffe","integrity":"sha512-BoR8eXHJH54APEVVb9VV1oEZD4ommxhyu6aAlpR40iu1c2kLGxabfSFVXGdx56Y4NbCil+w0shag0SKTxUDDUg==","tarball":"https://registry.npmjs.org/@codereb00t/next-request-telemetry/-/next-request-telemetry-1.0.1.tgz","fileCount":194,"unpackedSize":235877,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIFT/YU07ZFd6tiTRFNt9YJAiasLPK7j/vFCY2k42U3qjAiBFZCM/1znePu2a1/ANffRO3gk3BkvsfumkXQb5X1/Nsw=="}]},"_npmUser":{"name":"codreb00t","email":"devanshkg19@gmail.com"},"directories":{},"maintainers":[{"name":"codreb00t","email":"devanshkg19@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/next-request-telemetry_1.0.1_1784299095111_0.008792516627226021"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-17T14:33:00.236Z","modified":"2026-07-17T14:38:15.427Z","1.0.0":"2026-07-17T14:33:00.608Z","1.0.1":"2026-07-17T14:38:15.240Z"},"license":"MIT","keywords":["nextjs","telemetry","observability","opentelemetry","otel","hyperdx","tracing","monitoring","fetch","xhr","http","instrumentation"],"repository":{"type":"git","url":"https://github.com/codereb00t/next-request-telemetry"},"description":"Framework-agnostic request telemetry SDK for Next.js — captures, enriches, batches, and exports all client and server HTTP requests.","maintainers":[{"name":"codreb00t","email":"devanshkg19@gmail.com"}],"readme":"# next-request-telemetry\n\n> Framework-agnostic request telemetry SDK for Next.js.  \n> Automatically captures **all HTTP requests** on both client and server, enriches them with metadata, batches them efficiently, and exports to any telemetry provider.\n\n[![npm version](https://img.shields.io/npm/v/next-request-telemetry.svg)](https://www.npmjs.com/package/next-request-telemetry)\n[![TypeScript](https://img.shields.io/badge/TypeScript-first-blue.svg)](https://www.typescriptlang.org/)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\n---\n\n## What it does\n\n| Question | Answer |\n|---|---|\n| Which APIs are being called? | ✅ Captured automatically |\n| How many times per page load? | ✅ Per-event with pageUrl |\n| Which requests are slow? | ✅ Duration on every event |\n| Which requests fail? | ✅ Status + error field |\n| Client-side or server-side? | ✅ `side: \"client\" \\| \"server\"` |\n| Which page triggered it? | ✅ `pageUrl` field |\n| Request & response sizes? | ✅ Content-Length captured |\n| External API visibility? | ✅ `category: \"external\"` |\n\n---\n\n## Installation\n\n```bash\nnpm install next-request-telemetry\n# or\nyarn add next-request-telemetry\n# or\npnpm add next-request-telemetry\n```\n\n**Requirements:** Node.js ≥ 18, Next.js ≥ 14, React ≥ 18.\n\n---\n\n## Quick Start\n\n### 1. Server-side (instrumentation.ts)\n\nCreate `instrumentation.ts` in your project root (alongside `next.config.ts`):\n\n```ts\n// instrumentation.ts\nexport async function register() {\n  if (process.env.NEXT_RUNTIME === \"nodejs\") {\n    const {\n      initTelemetry,\n      installServerInterceptor,\n      hyperdxExporter,\n      consoleExporter,\n    } = await import(\"next-request-telemetry/server\");\n\n    initTelemetry({\n      service: \"my-next-app\",\n      squad: \"platform\",\n      environment: process.env.NODE_ENV,\n      version: process.env.NEXT_PUBLIC_APP_VERSION,\n\n      exporters: [\n        hyperdxExporter({\n          endpoint: \"https://in-otel.hyperdx.io/v1/logs\",\n          apiKey: process.env.HYPERDX_API_KEY!,\n        }),\n        // Add consoleExporter() during development\n      ],\n\n      ignorePatterns: [\"/_next/\", \"/favicon.ico\", \"/api/health\"],\n    });\n\n    installServerInterceptor();\n  }\n}\n```\n\nEnable `instrumentationHook` in `next.config.ts` (Next.js < 15):\n\n```ts\n// next.config.ts\nconst nextConfig = {\n  experimental: {\n    instrumentationHook: true, // Not needed for Next.js 15+\n  },\n};\n\nexport default nextConfig;\n```\n\n---\n\n### 2. Client-side (App Router)\n\n```tsx\n// app/providers.tsx\n\"use client\";\n\nimport { useEffect } from \"react\";\nimport {\n  initTelemetry,\n  installClientInterceptor,\n  hyperdxExporter,\n} from \"next-request-telemetry\";\n\nexport function TelemetryProvider({ children }: { children: React.ReactNode }) {\n  useEffect(() => {\n    initTelemetry({\n      service: \"my-next-app\",\n      environment: process.env.NODE_ENV,\n      exporters: [\n        hyperdxExporter({\n          endpoint: process.env.NEXT_PUBLIC_HYPERDX_ENDPOINT!,\n          apiKey: process.env.NEXT_PUBLIC_HYPERDX_API_KEY!,\n        }),\n      ],\n    });\n\n    installClientInterceptor();\n  }, []);\n\n  return <>{children}</>;\n}\n```\n\n```tsx\n// app/layout.tsx\nimport { TelemetryProvider } from \"./providers\";\n\nexport default function RootLayout({ children }: { children: React.ReactNode }) {\n  return (\n    <html lang=\"en\">\n      <body>\n        <TelemetryProvider>{children}</TelemetryProvider>\n      </body>\n    </html>\n  );\n}\n```\n\n### 2b. Client-side (Pages Router)\n\n```tsx\n// pages/_app.tsx\nimport type { AppProps } from \"next/app\";\nimport { useEffect } from \"react\";\nimport { initTelemetry, installClientInterceptor, consoleExporter } from \"next-request-telemetry\";\n\nlet installed = false;\n\nexport default function MyApp({ Component, pageProps }: AppProps) {\n  useEffect(() => {\n    if (installed) return;\n    installed = true;\n\n    initTelemetry({\n      service: \"my-next-app\",\n      environment: process.env.NODE_ENV,\n      exporters: [consoleExporter()],\n    });\n\n    installClientInterceptor();\n  }, []);\n\n  return <Component {...pageProps} />;\n}\n```\n\n---\n\n## Configuration\n\n```ts\ninitTelemetry({\n  // ── Required ────────────────────────────────\n  service: \"my-next-app\",        // Appears on every event\n  environment: \"production\",     // \"development\" | \"staging\" | \"production\"\n  exporters: [consoleExporter()],\n\n  // ── Optional ────────────────────────────────\n  squad: \"platform\",             // Team/squad identifier\n  version: \"1.2.3\",              // App version / git SHA\n\n  // URLs to never capture (string prefix match or RegExp)\n  ignorePatterns: [\n    \"/_next/\",\n    \"/favicon.ico\",\n    \"/api/health\",\n    /^https:\\/\\/analytics\\./,\n  ],\n\n  // Override URL categorization\n  categorizer: (url) => {\n    if (url.includes(\"/graphql\")) return \"api\";\n    return null; // null falls back to built-in logic\n  },\n\n  // Batching (these are the defaults)\n  batching: {\n    maxSize: 50,           // Flush when queue reaches this size\n    flushIntervalMs: 3000, // Or after this many milliseconds\n    maxRetries: 3,         // Retry failed exports this many times\n    retryBaseDelayMs: 500, // Exponential backoff starting delay\n  },\n\n  debug: false,            // Log SDK internals to console\n});\n```\n\n---\n\n## Telemetry Event Schema\n\nEvery captured request produces a `RequestTelemetryEvent`:\n\n```ts\n{\n  id: \"lf3x2k-abc12-001\",          // Unique event ID\n  timestamp: \"2024-01-15T10:30:00.000Z\",\n\n  method: \"POST\",\n  url: \"https://api.stripe.com/v1/payment_intents\",\n  normalizedPath: \"/v1/payment_intents\",  // Dynamic segments replaced with :id\n\n  status: 200,\n  duration: 234,            // Milliseconds\n\n  requestSize: 512,         // Bytes (from Content-Length or body estimate)\n  responseSize: 1024,       // Bytes (from Content-Length header)\n\n  side: \"server\",           // \"client\" | \"server\"\n  runtime: \"node\",          // \"browser\" | \"node\"\n\n  pageUrl: \"https://myapp.com/checkout\",  // Current page (client-side only)\n  route: undefined,\n\n  category: \"external\",     // See URL categorization below\n  error: undefined,\n\n  metadata: {\n    service: \"my-next-app\",\n    squad: \"payments\",\n    environment: \"production\",\n    version: \"2.1.0\",\n    userAgent: \"Mozilla/5.0 ...\",   // Client-side only\n  }\n}\n```\n\n---\n\n## URL Categorization\n\nRequests are automatically classified:\n\n| Pattern | Category |\n|---|---|\n| `/api/*` | `api` |\n| `/_next/static/*`, `/_next/*` | `static` |\n| `.js`, `.mjs`, `.jsx` | `script` |\n| `.css`, `.scss` | `style` |\n| `.png`, `.jpg`, `.webp`, `.svg`, etc. | `image` |\n| `.woff2`, `.ttf`, `fonts/` | `font` |\n| Different origin | `external` |\n| Everything else | `other` |\n\nOverride with a custom `categorizer`:\n\n```ts\ninitTelemetry({\n  categorizer: (url) => {\n    if (url.includes(\"/graphql\")) return \"api\";\n    if (url.includes(\"cdn.myapp.com\")) return \"static\";\n    return null; // null = use built-in rules\n  },\n  // ...\n});\n```\n\n---\n\n## Exporters\n\n### Console Exporter (development)\n\n```ts\nimport { consoleExporter } from \"next-request-telemetry\";\n\nconsoleExporter()\n// Prints a console.table of captured events\n```\n\n### HyperDX Exporter\n\n```ts\nimport { hyperdxExporter } from \"next-request-telemetry\";\n\nhyperdxExporter({\n  endpoint: \"https://in-otel.hyperdx.io/v1/logs\",\n  apiKey: process.env.HYPERDX_API_KEY,\n  timeoutMs: 5000, // optional, default 5000\n})\n```\n\n### OTEL Exporter (OTLP HTTP)\n\n```ts\nimport { otelExporter } from \"next-request-telemetry/server\";\n\notelExporter({\n  endpoint: \"http://otel-collector:4318/v1/traces\",\n  headers: {\n    Authorization: \"Bearer my-token\",\n  },\n  timeoutMs: 5000,\n})\n```\n\n### Custom Exporter\n\nImplement the `TelemetryExporter` interface:\n\n```ts\nimport type { TelemetryExporter, RequestTelemetryEvent } from \"next-request-telemetry\";\n\nconst myExporter: TelemetryExporter = {\n  name: \"my-exporter\",\n\n  async send(events: RequestTelemetryEvent[]): Promise<void> {\n    await fetch(\"https://my-backend.com/ingest\", {\n      method: \"POST\",\n      headers: { \"Content-Type\": \"application/json\" },\n      body: JSON.stringify(events),\n    });\n  },\n};\n```\n\n---\n\n## Batching & Performance\n\nEvents are **never sent one-by-one**. The SDK queues events and flushes in batches:\n\n- **Timer flush**: every `flushIntervalMs` (default 3 s)\n- **Size flush**: when queue reaches `maxSize` (default 50 events)\n- **Keepalive**: uses `fetch(..., { keepalive: true })` so batches survive page unloads\n- **Retry**: failed batches are retried up to `maxRetries` times with exponential backoff\n- **Process safety**: errors in export **never** throw or crash the host app\n\n```\n100 API calls → queue → [flush every 3s] → 2–3 batched uploads\n```\n\n---\n\n## Architecture\n\n```\nsrc/\n├── types/          # Core interfaces (RequestTelemetryEvent, TelemetryConfig, …)\n├── config/         # Config resolution, defaults, exporter endpoint registry\n├── utils/          # ID gen, URL normalization, categorization, size helpers\n├── enrichers/      # Raw data → normalized RequestTelemetryEvent\n├── batching/       # BatchQueue with timer, size trigger, retry + backoff\n├── interceptors/\n│   ├── fetch.client.ts   # Browser fetch() patch\n│   ├── xhr.client.ts     # Browser XMLHttpRequest patch\n│   ├── fetch.server.ts   # Node.js global fetch() patch\n│   └── http.server.ts    # Node.js http/https.request patch\n├── exporters/\n│   ├── console.ts        # Development console.table exporter\n│   ├── hyperdx.ts        # HyperDX OTLP/HTTP exporter\n│   └── otel.ts           # OTLP HTTP trace exporter\n├── transport/      # Module-level registry, prevents double-install\n├── runtime/        # Runtime environment detection\n├── index.ts        # Client public API\n└── server.ts       # Server public API\n```\n\n**Key design decisions:**\n\n- **No singletons by default** — the registry is module-scoped to the runtime (browser window or Node.js process), not a class static. This is the correct scope.\n- **Safe monkey-patching** — every interceptor checks `__nrt_*_patched__` before installing and provides a restore function.\n- **Tree-shakeable** — `sideEffects: false`. Server interceptors are in a separate entry point (`/server`) and never bundled into client code.\n- **No `any` types** — strictly typed throughout.\n- **Exporter endpoint auto-exclusion** — registered exporter URLs are excluded from capture automatically to prevent telemetry-on-telemetry loops.\n\n---\n\n## Safety Guarantees\n\n| Risk | Mitigation |\n|---|---|\n| Infinite loops (telemetry capturing itself) | Exporter URLs registered and auto-excluded |\n| Double-patching | `__nrt_*_patched__` guard on every interceptor |\n| Crashing host app | All exporter errors are caught and logged, never re-thrown |\n| Memory leaks | Queue is drained on flush; `destroy()` for graceful shutdown |\n| Edge Runtime crashes | Server entry guarded by `NEXT_RUNTIME === \"nodejs\"` |\n| Performance overhead | Async queue; never blocks the intercepted request |\n\n---\n\n## Environment Variables\n\n| Variable | Used in | Description |\n|---|---|---|\n| `HYPERDX_API_KEY` | Server | HyperDX API key (server-side) |\n| `HYPERDX_ENDPOINT` | Server | HyperDX OTLP endpoint |\n| `NEXT_PUBLIC_HYPERDX_API_KEY` | Client | HyperDX API key (client-side) |\n| `NEXT_PUBLIC_HYPERDX_ENDPOINT` | Client | HyperDX OTLP endpoint |\n| `NEXT_PUBLIC_SERVICE_NAME` | Both | Service name |\n| `NEXT_PUBLIC_SQUAD` | Both | Team/squad identifier |\n| `NEXT_PUBLIC_APP_VERSION` | Both | App version / git SHA |\n| `NEXT_RUNTIME` | Server | Set by Next.js — used to guard Node-only code |\n\n---\n\n## TypeScript\n\nThe package is written entirely in TypeScript. All types are exported:\n\n```ts\nimport type {\n  TelemetryConfig,\n  TelemetryExporter,\n  RequestTelemetryEvent,\n  EventMetadata,\n  RequestCategory,\n  BatchingConfig,\n  UrlCategorizerFn,\n} from \"next-request-telemetry\";\n```\n\n---\n\n## License\n\nMIT\n","readmeFilename":"README.md","author":{}}