Kraken.js Middleware: Why PayPal Put the Request Pipeline in a JSON File
Kraken.js moved Express middleware order into JSON config via the meddleware module. Here's how priority-based pipelines work, how to verify them, and when the indirection isn't worth it.
09 Jan 2026, 14:44 UTC

If you've ever opened a mature Express app and tried to answer a simple question — "in what order does middleware actually run?" — you know the pain. The answer is scattered across app.js, a few required modules, and maybe some environment conditionals. Kraken.js, PayPal's thin layer over Express, made one opinionated bet: the middleware pipeline should be declared in configuration, not buried in code. That decision is the most interesting thing about the framework, and it's worth understanding even if you never ship a Kraken app.
The problem: implicit ordering
Express middleware runs in registration order. That's simple and predictable, but in a growing app the registration calls end up spread across files. Someone adds a security header middleware in one place, request logging in another, and six months later nobody can say confidently whether the auth check runs before or after the body parser without reading the whole startup path.
Kraken's answer is to make the pipeline a first-class, reviewable artifact. In config/config.json, a middleware object declares each entry: which module to load, optional arguments, and a numeric priority that determines execution order. A small module called meddleware reads that config at startup and registers everything with Express in priority order. Lower numbers run earlier.
What a declared pipeline looks like
A trimmed-down example of the middleware section:
{
"middleware": {
"requestLogger": {
"module": { "name": "./lib/middleware/request-logger" },
"priority": 10
},
"cookieParser": {
"module": { "name": "cookie-parser" },
"priority": 20
},
"router": {
"module": { "name": "path-middleware" },
"priority": 100
}
}
}Exact key shapes vary by Kraken version, so treat this as illustrative and check the docs for the version pinned in your package.json. The idea is stable, though: reordering the pipeline means editing numbers in one file, not moving app.use() calls around. A reviewer can see the entire request path in a single diff.
Because Kraken sits on top of Express rather than replacing it, each module is just a standard middleware factory — a function returning (req, res, next) => .... Your custom request-logger is plain Express code; only its registration is config-driven.
Where the approach earns its keep: environments
Kraken merges environment-specific files on top of config.json — development.json overrides for dev, production.json for prod. Combined with declared middleware, this makes environment-specific pipelines clean. A verbose error handler that prints stack traces can exist only in development.json, with zero if (env === 'development') branches in application code. The pipeline itself becomes data that differs per environment, which is exactly what configuration is for.
Verifying the order actually took effect
Don't trust the config blindly — verify at runtime. Two cheap checks:
- Swap test: give two custom middleware entries priorities 10 and 20, have each log its name, start the app, and hit a route. Then swap the priorities and confirm the log order flips. If it doesn't, your config isn't being picked up (wrong file, wrong environment, or a version mismatch in the config schema).
- Stack inspection: at startup, after the app is configured, log the registered layers. In Express 4-style apps,
app._router.stackexposes layer names — run this from a startup script in your project root with the sameNODE_ENVyou deploy with, since the merged config depends on it. Note this touches Express internals, so it can change across Express versions; use it as a diagnostic, not production code.
The trade-off: indirection, and a bigger caveat
The declarative pipeline costs you indirection. When middleware misbehaves, you now debug two things: the middleware itself and meddleware's priority resolution. For a small app with five middleware entries, plain Express app.use() calls are honestly simpler and every Node developer already understands them. The config approach pays off when the pipeline is long, changes per environment, or is reviewed by people who don't read the whole codebase.
The bigger caveat: Kraken's development has slowed considerably in recent years, and the Dust.js templating it historically shipped with is largely unmaintained (many teams swapped in another view engine). Before adopting it for new work, check the krakenjs GitHub repository's commit activity and open issues. The config-driven middleware idea, however, stands on its own — meddleware-style priority ordering is a pattern you can adopt in plain Express with a dozen lines of code if the concept appeals but the framework's maintenance status doesn't.
Actionable takeaway: whether or not you use Kraken, write down your middleware order somewhere reviewable — a config file, a single middleware.js that does nothing but ordered app.use() calls, or a comment block. Implicit ordering is a bug farm; explicit ordering is a design decision.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.