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

@tetsujs/request-id

v0.5.3

Published

Request ids for Tetsu

Readme

@tetsujs/request-id

A request id on every request and response, for the log lines and failure reports of one request to share.

bun add @tetsujs/request-id

Usage

import { requestId } from "@tetsujs/request-id";

const id = requestId();

createApp({ hooks: { beforeParse: [id] }, routes });

requestId() is one beforeParse hook. Every response gets an x-request-id header, and ctx.requestId holds the id for the rest of the request. The request logs of @tetsujs/request-log and a reportError receiver pick it up when this hook ran before them.

Reading the id in a handler

requestId() adds ctx.requestId. Mounted on the application it is there at runtime, but not in a route's types — the application does not know which routes it will hold. To have it typed, mount it on the route:

const id = requestId();

route({
  method: "GET",
  path: "/orders",
  hooks: { beforeParse: [id] },
  handler: (ctx) => logger.info({ requestId: ctx.requestId }, "listing"),
  //                                   ^? string
});

or declare it where it is read, with Requires<{ requestId: string }>, and the compiler checks that something provides it. A hook of the application mounted after requestId() sees it typed too — that is how the AsyncLocalStorage recipe below works.

Options

| requestId() | Default | | | --- | --- | --- | | header | "x-request-id" | the header the id is written to, and read from when trusted | | trustIncoming | false | use the id the client sent; enable only behind a proxy that sets the header | | generate | crypto.randomUUID | how a new id is made |

Notes

  • An incoming id is not trusted by default: it is a value a client chose, and trusting it lets one client stamp another's log lines.
  • Failures share the id when they go to your logger: pass reportError to createApp and log ctx?.requestId with the error — see Logging in the core README.

The id deeper than the handler

Code that never receives ctx — a repository several calls down — can still read the id through AsyncLocalStorage. It is a few lines, so this package leaves it to you:

import { AsyncLocalStorage } from "node:async_hooks";
import { hook, type Requires } from "@tetsujs/core";

const store = new AsyncLocalStorage<{ requestId: string }>();

export const scope = hook.beforeParse((ctx: Requires<{ requestId: string }>) => {
  store.enterWith({ requestId: ctx.requestId });
});

export const current = () => store.getStore();

Mount scope after requestId(), on the application so that every request has it, a 404 included:

createApp({ hooks: { beforeParse: [id, scope] }, routes });

The order is what the compiler checks: a hook of the application or of a group sees what the hooks before it at the same level contributed, and scope placed before id does not compile.

It uses enterWith rather than run because a hook is not handed the rest of the request as a callback, and it costs about 12 ns a request. With pino, mixin: () => ({ ...current() }) puts the id on every line the application writes, from wherever it writes it. The copy matters: pino merges each line's own fields into the object mixin returns, so handing it the stored object itself would carry one line's fields into every line after it for the rest of the request. Keep the store to things like ids and trace labels: anything a decision depends on — a user, a role — belongs in ctx, where the compiler checks it is there.