axum_sql_viewer/
layer.rs

1//! SqlViewerLayer - Main Axum integration layer
2//!
3//! This module provides the main entry point for integrating axum-sql-viewer
4//! into an Axum application.
5
6use crate::database::traits::DatabaseProvider;
7use axum::{routing::get, routing::post, Router};
8use std::sync::Arc;
9use tower_http::cors::CorsLayer;
10
11#[cfg(feature = "sqlite")]
12use crate::database::sqlite::SqliteProvider;
13
14#[cfg(feature = "postgres")]
15use crate::database::postgres::PostgresProvider;
16
17use crate::api::{
18    count_rows_handler, execute_query_handler, get_rows_handler, get_table_schema_handler,
19    list_tables_handler,
20};
21use crate::frontend::create_frontend_router;
22
23/// Main layer for integrating SQL viewer into an Axum application
24///
25/// # Example
26///
27/// ```rust,no_run
28/// use axum::Router;
29/// use axum_sql_viewer::SqlViewerLayer;
30/// use sqlx::SqlitePool;
31///
32/// # async fn example() {
33/// let pool = SqlitePool::connect("sqlite::memory:").await.unwrap();
34/// let viewer = SqlViewerLayer::sqlite("/sql-viewer", pool);
35/// let app = Router::new().merge(viewer.into_router());
36/// # }
37/// ```
38pub struct SqlViewerLayer<DB: DatabaseProvider> {
39    base_path: String,
40    database: Arc<DB>,
41}
42
43impl<DB: DatabaseProvider> SqlViewerLayer<DB> {
44    /// Create a new SQL viewer at the given base path
45    ///
46    /// # Arguments
47    ///
48    /// * `base_path` - The URL path where the viewer will be mounted (e.g., "/sql-viewer")
49    /// * `database` - The database provider implementation
50    pub fn new(base_path: impl Into<String>, database: DB) -> Self {
51        Self {
52            base_path: base_path.into(),
53            database: Arc::new(database),
54        }
55    }
56
57    /// Convert into an Axum Router that can be merged
58    ///
59    /// This method consumes the layer and returns a Router that can be merged
60    /// into your main application router.
61    ///
62    /// The returned router includes:
63    /// - Frontend serving at `{base_path}/`
64    /// - API endpoints at `{base_path}/api/*`
65    /// - Permissive CORS middleware for development
66    pub fn into_router(self) -> Router {
67        let database = self.database.clone();
68        let base_path = self.base_path.clone();
69
70        // Create API router with all endpoints
71        // Note: Axum 0.8 uses {param} syntax instead of :param
72        let api_router = Router::new()
73            .route("/tables", get(list_tables_handler::<DB>))
74            .route("/tables/{name}", get(get_table_schema_handler::<DB>))
75            .route("/tables/{name}/rows", get(get_rows_handler::<DB>))
76            .route("/tables/{name}/count", get(count_rows_handler::<DB>))
77            .route("/query", post(execute_query_handler::<DB>))
78            .with_state(database);
79
80        // Create frontend router
81        let frontend_router = create_frontend_router(base_path.clone());
82
83        // Nest API router under /api and frontend at root
84        // Apply permissive CORS for development
85        Router::new()
86            .nest(&format!("{}/api", base_path), api_router)
87            .nest(&base_path, frontend_router)
88            .layer(
89                CorsLayer::permissive(), // Permissive CORS for development
90            )
91    }
92}
93
94#[cfg(feature = "sqlite")]
95impl SqlViewerLayer<SqliteProvider> {
96    /// Create a new SQL viewer for SQLite
97    ///
98    /// # Arguments
99    ///
100    /// * `base_path` - The URL path where the viewer will be mounted
101    /// * `pool` - The SQLite connection pool
102    pub fn sqlite(base_path: impl Into<String>, pool: sqlx::SqlitePool) -> Self {
103        Self::new(base_path, SqliteProvider::new(pool))
104    }
105}
106
107#[cfg(feature = "postgres")]
108impl SqlViewerLayer<PostgresProvider> {
109    /// Create a new SQL viewer for PostgreSQL
110    ///
111    /// # Arguments
112    ///
113    /// * `base_path` - The URL path where the viewer will be mounted
114    /// * `pool` - The PostgreSQL connection pool
115    pub fn postgres(base_path: impl Into<String>, pool: sqlx::PgPool) -> Self {
116        Self::new(base_path, PostgresProvider::new(pool))
117    }
118}