Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

gateway-picker

An interactive, self-contained decision tool that recommends an API gateway from a team's stated requirements — and then shows you every point of the arithmetic that produced the recommendation, including the reasons against it.

It covers six options:

Option What it is
Kong Gateway OpenResty/NGINX underneath, large plugin ecosystem, admin API, decK, workspaces
Envoy Proxy C++ L7 proxy driven by xDS; exceptional protocol support, unforgiving config surface
NGINX / NGINX Plus The default for raw HTTP proxying; API-management features mostly yours to build
Tyk Go gateway with a strong dashboard, developer portal, SOAP/REST translation, managed cloud
AWS API Gateway Nothing to operate, priced per request, deeply tied to AWS
Purpose-built custom proxy Your own proxy; total control and a permanent roadmap line item

Open index.html in a browser. That is the whole program. No build step, no server, no network access, no dependencies — it works from a file:// URL with the network cable unplugged. A hosted copy of the same file is available as a live version of gateway-picker if you would rather link colleagues to it than have them clone.

Why this exists

Gateway selection discussions tend to collapse into one of two failure modes: a vendor comparison table where every row is a green tick, or a spreadsheet whose weights were chosen after the conclusion. Both produce a decision nobody can later reconstruct.

This tool takes the opposite approach. Every opinion is a small integer in a table you can read, and the result page shows which of your answers actually moved the ranking. If you disagree with a number, you can find it, change it, and watch the recommendation change. That is the point: the output is not the recommendation, it is the argument.

The background reading behind the dimensions modelled here is on gateway selection criteria and Kong vs Tyk vs Envoy for microservices.

Running it

Clone the repository and open the page directly:

git clone <this-repository>
cd gateway-picker
xdg-open index.html      # Linux
open index.html          # macOS
start index.html         # Windows

There is nothing to install and nothing to fetch. It is not published to npm and never will be. If you prefer to serve it over HTTP (the clipboard API is slightly happier that way), any static file server pointed at the clone will do.

To run the tests you need Node 18 or newer, purely for its built-in test runner:

node --test tests/test_scoring.mjs

What the page gives you

A questionnaire of eleven dimensions. Each has three to five answers and a short explanation of why the dimension moves the needle at all. Every question is optional; skipping one leaves it out of the arithmetic entirely rather than scoring it as neutral.

A ranked result with the reasoning attached. The winner is shown with a percentage fit, its top three reasons for, its top reasons against, and a table of which answers were decisive. Candidates eliminated by a hard constraint are listed separately as ruled out, with the reason — never quietly buried at the bottom of the ranking.

A shareable URL. Your answers are encoded in the location hash, so a filled-in comparison can be pasted straight into a design review and it will reopen exactly as you left it.

An ADR-style markdown export. "Copy summary as markdown" produces a decision record with context, decision, alternatives considered, hard constraints applied and decisive answers — the shape most architecture decision record templates expect.

An answer-independent reference matrix. Every dimension against every candidate, showing where each is at its best and worst. This is derived from the same score tables as the recommendation, so it cannot drift out of sync with it, and it makes the page useful as a reference even if you never answer a question.

The dimensions

# Dimension Weight Why it is in here
1 Deployment model 3.0 The only dimension that routinely eliminates candidates outright
2 Peak RPS 2.0 Decides whether per-request pricing or per-request plugin cost dominates
3 p99 latency budget 2.5 Interpreted plugin chains and cold starts show up in the tail first
4 Authentication 2.0 Where gateway projects actually spend their time
5 Extension language 2.0 The binding constraint is who can debug it at 03:00, not what is possible
6 Protocols 2.5 The most common source of late, expensive surprises
7 Multi-tenancy 1.5 An isolation problem, not a routing problem
8 Config management 1.5 Decides who can safely change routing at 5pm on a Friday
9 Team size and ops maturity 2.0 Capability is priced in headcount
10 Budget and licensing 2.0 Decides which features you can use, not just what you pay
11 Service mesh coexistence 1.5 Gateway and mesh overlap heavily on retries, mTLS and telemetry

Each dimension links out to the relevant depth: for example protocol translation patterns for the protocols question, implementing mTLS at the gateway edge for client-certificate authentication, multi-tenant routing strategies for tenancy, and API gateway vs service mesh for the mesh question.

The scoring methodology

The algorithm

  1. Resolve answers. Answers arrive as a map of dimensionId -> optionId, from the form or from the URL hash. Unknown ids are dropped silently rather than throwing, because the hash is user-editable.
  2. Apply hard disqualifiers first. An answer may carry disqualify entries. A disqualified candidate is removed from the ranking entirely and no longer contributes to any average. Answering "fully managed", for example, eliminates Kong, Envoy, NGINX and a custom proxy — not because they are bad, but because they are things you install.
  3. Sum weighted scores. Each surviving candidate gets weight * score for every answered dimension, where score runs from -3 to +3.
  4. Normalise to a percentage. The total is mapped linearly from the theoretical worst (-3 on every answered dimension) to the theoretical best (+3 on every one). 50% means exactly neutral, not "half good".
  5. Compute decisiveness. For each answer, a candidate's delta is its weighted score minus the mean weighted score of all surviving candidates on that same answer.
  6. Rank. Descending by total; ties break by declaration order in the candidate list, so output is fully deterministic.

Why decisiveness is not the raw score

This is the part most weighted-scoring spreadsheets get wrong. A requirement that every candidate satisfies equally well cannot possibly explain why one of them won, however important it is to you and however high its raw scores are. "We need HTTP/JSON" is critical and completely uninformative.

So the "decisive answers" table reports separation from the field, not absolute merit. In the worked example below, "HTTP/JSON only" scores +3 for the winner and still registers as near-zero decisiveness, while "dynamic xDS from a control plane" dominates the explanation.

The score scale

Score Meaning
+3 A headline strength — the reason people pick this gateway
+2 Solid, well-trodden
+1 Fine
0 Neutral, no opinion
-1 Friction, but workable
-2 Fighting the tool
-3 The wrong tool for this

Honest biases and limitations

You should know what you are inheriting if you use these numbers unmodified.

  • The weights are one person's judgement. They are not derived from data, and there is no data that could derive them. Deployment model is weighted twice as heavily as multi-tenancy because that ordering is defensible, not because it is measured.
  • It is biased toward operational conservatism. "Purpose-built custom proxy" scores badly on almost every team-size answer, and "nobody owns infrastructure" is treated as a strong signal toward a managed service. That is a value judgement about what usually goes wrong, not a technical fact.
  • It is biased against novelty. Boring options with two decades of operational precedent score well on the things that are hard to measure. If you want to be an early adopter, this model will argue with you.
  • Vendor feature sets move. Scores encode roughly where these products sat in early 2026. Managed offerings in particular change fast, and the OSS-versus-enterprise feature split moves even faster. A "ruled out" verdict may be stale within a year.
  • Linear additive scoring cannot express interactions. In reality "extreme latency budget" and "we cannot write extensions" is worse than the sum of its parts, because the usual answer to one is the thing the other forbids. This model just adds the two numbers. Hard disqualifiers exist precisely because some constraints genuinely are not additive, but they are a blunt instrument.
  • The candidate list is not exhaustive. HAProxy, Traefik, APISIX, Azure API Management and Apigee are all reasonable options and none of them are here. Adding one means writing a score for all 44 answers, which is exactly as much work as it sounds and is the honest price of having an opinion.
  • The percentage is not a probability. 93% fit does not mean a 93% chance of success. It means "this option is 93% of the way from the worst conceivable candidate to the best conceivable one, on eleven axes I chose".

Treat the ranking as a way to structure an argument, not to end one. If the tool tells you something that contradicts your instinct, the interesting output is the decisive-answers table, which tells you exactly which assumption you and the model disagree about.

The data model

Everything you might want to fork lives at the top of assets/scoring.js, above the engine. There are two tables.

CANDIDATES

{
  id:    'kong',                  // stable key, used in the URL hash and score tables
  name:  'Kong Gateway',          // shown in headings and the ADR export
  short: 'Kong',                  // shown in narrow table columns
  url:   '<further-reading-url>',  // further reading for this specific option
  blurb: 'OpenResty/NGINX ...'    // one-sentence characterisation, used in the ADR export
}

Order matters: it is the tie-break for equal scores.

DIMENSIONS

{
  id:       'protocols',                        // stable key, used in the URL hash
  question: 'What protocols must the gateway handle?',
  why:      'Protocol support is the most common ...',  // shown under the question
  weight:   5,                                  // pre-multiplied by WEIGHT_SCALE (2), so 2.5
  options: [
    {
      id:    'rest_grpc',
      label: 'gRPC, including REST/gRPC transcoding',
      hint:  'optional clarifying sub-label',
      scores: {                                 // -3..+3, per candidate
        kong: 1, envoy: 3, nginx: 2, tyk: 2, aws: 0, custom: 1
      },
      notes: {                                  // rationale strings, shown in results
        envoy: 'The gRPC-JSON transcoder is the reference implementation ...'
      },
      disqualify: {                             // hard elimination, not a penalty
        aws: 'AWS API Gateway is a cloud service ...'
      }
    }
  ]
}

Rules the test suite enforces on this data:

  • Dimension and option ids are unique; every candidate id referenced actually exists.
  • Every score is an integer in -3..3.
  • A candidate is either scored or disqualified on a given option, never both.
  • A note may not exist without a corresponding score — a rationale for an opinion that is not encoded is worse than no rationale.
  • No single answer may disqualify the entire field.
  • Every dimension has 3–5 options and a further-reading entry in READING.

Notes are present for opinionated scores (roughly |score| >= 2) and absent for unremarkable ones. An absent note means "no strong view", and the UI substitutes neutral relative phrasing rather than inventing a rationale.

WEIGHT_SCALE

Weights are stored pre-multiplied by 2 so that all internal arithmetic stays in integers and results are byte-for-byte identical across JavaScript engines. A stored weight of 5 is displayed and documented as 2.5. Divide by WEIGHT_SCALE when reading, multiply when writing.

URL hash format

Answers are encoded as comma-separated dimensionId:optionId pairs, always emitted in dimension declaration order so the same answers always produce the same URL:

#deployment:self_host,rps:over_50k,latency:strict,auth:mtls,extension:wasm

Decoding is tolerant: unknown dimensions, unknown options and malformed pairs are dropped, and whatever remains is applied. A corrupt hash degrades to a partially filled form rather than an error.

Worked example

Load the page and answer as follows — a large infrastructure organisation putting a high-throughput gRPC edge in front of an existing Istio mesh:

Requirement Answer
Deployment Self-hosted
Peak RPS Over 50,000
Latency budget Strict (under ~10 ms at p99)
Authentication Mutual TLS
Extension language WebAssembly
Protocols gRPC, including REST/gRPC transcoding
Multi-tenancy Many external tenants needing hard isolation
Config management Dynamic xDS from a control plane
Team A large infrastructure organisation
Budget Open source only
Service mesh We already run Istio or another Envoy-based mesh

The resulting URL hash is:

#deployment:self_host,rps:over_50k,latency:strict,auth:mtls,extension:wasm,protocols:rest_grpc,tenancy:many_tenants,config:xds,team:large_infra,budget:zero_oss,mesh:istio_envoy

and the ranking is:

#1 Envoy Proxy                 99% fit, score 66
#2 NGINX / NGINX Plus          77% fit, score 36
#3 Purpose-built custom proxy  72% fit, score 30
#4 Kong Gateway                71% fit, score 28.5
#5 Tyk                         65% fit, score 20.5
-- AWS API Gateway             ruled out: AWS API Gateway is a cloud service.
                               There is no self-hosted distribution to install.

with these reasons for the winner:

Dynamic xDS from a control plane                  +5.7 vs field
  xDS is Envoy's native configuration protocol; nothing else comes close.
We already run Istio or another Envoy-based mesh  +4.2 vs field
  Shared telemetry, shared certificates and one set of concepts to learn.
Over 50,000 RPS                                   +3.2 vs field
  One of the few options that is routinely run at this scale by multiple large operators.

Note that no reasons against are produced here. That is not the tool being a fan: with these particular answers Envoy is genuinely not beaten on any dimension, and the page says so explicitly rather than manufacturing a criticism.

A case with real trade-offs

Change the answers to a mid-sized platform team running many external tenants on an enterprise budget, with Lua skills and a strict latency target:

#1 Kong Gateway                93% fit, score 58
#2 Tyk                         85% fit, score 47.5
#3 NGINX / NGINX Plus          84% fit, score 45.5
#4 Envoy Proxy                 72% fit, score 30
#5 Purpose-built custom proxy  64% fit, score 18.5
-- AWS API Gateway             ruled out (self-hosted)

For:
  [+4.8] Lua is Kong's native plugin language; the PDK is well documented.
  [+3.2] Consumers, credentials and per-consumer rate limits are core Kong objects.
  [+2.8] Enough capacity to run plugins and upgrades, without needing Envoy-grade depth.

Against:
  [-2.0] On "How much latency may the gateway itself add at p99?", answering "Strict"
         is handled adequately, but other candidates do it better, so it still counts
         against this one.
  [-1.2] On "How do you want the gateway to be deployed and operated?", answering
         "Self-hosted" is handled adequately, but other candidates do it better, so it
         still counts against this one.

The second entry is worth dwelling on. Kong scores +2 on self-hosting — it is objectively good at it — and the entry still appears under "against", because Envoy and NGINX score +3. The tool deliberately does not reuse the complimentary rationale string here, since presenting praise as a criticism is how comparison tables become untrustworthy.

Tests

The scoring logic is a plain ES-importable module, so it can be tested in Node without a browser or a DOM:

$ node --test tests/test_scoring.mjs
✔ every dimension is well formed
✔ no answer eliminates the entire field
✔ no answers means no opinion
✔ unknown dimensions and options in the hash are ignored, not fatal
✔ scoring is deterministic and ties break by declaration order
✔ percentages map the theoretical worst and best onto 0 and 100
✔ unanswered dimensions contribute nothing rather than a neutral score
✔ "fully managed" eliminates everything you would have to install
✔ self-hosting and air-gapping both eliminate the cloud-only option
✔ a disqualified candidate is eliminated regardless of how well it scores elsewhere
✔ persona: small team, wants managed, low volume
✔ persona: high-throughput gRPC at the edge with an existing Envoy mesh
✔ persona: regulated, air-gapped mTLS deployment with no appetite for gateway code
✔ persona: SOAP modernisation for a mid-sized platform team with a budget
✔ persona: many external tenants on an enterprise budget with Lua skills
✔ persona: a bespoke wire protocol at a latency budget nothing else meets
✔ explain reports honest trade-offs, not only strengths
✔ decisiveness measures separation from the field, not raw score
✔ a complimentary note is never rendered as a reason against
✔ explain returns null for an unknown candidate
✔ the matrix covers every dimension and candidate and derives from the same tables
✔ answers round-trip through the URL hash in dimension order
✔ a corrupt hash degrades to the answers it can understand
✔ markdown export is a usable ADR
✔ markdown export is deterministic for a fixed date
✔ markdown export copes with an empty questionnaire
✔ every candidate has descriptive anchor text for an approved site URL
ℹ tests 27
ℹ pass 27
ℹ fail 0

Exit code is 0 when every test passes and 1 otherwise, per Node's test runner.

The persona tests are the interesting ones. They pin down the recommendation for six realistic teams, so a change to any score that would flip one of those outcomes fails loudly. If you fork the opinions, expect to update them — and treat any persona you have to change as a decision worth writing down.

Forking the opinions

The whole point is that you disagree. To change the model:

  1. Edit the tables at the top of assets/scoring.js. Nothing else needs to change; the UI and the reference matrix are both generated from them.
  2. Run node --test tests/test_scoring.mjs. The data-integrity tests catch structural mistakes immediately; the persona tests tell you which real-world outcomes you just changed.
  3. Update the persona expectations if the change was intended.

To add a candidate, add an entry to CANDIDATES and a score for it on every option — all 44 of them. The integrity tests will not tell you a score is missing (an absent score is treated as 0, deliberately, so partial edits still run), but the reference matrix will show the candidate as flatly neutral everywhere, which is a fairly obvious tell.

Browser support

Any current version of Firefox, Chrome, Safari or Edge. The page uses :has() for the selected-option highlight, which degrades to a plain border on older engines without affecting anything functional. Dark and light themes follow prefers-color-scheme.

On file:// URLs the asynchronous clipboard API is unavailable in several browsers, so the copy buttons fall back to a hidden textarea and document.execCommand('copy'). If both fail, the page says so rather than silently doing nothing.

FAQ

Is my data sent anywhere? No. There is no network code in this repository at all — no fetch, no analytics, no fonts, no CDN. Your answers live in the URL bar and nowhere else. You can verify this by reading the two JavaScript files, which is a realistic amount of reading.

Why is my favourite gateway not here? Because adding one means writing a defensible score for all 44 answers. See "Forking the opinions" above. HAProxy, Traefik, APISIX, Azure API Management and Apigee are the most common requests, and all are reasonable choices this tool has nothing to say about.

Why does a 50% fit look bad when it is the top result? 50% is the neutral point, not the midpoint of "acceptable". If the winner is near 50% the model is telling you your requirements do not actually discriminate between the options — which usually means the real constraints are political or commercial rather than technical.

Why does the tool refuse to recommend anything sometimes? Because you gave it contradictory hard constraints — most often "fully managed" together with "air-gapped". That is a genuine result: one of the requirements has to give, and it is better to say so than to rank the least-bad violation.

Can I use this to justify a decision to management? The markdown export exists for exactly that, and it deliberately includes the reasons against the winner and the constraints that eliminated the alternatives. A decision record that only lists advantages is not a decision record.

Why does the winner sometimes have no reasons against it? Because against the answers you gave, it genuinely is not beaten on any dimension. The page says so and notes that this is a function of what you were asked, not proof that no weakness exists.

Should I trust the numbers? Not blindly, no. Read "Honest biases and limitations" above. The numbers are worth exactly as much as the reasoning attached to them, which is why every one of them has reasoning attached to it.

Further reading

The trade-offs modelled here are covered in more depth at request-routing.com, in particular API gateway fundamentals and architecture.

Licence

MIT. See LICENSE.

About

An interactive, dependency-free decision tool that recommends an API gateway - Kong, Envoy, NGINX, Tyk, AWS API Gateway or a custom proxy - from your stated requirements, showing the full scoring arithmetic, honest trade-offs, hard disqualifiers and a shareable ADR-style decision record.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages