Generating server (axum) based on the OpenAPI specification

Grzegorz Bylica
3 min readFeb 24, 2025

--

Software Installation: openapi-generator-cli-7.9.0.jar

I create a repository. Then, within the repository, I create a directory to store the OpenAPI definitions.

openapi: 3.0.0
info:
title: Sample API
version: 1.0.0
paths:
/hello:
get:
summary: Returns a greeting
responses:
'200':
description: A JSON object with a greeting message.
content:
application/json:
schema:
type: object
properties:
message:
type: string

I create a script that generates API definitions in Rust for the axum framework.

gen.sh

java -jar openapi-generator-cli.jar generate \
-i ./example-openapi.yaml \
-g rust-axum \
-o ../api

I run the generation

./gen.sh

In the project’s main directory, an api folder is created containing the project.

In it, within the sources, I add a bin directory with a main.rs file where include the implementation.

use http::Method;
use axum_extra::extract::CookieJar;
use axum::extract::Host;
use openapi::server;
use openapi::apis::default::{Default, HelloGetResponse};
use openapi::models::HelloGet200Response;

#[derive(Clone)]
struct MyApi;


#[async_trait::async_trait]
impl Default for MyApi {
async fn hello_get(
&self,
_method: Method,
_host: Host,
_cookies: CookieJar,
) -> Result<HelloGetResponse, String> {
let hello = HelloGet200Response { message: Some("hello".to_string()) };
Ok(HelloGetResponse::Status200_AJSONObjectWithAGreetingMessage(hello))
}
}

impl AsRef<MyApi> for MyApi {
fn as_ref(&self) -> &MyApi {
self
}
}

#[tokio::main]
async fn main() {

let api_impl = MyApi;

let app = server::new(api_impl);

// run it
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000")
.await
.unwrap();
println!("listening on {}", listener.local_addr().unwrap());

if let Err(e) = axum::serve(listener, app).await {
eprintln!("server error: {}", e);
}
}

Starting the service

cd ../api
cargo run --bin main

I check if it works

curl http://127.0.0.1:3000/hello

Several advantages of this approach:

Clear API contract: The OpenAPI documentation defines a clear contract that specifies which endpoints exist, what parameters they accept, and what responses they return. This ensures that both the backend team, the frontend team, and API clients have an unambiguous agreement regarding the interface.

Consistency and standardization: OpenAPI enforces a unified documentation format, which facilitates maintaining consistency and using automation tools (e.g., client generators, testing tools, stubs). In a code-first approach, the documentation might deviate from the actual implementation if the code and documentation are not maintained in parallel.

Early error detection: Designing the API from the very beginning based on the specification allows for early identification of potential ambiguities or errors in the contract. This can be verified before implementation begins.

Facilitated collaboration: Thanks to the explicit specification, the entire team (and any external partners) can better understand how the API works. This is especially important in distributed projects or when multiple teams are working on different parts of the system.

Independence from implementation: The OpenAPI documentation is independent of the specific code implementation. This allows for later changes in the code without the risk that the documentation will become inconsistent or outdated.

Source code:

Reading materials:

https://openapi-generator.tech/

https://openapi-generator.tech/docs/generators/rust-axum/

https://www.ibm.com/docs/pl/app-connect/12.0?topic=apis-openapi-30

Thank you :)

--

--

Grzegorz Bylica
Grzegorz Bylica

Written by Grzegorz Bylica

I work with different technologies, but programming in Rust is especially fun. Connect with me on LinkedIn https://www.linkedin.com/in/grzegorz-bylica

No responses yet