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

@jbingen/pathparams

v0.1.0

Published

Type-safe path builder and matcher for URL templates.

Readme

🔗 pathparams

npm version npm bundle size license

Type-safe path builder and matcher for URL templates.

For anyone tired of hand-rolling /users/${id}/posts/${postId} without encoding safety, missing-param checks, or type guarantees.

npm install @jbingen/pathparams
// before
`/users/${id}/posts/${postId}`; // untyped, unencoded, unchecked

// after
userPost.build({ id: "1", postId: "2" }); // typed, encoded, safe

Param names are inferred directly from the template string. No manual type annotations needed.

import { path } from '@jbingen/pathparams';

const userPost = path("/users/:id/posts/:postId");

userPost.build({ id: "1", postId: "2" });
// "/users/1/posts/2"

userPost.build({ id: "1" });
// compile error - missing postId

userPost.match("/users/1/posts/2");
// { id: "1", postId: "2" }

userPost.match("/other/path");
// null

Why

Everyone hand-rolls URL construction with template literals. That works until someone forgets to encode a value, misses a param, or changes a route shape without updating every call site.

pathparams fixes all three in ~70 lines with zero dependencies. The template string is the single source of truth - TypeScript infers the rest.

API

path(template)

Creates a typed path from a URL template. Params are :name segments.

const p = path("/users/:id/posts/:postId");

Returns an object with template, build, and match.

.build(params)

Builds a URL string from params. Values are stringified and URL-encoded.

p.build({ id: "1", postId: "2" }); // "/users/1/posts/2"
p.build({ id: 42, postId: 7 }); // "/users/42/posts/7"

Static paths (no params) take no arguments:

const health = path("/health");
health.build(); // "/health"

Throws at runtime if a param is missing. Catches it at compile time if you're using TypeScript.

.match(pathname)

Matches a pathname against the template. Returns typed params or null.

p.match("/users/1/posts/2"); // { id: "1", postId: "2" }
p.match("/users/1"); // null
p.match("/other"); // null

Values are URL-decoded. Match is exact - no partial matches, no trailing segment tolerance.

.template

The original template string, preserved as a string literal type.

p.template; // "/users/:id/posts/:postId"

How types work

Param names are extracted from the template at the type level using template literal inference:

path("/users/:id/posts/:postId");
// build requires: { id: string | number | boolean, postId: string | number | boolean }
// match returns:  { id: string | number | boolean, postId: string | number | boolean } | null

path("/health");
// build requires: no arguments
// match returns:  {} | null

No generics to pass manually. The template string is the source of truth.

Status

Early, but stable. The API surface is intentionally small and expected to remain mostly additive.

Design decisions

  • Zero dependencies. ~70 lines of TypeScript.
  • Param names inferred from template literals at compile time.
  • Values are stringified, URL-encoded on build, URL-decoded on match.
  • Static segments are regex-escaped for safe matching.
  • No partial matching, no wildcards, no query strings. Just path params.