Skip to main content

mina_node_native/http_server/
mod.rs

1//! Axum-based HTTP server for the Mina node RPC API.
2//!
3//! This module provides REST endpoints for node status, state inspection,
4//! snark pool management, and transaction handling.
5
6#[macro_use]
7mod macros;
8mod openapi;
9mod routes;
10mod types;
11
12pub use routes::openapi_spec;
13pub use types::{AppError, AppResult, AppState, JsonErrorResponse};
14
15use mina_node_common::rpc::RpcSender;
16use tokio::net::TcpListener;
17use tower_http::trace::TraceLayer;
18use types::cors_layer;
19
20#[cfg(feature = "swagger-ui")]
21use utoipa_swagger_ui::SwaggerUi;
22
23#[cfg(feature = "scalar")]
24use utoipa_scalar::{Scalar, Servable};
25
26/// Runs the HTTP server on the specified port.
27///
28/// Returns an error if binding to the port fails or the server encounters an I/O error.
29pub async fn run(port: u16, rpc_sender: RpcSender) -> std::io::Result<()> {
30    let state = AppState::new(rpc_sender);
31
32    let trace_layer = TraceLayer::new_for_http()
33        .make_span_with(|request: &axum::http::Request<_>| {
34            tracing::info_span!(
35                "http_request",
36                method = %request.method(),
37                uri = %request.uri(),
38            )
39        })
40        .on_response(
41            |response: &axum::http::Response<_>, latency, span: &tracing::Span| {
42                let status = response.status();
43                if status.is_server_error() || status.is_client_error() {
44                    tracing::error!(parent: span, status = %status, latency = ?latency, "request failed");
45                } else {
46                    tracing::info!(parent: span, status = %status, latency = ?latency, "request completed");
47                }
48            },
49        );
50
51    // Split to get Router and OpenApi spec
52    let (app, api) = routes::openapi_router().split_for_parts();
53
54    // GraphQL (not documented in OpenAPI)
55    let app = routes::graphql::routes(app);
56
57    // OpenAPI documentation UIs
58    #[cfg(feature = "swagger-ui")]
59    let app = app
60        .merge(SwaggerUi::new("/api-docs/swagger-ui").url("/api-docs/openapi.json", api.clone()));
61
62    #[cfg(feature = "scalar")]
63    let app = app.merge(Scalar::with_url("/api-docs/scalar", api));
64
65    #[cfg(feature = "stoplight-elements")]
66    let app = app.route(
67        "/api-docs/stoplight",
68        axum::routing::get(openapi::stoplight_elements),
69    );
70
71    let app = app.layer(trace_layer).layer(cors_layer()).with_state(state);
72
73    let listener = TcpListener::bind(("0.0.0.0", port)).await?;
74    tracing::info!(port, "HTTP server listening");
75    axum::serve(listener, app).await
76}