So we generate a server stub from our OpenAPI spec yaml
using openapi-generator
. We integrate the generated server code with Axum.
Following an excerpt from generating a server of OpenAPI's pet store example:
#[async_trait]
impl<C> Api<C> for Server<C> where C: Has<XSpanIdString> + Send + Sync
{
/// Create a pet
async fn create_pets(
&self,
context: &C) -> Result<CreatePetsResponse, ApiError>
{
let context = context.clone();
info!("create_pets() - X-Span-ID: {:?}", context.get().0.clone());
Err(ApiError("Generic failure".into()))
}
/// List all pets
async fn list_pets(
&self,
limit: Option<i32>,
context: &C) -> Result<ListPetsResponse, ApiError>
{
let context = context.clone();
info!("list_pets({:?}) - X-Span-ID: {:?}", limit, context.get().0.clone());
Err(ApiError("Generic failure".into()))
}
/// Info for a specific pet
async fn show_pet_by_id(
&self,
pet_id: String,
context: &C) -> Result<ShowPetByIdResponse, ApiError>
{
let context = context.clone();
info!("show_pet_by_id(\"{}\") - X-Span-ID: {:?}", pet_id, context.get().0.clone());
Err(ApiError("Generic failure".into()))
}
}
As you can see the generated Api
trait dictates the Result
's error case to be of type ApiError
, which is generated as follows:
/// Very simple error type - just holds a description of the error. This is useful for human
/// diagnosis and troubleshooting, but not for applications to parse. The justification for this
/// is to deny applications visibility into the communication layer, forcing the application code
/// to act solely on the logical responses that the API provides, promoting abstraction in the
/// application code.
#[derive(Clone, Debug)]
pub struct ApiError(pub String);
However, in our application we actually want to return something like (HttpStatusCode, Json<>)
instead.
How can we achieve this without touching the generated code? Because if our OpenAPI spec changes, the server code would be generated anew and thus would overwrite our adjustments.
Thanks for your support