# High-Performance Rust API Architecture: Axact vs Actix-Web Integration with Nginx Reverse Proxy & PostgreSQL Connection Pooling

> Diterbitkan oleh Literally Web ID pada 21 Oct 2026
> Sumber: https://literally.web.id/articles/high-performance-rust-api-architecture-axact-vs-actix-web-integration-with-nginx-reverse-proxy-postgresql-connection-pooling

---

Pernahkah Anda berada di ruang kendali pada pukul dua malam, ketika pager berdering liar dan grafik Latensi P99 mendadak melonjak vertikal seperti roket SpaceX? Saya ingat betul malam itu, ketika stack backend warisan kami yang berbasis interpretasi bahasa dinamis mulai menyerah pada hantaman traffic puluhan ribu request per detik dari sebuah kampanye flash sale. CPU melompat ke angka 100%, memori terkuras habis karena masalah garbage collection, dan pool database PostgreSQL kami langsung kehabisan koneksi akibat thread blocking yang menumpuk. Di saat-saat genting seperti itu, Anda menyadari bahwa arsitektur web konvensional memiliki batas fisik yang kejam. Di dunia infrastruktur modern berskala besar, kita tidak bisa lagi hanya mengandalkan penambahan instance server mentah tanpa membenahi akar permasalahan di level runtime dan pengolahan konkurensi.

Perjalanan keluar dari lubang jarum tersebut menuntut sebuah perombakan total, membawa tim kami masuk ke dalam teritori ekosistem pemrograman sistem yang menjamin performa tanpa kompromi: Rust. Namun, migrasi ke Rust bukanlah sekadar pelarian sintaksis; ini adalah tentang memahami bagaimana sebuah event loop asinkron dan manajemen memori tanpa *garbage collector* bekerja di balik layar. Ketika kita berbicara tentang membangun API berkinerja tinggi, pertarungan klasik sering kali bermuara pada pemilihan antara Axum dan Actix-Web, dipadukan dengan mesin *async runtime* Tokio yang legendaris. Keduanya menawarkan performa luar biasa, tetapi filosofi desain di balik keduanya menciptakan dinamika operasional yang sangat berbeda di lingkungan produksi.

Sebelum kita terjun lebih jauh ke dalam kode, mari kita bedah bagaimana ekosistem ini bekerja tanpa sihir. Di balik setiap HTTP request yang masuk ke server Anda, ada serangkaian proses sistem operasi yang sibuk: mulai dari penanganan file descriptor oleh epoll/kqueue, penjadwalan task non-blocking oleh Tokio reactor, hingga manajemen buffer memori yang presisi. Axum, yang dibangun di atas fondasi Tower dan Hyper, memberikan fleksibilitas modular yang luar biasa lewat konsep *middleware* berbasis Service trait. Di sisi lain, Actix-Web menggunakan pendekatan aktor-ish yang sangat cepat dengan sistem manajemen state internal yang sangat matang. Keduanya sama-sama tangguh, namun keputusan untuk memilih salah satu harus didasarkan pada karakteristik beban kerja sistem Anda—apakah Anda membutuhkan ekosistem middleware yang kaya ala Axum, atau kecepatan mentah tanpa kompromi dari Actix-Web.

> [!IMPORTANT]
> Pemilihan runtime asinkron seperti Tokio memerlukan pemahaman mendalam tentang *cooperative multitasking*. Jangan pernah menyisipkan operasi *blocking* (seperti I/O sinkron atau komputasi berat) di dalam thread utama async tanpa membungkusnya menggunakan `tokio::task::spawn_blocking`, atau Anda akan melihat lonjakan latensi yang merusak seluruh performa API.

Memilih pendekatan yang tepat menuntut kompromi yang realistis. Dalam arsitektur yang kami rancang kali ini, kami memutuskan untuk mengombinasikan Axum dengan SQLx untuk manajemen koneksi database yang aman secara tipe (*type-safe*), serta meletakkan Nginx sebagai *reverse proxy* di garda terdepan untuk menangani terminasi SSL/TLS dan *upstream keepalive*. Mengapa Nginx? Karena membebaskan aplikasi Rust dari beban dekripsi kriptografi dan koneksi TCP *handshake* berulang adalah kunci utama untuk menjaga utilisasi CPU server tetap rendah.

Mari kita siapkan lingkungan kerja kita di atas sistem operasi Ubuntu Server. Pastikan Anda telah menginstal toolchain Rust terbaru melalui rustup, serta server PostgreSQL yang siap menerima koneksi.

```bash
# Memperbarui sistem dan menginstal dependensi esensial
sudo apt update && sudo apt upgrade -y
sudo apt install build-essential curl pkg-config libssl-dev nginx postgresql postgresql-contrib -y

# Menginstal Rust toolchain secara resmi
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
source "$HOME/.cargo/env"
```

Setelah lingkungan siap, kita inisialisasi proyek Cargo baru kita. Struktur proyek ini dirancang agar modular, memisahkan lapisan routing, handler, konfigurasi database, dan lapisan data transfer object (DTO).

```bash
cargo new rust_high_perf_api --bin
cd rust_high_perf_api
```

Buka file `Cargo.toml` dan tambahkan dependensi yang kita butuhkan. Kita akan menggunakan Axum sebagai web framework, Tokio sebagai runtime, SQLx dengan dukungan PostgreSQL dan connection pooling, serta serde untuk serialisasi JSON.

```toml
[package]
name = "rust_high_perf_api"
version = "0.1.0"
edition = "2021"

[dependencies]
axum = "0.7"
tokio = { version = "1.38", features = ["full"] }
sqlx = { version = "0.8", features = ["runtime-tokio-native-tls", "postgres", "chrono", "macros"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
dotenv = "0.15"
chrono = { version = "0.4", features = ["serde"] }
```

> [!TIP]
> Selalu kunci versi dependensi Anda di lingkungan produksi. Fitur `features` pada SQLx harus disesuaikan secara spesifik (misalnya menggunakan `native-tls` atau `rustls-tls`) untuk menghindari konflik dependensi SSL pada saat kompilasi silang (*cross-compilation*).

Sekarang, mari kita bedah baris kode krusial yang kerap menjebak para developer pemula: inisialisasi `PgPool` dari SQLx dan bagaimana Axum berbagi state aplikasi secara aman di antara banyak thread worker menggunakan `Arc` implisit.

```rust
use axum,{
    extract::State,
    http::StatusCode,
    routing::{get, post},
    Json,
    Router,
};
use serde::{Deserialize, Serialize};
use sqlx::{FromRow, PgPool};
use std::net::SocketAddr;
use std::sync::Arc;

#[derive(Clone)]
struct AppState {
    db: PgPool,
}

#[derive(Serialize, Deserialize, FromRow)]
struct User {
    id: i64,
    username: String,
    email: String,
}

#[derive(Deserialize)]
struct CreateUser {
    username: String,
    email: String,
}

#[tokio::main]
async fn main() {
    dotenv::dotenv().ok();
    let database_url = std::env::var("DATABASE_URL")
        .expect("DATABASE_URL harus diatur di environment variable");

    // Mengonfigurasi connection pool dengan batasan ukuran yang sehat
    let db = PgPool::connect_with(
        database_url.parse::<sqlx::postgres::PgConnectOptions>()
            .unwrap()
            .max_connections(50)
    )
    .await
    .expect("Gagal terhubung ke database PostgreSQL");

    let state = AppState { db };

    let app = Router::new()
        .route("/users", get(get_users).post(create_user))
        .with_state(state);

    let addr = SocketAddr::from(([127, 0, 0, 1], 8080));
    println!("Server berjalan di http://{}", addr);
    
    let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

async fn get_users(
    State(state): State<AppState>,
) -> Result<Json<Vec<User>>, StatusCode> {
    let users = sqlx::query_as::<_, User>("SELECT id, username, email FROM users")
        .fetch_all(&state.db)
        .await
        .map_err(|e| {
            eprintln!("Database error: {}", e);
            StatusCode::INTERNAL_SERVER_ERROR
        })?;

    Ok(Json(users))
}

async fn create_user(
    State(state): State<AppState>,
    Json(payload): Json<CreateUser>,
) -> Result<(StatusCode, Json<User>), StatusCode> {
    let user = sqlx::query_as::<_, User>(
        "INSERT INTO users (username, email) VALUES ($1, $2) RETURNING id, username, email",
    )
    .bind(payload.username)
    .bind(payload.email)
    .fetch_one(&state.db)
    .await
    .map_err(|e| {
        eprintln!("Database error: {}", e);
        StatusCode::INTERNAL_SERVER_ERROR
    })?;

    Ok((StatusCode::CREATED, Json(user)))
}
```

> [!WARNING]
> Jangan pernah membuat koneksi database baru di dalam setiap handler HTTP. Hal ini akan menghabiskan port ephemeral sistem operasi dan memicu lonjakan latensi fatal akibat *TCP handshake* berulang ke PostgreSQL.

Agar performa aplikasi Rust kita terlindungi secara maksimal di garda depan, kita membutuhkan konfigurasi Nginx *reverse proxy* yang dioptimalkan untuk menjaga koneksi tetap hidup (*keepalive*) dan melakukan terminasi SSL/TLS secara efisien. Berikut adalah konfigurasi valid yang biasa kami terapkan di lingkungan produksi kelas enterprise.

```nginx
user www-data;
worker_processes auto;
worker_rlimit_nofile 65535;

events {
    worker_connections 10240;
    use epoll;
    multi_accept on;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;

    # Konfigurasi upstream dengan keepalive untuk koneksi persisten ke server Rust
    upstream rust_backend {
        server 127.0.0.1:8080;
        keepalive 64;
    }

    server {
        listen 80;
        server_name api.domain.com;
        return 301 https://$host$request_uri;
    }

    server {
        listen 443 ssl http2;
        server_name api.domain.com;

        ssl_certificate /etc/letsencrypt/live/api.domain.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/api.domain.com/privkey.pem;
        ssl_protocols TLSv1.2 TLSv1.3;
        ssl_ciphers HIGH:!aNULL:!MD5;
        ssl_session_cache shared:SSL:10m;
        ssl_session_timeout 1d;

        client_max_body_size 10M;

        location / {
            proxy_pass http://rust_backend;
            proxy_http_version 1.1;
            proxy_set_header Connection "";
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            
            proxy_buffering on;
            proxy_buffer_size 4k;
            proxy_buffers 8 4k;
        }
    }
}
```

Untuk memastikan biner Rust kita berjalan sebagai layanan sistem yang andal, selalu hidup kembali jika terjadi kegagalan (*crash*), dan dikelola dengan baik oleh kernel Linux, kita wajib membungkusnya dalam konfigurasi systemd. Buat file service di `/etc/systemd/system/rust-api.service`.

```ini
[Unit]
Description=High-Performance Rust Axum API Server
After=network.target postgresql.service

[Service]
Type=simple
User=www-data
WorkingDirectory=/var/www/rust_high_perf_api
ExecStart=/var/www/rust_high_perf_api/target/release/rust_high_perf_api
Restart=always
RestartSec=5
LimitNOFILE=65535
Environment=DATABASE_URL=postgres://postgres:password@localhost/production_db

[Install]
WantedBy=multi-user.target
```

Aktifkan dan jalankan layanan tersebut dengan perintah berikut:

```bash
sudo systemctl daemon-reload
sudo systemctl enable rust-api
sudo systemctl start rust-api
```

Ketika berbicara tentang ketahanan di bawah tekanan, asumsi adalah musuh utama seorang arsitek. Kita perlu menguji nyali arsitektur ini menggunakan skrip benchmark k6 untuk mensimulasikan ribuan pengguna konkuren yang menghantam endpoint API secara bersamaan.

```javascript
import http from 'k6/http';
import { check, sleep } from 'k6';

export const options = {
    stages: [
        { duration: '30s', target: 500 },  // Ramp-up ke 500 virtual users
        { duration: '1m', target: 2000 },  // Beban puncak 2000 virtual users
        { duration: '30s', target: 0 },    // Ramp-down
    ],
    thresholds: {
        http_req_duration: ['p(99)<25'], // P99 latensi harus di bawah 25ms
        http_req_failed: ['rate<0.01'],  // Error rate di bawah 1%
    },
};

export default function () {
    const url = 'https://api.domain.com/users';
    const res = http.get(url);
    
    check(res, {
        'status adalah 200': (r) => r.status === 200,
        'waktu respon < 20ms': (r) => r.timings.duration < 20,
    });
    
    sleep(0.1);
}
```

Jalankan benchmark ini dari mesin terpisah untuk melihat bagaimana kombinasi Nginx, Axum, Tokio, dan SQLx menjaga konsumsi memori tetap stabil di kisaran puluhan megabita saja, jauh meninggalkan bahasa ber-garbage-collector yang sering kali mengalami lonjakan memori tak terkendali (*memory bloat*) di bawah beban tinggi.

Sebagai catatan lapangan penutup, selalu periksa batas file descriptor sistem operasi (`ulimit -n`) pada server Ubuntu Anda. Kegagalan mengatur batas ini sering kali menjadi pembunuh senyap yang membuat server menolak koneksi baru meskipun CPU dan RAM masih sangat longgar. Terapkan checklist rilis ini dengan disiplin, dan Anda akan memiliki benteng infrastruktur API yang siap menghadapi gelombang traffic terbesar sekalipun.