The final product is williannguyen.com.
Full personal reflection on this project can be found on this post.
Note:
There is a regular status service to check that the website should work. This is one of the feature can be seen in the source code. If it does not (mismatch with status), please dm me. Thank you for your helps.
This application follows a full-stack Rust architecture using:
- Frontend: Leptos with hydration for interactive client-side features
- Backend: Axum web framework with PostgreSQL database
- Rendering: Server-side rendering (SSR) with client-side hydration
- Content: Markdown-based blog posts with syntax highlighting
- Build System: Custom build script for static content generation
- Leptos 0.8 - Reactive web framework for Rust
- Axum 0.8 - Modern async web framework
- Tokio - Async runtime
- PostgreSQL - Primary database
- SQLx 0.8 - Async SQL toolkit with compile-time checked queries
- Database Migrations - Version-controlled schema management
- pulldown-cmark - Markdown parser
- syntect - Syntax highlighting for code blocks
- Static Content Generation - Build-time markdown processing
- cargo-leptos - Leptos build tool
- Hot Reload - Development server with live reloading
- WASM Optimization - Size-optimized WebAssembly builds
- End-to-End Testing - Playwright integration
The implementation is obtained from cargo-leptos axum template. For more detail, please follow the the instruction.
src/
βββ app.rs # App component and routing
βββ client.rs # Client-side hydration entry
βββ app/
β βββ helpers.rs # UI utility functions
β βββ components.rs # Component module definitions
β βββ components/
β βββ pages.rs
β βββ pages/ # Page module definitions
The backend structure seen below is over-engineered for the purpose of personal blog with only public newsletter subcriber feature.
However, the industry-graded architecture is purposely used to study fullstack technology with Rust. The architecture is learnt from the book FullStack Rust with Axum from Martin Fabio
βββ main.rs # Application entry point
βββ server.rs # Server orchestration & middleware stack
βββ server/ # Modular backend architecture
βββ db.rs # Database module coordinator
βββ db/
β βββ config.rs # Database URL & connection config
β βββ pool.rs # PgPool initialization & management
β βββ state.rs # AppState with shared resources
β βββ error.rs # Database-specific error handling
βββ middleware.rs # Middleware module coordinator
βββ middleware/
β βββ cache.rs # HTTP caching strategies
β βββ governor.rs # Rate limiting (IP-based)
β βββ csrf.rs # CSRF token protection
β βββ throttle.rs # Request throttling
β βββ global_layer.rs # Middleware layer coordinator
β βββ global_layer/
β βββ cors.rs # Cross-Origin Resource Sharing
β βββ security_headers.rs # Security headers middleware
βββ models.rs # Data model coordinator
βββ models/
β βββ subscriber.rs # Newsletter subscriber model
β βββ status.rs # Status badge model (for shields.io)
βββ repositories.rs # Data access coordinator
βββ repositories/
β βββ subscriber.rs # Database queries & data access
β βββ status.rs # Status badge logic (checks and aggregates status)
βββ services.rs # Business logic coordinator
βββ services/
β βββ subscriber.rs # Newsletter business logic
β βββ status.rs # Status badge update logic (periodic background updater)
βββ handlers.rs # Request handler coordinator
βββ handlers/
β βββ subscriber.rs # HTTP request/response handling
β βββ status.rs # Status badge API handler (serves cached status)
βββ routes.rs # API route coordinator
βββ routes/
βββ subscriber.rs # Newsletter API endpoints
βββ status.rs # Status badge AP endpoint (`/status-badge` for shields.io)
graph TD
Middleware["π§© Middleware Stack<br/>(server.rs & routes/)"]
Middleware -->|Applied globally and per route| Routes["π£οΈ Routes"]
Routes -->|Use| Handlers["π Handlers"]
Handlers -->|Use| Services["ποΈ Services"]
Services -->|Use| Repositories["π¦ Repositories"]
Repositories -->|Use| Models["π Models"]
Repositories -->|Use| DB["ποΈ DB"]
- Global
- Compression: Brotli compression for responses
- Request Timeout: Configurable request timeouts
- Security Headers: Comprehensive HTTP security headers
- Route specific
- Rate Limiting: Per-IP request throttling using Governor
- CORS: Cross-Origin Resource Sharing configuration
- CSRF Protection: Token-based CSRF mitigation
- Routes: HTTP endpoints + middleware application, delegate to handlers
- Handlers: HTTP request/response processing, input validation
- Services: Business logic, orchestration, transaction management
- Repositories: Data access queries, DB operations using models
- Models: Data structures, serialization, validation rules
- DB: Connection pooling, configuration, state management
The project uses a custom build script (build.rs
) that:
- Processes Markdown Files: Reads blog posts from
contents/posts/
- Syntax Highlighting: Applies code highlighting using Syntect
- Static Generation: Converts markdown to HTML at build time
- Optimized Output: Generates Rust code with static post data
- Server-Side Rendering (SSR): Fast initial page loads
- Hydration: Interactive client-side features without full SPA overhead
- Static Content: Build-time markdown processing reduces runtime overhead
- Compression: Brotli compression for smaller payload sizes
- Connection Pooling: Efficient database connection management
- Request Timeout: Prevents long-running requests from blocking resources
- HTTP Caching Strategy: Multi-tier caching system for optimal performance
- WASM Optimization: Aggressive size optimization for client-side bundles
The WebAssembly build process includes several standard optimization techniques to minimize bundle size:
- Size-focused compilation (opt-level = 'z')
- Link-time optimization (LTO)
- Strip debug symbols (strip = true)
- Abort on panic (panic = "abort")
- Single codegen unit
- Use wee_alloc for smaller WASM allocator
Below information is obtained from the actual implementation on the project and get benchmark to show the efficency of these technuques.
Metric | Before Optimization | After Optimization | Improvement |
---|---|---|---|
Bundle Size | 8.5MB | 1.5MB | 82.4% smaller |
Gzipped Size | ~2.1MB | ~400-600KB | ~75% smaller |
Load Time Impact | Baseline | Significantly improved | 5.6x smaller |
- Faster page loads: 82% smaller WASM bundles load much faster
- Reduced bandwidth: Significant savings in data transfer
- Better mobile experience: Smaller bundles improve performance on slower connections
- Production ready: Size is now within reasonable limits for web deployment
Deploy standard industry practices
- Static Assets:
- Uses
Cache-Control: public, max-age=31536000
for 1-year caching. - Assets are versioned for cache busting, ensuring users get updates when files change.
- Uses
- API Responses:
- Uses
Cache-Control: public, max-age=60
for short-term caching (1 minute). - Improves performance for read-only endpoints and reduces database load.
- Uses
- Sensitive/Dynamic Endpoints:
- Uses
Cache-Control: no-store
to prevent caching of user actions and sensitive data.
- Uses
- Centralized Error Handling (Status: Planned)
To add a middleware layer to catch errors, log them, and provide consistent user-friendly responses. Implementation is postponed until the app grows in complexity.
- Modulized global layer (Status: Planned)
This was planned out at the begginning with tower
crate 's ServiceBuilder as a global layer which is then called into server.rs. However, refactoring this seperated out from server.rs run is more troublesome than expected. Until the project expands further, it is placed directly in server.rs.
- Book FullStack Rust with Axum from Martin Fabio
- Leptos resources
- awesome-leptos repo
- The wild internet, AIs and various other sources...