Skip to content

Refactor to faster API shape - #103

Open
blakeembrey wants to merge 5 commits into
masterfrom
be/faster-api-shape
Open

Refactor to faster API shape#103
blakeembrey wants to merge 5 commits into
masterfrom
be/faster-api-shape

Conversation

@blakeembrey

Copy link
Copy Markdown
Member

Refactors the API to be a cached/preprocessed function closure instead of parsing the matching pattern every time. Additionally solves #14 by parsing parameters and matching them when provided.

It also changes the returned type to always be the matched one, instead of returning the matched except when it contained a wildcard.

Drops usage of mime-types from the library so it can be more easily used in a browser (the dependency is huge): #45. However, I kept an option for extensions so it's possible for mime-types to still be passed into the library upstream (e.g. in express) without largely breaking expectations if wanted.

@codecov

codecov Bot commented Aug 19, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (e4f4fbf) to head (92bb041).

Additional details and impacted files
@@            Coverage Diff            @@
##            master      #103   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files            1         1           
  Lines           64       112   +48     
  Branches        31        49   +18     
=========================================
+ Hits            64       112   +48     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@blakeembrey

Copy link
Copy Markdown
Member Author

Perf before:

 ✓ src/index.bench.ts > request 3087ms
     name                      hz     min     max    mean     p75     p99    p995    p999     rme  samples
   · exact match     2,270,975.11  0.0003  2.3164  0.0004  0.0005  0.0005  0.0005  0.0010  ±0.93%  1135488
   · wildcard match  1,796,617.10  0.0005  0.2610  0.0006  0.0006  0.0006  0.0007  0.0008  ±0.15%   898309

 ✓ src/index.bench.ts > hasBody 12958ms
     name                             hz     min     max    mean     p75     p99    p995    p999     rme   samples
   · content-length        25,810,995.99  0.0000  0.0559  0.0000  0.0000  0.0000  0.0000  0.0001  ±0.10%  12905499
   · transfer-encoding     25,260,193.85  0.0000  1.7167  0.0000  0.0000  0.0000  0.0001  0.0001  ±1.02%  12630097
   · without body headers  25,876,518.45  0.0000  0.0483  0.0000  0.0000  0.0000  0.0000  0.0001  ±0.09%  12938260

 ✓ src/index.bench.ts > is 5014ms
     name                       hz     min     max    mean     p75     p99    p995    p999     rme  samples
   · exact match      2,896,511.90  0.0002  0.0504  0.0003  0.0003  0.0004  0.0005  0.0005  ±0.10%  1448256
   · wildcard match   2,330,621.71  0.0003  0.3332  0.0004  0.0004  0.0005  0.0005  0.0010  ±0.29%  1165311
   · suffix match     2,629,093.98  0.0002  0.4095  0.0004  0.0004  0.0004  0.0005  0.0008  ±0.42%  1314547

 ✓ src/index.bench.ts > normalize 12661ms
     name                  hz     min     max    mean     p75     p99    p995    p999     rme   samples
   · mime type  20,105,101.28  0.0000  0.0483  0.0000  0.0000  0.0001  0.0001  0.0001  ±0.10%  10052551
   · extension   4,498,251.62  0.0001  0.0848  0.0002  0.0002  0.0003  0.0003  0.0004  ±0.10%   2249126
   · shortcut   25,066,428.75  0.0000  0.9032  0.0000  0.0000  0.0000  0.0000  0.0001  ±0.42%  12533215
   · suffix     24,440,433.90  0.0000  0.0581  0.0000  0.0000  0.0000  0.0001  0.0001  ±0.10%  12220217

 ✓ src/index.bench.ts > match 8080ms
     name                         hz     min     max    mean     p75     p99    p995    p999     rme  samples
   · exact              9,655,560.47  0.0000  0.0674  0.0001  0.0001  0.0001  0.0002  0.0002  ±0.10%  4827781
   · type wildcard      9,786,125.06  0.0000  0.0631  0.0001  0.0001  0.0002  0.0002  0.0002  ±0.11%  4893063
   · subtype wildcard  10,404,581.48  0.0000  0.5707  0.0001  0.0001  0.0001  0.0001  0.0002  ±0.25%  5202291
   · suffix wildcard    9,863,499.49  0.0000  0.0481  0.0001  0.0001  0.0001  0.0001  0.0002  ±0.09%  4931750

Perf after:

 ✓ src/index.bench.ts > request 2149ms
     name                      hz     min     max    mean     p75     p99    p995    p999     rme  samples
   · exact match     3,287,572.06  0.0001  6.9777  0.0003  0.0003  0.0005  0.0008  0.0020  ±4.66%  1643787
   · wildcard match  2,494,643.59  0.0003  0.3694  0.0004  0.0004  0.0005  0.0005  0.0008  ±0.27%  1247322

 ✓ src/index.bench.ts > hasBody 12041ms
     name                             hz     min     max    mean     p75     p99    p995    p999     rme   samples
   · content-length        24,787,256.31  0.0000  2.2491  0.0000  0.0000  0.0000  0.0001  0.0001  ±1.14%  12393629
   · transfer-encoding     24,975,833.85  0.0000  0.3718  0.0000  0.0000  0.0000  0.0001  0.0001  ±0.26%  12487917
   · without body headers  25,726,899.49  0.0000  0.0601  0.0000  0.0000  0.0000  0.0000  0.0001  ±0.10%  12863450

 ✓ src/index.bench.ts > is 4953ms
     name                       hz     min     max    mean     p75     p99    p995    p999     rme  samples
   · exact match     10,168,937.27  0.0000  0.2648  0.0001  0.0001  0.0001  0.0002  0.0002  ±0.16%  5084469
   · wildcard match   5,743,215.43  0.0001  0.0465  0.0002  0.0002  0.0002  0.0002  0.0003  ±0.09%  2871608
   · suffix match     5,791,282.04  0.0001  0.0510  0.0002  0.0002  0.0002  0.0002  0.0003  ±0.09%  2895642

 ✓ src/index.bench.ts > is one shot 3345ms
     name                      hz     min     max    mean     p75     p99    p995    p999     rme  samples
   · exact match     4,526,294.07  0.0001  0.2533  0.0002  0.0002  0.0003  0.0003  0.0003  ±0.17%  2263148
   · wildcard match  2,660,104.95  0.0002  0.3157  0.0004  0.0004  0.0005  0.0005  0.0010  ±0.46%  1330053
   · suffix match    3,137,052.12  0.0002  0.2946  0.0003  0.0003  0.0004  0.0004  0.0009  ±0.49%  1568527

 ✓ src/index.bench.ts > normalize 13156ms
     name                  hz     min     max    mean     p75     p99    p995    p999     rme   samples
   · mime type  21,110,815.41  0.0000  0.0610  0.0000  0.0000  0.0001  0.0001  0.0001  ±0.08%  10555408
   · extension  17,913,210.42  0.0000  0.8524  0.0001  0.0001  0.0001  0.0001  0.0002  ±0.38%   8956606
   · shortcut   16,117,687.03  0.0000  0.3179  0.0001  0.0001  0.0001  0.0001  0.0002  ±0.16%   8058844
   · suffix     20,295,540.58  0.0000  0.1576  0.0000  0.0000  0.0001  0.0001  0.0002  ±0.14%  10147771

 ✓ src/index.bench.ts > match 14470ms
     name                         hz     min     max    mean     p75     p99    p995    p999     rme   samples
   · exact             33,101,032.35  0.0000  0.1096  0.0000  0.0000  0.0000  0.0000  0.0001  ±0.12%  16550517
   · type wildcard     17,624,617.25  0.0000  0.0877  0.0001  0.0001  0.0001  0.0001  0.0002  ±0.13%   8812310
   · subtype wildcard  16,402,670.98  0.0000  0.0481  0.0001  0.0001  0.0001  0.0001  0.0002  ±0.09%   8201336
   · suffix wildcard   14,482,164.06  0.0000  0.0435  0.0001  0.0001  0.0001  0.0001  0.0002  ±0.09%   7241083

 ✓ src/index.bench.ts > match one shot 7885ms
     name                         hz     min      max    mean     p75     p99    p995    p999     rme  samples
   · exact             12,299,659.53  0.0000  13.2278  0.0001  0.0001  0.0001  0.0002  0.0005  ±5.37%  6149830
   · type wildcard     10,124,754.18  0.0000   0.2762  0.0001  0.0001  0.0001  0.0002  0.0003  ±0.43%  5062378
   · subtype wildcard   8,889,857.41  0.0000   4.4892  0.0001  0.0001  0.0001  0.0002  0.0005  ±1.80%  4444929
   · suffix wildcard    7,595,721.99  0.0000   0.2727  0.0001  0.0001  0.0002  0.0002  0.0005  ±0.40%  3797861

The new APIs are much faster, but I've tried my best that even if you use the new API is the one shot approach to have comparable performance. E.g. compare match one shot to before match.

@blakeembrey

Copy link
Copy Markdown
Member Author

The only thing I didn't tackle from the plan in #96 was the class approach. I will do a follow up with that, it would be better for re-use since request, is and (if added) contentType all need the same underlying behavior.

@blakeembrey
blakeembrey requested review from a team and Phillip9587 August 19, 2026 21:36
Comment thread src/index.ts Outdated

for (const t of types) {
const contentType = parse(t);
hasParameters ||= Object.keys(contentType.parameters).length > 0;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perf de-optimization only when a potential match has requested parameters to be matched.

Comment thread src/index.ts Outdated

return false;
export interface NormalizeOptions {
extensions?: Record<string, string | string[]>;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It probably makes more sense to be a function to support https://www.npmjs.com/package/mime and others that may not expose in object format.

@blakeembrey

Copy link
Copy Markdown
Member Author

The last commit replaces the functional style with a class. I'm indifferent on which direction is best, they both have up/downsides.

The class might be helpful if you want to parse once and use in multiple places. The functional is a bit more verbose but helps with code splitting, e.g. in browsers, where you only use is and never the request/hasBody versions.

@bjohansebas bjohansebas left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, it took me a little while to understand, but the changes look good to me

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants