I've been building a Rust web framework called Toxi, built around Hyper, Tokio, and Tower, and I'm looking for an architectural/code review rather than general project feedback.

The main idea is to provide a batteries-included backend stack while keeping the individual components usable independently.

Architecture

The main application path is:

Application │ ├── Router │ ├── Middleware │ └── Server │ ▼ Hyper accept loop 

The high-level API is:

let mut app = Application::new(Config::load().unwrap()); app.router_mut().get("/", hello); app.run().await; 

Handlers are ordinary async functions:

async fn hello(_req: Request) -> Result<Response> { Ok(Response::text("Hello, Toxi!")) } 

The application abstraction is optional. The router and server can also be used directly:

let mut router = Router::new(); router.get("/", hello); Server::new(router) .listen("127.0.0.1:3000".parse().unwrap()) .await; 

Middleware can be composed around the router:

let router = app.into_router(); Server::new(Logger::new(router)) .listen(addr) .await; 

The server provides configuration for CORS, body limits and HTTP versions, with TLS and HTTP/3 behind feature flags.

The CLI provides development and process-management commands such as:

toxi dev toxi serve toxi serve --bin name --port 3000 toxi pm2 start 

All of these paths eventually use the same Hyper-based server implementation.

One of the things I'd particularly like reviewed is whether the boundary between Application, Router, Middleware and Server is appropriate, or whether some of these abstractions should be combined or separated differently.

Framework structure

The facade crate provides the batteries-included framework, while the individual components are separate crates.

The current stack includes:

  • routing and HTTP server

  • Tower-compatible middleware

  • configuration

  • SQLx-based ORM and migrations

  • authentication and sessions

  • templates

  • WebSockets, SSE and pub/sub

  • background jobs and queues

  • caching

  • storage

  • mail

  • GraphQL

  • OpenAPI

  • testing utilities

  • CLI and project generation

For example:

#[derive(Model)] struct User { id: i64, name: String, email: String, } let users = User::query() .filter_eq("name", "Alice") .fetch_all(&db) .await?; 

The intention is that the facade provides the integrated experience, while users can depend on individual crates when they don't need the entire stack.

The design philosophy is roughly:

own the stack, but don't make the components inseparable.

Architectural questions

There are several areas where I'd particularly like criticism.

Router

The router is currently hand-written and uses compiled regular expressions for route matching.

I'm considering whether this is a reasonable implementation choice or whether a trie-based approach such as matchit would be a better fit.

I'd be interested in feedback based on actual routing workloads, not just benchmark numbers.

Error handling

The framework currently has a central Error enum for framework-level errors:

InternalServerError NotFound BadRequest Unauthorized Forbidden Conflict Validation ... 

This makes response mapping straightforward, but some structured information, particularly validation errors, currently ends up represented as strings.

I'm unsure whether the ergonomics of one framework-level error type justify that tradeoff.

Crate boundaries

The crates are independently versioned rather than being maintained as one Cargo workspace.

That gives the components genuine crate-level independence, but coordinating API changes and releases across multiple repositories is more complicated.

I'd like opinions on whether this is a useful form of modularity or unnecessary operational complexity.

Framework scope

This is probably the largest architectural question.

A framework such as Axum primarily provides the HTTP layer and leaves the rest of the application stack to the ecosystem.

Toxi instead owns a larger set of backend components: ORM, authentication, queues, templates, mail, API tooling, and so on.

I'm interested in whether this approach makes sense in Rust, particularly from an API stability and maintenance perspective, or whether the framework would be better off remaining thinner and composing existing crates.

Current state

Toxi is currently in the 3.x series, with the crates published separately.

There are example applications covering things such as task boards, blogs, short links, TODOs, notifications and pastebins.

The core has tests and benchmarks covering routing, extractors, responses, middleware, database operations, authentication, caching, templates, queues and realtime.

There are also known unfinished areas. GraphQL currently uses a fixed schema rather than providing a fully extensible application schema, and the file-based plugin loader is not finished.

Performance

I've also done a basic local comparison using an Intel Core i5 with 8 GB RAM.

The test used release builds, 125 connections, a 30-second run, GET /json, a "Hello World" response, and 100% successful requests:

Toxi 40,812 req/s p99 8.60 ms Warp 40,681 req/s p99 11.51 ms Poem 40,359 req/s p99 8.44 ms Loco 28,994 req/s p99 10.49 ms Salvo 27,759 req/s p99 33.78 ms Rocket 23,207 req/s p99 36.34 ms 

I'm not presenting this as a framework ranking. The benchmark still needs better methodology around repetitions, warmup, exact software/hardware information and endpoint parity.

What I'd like reviewed

The main things I'd like someone to challenge are:

  • Is the Application → Router → Middleware → Server boundary sound?

  • Is the regex router implementation a reasonable choice?

  • Is the central Error type too broad?

  • Are the crate boundaries and independent versioning sensible?

  • Is owning this much of the backend stack a good architectural decision?

  • Are there API or abstraction decisions that are likely to become difficult to change later?

  • Are there places where I'm unnecessarily reinventing something that the Rust ecosystem already handles better?

I've been making most of these architectural decisions myself for quite a while, so I'm particularly interested in criticism from people who have worked on Rust web frameworks, routers, middleware, Hyper/Tokio services, or async libraries.

Project:

Toxi on GitHub

Examples:

Toxi applications

3 posts - 2 participants

Read full topic