ullun-api
Express.js-inspired web framework for Rust — simple, fast, batteries-included
⚠️ Production Note: Requires proper deployment (TLS, auth, rate limiting). See Production Readiness below.
ullun (уллун; Yakutian/Sakha) = "large/great" — powerful yet simple
Why ullun-api?
The Problem: Actix-web is fast but complex. Rocket is ergonomic but heavy. Axum ties you to Tokio internals.
The Solution: An Express.js-style framework for Rust — single-file APIs, minimal boilerplate, maximum performance.
use *;
async
Features
- 🚀 Fast: Built on hyper + tokio, measured numbers in PERFORMANCE.md
- 🎯 Simple: Express.js-style routing, zero boilerplate
- 🔒 Type-safe: Path parameters validated at compile-time
- 🔗 Middleware: Chain middleware like Express.js
- 📦 Batteries-included: JSON serialization, error handling, CORS built-in
- 🎨 Ergonomic: Result<T, E> maps to HTTP status codes automatically
- 🍪 Cookie Support: Parse and set HTTP cookies with ease
- 📁 Static Files: Serve static files from directories
- 🎯 Route Groups: Organize routes with common prefixes
⚠️ Production Readiness
ullun-api is production-ready for small-to-medium APIs (<100k users), but requires proper deployment practices:
✅ Framework Provides
- Memory safety (zero
unsafecode) - Type-safe routing
- Graceful error handling
- CORS & security headers middleware
⚠️ User Must Implement
- Authentication/Authorization - Use middleware (see
examples/todo_api.rs) - Rate Limiting - Use reverse proxy (nginx/Caddy) or custom middleware
- TLS/HTTPS - Use reverse proxy (never expose raw HTTP in production)
- Input Validation - Validate business logic (framework validates types)
- SQL Injection Prevention - Use parameterized queries (sqlx, diesel)
- XSS Protection - Sanitize HTML output (if serving HTML)
📋 Pre-Production Checklist
- ✅ Implement authentication middleware
- ✅ Use reverse proxy (nginx/Caddy) for TLS termination
- ✅ Add monitoring/observability (Prometheus, Sentry)
- ✅ Follow SECURITY.md checklist
- ✅ Load test your specific workload (
wrk,hey,ab) - ✅ Set up error logging and alerting
See SECURITY.md for comprehensive security guidelines.
Installation
[]
= "0.2"
= { = "1", = ["full"] }
= { = "1", = ["derive"] }
= "1"
Quick Start
Hello World
use *;
async
Path Parameters
new
.get_with_params
.run
.await
.unwrap;
JSON Request Body
async
new
.post
.run
.await
.unwrap;
Middleware
use ;
new
.middleware
.middleware
.get
.run
.await
.unwrap;
Route Groups (v0.2.0)
new
.group
.group
.run
.await?;
Cookie Handling (v0.2.0)
use ;
// Reading cookies
async
// Setting cookies
async
Static Files (v0.2.0)
new
.serve_static // Serve from 'public' directory
.serve_static // Serve from 'dist' at /assets
.run
.await?;
Body Size Limits (v0.2.0)
new
.max_body_size // 5 MB limit
// or
.max_body_size // Unlimited (not recommended)
.run
.await?;
API Reference
App
let app = new
.get // GET route
.post // POST route
.put // PUT route
.delete // DELETE route
.get_with_params // GET with path params extractor
.middleware // Add middleware
.run; // Start server (graceful shutdown on CTRL+C)
Need the bound port, or no signal handler (tests, benchmarks, embedding)? Bind yourself and use serve:
let listener = bind.await?;
println!;
app.serve.await?;
Middleware runs for every request, including unmatched routes (404) and CORS preflight (OPTIONS).
Handlers
Handlers can be:
- Async functions taking
Request→Result<Response> - Closures with path params:
Params→Result<Response>
// Full request access
async
// Path params only
.get_with_params
Error Handling
Errors automatically map to HTTP status codes:
return Err; // 400
return Err; // 401
return Err; // 403
return Err; // 404
return Err; // 500
return Err; // Custom
Middleware
Create custom middleware:
async
new
.middleware
// ...
Examples
See the examples/ directory. Basics:
hello_world.rs- Minimal examplefull_api.rs- CRUD API with middlewaretodo_api.rs- TODO API with token authenticationcomplete_api.rs- In-memory store, sessions, groupsfeatures_0_2_0.rs- Route groups, cookies, static files
Scenarios (each is a self-contained deployment shape):
| Scenario | Example | Shows |
|---|---|---|
| Containerised microservice | microservice.rs |
/healthz + /readyz probes, request ids, Prometheus metrics, PORT config, graceful shutdown |
| Public API for third parties | public_api.rs |
API keys, per-key rate limiting with retry-after, CORS preflight, versioned group, pagination |
| SPA + backend in one binary | spa_backend.rs |
Static frontend at /, JSON API under /api, security headers |
| Webhook receiver | webhook_receiver.rs |
Constant-time token check, body limit, idempotency, 202 + background worker |
| Server-rendered HTML | html_forms.rs |
Form posts, cookie sessions, flash messages, Post/Redirect/Get, HTML escaping |
Run examples:
PORT=8080
Test the API:
# GET /
# GET with path params
# POST with JSON
Comparison
| Framework | Learning Curve | Middleware | Style |
|---|---|---|---|
| Actix-web | High | Complex | Actor/extractor based |
| Rocket | Medium | Fairings | Macro/attribute based |
| Axum | Medium | Tower layers | Extractor based |
| ullun-api | Low | Express-style (req, next) |
Closures and async fn(Request) |
This table is qualitative. We do not publish cross-framework speed claims; see PERFORMANCE.md for what is measured.
Testing & Benchmarks
# Run tests
# Run benchmarks (micro + end-to-end HTTP)
# Check coverage
See TESTING.md and PERFORMANCE.md for details.
Security
✅ No unsafe code
✅ Memory safe (Rust guarantees)
✅ No unwrap() in production paths
✅ Header validation (prevents malformed headers)
✅ Graceful error handling (no panics)
⚠️ User Responsibilities:
- Authentication/authorization implementation
- Rate limiting (reverse proxy or middleware)
- Input validation (business logic)
- SQL injection prevention (parameterized queries)
- XSS protection (if serving HTML)
See SECURITY.md for comprehensive guidelines and best practices.
Roadmap
Done
- Route groups, cookies, static file serving, body size limits (0.2)
- Scenario examples, end-to-end HTTP benchmarks
Next
- Group-level middleware (
RouteGroup::middlewareis accepted but ignored today) - OpenAPI/Swagger generation
- WebSocket support
- Form data parsing helpers
- Rate limiting, compression and JWT helpers (patterns in
examples/public_api.rs) - Template engine integration
CI/CD
GitHub Actions (.github/workflows/ci.yml):
- ✅ Lint:
rustfmt,clippy -D warnings, rustdoc,cargo auditon every PR and onmain - ✅ Test: Linux, macOS, Windows
- ✅ Build: release build of all examples, benchmarks compile
- ✅ Commits: PR title and commits must be Conventional Commits
- ✅ Release: on
main, only after lint, tests and build passed, release-plz derives the next version from the commits, maintains a release PR with the changelog, and on merge tags, creates the GitHub release and publishes to crates.io
See CONTRIBUTING.md for the details and required secrets.
Contributing
Contributions welcome! Commits follow Conventional Commits; see CONTRIBUTING.md for guidelines.
License
Licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Documentation
- Architecture Guide - Technical deep-dive
- Deployment Guide - Production deployment
- Testing Guide - Testing strategies
- Performance Guide - Optimization tips
- Security Policy - Security best practices
Acknowledgments
- Built on hyper and tokio
- Routing by matchit
- Inspired by Express.js
Made with ❤️ by yedoma-labs