Quick Start

Get up and running with Unique.js in 5 minutes.

1. Install

Unique.js requires Rust 1.96+ for the HTTP engine. Install it from rustup.rs if you have not already. Then create a new project:

# Install Rust (one-time setup)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# Create a new Unique.js project
cargo new myapp
cd myapp

Add Unique.js to your Cargo.toml:

[dependencies]
unique = "1"
unique-core = "1"
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"

2. Your First App

Open src/main.rs and replace the contents with:

use unique::Unique;

#[tokio::main]
async fn main() {
    Unique::new()
        .handle_get("/", |_req, res| res.text("Hello, World!"))
        .handle_get("/api/health", |_req, res| {
            res.json(r#"{"status":"ok"}"#)
        })
        .run("0.0.0.0:3000")
        .await
        .unwrap();
}

This creates a Unique.js app with two routes: a homepage that returns plain text, and a health check endpoint that returns JSON. Security headers, CORS, rate limiting, and logging are all on by default.

3. Run It

cargo run

Open http://localhost:3000 in your browser. You should see "Hello, World!". Visit/api/health for the JSON endpoint.

Try the auto-generated API docs at/docs — you will see a Swagger UI listing both routes. No annotations needed.

4. Add Routes

Add path parameters, wildcards, and POST handlers:

Unique::new()
    // Path parameter: /users/42
    .handle_get("/users/:id", |req, res| {
        let id = req.param("id").unwrap_or("0");
        res.text(format!("User {}", id))
    })
    // Wildcard: /assets/css/app.css
    .handle_get("/assets/*path", |req, res| {
        let path = req.param("path").unwrap_or("");
        res.text(format!("File: {}", path))
    })
    // POST with JSON body
    .handle_post("/api/echo", |req, res| {
        // Echo the request body back
        res.header("content-type", "application/json")
            .text(&req.body)
    })
    .run("0.0.0.0:3000").await.unwrap();

5. Add a Database

Add the ORM to your Cargo.toml:

unique-orm = { version = "1", features = ["sqlite"] }
unique-macros = "1"

Define a model and CRUD routes:

use unique_orm::{Db, DbConfig};
use unique_macros::Model;
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize, Model)]
#[table(name = "todos")]
struct Todo {
    #[field(primary, auto_increment)]
    id: i64,
    title: String,
    #[field(default = "false")]
    done: bool,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let db = Db::connect(DbConfig {
        url: "sqlite://todos.db".into(),
        max_connections: 5,
        min_connections: 1,
    }).await?;

    // Auto-create the table
    db.migrate(&[Todo::create_table_sql()]).await?;

    Unique::new()
        .handle_get("/api/todos", move |_req, res| {
            let db = db.clone();
            Box::pin(async move {
                let todos = Todo::all(&db).await.unwrap_or_default();
                res.json(serde_json::to_string(&todos).unwrap())
            })
        })
        .run("0.0.0.0:3000").await?;
    Ok(())
}

6. Deploy

Build a production binary:

cargo build --release
# Binary is at target/release/myapp

Or generate Docker + systemd configs:

# Generate Dockerfile + docker-compose.yml
unique deploy --target docker

# Generate systemd service file
unique deploy --target systemd

For production, put Unique.js behind a reverse proxy (nginx or Caddy) for TLS termination. See the FAQ for deployment details.

Next Steps

  • Read the full 50-chapter tutorial for deep dives into every feature
  • Browse the API Reference for complete method documentation
  • Copy a ready-to-run example for WebSocket, JWT auth, file upload, and more
  • Check the FAQ if you get stuck