Serving a SPA and APIs #867
-
I am currently in the process of trying to switch from actix-web to axum. I have an Angular app that uses client-side routing. I want to achieve the following:
I currently have it setup like this: Router::new()
.nest("/api", crate::api::api_router())
.nest(
"/dist",
get_service(ServeDir::new("./dist"))
.handle_error(|_: std::io::Error| async move {
(StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "Internal Server Error"})))
}),
)
.fallback(
get_service(ServeFile::new("./dist/index.html"))
.handle_error(|_: std::io::Error| async move {
(StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "Internal Server Error"})))
}),
); This does not really work, because when i enter In actix-web I achieved that with the following code: App::new()
.service(crate::api::api_scope())
.service(Files::new("/", "./dist").index_file("index.html"))
.default_service(|r: ServiceRequest| futures::future::ok(spa_index(r)));
fn spa_index(service_request: ServiceRequest) -> ServiceResponse {
let (req, _payload) = service_request.into_parts();
let file_response = NamedFile::open("./dist/index.html").respond_to(&req);
ServiceResponse::new(req, file_response.map_into_boxed_body())
} Is it possible to achieve this using some kind of combination of fallback, nest and ServeDir? |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 11 replies
-
Try this let app = Router::new()
.route("/api/auth", get(|| async { "API: /auth" }))
.fallback(
get_service(ServeDir::new("./dist")).handle_error(|_| async move {
(StatusCode::INTERNAL_SERVER_ERROR, "internal server error")
}),
); |
Beta Was this translation helpful? Give feedback.
-
@Stefan99353 you'll need fallback route to SPA I think, in axum, for the equal for actix // serve './dist/index.html' because no other route matched and make the SPA route work
.fallback(get(|_| async {
Html(include_str!("./dist/index.html"))
})) This is what we use in production, for now it works good. We have API mounted under And all other route is controlled by the frontend SPA. This will make frontend route things like and I see you use for the assets you can use: .nest(
"/static",
get_service(ServeDir::new("./dist")).handle_error(|error: std::io::Error| async move {
(StatusCode::INTERNAL_SERVER_ERROR, format!("Unhandled internal error: {}", error))
}),
) |
Beta Was this translation helpful? Give feedback.
-
If anyone is look here with Axum v6+, here is what I have for my SPA, using SolidJS with client side routing .nest_service(
"/assets",
get_service(ServeDir::new("./frontend/dist/assets")).handle_error(
|error: std::io::Error| async move {
(
StatusCode::INTERNAL_SERVER_ERROR,
format!("Unhandled internal error: {}", error),
)
},
),
)
.fallback_service(
get_service(ServeFile::new("./frontend/dist/index.html")).handle_error(
|_| async move { (StatusCode::INTERNAL_SERVER_ERROR, "internal server error") },
),
) My solidjs code lives in a folder called If you're using client side routing than every HTTP request that comes through your backend needs to check your API routes first then hit a fallback that returns |
Beta Was this translation helpful? Give feedback.
Try this