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

Example:
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.

Parameters:
  • 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

Example:
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.

Parameters:
  • path (&str): URL path pattern
  • handler (impl Fn(Request, Response) -> BoxFuture): Async closure
Example:
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.

Parameters:
  • middleware (Arc<impl Fn(Request, Next) -> BoxFuture>): Async closure that can inspect/modify the request, call next(req), and inspect/modify the response
Example:
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.

Parameters:
  • path (&str): URL path for the WebSocket endpoint
  • handler (impl Fn(WebSocket) -> BoxFuture): Async closure that receives a WebSocket connection
Example:
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.

Parameters:
  • addr (&str): Bind address (e.g. "0.0.0.0:3000")

Returns: Ok(()) on graceful shutdown, Err on bind failure

Example:
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.

Parameters:
  • addr (&str): Bind address
  • cert_path (&str): Path to TLS certificate (PEM format)
  • key_path (&str): Path to TLS private key (PEM format)
Example:
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.

Parameters:
  • name (&str): Parameter name (without the colon)

Returns: Some(value) if the parameter exists, None otherwise

Example:
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".

Parameters:
  • 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).

Parameters:
  • name (&str): Header name (e.g. "content-type", "authorization")

Returns: Some(value) if the header exists, None otherwise

Example:
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

Example:
#[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.

Parameters:
  • code (StatusCode): HTTP status code

Returns: Mutable reference for chaining

Example:
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.

Parameters:
  • name (&str): Header name
  • value (&str): Header value
Example:
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.

Parameters:
  • body (&str): Response body text
Example:
res.text("hello world")

html(body: &str) -> Self

Set the response body as HTML with content-type: text/html; charset=utf-8.

Parameters:
  • body (&str): HTML content
Example:
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.

Parameters:
  • body (&str): Pre-serialized JSON string
Example:
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).

Parameters:
  • body (&[u8]): Raw bytes
  • content_type (&str): MIME type (e.g. "image/png")

redirect(to: &str) -> Self

Create a 302 Found redirect response with a Location header.

Parameters:
  • to (&str): URL to redirect to
Example:
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.

Parameters:
  • 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.

Parameters:
  • method (&Method): HTTP method
  • path (&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.

Parameters:
  • 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.

Parameters:
  • burst (u32): Maximum burst size
  • rps (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.

Parameters:
  • 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.

Parameters:
  • 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://.

Parameters:
  • config (DbConfig): Connection config (url, max_connections, min_connections)

Returns: A database connection pool

Example:
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

Example:
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.

Parameters:
  • 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.

Parameters:
  • pk (impl Serialize): Primary key value
  • sets (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

Example:
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

Example:
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.

Parameters:
  • text (&str): Message text

ws.send_binary(data: &[u8]) -> Result<()>

Send a binary message (e.g. image data, protobuf).

Parameters:
  • data (&[u8]): Binary message data

ws.broadcast(text: &str) -> Result<()>

Broadcast a message to ALL connected WebSocket clients on the same endpoint.

Parameters:
  • 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.

Parameters:
  • class_string (&str): Space-separated utility classes (e.g. "flex p-4 text-red-500")

Returns: A CSS string with only the used classes

Example:
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.

Parameters:
  • dir (&str): Directory path to scan (e.g. "./src")

Returns: A tree-shaken CSS string with only the classes used in the scanned files

Example:
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.

Parameters:
  • 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.

Parameters:
  • router (&mut Router): The router to register routes in
  • pages_dir (&Path): Path to the pages directory (e.g. "src/pages")

Returns: Number of pages registered

Example:
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.

Parameters:
  • file (&Path): Path to the .kng file
  • req_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.

Parameters:
  • file (&KungfuFile): Parsed .kng file (code + static_html + route_path)
  • ctx (&SsrContext): SSR context
  • body (&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`.

Parameters:
  • 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.

Parameters:
  • name (string): Project name (becomes the directory name)
  • --lang (string): Language: rust (default), javascript, typescript, python, go, etc.
Example:
unique new myapp --lang rust
cd myapp
cargo run

unique 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.

Parameters:
  • --watch (flag): Enable file watching and hot reload
  • --port (number): Port to listen on (default: 3000)
Example:
unique start --watch --port 8080

unique build [--release] [--features <features>]

Build the project for production. Equivalent to cargo build --release but with the right features enabled by default.

Parameters:
  • --release (flag): Build in release mode with optimizations (default)
  • --features (string): Comma-separated features: io_uring, simd, ffi
Example:
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.

Parameters:
  • --generate (flag): Generate migration SQL from model structs
  • --apply (flag): Apply pending migrations to the database
Example:
unique migrate --generate
unique migrate --apply

unique generate admin <Model>

Generate an admin CRUD dashboard for a model. Creates routes for list, create, edit, delete with a Bootstrap-based UI.

Parameters:
  • Model (string): Model struct name (e.g. User, Post, Todo)
Example:
unique generate admin User

unique deploy --target <docker|systemd>

Generate deployment configuration files. For Docker: Dockerfile + docker-compose.yml. For systemd: .service file.

Parameters:
  • --target (string): Deployment target: docker or systemd
Example:
unique deploy --target docker
unique deploy --target systemd