๐ฆ Setting Up Rust Web Applications on Alpine Linux: Modern Web Development
Letโs master Rust web development on Alpine Linux! ๐ This comprehensive tutorial shows you how to install, configure, and deploy modern Rust web applications with high performance and memory safety. Perfect for backend developers, system programmers, and teams building scalable web services! ๐
๐ค What are Rust Web Applications?
Rust web applications are high-performance, memory-safe web services built using the Rust programming language, leveraging frameworks like Actix Web, Rocket, or Axum to create fast, concurrent, and reliable backend systems with zero-cost abstractions!
Rust web applications are like:
- ๐๏ธ High-performance race car thatโs both incredibly fast and completely safe
- ๐ก๏ธ Fortress that protects against memory vulnerabilities while delivering lightning speed
- โก Lightning bolt that strikes with precision, power, and unwavering reliability
๐ฏ What You Need
Before we start, you need:
- โ Alpine Linux system with adequate resources (2GB+ RAM recommended)
- โ Understanding of web development concepts and HTTP protocols
- โ Basic knowledge of Rust programming language fundamentals
- โ Familiarity with command-line tools and package management
๐ Step 1: Install Rust Development Environment
Install Rust Toolchain and Development Tools
Letโs set up the complete Rust development environment! ๐
What weโre doing: Installing Rust compiler, Cargo package manager, and essential development tools for web application development.
# Update package list
apk update
# Install system dependencies for Rust
apk add curl gcc musl-dev openssl-dev pkgconfig
apk add git make cmake
# Install Rust using rustup (recommended method)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
# Source the Rust environment
source ~/.cargo/env
# Add Rust to PATH permanently
echo 'source ~/.cargo/env' >> ~/.bashrc
# Install additional Rust components
rustup component add rustfmt clippy
rustup target add x86_64-unknown-linux-musl
# Install cargo extensions for web development
cargo install cargo-watch cargo-edit cargo-audit
cargo install diesel_cli --no-default-features --features postgres
cargo install sqlx-cli
# Install development tools
apk add postgresql postgresql-dev
apk add redis
apk add nginx
# Verify Rust installation
rustc --version
cargo --version
rustfmt --version
clippy-driver --version
echo "Rust development environment installed! ๐ฆ"
What this does: ๐ Installs complete Rust toolchain with web development tools and database support.
Example output:
rustc 1.75.0 (82e1608df 2023-12-21)
cargo 1.75.0 (1d8b05cdd 2023-11-20)
rustfmt 1.6.0-stable (82e1608df 2023-12-21)
clippy 0.1.75 (82e1608df 2023-12-21)
Rust development environment installed! ๐ฆ
What this means: Rust development environment is ready for web application development! โ
Configure Rust for Alpine Linux Optimization
Letโs optimize Rust for Alpine Linux performance! ๐ฏ
What weโre doing: Configuring Rust compiler settings and creating Alpine-optimized build configurations.
# Create Cargo configuration for Alpine optimization
mkdir -p ~/.cargo
cat > ~/.cargo/config.toml << 'EOF'
[target.x86_64-unknown-linux-musl]
linker = "rust-lld"
[build]
target = "x86_64-unknown-linux-musl"
rustflags = ["-C", "target-cpu=native", "-C", "opt-level=3"]
[profile.release]
opt-level = 3
lto = true
codegen-units = 1
panic = "abort"
strip = true
[profile.dev]
opt-level = 1
debug = true
incremental = true
[alias]
build-release = "build --release --target x86_64-unknown-linux-musl"
run-release = "run --release --target x86_64-unknown-linux-musl"
EOF
# Create web development workspace
mkdir -p ~/RustWeb/{projects,templates,configs,scripts}
# Create project template structure
mkdir -p ~/RustWeb/templates/web-api/{src,migrations,static,templates}
echo "Rust Alpine optimization configured! โก"
What this does: ๐ Optimizes Rust builds for Alpine Linux with performance tuning and static linking.
๐ Step 2: Create Web Application Framework Templates
Set Up Actix Web Application Template
Letโs create a high-performance web API template! ๐
What weโre doing: Setting up Actix Web framework with authentication, database integration, and modern web features.
# Navigate to projects directory
cd ~/RustWeb/projects
# Create new Actix Web project
cargo new --bin rust-web-api
cd rust-web-api
# Configure Cargo.toml with web dependencies
cat > Cargo.toml << 'EOF'
[package]
name = "rust-web-api"
version = "0.1.0"
edition = "2021"
[dependencies]
# Web framework
actix-web = "4.4"
actix-cors = "0.6"
actix-files = "0.6"
# Async runtime
tokio = { version = "1.35", features = ["full"] }
# Serialization
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
# Database
sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "postgres", "uuid", "chrono"] }
uuid = { version = "1.6", features = ["v4", "serde"] }
# Configuration
config = "0.13"
dotenv = "0.15"
# Logging
env_logger = "0.10"
log = "0.4"
# Date/Time
chrono = { version = "0.4", features = ["serde"] }
# Validation
validator = { version = "0.16", features = ["derive"] }
# Authentication
jsonwebtoken = "9.2"
bcrypt = "0.15"
# Error handling
anyhow = "1.0"
thiserror = "1.0"
# HTTP client
reqwest = { version = "0.11", features = ["json"] }
[dev-dependencies]
actix-rt = "2.9"
EOF
# Create application structure
mkdir -p src/{handlers,models,services,middleware,config,utils}
# Create main application file
cat > src/main.rs << 'EOF'
use actix_web::{web, App, HttpServer, middleware::Logger};
use actix_cors::Cors;
use std::env;
mod handlers;
mod models;
mod services;
mod middleware;
mod config;
mod utils;
use handlers::{health, users, auth};
use config::database::init_db_pool;
#[actix_web::main]
async fn main() -> std::io::Result<()> {
env_logger::init();
dotenv::dotenv().ok();
let database_url = env::var("DATABASE_URL")
.expect("DATABASE_URL must be set");
let db_pool = init_db_pool(&database_url).await
.expect("Failed to create database pool");
let bind_address = env::var("BIND_ADDRESS")
.unwrap_or_else(|_| "127.0.0.1:8080".to_string());
println!("๐ฆ Starting Rust Web API server on {}", bind_address);
HttpServer::new(move || {
let cors = Cors::default()
.allow_any_origin()
.allow_any_method()
.allow_any_header()
.max_age(3600);
App::new()
.app_data(web::Data::new(db_pool.clone()))
.wrap(Logger::default())
.wrap(cors)
.service(
web::scope("/api/v1")
.route("/health", web::get().to(health::health_check))
.service(
web::scope("/auth")
.route("/login", web::post().to(auth::login))
.route("/register", web::post().to(auth::register))
)
.service(
web::scope("/users")
.route("", web::get().to(users::get_users))
.route("", web::post().to(users::create_user))
.route("/{id}", web::get().to(users::get_user))
.route("/{id}", web::put().to(users::update_user))
.route("/{id}", web::delete().to(users::delete_user))
)
)
})
.bind(&bind_address)?
.run()
.await
}
EOF
# Create health check handler
cat > src/handlers/mod.rs << 'EOF'
pub mod health;
pub mod users;
pub mod auth;
EOF
cat > src/handlers/health.rs << 'EOF'
use actix_web::{HttpResponse, Result};
use serde_json::json;
pub async fn health_check() -> Result<HttpResponse> {
Ok(HttpResponse::Ok().json(json!({
"status": "healthy",
"timestamp": chrono::Utc::now(),
"service": "rust-web-api",
"version": env!("CARGO_PKG_VERSION")
})))
}
EOF
# Create user models
cat > src/models/mod.rs << 'EOF'
pub mod user;
pub mod auth;
EOF
cat > src/models/user.rs << 'EOF'
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use chrono::{DateTime, Utc};
use validator::Validate;
#[derive(Debug, Serialize, Deserialize, sqlx::FromRow)]
pub struct User {
pub id: Uuid,
pub email: String,
pub username: String,
pub first_name: Option<String>,
pub last_name: Option<String>,
pub is_active: bool,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
#[derive(Debug, Deserialize, Validate)]
pub struct CreateUser {
#[validate(email)]
pub email: String,
#[validate(length(min = 3, max = 50))]
pub username: String,
#[validate(length(min = 8))]
pub password: String,
pub first_name: Option<String>,
pub last_name: Option<String>,
}
#[derive(Debug, Deserialize, Validate)]
pub struct UpdateUser {
#[validate(email)]
pub email: Option<String>,
#[validate(length(min = 3, max = 50))]
pub username: Option<String>,
pub first_name: Option<String>,
pub last_name: Option<String>,
pub is_active: Option<bool>,
}
#[derive(Debug, Serialize)]
pub struct UserResponse {
pub id: Uuid,
pub email: String,
pub username: String,
pub first_name: Option<String>,
pub last_name: Option<String>,
pub is_active: bool,
pub created_at: DateTime<Utc>,
}
impl From<User> for UserResponse {
fn from(user: User) -> Self {
UserResponse {
id: user.id,
email: user.email,
username: user.username,
first_name: user.first_name,
last_name: user.last_name,
is_active: user.is_active,
created_at: user.created_at,
}
}
}
EOF
# Create authentication models
cat > src/models/auth.rs << 'EOF'
use serde::{Deserialize, Serialize};
use validator::Validate;
#[derive(Debug, Deserialize, Validate)]
pub struct LoginRequest {
#[validate(email)]
pub email: String,
#[validate(length(min = 1))]
pub password: String,
}
#[derive(Debug, Serialize)]
pub struct LoginResponse {
pub token: String,
pub user_id: uuid::Uuid,
pub expires_at: chrono::DateTime<chrono::Utc>,
}
#[derive(Debug, Serialize)]
pub struct AuthError {
pub message: String,
}
EOF
echo "Actix Web application template created! ๐"
What this does: ๐ Creates a complete Actix Web application template with modern web development patterns.
Create Database Configuration and Services
Letโs add database integration and business logic! ๐พ
What weโre doing: Setting up PostgreSQL integration, migration system, and service layer architecture.
# Create database configuration
cat > src/config/mod.rs << 'EOF'
pub mod database;
pub mod jwt;
EOF
cat > src/config/database.rs << 'EOF'
use sqlx::{PgPool, postgres::PgPoolOptions};
use std::time::Duration;
pub async fn init_db_pool(database_url: &str) -> Result<PgPool, sqlx::Error> {
PgPoolOptions::new()
.max_connections(10)
.acquire_timeout(Duration::from_secs(3))
.connect(database_url)
.await
}
pub async fn run_migrations(pool: &PgPool) -> Result<(), sqlx::migrate::MigrateError> {
sqlx::migrate!("./migrations").run(pool).await
}
EOF
# Create JWT configuration
cat > src/config/jwt.rs << 'EOF'
use jsonwebtoken::{encode, decode, Header, Algorithm, Validation, EncodingKey, DecodingKey};
use serde::{Deserialize, Serialize};
use chrono::{Utc, Duration};
use uuid::Uuid;
#[derive(Debug, Serialize, Deserialize)]
pub struct Claims {
pub sub: String, // Subject (user ID)
pub exp: usize, // Expiration time
pub iat: usize, // Issued at
pub user_id: Uuid,
}
pub struct JwtService {
encoding_key: EncodingKey,
decoding_key: DecodingKey,
}
impl JwtService {
pub fn new(secret: &str) -> Self {
Self {
encoding_key: EncodingKey::from_secret(secret.as_ref()),
decoding_key: DecodingKey::from_secret(secret.as_ref()),
}
}
pub fn create_token(&self, user_id: Uuid) -> Result<String, jsonwebtoken::errors::Error> {
let now = Utc::now();
let expires_at = now + Duration::hours(24);
let claims = Claims {
sub: user_id.to_string(),
exp: expires_at.timestamp() as usize,
iat: now.timestamp() as usize,
user_id,
};
encode(&Header::default(), &claims, &self.encoding_key)
}
pub fn verify_token(&self, token: &str) -> Result<Claims, jsonwebtoken::errors::Error> {
let token_data = decode::<Claims>(
token,
&self.decoding_key,
&Validation::new(Algorithm::HS256),
)?;
Ok(token_data.claims)
}
}
EOF
# Create user services
cat > src/services/mod.rs << 'EOF'
pub mod user_service;
pub mod auth_service;
EOF
cat > src/services/user_service.rs << 'EOF'
use sqlx::PgPool;
use uuid::Uuid;
use chrono::Utc;
use bcrypt::{hash, DEFAULT_COST};
use crate::models::user::{User, CreateUser, UpdateUser};
pub struct UserService {
pool: PgPool,
}
impl UserService {
pub fn new(pool: PgPool) -> Self {
Self { pool }
}
pub async fn create_user(&self, user_data: CreateUser) -> Result<User, sqlx::Error> {
let user_id = Uuid::new_v4();
let password_hash = hash(&user_data.password, DEFAULT_COST)
.map_err(|_| sqlx::Error::Protocol("Failed to hash password".into()))?;
let now = Utc::now();
let user = sqlx::query_as!(
User,
r#"
INSERT INTO users (id, email, username, password_hash, first_name, last_name, is_active, created_at, updated_at)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
RETURNING id, email, username, first_name, last_name, is_active, created_at, updated_at
"#,
user_id,
user_data.email,
user_data.username,
password_hash,
user_data.first_name,
user_data.last_name,
true,
now,
now
)
.fetch_one(&self.pool)
.await?;
Ok(user)
}
pub async fn get_user_by_id(&self, user_id: Uuid) -> Result<Option<User>, sqlx::Error> {
let user = sqlx::query_as!(
User,
"SELECT id, email, username, first_name, last_name, is_active, created_at, updated_at FROM users WHERE id = $1",
user_id
)
.fetch_optional(&self.pool)
.await?;
Ok(user)
}
pub async fn get_user_by_email(&self, email: &str) -> Result<Option<User>, sqlx::Error> {
let user = sqlx::query_as!(
User,
"SELECT id, email, username, first_name, last_name, is_active, created_at, updated_at FROM users WHERE email = $1",
email
)
.fetch_optional(&self.pool)
.await?;
Ok(user)
}
pub async fn get_all_users(&self, limit: i32, offset: i32) -> Result<Vec<User>, sqlx::Error> {
let users = sqlx::query_as!(
User,
"SELECT id, email, username, first_name, last_name, is_active, created_at, updated_at FROM users ORDER BY created_at DESC LIMIT $1 OFFSET $2",
limit as i64,
offset as i64
)
.fetch_all(&self.pool)
.await?;
Ok(users)
}
pub async fn update_user(&self, user_id: Uuid, user_data: UpdateUser) -> Result<Option<User>, sqlx::Error> {
let now = Utc::now();
let user = sqlx::query_as!(
User,
r#"
UPDATE users
SET email = COALESCE($2, email),
username = COALESCE($3, username),
first_name = COALESCE($4, first_name),
last_name = COALESCE($5, last_name),
is_active = COALESCE($6, is_active),
updated_at = $7
WHERE id = $1
RETURNING id, email, username, first_name, last_name, is_active, created_at, updated_at
"#,
user_id,
user_data.email,
user_data.username,
user_data.first_name,
user_data.last_name,
user_data.is_active,
now
)
.fetch_optional(&self.pool)
.await?;
Ok(user)
}
pub async fn delete_user(&self, user_id: Uuid) -> Result<bool, sqlx::Error> {
let result = sqlx::query!(
"DELETE FROM users WHERE id = $1",
user_id
)
.execute(&self.pool)
.await?;
Ok(result.rows_affected() > 0)
}
}
EOF
echo "Database services and configuration created! ๐พ"
What this does: ๐ Implements complete database integration with user management and JWT authentication services.
๐ Step 3: Implement API Handlers and Middleware
Create REST API Handlers
Letโs implement the HTTP request handlers! ๐
What weโre doing: Creating RESTful API endpoints with proper error handling, validation, and response formatting.
# Create user handlers
cat > src/handlers/users.rs << 'EOF'
use actix_web::{web, HttpResponse, Result};
use sqlx::PgPool;
use uuid::Uuid;
use validator::Validate;
use crate::models::user::{CreateUser, UpdateUser, UserResponse};
use crate::services::user_service::UserService;
pub async fn create_user(
pool: web::Data<PgPool>,
user_data: web::Json<CreateUser>,
) -> Result<HttpResponse> {
// Validate input
if let Err(errors) = user_data.validate() {
return Ok(HttpResponse::BadRequest().json(serde_json::json!({
"error": "Validation failed",
"details": errors
})));
}
let user_service = UserService::new(pool.get_ref().clone());
match user_service.create_user(user_data.into_inner()).await {
Ok(user) => {
let response: UserResponse = user.into();
Ok(HttpResponse::Created().json(response))
}
Err(sqlx::Error::Database(db_err)) if db_err.constraint().is_some() => {
Ok(HttpResponse::Conflict().json(serde_json::json!({
"error": "User already exists"
})))
}
Err(err) => {
log::error!("Failed to create user: {:?}", err);
Ok(HttpResponse::InternalServerError().json(serde_json::json!({
"error": "Internal server error"
})))
}
}
}
pub async fn get_user(
pool: web::Data<PgPool>,
path: web::Path<Uuid>,
) -> Result<HttpResponse> {
let user_id = path.into_inner();
let user_service = UserService::new(pool.get_ref().clone());
match user_service.get_user_by_id(user_id).await {
Ok(Some(user)) => {
let response: UserResponse = user.into();
Ok(HttpResponse::Ok().json(response))
}
Ok(None) => {
Ok(HttpResponse::NotFound().json(serde_json::json!({
"error": "User not found"
})))
}
Err(err) => {
log::error!("Failed to get user: {:?}", err);
Ok(HttpResponse::InternalServerError().json(serde_json::json!({
"error": "Internal server error"
})))
}
}
}
pub async fn get_users(
pool: web::Data<PgPool>,
query: web::Query<PaginationQuery>,
) -> Result<HttpResponse> {
let user_service = UserService::new(pool.get_ref().clone());
let limit = query.limit.unwrap_or(10).min(100);
let offset = query.offset.unwrap_or(0);
match user_service.get_all_users(limit, offset).await {
Ok(users) => {
let responses: Vec<UserResponse> = users.into_iter().map(Into::into).collect();
Ok(HttpResponse::Ok().json(serde_json::json!({
"users": responses,
"pagination": {
"limit": limit,
"offset": offset,
"total": responses.len()
}
})))
}
Err(err) => {
log::error!("Failed to get users: {:?}", err);
Ok(HttpResponse::InternalServerError().json(serde_json::json!({
"error": "Internal server error"
})))
}
}
}
pub async fn update_user(
pool: web::Data<PgPool>,
path: web::Path<Uuid>,
user_data: web::Json<UpdateUser>,
) -> Result<HttpResponse> {
let user_id = path.into_inner();
if let Err(errors) = user_data.validate() {
return Ok(HttpResponse::BadRequest().json(serde_json::json!({
"error": "Validation failed",
"details": errors
})));
}
let user_service = UserService::new(pool.get_ref().clone());
match user_service.update_user(user_id, user_data.into_inner()).await {
Ok(Some(user)) => {
let response: UserResponse = user.into();
Ok(HttpResponse::Ok().json(response))
}
Ok(None) => {
Ok(HttpResponse::NotFound().json(serde_json::json!({
"error": "User not found"
})))
}
Err(err) => {
log::error!("Failed to update user: {:?}", err);
Ok(HttpResponse::InternalServerError().json(serde_json::json!({
"error": "Internal server error"
})))
}
}
}
pub async fn delete_user(
pool: web::Data<PgPool>,
path: web::Path<Uuid>,
) -> Result<HttpResponse> {
let user_id = path.into_inner();
let user_service = UserService::new(pool.get_ref().clone());
match user_service.delete_user(user_id).await {
Ok(true) => {
Ok(HttpResponse::NoContent().finish())
}
Ok(false) => {
Ok(HttpResponse::NotFound().json(serde_json::json!({
"error": "User not found"
})))
}
Err(err) => {
log::error!("Failed to delete user: {:?}", err);
Ok(HttpResponse::InternalServerError().json(serde_json::json!({
"error": "Internal server error"
})))
}
}
}
#[derive(serde::Deserialize)]
pub struct PaginationQuery {
pub limit: Option<i32>,
pub offset: Option<i32>,
}
EOF
# Create authentication handlers
cat > src/handlers/auth.rs << 'EOF'
use actix_web::{web, HttpResponse, Result};
use sqlx::PgPool;
use validator::Validate;
use bcrypt::verify;
use crate::models::auth::{LoginRequest, LoginResponse};
use crate::models::user::CreateUser;
use crate::services::user_service::UserService;
use crate::config::jwt::JwtService;
pub async fn register(
pool: web::Data<PgPool>,
user_data: web::Json<CreateUser>,
) -> Result<HttpResponse> {
if let Err(errors) = user_data.validate() {
return Ok(HttpResponse::BadRequest().json(serde_json::json!({
"error": "Validation failed",
"details": errors
})));
}
let user_service = UserService::new(pool.get_ref().clone());
match user_service.create_user(user_data.into_inner()).await {
Ok(user) => {
// Create JWT token
let jwt_secret = std::env::var("JWT_SECRET").unwrap_or_else(|_| "default-secret".to_string());
let jwt_service = JwtService::new(&jwt_secret);
match jwt_service.create_token(user.id) {
Ok(token) => {
let response = LoginResponse {
token,
user_id: user.id,
expires_at: chrono::Utc::now() + chrono::Duration::hours(24),
};
Ok(HttpResponse::Created().json(response))
}
Err(err) => {
log::error!("Failed to create JWT token: {:?}", err);
Ok(HttpResponse::InternalServerError().json(serde_json::json!({
"error": "Failed to create authentication token"
})))
}
}
}
Err(sqlx::Error::Database(db_err)) if db_err.constraint().is_some() => {
Ok(HttpResponse::Conflict().json(serde_json::json!({
"error": "User already exists"
})))
}
Err(err) => {
log::error!("Failed to create user: {:?}", err);
Ok(HttpResponse::InternalServerError().json(serde_json::json!({
"error": "Internal server error"
})))
}
}
}
pub async fn login(
pool: web::Data<PgPool>,
credentials: web::Json<LoginRequest>,
) -> Result<HttpResponse> {
if let Err(errors) = credentials.validate() {
return Ok(HttpResponse::BadRequest().json(serde_json::json!({
"error": "Validation failed",
"details": errors
})));
}
let user_service = UserService::new(pool.get_ref().clone());
// Get user with password hash
let user_with_password = match sqlx::query!(
"SELECT id, password_hash, is_active FROM users WHERE email = $1",
credentials.email
)
.fetch_optional(pool.get_ref())
.await
{
Ok(Some(user)) => user,
Ok(None) => {
return Ok(HttpResponse::Unauthorized().json(serde_json::json!({
"error": "Invalid credentials"
})));
}
Err(err) => {
log::error!("Failed to get user: {:?}", err);
return Ok(HttpResponse::InternalServerError().json(serde_json::json!({
"error": "Internal server error"
})));
}
};
// Verify password
if !user_with_password.is_active {
return Ok(HttpResponse::Unauthorized().json(serde_json::json!({
"error": "Account is disabled"
})));
}
match verify(&credentials.password, &user_with_password.password_hash) {
Ok(true) => {
// Create JWT token
let jwt_secret = std::env::var("JWT_SECRET").unwrap_or_else(|_| "default-secret".to_string());
let jwt_service = JwtService::new(&jwt_secret);
match jwt_service.create_token(user_with_password.id) {
Ok(token) => {
let response = LoginResponse {
token,
user_id: user_with_password.id,
expires_at: chrono::Utc::now() + chrono::Duration::hours(24),
};
Ok(HttpResponse::Ok().json(response))
}
Err(err) => {
log::error!("Failed to create JWT token: {:?}", err);
Ok(HttpResponse::InternalServerError().json(serde_json::json!({
"error": "Failed to create authentication token"
})))
}
}
}
Ok(false) => {
Ok(HttpResponse::Unauthorized().json(serde_json::json!({
"error": "Invalid credentials"
})))
}
Err(err) => {
log::error!("Failed to verify password: {:?}", err);
Ok(HttpResponse::InternalServerError().json(serde_json::json!({
"error": "Internal server error"
})))
}
}
}
EOF
echo "API handlers implemented! ๐"
What this does: ๐ Implements complete REST API handlers with validation, error handling, and authentication.
๐ Step 4: Database Migrations and Environment Setup
Create Database Schema and Migrations
Letโs set up the database structure! ๐๏ธ
What weโre doing: Creating PostgreSQL migrations, environment configuration, and database initialization scripts.
# Create migrations directory
mkdir -p migrations
# Create initial user table migration
cat > migrations/001_create_users_table.sql << 'EOF'
-- Create users table
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
email VARCHAR(255) NOT NULL UNIQUE,
username VARCHAR(50) NOT NULL UNIQUE,
password_hash VARCHAR(255) NOT NULL,
first_name VARCHAR(100),
last_name VARCHAR(100),
is_active BOOLEAN NOT NULL DEFAULT true,
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
);
-- Create indexes for better performance
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_username ON users(username);
CREATE INDEX idx_users_created_at ON users(created_at);
-- Create updated_at trigger function
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$ language 'plpgsql';
-- Create trigger for automatic updated_at
CREATE TRIGGER update_users_updated_at
BEFORE UPDATE ON users
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();
EOF
# Create environment configuration files
cat > .env.example << 'EOF'
# Database Configuration
DATABASE_URL=postgresql://username:password@localhost:5432/rust_web_db
# Server Configuration
BIND_ADDRESS=127.0.0.1:8080
RUST_LOG=info
# JWT Configuration
JWT_SECRET=your-super-secret-jwt-key-here
# CORS Configuration
ALLOWED_ORIGINS=http://localhost:3000,http://localhost:8080
EOF
# Create actual .env file
cp .env.example .env
# Create database setup script
cat > scripts/setup_database.sh << 'EOF'
#!/bin/bash
"""
Database Setup Script
Creates PostgreSQL database and runs migrations
"""
set -e
DB_NAME="rust_web_db"
DB_USER="rust_web_user"
DB_PASSWORD="rust_web_password"
echo "๐๏ธ Setting up PostgreSQL database..."
# Start PostgreSQL service
sudo service postgresql start
# Create database user
sudo -u postgres psql -c "CREATE USER $DB_USER WITH PASSWORD '$DB_PASSWORD';" || true
# Create database
sudo -u postgres createdb -O $DB_USER $DB_NAME || true
# Grant privileges
sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE $DB_NAME TO $DB_USER;"
# Update .env file with database URL
sed -i "s|DATABASE_URL=.*|DATABASE_URL=postgresql://$DB_USER:$DB_PASSWORD@localhost:5432/$DB_NAME|" .env
echo "โ
Database setup complete!"
echo "๐ Database URL: postgresql://$DB_USER:$DB_PASSWORD@localhost:5432/$DB_NAME"
echo "๐ Run migrations with: sqlx migrate run"
EOF
chmod +x scripts/setup_database.sh
# Create Docker configuration for development
cat > docker-compose.yml << 'EOF'
version: '3.8'
services:
postgres:
image: postgres:15-alpine
environment:
POSTGRES_DB: rust_web_db
POSTGRES_USER: rust_web_user
POSTGRES_PASSWORD: rust_web_password
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U rust_web_user -d rust_web_db"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
ports:
- "6379:6379"
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 5
volumes:
postgres_data:
EOF
# Create development scripts
mkdir -p scripts
cat > scripts/dev.sh << 'EOF'
#!/bin/bash
"""
Development Server Script
Starts the application with hot reloading
"""
export RUST_LOG=debug
export RUST_BACKTRACE=1
echo "๐ฆ Starting Rust Web API in development mode..."
# Check if database is running
if ! pg_isready -h localhost -p 5432 -U rust_web_user -d rust_web_db 2>/dev/null; then
echo "โ ๏ธ PostgreSQL is not running. Starting with Docker..."
docker-compose up -d postgres redis
# Wait for database to be ready
echo "โณ Waiting for database to be ready..."
while ! pg_isready -h localhost -p 5432 -U rust_web_user -d rust_web_db 2>/dev/null; do
sleep 2
done
fi
# Run migrations
echo "๐ Running database migrations..."
sqlx migrate run
# Start development server with hot reloading
cargo watch -x run
EOF
chmod +x scripts/dev.sh
# Create production build script
cat > scripts/build.sh << 'EOF'
#!/bin/bash
"""
Production Build Script
Creates optimized binary for deployment
"""
echo "๐๏ธ Building Rust Web API for production..."
# Clean previous builds
cargo clean
# Build optimized release binary
cargo build-release
# Create deployment directory
mkdir -p deploy
# Copy binary and configuration
cp target/x86_64-unknown-linux-musl/release/rust-web-api deploy/
cp -r migrations deploy/
cp .env.example deploy/
echo "โ
Production build complete!"
echo "๐ฆ Deployment files in: ./deploy/"
echo "๐ Run with: ./deploy/rust-web-api"
EOF
chmod +x scripts/build.sh
echo "Database migrations and environment setup complete! ๐๏ธ"
What this does: ๐ Creates complete database schema, migrations, environment configuration, and development scripts.
๐ Step 5: Testing and Documentation
Create Comprehensive Testing Suite
Letโs add thorough testing capabilities! ๐งช
What weโre doing: Setting up unit tests, integration tests, and API documentation for the Rust web application.
# Create testing utilities
mkdir -p src/utils tests
cat > src/utils/mod.rs << 'EOF'
pub mod test_helpers;
EOF
cat > src/utils/test_helpers.rs << 'EOF'
#[cfg(test)]
use sqlx::{PgPool, postgres::PgPoolOptions};
use std::sync::Once;
static INIT: Once = Once::new();
#[cfg(test)]
pub async fn setup_test_db() -> PgPool {
INIT.call_once(|| {
env_logger::builder()
.filter_level(log::LevelFilter::Debug)
.init();
});
let database_url = std::env::var("TEST_DATABASE_URL")
.unwrap_or_else(|_| "postgresql://rust_web_user:rust_web_password@localhost:5432/rust_web_test".to_string());
let pool = PgPoolOptions::new()
.max_connections(5)
.connect(&database_url)
.await
.expect("Failed to connect to test database");
// Run migrations
sqlx::migrate!("./migrations")
.run(&pool)
.await
.expect("Failed to run migrations");
pool
}
#[cfg(test)]
pub async fn cleanup_test_db(pool: &PgPool) {
sqlx::query!("TRUNCATE TABLE users RESTART IDENTITY CASCADE")
.execute(pool)
.await
.expect("Failed to cleanup test database");
}
EOF
# Create integration tests
cat > tests/integration_tests.rs << 'EOF'
use actix_web::{test, web, App};
use serde_json::Value;
use uuid::Uuid;
use rust_web_api::handlers::{health, users, auth};
use rust_web_api::models::user::CreateUser;
use rust_web_api::models::auth::LoginRequest;
use rust_web_api::utils::test_helpers::{setup_test_db, cleanup_test_db};
#[actix_rt::test]
async fn test_health_check() {
let app = test::init_service(
App::new().route("/health", web::get().to(health::health_check))
).await;
let req = test::TestRequest::get().uri("/health").to_request();
let resp = test::call_service(&app, req).await;
assert!(resp.status().is_success());
let body: Value = test::read_body_json(resp).await;
assert_eq!(body["status"], "healthy");
}
#[actix_rt::test]
async fn test_user_registration_and_login() {
let pool = setup_test_db().await;
let app = test::init_service(
App::new()
.app_data(web::Data::new(pool.clone()))
.service(
web::scope("/api/v1")
.service(
web::scope("/auth")
.route("/register", web::post().to(auth::register))
.route("/login", web::post().to(auth::login))
)
)
).await;
// Test user registration
let new_user = CreateUser {
email: "[email protected]".to_string(),
username: "testuser".to_string(),
password: "securepassword123".to_string(),
first_name: Some("Test".to_string()),
last_name: Some("User".to_string()),
};
let req = test::TestRequest::post()
.uri("/api/v1/auth/register")
.set_json(&new_user)
.to_request();
let resp = test::call_service(&app, req).await;
assert!(resp.status().is_success());
let registration_body: Value = test::read_body_json(resp).await;
assert!(registration_body["token"].is_string());
assert!(registration_body["user_id"].is_string());
// Test user login
let login_request = LoginRequest {
email: "[email protected]".to_string(),
password: "securepassword123".to_string(),
};
let req = test::TestRequest::post()
.uri("/api/v1/auth/login")
.set_json(&login_request)
.to_request();
let resp = test::call_service(&app, req).await;
assert!(resp.status().is_success());
let login_body: Value = test::read_body_json(resp).await;
assert!(login_body["token"].is_string());
assert!(login_body["user_id"].is_string());
cleanup_test_db(&pool).await;
}
#[actix_rt::test]
async fn test_user_crud_operations() {
let pool = setup_test_db().await;
let app = test::init_service(
App::new()
.app_data(web::Data::new(pool.clone()))
.service(
web::scope("/api/v1")
.service(
web::scope("/users")
.route("", web::post().to(users::create_user))
.route("", web::get().to(users::get_users))
.route("/{id}", web::get().to(users::get_user))
.route("/{id}", web::put().to(users::update_user))
.route("/{id}", web::delete().to(users::delete_user))
)
)
).await;
// Create user
let new_user = CreateUser {
email: "[email protected]".to_string(),
username: "cruduser".to_string(),
password: "securepassword123".to_string(),
first_name: Some("CRUD".to_string()),
last_name: Some("Test".to_string()),
};
let req = test::TestRequest::post()
.uri("/api/v1/users")
.set_json(&new_user)
.to_request();
let resp = test::call_service(&app, req).await;
assert!(resp.status().is_success());
let create_body: Value = test::read_body_json(resp).await;
let user_id = create_body["id"].as_str().unwrap();
// Get user by ID
let req = test::TestRequest::get()
.uri(&format!("/api/v1/users/{}", user_id))
.to_request();
let resp = test::call_service(&app, req).await;
assert!(resp.status().is_success());
let get_body: Value = test::read_body_json(resp).await;
assert_eq!(get_body["email"], "[email protected]");
// Get all users
let req = test::TestRequest::get()
.uri("/api/v1/users")
.to_request();
let resp = test::call_service(&app, req).await;
assert!(resp.status().is_success());
let list_body: Value = test::read_body_json(resp).await;
assert!(list_body["users"].is_array());
cleanup_test_db(&pool).await;
}
EOF
# Create API documentation
cat > API.md << 'EOF'
# Rust Web API Documentation
## Overview
A high-performance web API built with Rust, Actix Web, and PostgreSQL.
## Authentication
All protected endpoints require a JWT token in the Authorization header:
Authorization: Bearer <jwt_token>
## Endpoints
### Health Check
#### GET /api/v1/health
Returns API health status.
**Response:**
```json
{
"status": "healthy",
"timestamp": "2023-12-21T10:30:00Z",
"service": "rust-web-api",
"version": "0.1.0"
}
Authentication
POST /api/v1/auth/register
Register a new user account.
Request Body:
{
"email": "[email protected]",
"username": "username",
"password": "securepassword123",
"first_name": "John",
"last_name": "Doe"
}
Response:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user_id": "123e4567-e89b-12d3-a456-426614174000",
"expires_at": "2023-12-22T10:30:00Z"
}
POST /api/v1/auth/login
Authenticate user and receive JWT token.
Request Body:
{
"email": "[email protected]",
"password": "securepassword123"
}
Response:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user_id": "123e4567-e89b-12d3-a456-426614174000",
"expires_at": "2023-12-22T10:30:00Z"
}
Users
GET /api/v1/users
Get paginated list of users.
Query Parameters:
limit
(optional): Number of users to return (default: 10, max: 100)offset
(optional): Number of users to skip (default: 0)
Response:
{
"users": [
{
"id": "123e4567-e89b-12d3-a456-426614174000",
"email": "[email protected]",
"username": "username",
"first_name": "John",
"last_name": "Doe",
"is_active": true,
"created_at": "2023-12-21T10:30:00Z"
}
],
"pagination": {
"limit": 10,
"offset": 0,
"total": 1
}
}
GET /api/v1/users/{id}
Get user by ID.
Response:
{
"id": "123e4567-e89b-12d3-a456-426614174000",
"email": "[email protected]",
"username": "username",
"first_name": "John",
"last_name": "Doe",
"is_active": true,
"created_at": "2023-12-21T10:30:00Z"
}
POST /api/v1/users
Create a new user.
Request Body:
{
"email": "[email protected]",
"username": "username",
"password": "securepassword123",
"first_name": "John",
"last_name": "Doe"
}
PUT /api/v1/users/{id}
Update user information.
Request Body:
{
"email": "[email protected]",
"first_name": "Jane",
"last_name": "Smith",
"is_active": false
}
DELETE /api/v1/users/{id}
Delete user account.
Response: 204 No Content
Error Responses
All error responses follow this format:
{
"error": "Error message",
"details": {} // Optional additional details
}
HTTP Status Codes
200 OK
: Successful request201 Created
: Resource created successfully204 No Content
: Successful request with no response body400 Bad Request
: Invalid request data401 Unauthorized
: Authentication required403 Forbidden
: Access denied404 Not Found
: Resource not found409 Conflict
: Resource already exists500 Internal Server Error
: Server error
Development
Running Tests
cargo test
Starting Development Server
./scripts/dev.sh
Building for Production
./scripts/build.sh
EOF
echo โTesting suite and documentation created! ๐งชโ
**What this does:** ๐ Creates comprehensive testing framework, integration tests, and API documentation.
## ๐ Testing Your Rust Web Application
Let's verify everything works correctly! ๐งช
```bash
# Set up the development environment
cd ~/RustWeb/projects/rust-web-api
# Set up database
./scripts/setup_database.sh
# Run tests
cargo test
# Start development server
./scripts/dev.sh
# In another terminal, test the API
curl http://localhost:8080/api/v1/health
echo "๐ฆ Rust web application setup complete!"
๐ Next Steps
Your Alpine Linux Rust web development environment is ready! Hereโs what you can do:
Immediate Actions:
- Start Development: Begin building your web application with the provided template
- Add Features: Implement additional endpoints and business logic
- Database Design: Extend the database schema for your specific needs
- Frontend Integration: Connect with a frontend framework
Advanced Features:
- Caching: Add Redis caching for improved performance
- Rate Limiting: Implement API rate limiting and throttling
- File Upload: Add file upload and storage capabilities
- WebSocket: Implement real-time features with WebSocket support
Production Deployment:
- Containerization: Create Docker containers for deployment
- Load Balancing: Set up Nginx for load balancing and SSL termination
- Monitoring: Add application monitoring and logging
- CI/CD: Set up automated testing and deployment pipelines
๐ฏ Pro Tips
- Performance: Rust provides zero-cost abstractions and excellent performance
- Memory Safety: Take advantage of Rustโs ownership system for safe concurrency
- Error Handling: Use proper error handling with Result types and custom errors
- Testing: Write comprehensive tests for reliability and maintainability
- Documentation: Keep API documentation updated as your application grows
Your Alpine Linux system is now a powerful Rust web development platform! ๐ฆโจ