Expand description
Axum framework integration.
This module provides helpers and pre-built handlers for using BrowserPool
with Axum. You can choose between using the pre-built handlers for
quick setup, or writing custom handlers for full control.
§Quick Start
§Option 1: Pre-built Routes (Fastest Setup)
Use configure_routes to add all PDF endpoints with a single line:
ⓘ
use axum::Router;
use html2pdf_api::prelude::*;
#[tokio::main]
async fn main() {
let pool = init_browser_pool().await
.expect("Failed to initialize browser pool");
let app = Router::new()
.merge(html2pdf_api::integrations::axum::configure_routes())
.with_state(pool);
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000").await.unwrap();
axum::serve(listener, app).await.unwrap();
}This gives you the following endpoints:
| Method | Path | Description |
|---|---|---|
| GET | /pdf?url=... | Convert URL to PDF |
| POST | /pdf/html | Convert HTML to PDF |
| GET | /pool/stats | Pool statistics |
| GET | /health | Health check |
| GET | /ready | Readiness check |
§Option 2: Mix Pre-built and Custom Handlers
Use individual pre-built handlers alongside your own:
ⓘ
use axum::{Router, routing::get};
use html2pdf_api::prelude::*;
use html2pdf_api::integrations::axum::{pdf_from_url, health_check};
async fn my_custom_handler() -> &'static str {
"Custom response"
}
#[tokio::main]
async fn main() {
let pool = init_browser_pool().await.unwrap();
let app = Router::new()
.route("/pdf", get(pdf_from_url))
.route("/health", get(health_check))
.route("/custom", get(my_custom_handler))
.with_state(pool);
// ... serve app
}§Option 3: Custom Handlers with Service Functions
For full control, use the service functions directly:
ⓘ
use axum::{
extract::{Query, State},
http::StatusCode,
response::IntoResponse,
};
use html2pdf_api::prelude::*;
use html2pdf_api::service::{generate_pdf_from_url, PdfFromUrlRequest};
async fn my_pdf_handler(
State(pool): State<SharedBrowserPool>,
Query(request): Query<PdfFromUrlRequest>,
) -> impl IntoResponse {
// Call service in blocking context
let result = tokio::task::spawn_blocking(move || {
generate_pdf_from_url(&pool, &request)
}).await;
match result {
Ok(Ok(pdf)) => {
// Custom post-processing
(
[(axum::http::header::CONTENT_TYPE, "application/pdf")],
pdf.data,
).into_response()
}
Ok(Err(_)) => StatusCode::BAD_REQUEST.into_response(),
Err(_) => StatusCode::INTERNAL_SERVER_ERROR.into_response(),
}
}§Option 4: Full Manual Control (Original Approach)
For complete control over browser operations:
ⓘ
use axum::{extract::State, http::StatusCode, response::IntoResponse};
use html2pdf_api::prelude::*;
async fn manual_pdf_handler(
State(pool): State<SharedBrowserPool>,
) -> Result<impl IntoResponse, StatusCode> {
let pool_guard = pool.lock()
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
let browser = pool_guard.get()
.map_err(|_| StatusCode::SERVICE_UNAVAILABLE)?;
let tab = browser.new_tab()
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
tab.navigate_to("https://example.com")
.map_err(|_| StatusCode::BAD_GATEWAY)?;
tab.wait_until_navigated()
.map_err(|_| StatusCode::BAD_GATEWAY)?;
let pdf_data = tab.print_to_pdf(None)
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
Ok((
[(axum::http::header::CONTENT_TYPE, "application/pdf")],
pdf_data,
))
}§Setup
Add to your Cargo.toml:
[dependencies]
html2pdf-api = { version = "0.3", features = ["axum-integration"] }
axum = "0.8"§Graceful Shutdown
For proper cleanup with graceful shutdown:
ⓘ
use axum::Router;
use html2pdf_api::prelude::*;
use std::sync::Arc;
use tokio::signal;
#[tokio::main]
async fn main() {
let pool = init_browser_pool().await.unwrap();
let shutdown_pool = Arc::clone(&pool);
let app = Router::new()
.merge(html2pdf_api::integrations::axum::configure_routes())
.with_state(pool);
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000").await.unwrap();
axum::serve(listener, app)
.with_graceful_shutdown(shutdown_signal(shutdown_pool))
.await
.unwrap();
}
async fn shutdown_signal(pool: SharedBrowserPool) {
let ctrl_c = async {
signal::ctrl_c().await.expect("Failed to listen for ctrl+c");
};
#[cfg(unix)]
let terminate = async {
signal::unix::signal(signal::unix::SignalKind::terminate())
.expect("Failed to install signal handler")
.recv()
.await;
};
#[cfg(not(unix))]
let terminate = std::future::pending::<()>();
tokio::select! {
_ = ctrl_c => {},
_ = terminate => {},
}
println!("Shutting down...");
if let Ok(mut pool) = pool.lock() {
pool.shutdown();
}
}Traits§
- Browser
Pool Axum Ext - Extension trait for
BrowserPoolwith Axum helpers.
Functions§
- configure_
routes - Returns a router configured with all PDF routes.
- create_
extension - Create an Axum Extension from an existing shared pool.
- health_
check - Health check endpoint.
- pdf_
from_ html - Generate PDF from HTML content.
- pdf_
from_ url - Generate PDF from a URL.
- pool_
stats - Get browser pool statistics.
- readiness_
check - Readiness check endpoint.
Type Aliases§
- Browser
Pool State - Type alias for Axum
Stateextractor with the shared pool. - Shared
Pool - Type alias for shared browser pool.