Summary
Add support for native Rust middlewares that can be loaded dynamically at startup and executed directly inside Kito’s core, before any TypeScript middleware.
The idea is to allow anyone to write high-performance middleware as independent Rust crates. When compiled, these crates automatically generate a small npm package that acts as a descriptor used by the JS runtime to register the middleware into the Rust core.
Motivation
Right now, all middleware must be implemented in TypeScript, which:
- limits performance in the hottest part of the pipeline,
- makes it harder to implement heavy or low-level logic efficiently,
- prevents community developers from creating proper native extensions,
- and forces the core to keep growing instead of letting features live as external plugins.
With this feature, Kito gains a native plugin architecture, enabling:
- pure Rust execution for critical middleware,
- dynamic loading of third-party native extensions,
- clean integration through npm,
- real extensibility without recompiling the core,
- and maximal performance with Tokio + Hyper + Kito’s runtime
Proposed Solution
1. Third-party crates
Any developer will be able to create a Rust crate that implements the Middleware trait from kitojs.
A macro like #[export] will:
- generate the required
extern "C" functions to expose the middleware,
- handle JSON serialization/deserialization for the middleware options,
- and produce an npm descriptor package at build time through a build script.
2. npm descriptor package
The build script will generate a minimal npm package containing:
- a
package.json with metadata and "kito.native": true,
- the path to the generated .so/.dll/.dylib,
- a small
index.js file exporting a simple descriptor object.
This descriptor is what the TS runtime of Kito consumes.
3. Registration at startup
Whenever someone does:
app.use(cors({ origin: "*" }));
Kito’s JS runtime detects that it’s a native middleware and calls a N-API function in the core:
register_native_middleware(descriptor)
The call provides:
- the route (or global usage),
- middleware options as JSON,
- the path to the cdylib,
- the middleware’s name and version.
4. Dynamic loading
The Rust core uses libloading to:
- load the cdylib dynamically,
- fetch the exported
kito_new_middleware function,
- instantiate the middleware,
- store it inside a global structure like:
DashMap<Route, Vec<MiddlewareEntry>>
Each MiddlewareEntry contains:
- the instantiated middleware,
- the function pointer to handle,
- metadata,
- and information about whether it’s global or route-specific.
5. Pipeline execution
For each request:
- The core gathers global + route-specific middlewares.
- Native middlewares run first.
- If none of them terminate the request, JS middlewares run next.
4. Finally, the JS route handler runs.
Native middlewares receive the usual Context, can modify it, call next, or finish the response early.
Checklist
Summary
Add support for native Rust middlewares that can be loaded dynamically at startup and executed directly inside Kito’s core, before any TypeScript middleware.
The idea is to allow anyone to write high-performance middleware as independent Rust crates. When compiled, these crates automatically generate a small npm package that acts as a descriptor used by the JS runtime to register the middleware into the Rust core.
Motivation
Right now, all middleware must be implemented in TypeScript, which:
With this feature, Kito gains a native plugin architecture, enabling:
Proposed Solution
1. Third-party crates
Any developer will be able to create a Rust crate that implements the
Middlewaretrait from kitojs.A macro like
#[export]will:extern "C"functions to expose the middleware,2. npm descriptor package
The build script will generate a minimal npm package containing:
package.jsonwith metadata and"kito.native": true,index.jsfile exporting a simple descriptor object.This descriptor is what the TS runtime of Kito consumes.
3. Registration at startup
Whenever someone does:
Kito’s JS runtime detects that it’s a native middleware and calls a N-API function in the core:
The call provides:
4. Dynamic loading
The Rust core uses libloading to:
kito_new_middlewarefunction,Each MiddlewareEntry contains:
5. Pipeline execution
For each request:
4. Finally, the JS route handler runs.
Native middlewares receive the usual Context, can modify it, call next, or finish the response early.
Checklist