Unique.js API Reference
Complete reference for every class, method, and type in the Unique.js framework. All APIs are available in every supported language — the Rust core exposes a C ABI that each language binding wraps idiomatically.
Unique (App)
The main application class. Create a new app, register routes, add middleware, and start the server.
Unique::new() -> Unique
Create a new Unique.js application with default middleware (security headers, CORS, rate limiter, logger) pre-installed.
Returns: A new Unique app instance
let app = Unique::new();handle_get(path, handler) -> &mut Self
Register a GET route handler. The path supports static segments, :params, and *wildcards. The handler is a closure that receives a Request and Response.
path(&str): URL path pattern (e.g. "/", "/users/:id", "/assets/*path")handler(impl Fn(Request, Response) -> BoxFuture): Async closure that handles the request
Returns: Mutuable reference to self for chaining
app.handle_get("/hello", |_req, res| {
res.text("world")
});handle_post(path, handler) -> &mut Self
Register a POST route handler. Same path syntax as handle_get.
path(&str): URL path patternhandler(impl Fn(Request, Response) -> BoxFuture): Async closure
app.handle_post("/api/users", |req, res| {
let body: User = serde_json::from_str(&req.body)?;
// ... create user
res.status(StatusCode::Created).json(serde_json::to_string(&user)?)
});handle_put(path, handler) -> &mut Self
Register a PUT route handler for full-resource updates.
handle_delete(path, handler) -> &mut Self
Register a DELETE route handler.
handle_patch(path, handler) -> &mut Self
Register a PATCH route handler for partial updates.
use_middleware(middleware) -> &mut Self
Add a custom middleware to the onion-model pipeline. Middleware runs in registration order — the first registered wraps all subsequent ones.
middleware(Arc<impl Fn(Request, Next) -> BoxFuture>): Async closure that can inspect/modify the request, call next(req), and inspect/modify the response
app.use_middleware(Arc::new(|req, next| {
Box::pin(async move {
let start = std::time::Instant::now();
let mut resp = next(req).await;
let elapsed = start.elapsed();
resp.set_header("x-response-time", format!("{:?}", elapsed));
resp
})
}));ws(path, handler) -> &mut Self
Register a WebSocket handler. The connection is upgraded from HTTP automatically when the client sends an Upgrade: websocket header.
path(&str): URL path for the WebSocket endpointhandler(impl Fn(WebSocket) -> BoxFuture): Async closure that receives a WebSocket connection
app.ws("/chat", |mut ws| {
Box::pin(async move {
ws.send_text("Welcome!").await;
while let Some(msg) = ws.recv().await {
ws.send_text(format!("echo: {}", msg.to_text()?)).await;
}
})
});run(addr) -> Result<()>
Start the HTTP server on the given address. This call blocks until the server shuts down. Enables io_uring and SIMD JSON automatically if the features are compiled in.
addr(&str): Bind address (e.g. "0.0.0.0:3000")
Returns: Ok(()) on graceful shutdown, Err on bind failure
app.run("0.0.0.0:3000").await?;run_tls(addr, cert_path, key_path) -> Result<()>
Start the HTTPS server with TLS via rustls. Automatically enables HTTP/2 and HTTP/3.
addr(&str): Bind addresscert_path(&str): Path to TLS certificate (PEM format)key_path(&str): Path to TLS private key (PEM format)
app.run_tls("0.0.0.0:443", "./cert.pem", "./key.pem").await?;Request
The HTTP request object. Passed to every route handler. Provides access to method, path, headers, query parameters, path parameters, and body.
method() -> &Method
Get the HTTP method (GET, POST, PUT, DELETE, PATCH).
Returns: A reference to the Method enum
path() -> &str
Get the request path (e.g. "/users/42").
Returns: The URL path as a string slice
param(name: &str) -> Option<&str>
Get a path parameter extracted by the trie router. For /users/:id, param("id") returns the value from the URL.
name(&str): Parameter name (without the colon)
Returns: Some(value) if the parameter exists, None otherwise
app.handle_get("/users/:id", |req, res| {
let id = req.param("id").unwrap_or("0");
res.text(format!("User {}", id))
});query(name: &str) -> Option<&str>
Get a query string parameter. For /search?q=rust&limit=10, query("q") returns "rust".
name(&str): Query parameter name
Returns: Some(value) if the parameter exists, None otherwise
header(name: &str) -> Option<&str>
Get a request header by name (case-insensitive).
name(&str): Header name (e.g. "content-type", "authorization")
Returns: Some(value) if the header exists, None otherwise
let auth = req.header("authorization").unwrap_or("");body() -> &str
Get the request body as a string. For binary data, use body_bytes() instead.
Returns: The request body as a string slice
body_bytes() -> &[u8]
Get the raw request body as a byte slice. Use this for binary data (file uploads, images).
Returns: The request body as a byte slice
json<T: DeserializeOwned>() -> Result<T>
Parse the request body as JSON and deserialize into the given type. Returns an error if the body is not valid JSON or does not match the type.
Returns: Ok(T) if parsing succeeds, Err on invalid JSON
#[derive(Deserialize)]
struct CreateUser { name: String, email: String }
app.handle_post("/api/users", |req, res| {
let user: CreateUser = req.json()?;
// ... create user
});Response
The HTTP response object. Build and return from every route handler. Supports text, JSON, HTML, raw bytes, custom headers, and status codes.
Response::new() -> Response
Create a new empty response with status 200 OK.
Returns: A new Response instance
status(code: StatusCode) -> &mut Self
Set the HTTP status code. Common values: 200, 201, 204, 400, 401, 403, 404, 500.
code(StatusCode): HTTP status code
Returns: Mutable reference for chaining
Response::new().status(StatusCode::Created).json(body)header(name: &str, value: &str) -> &mut Self
Set a response header. Common headers: content-type, set-cookie, cache-control, location.
name(&str): Header namevalue(&str): Header value
Response::new().header("location", "/users/42").status(StatusCode::Redirect)text(body: &str) -> Self
Set the response body as plain text with content-type: text/plain.
body(&str): Response body text
res.text("hello world")html(body: &str) -> Self
Set the response body as HTML with content-type: text/html; charset=utf-8.
body(&str): HTML content
res.html("<h1>Welcome</h1><p>Hello!</p>")json(body: &str) -> Self
Set the response body as JSON with content-type: application/json. The body must already be a JSON string — use serde_json::to_string to serialize.
body(&str): Pre-serialized JSON string
res.json(serde_json::to_string(&user)?)bytes(body: &[u8], content_type: &str) -> Self
Set the response body as raw bytes with a custom content type. Use for binary data (images, files).
body(&[u8]): Raw bytescontent_type(&str): MIME type (e.g. "image/png")
redirect(to: &str) -> Self
Create a 302 Found redirect response with a Location header.
to(&str): URL to redirect to
res.redirect("/login")Router
The trie-based URL router. O(path depth) lookup. Supports static paths, :params, *wildcards, and automatic 405 Method Not Allowed.
Router::new() -> Router
Create a new empty router.
add(meta: RouteMeta, handler: Handler) -> Result<()>
Add a route with full metadata. Used internally by handle_get, handle_post, etc.
meta(RouteMeta): Route metadata (path, method, summary, tags)handler(Handler): Async handler closure
resolve(method: &Method, path: &str) -> RouteResolution
Resolve a URL to a route. Returns Found (with handler + params), NotFound, or MethodNotAllowed.
method(&Method): HTTP methodpath(&str): URL path
Returns: RouteResolution::Found { handler, params }, NotFound, or MethodNotAllowed
routes() -> &[RouteMeta]
Get all registered routes. Used by the OpenAPI generator to produce API docs.
Returns: Slice of all route metadata
Middleware
Onion-model middleware pipeline. Each middleware wraps the next. Short-circuit by returning a response without calling next().
security_headers() -> Middleware
Built-in: adds HSTS, CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy headers. On by default.
cors(config: CorsConfig) -> Middleware
Built-in: CORS with preflight handling. Configurable origins, methods, headers, max-age.
config(CorsConfig): CORS configuration (origins, methods, headers)
rate_limiter(burst: u32, rps: u32) -> Middleware
Built-in: leaky-bucket rate limiting per IP + path. Default: 200 burst, 100 rps. Returns 429 Too Many Requests when exceeded.
burst(u32): Maximum burst sizerps(u32): Steady-state requests per second
logger() -> Middleware
Built-in: structured request logging. Logs method, path, status, duration, and client IP.
serve_static(dir: &str) -> Middleware
Opt-in: serve static files from a directory. Automatically sets content-type based on file extension.
dir(&str): Directory path to serve files from
gzip() -> Middleware
Opt-in: gzip compression for responses larger than 1KB. Checks Accept-Encoding header.
auth_jwt(secret: &[u8]) -> Middleware
Opt-in: JWT authentication. Validates Bearer tokens and attaches the decoded claims to the request.
secret(&[u8]): HMAC secret for HS256, or public key for RS256/ES256
ORM (Database)
Built-in ORM with SQLite, PostgreSQL, and MySQL support. CRUD, JOINs, transactions, migrations, and Argon2id password hashing.
Db::connect(config: DbConfig) -> Result<Db>
Connect to a database. The database type is determined by the URL scheme: sqlite://, postgres://, or mysql://.
config(DbConfig): Connection config (url, max_connections, min_connections)
Returns: A database connection pool
let db = Db::connect(DbConfig {
url: "sqlite://app.db".into(),
max_connections: 5,
min_connections: 1,
}).await?;model.insert(&db) -> Result<Self>
Insert a new row. Auto-increment IDs are set automatically. Fields marked #[field(sensitive)] are Argon2id-hashed.
Returns: The inserted model with the generated ID
let user = User { id: 0, email: "a@b.c".into(), password: "secret".into() };
let inserted = user.insert(&db).await?;Model::find_by_pk(pk, &db) -> Result<Self>
Find a single row by its primary key.
pk(impl Serialize): Primary key value
Returns: The found model, or Error::NotFound
Model::all(&db) -> Result<Vec<Self>>
Get all rows from the table. Use Query for filtering, ordering, and pagination.
Returns: A vector of all models
Model::update_by_pk(&db, pk, sets) -> Result<u64>
Update a row by primary key with a list of (column, value) pairs.
pk(impl Serialize): Primary key valuesets(Vec<(&str, Value)>): Column-value pairs to update
Returns: Number of affected rows
Model::delete_by_pk(pk, &db) -> Result<u64>
Delete a row by primary key.
Returns: Number of deleted rows (0 or 1)
Query::<T>::select(table) -> Query<T>
Start a type-safe query builder. Chain .where_eq(), .where_in(), .order_by(), .limit(), .inner_join(), etc.
Returns: A query builder for the given model type
let users: Vec<User> = Query::<User>::select("users")
.where_eq("email", json!("a@b.c"))
.order_by("id", false)
.limit(10)
.all(&db).await?;db.transaction(|tx| async { ... }) -> Result<T>
Run a closure inside a database transaction. If the closure returns Err, the transaction is rolled back. If Ok, it is committed.
Returns: The closure return value on success, or the error on rollback
db.transaction(|tx| async {
Account::deduct(&tx, from_id, amount).await?;
Account::add(&tx, to_id, amount).await?;
Ok(())
}).await?;WebSocket
RFC 6455 WebSocket support. Full-duplex communication over a single TCP connection.
ws.recv() -> Option<WebSocketMessage>
Receive the next message. Returns None when the connection is closed. Blocks until a message arrives.
Returns: Some(WebSocketMessage) or None on close
ws.send_text(text: &str) -> Result<()>
Send a text message to the client.
text(&str): Message text
ws.send_binary(data: &[u8]) -> Result<()>
Send a binary message (e.g. image data, protobuf).
data(&[u8]): Binary message data
ws.broadcast(text: &str) -> Result<()>
Broadcast a message to ALL connected WebSocket clients on the same endpoint.
text(&str): Message to broadcast
ws.close() -> Result<()>
Close the WebSocket connection gracefully. Sends a close frame and waits for the client to acknowledge.
CSS Engine
Tailwind-like utility CSS engine. Scans .kng and .html files for class names and generates minimal CSS.
compile_classes(class_string: &str) -> Result<String>
Compile a space-separated string of utility classes into CSS rules. Only generates CSS for classes it recognizes — unknown classes are silently skipped.
class_string(&str): Space-separated utility classes (e.g. "flex p-4 text-red-500")
Returns: A CSS string with only the used classes
let css = compile_classes("flex p-4 text-red-500 hover:bg-blue-200")?;
// → .flex { display: flex; }
// .p-4 { padding: 1rem; }
// .text-red-500 { color: #ef4444; }
// .hover\:bg-blue-200:hover { background-color: #bfdbfe; }compile_directory(dir: &str) -> Result<String>
Recursively scan a directory for .kng, .html, .js, .ts files, extract all class= attributes, and compile the combined CSS. This is the main entry point for production builds.
dir(&str): Directory path to scan (e.g. "./src")
Returns: A tree-shaken CSS string with only the classes used in the scanned files
let css = compile_directory("./src")?;
std::fs::write("./static/app.css", css)?;compile_file(path: &str) -> Result<String>
Scan a single file for class= attributes and compile the CSS. Useful for incremental builds.
path(&str): Path to a .kng, .html, .js, or .ts file
Returns: CSS string for the classes used in that file
Frontend (SSR + .kng)
Server-side rendering with .kng files. Each .kng file exports data() and template() functions.
register_pages(router: &mut Router, pages_dir: &Path) -> Result<usize>
Walk a directory of .kng files and register each as a GET route. index.kng → /, about.kng → /about, users/[id].kng → /users/:id, assets/[...path].kng → /assets/*path.
router(&mut Router): The router to register routes inpages_dir(&Path): Path to the pages directory (e.g. "src/pages")
Returns: Number of pages registered
use unique_frontend::file_routing::register_pages;
use std::path::Path;
register_pages(app.router_mut(), Path::new("src/pages"))?;render_kungfu_file(file: &Path, req_json: &str, ctx: &SsrContext) -> Result<String>
Render a .kng file to HTML by calling its data() function with the request, then its template() function with the data. Uses a Node.js subprocess for JS/TS execution.
file(&Path): Path to the .kng filereq_json(&str): JSON string of the request object passed to data()ctx(&SsrContext): SSR context (url, headers, inject_livereload)
Returns: Rendered HTML string
render_page(file: &KungfuFile, ctx: &SsrContext, body: &str, data: &Value) -> String
Wrap rendered HTML in a full HTML page with CSS, hydration script, and optional livereload script. Called internally by render_kungfu_file.
file(&KungfuFile): Parsed .kng file (code + static_html + route_path)ctx(&SsrContext): SSR contextbody(&str): Inner HTML from template()data(&Value): JSON data from data(), injected for hydration
Returns: Complete HTML page string
DevMode::new(pages_dir: &Path) -> DevMode
Create a dev mode watcher that monitors the pages directory for changes and triggers live reload via WebSocket. Used by `unique start --watch`.
pages_dir(&Path): Directory to watch for .kng file changes
Returns: A DevMode instance
CLI Commands
The `unique` command-line tool. Install with `cargo install unique-cli`.
unique new <name> [--lang <language>]
Scaffold a new Unique.js project. Creates a directory with the right structure, Cargo.toml (or package.json / pyproject.toml), and a hello world example.
name(string): Project name (becomes the directory name)--lang(string): Language: rust (default), javascript, typescript, python, go, etc.
unique new myapp --lang rust
cd myapp
cargo rununique start [--watch] [--port <port>]
Start the development server. With --watch, enables hot reload: changes to .rs or .kng files automatically recompile and refresh the browser.
--watch(flag): Enable file watching and hot reload--port(number): Port to listen on (default: 3000)
unique start --watch --port 8080unique build [--release] [--features <features>]
Build the project for production. Equivalent to cargo build --release but with the right features enabled by default.
--release(flag): Build in release mode with optimizations (default)--features(string): Comma-separated features: io_uring, simd, ffi
unique build --features "io_uring simd"unique migrate [--generate] [--apply]
Generate database migrations from #[derive(Model)] structs, or apply pending migrations. Creates SQL files in migrations/ directory.
--generate(flag): Generate migration SQL from model structs--apply(flag): Apply pending migrations to the database
unique migrate --generate
unique migrate --applyunique generate admin <Model>
Generate an admin CRUD dashboard for a model. Creates routes for list, create, edit, delete with a Bootstrap-based UI.
Model(string): Model struct name (e.g. User, Post, Todo)
unique generate admin Userunique deploy --target <docker|systemd>
Generate deployment configuration files. For Docker: Dockerfile + docker-compose.yml. For systemd: .service file.
--target(string): Deployment target: docker or systemd
unique deploy --target docker
unique deploy --target systemd