This guide explains how to regenerate the OpenAPI specification files after making changes to API handlers or models.
The Pangolin API uses utoipa to automatically generate OpenAPI 3.0 specifications from Rust code annotations. The OpenAPI spec is available in two formats:
- JSON:
docs/api/openapi.json(4734 lines) - YAML:
docs/api/openapi.yaml(3051 lines)
Regenerate the OpenAPI documentation when you:
- Add new API endpoints
- Modify existing endpoint parameters or responses
- Add or update request/response models
- Change API descriptions or metadata
- Update security schemes
Ensure you have:
- Rust toolchain installed
- All dependencies up to date (
cargo update) - The
pangolin_apicrate compiles successfully
cd pangolin
cargo run -p pangolin_api --bin export_openapi json 2>/dev/null > ../docs/api/openapi.jsonOr from the project root:
cd pangolin && cargo run -p pangolin_api --bin export_openapi json 2>/dev/null > ../docs/api/openapi.jsoncd pangolin
cargo run -p pangolin_api --bin export_openapi yaml 2>/dev/null > ../docs/api/openapi.yamlOr from the project root:
cd pangolin && cargo run -p pangolin_api --bin export_openapi yaml 2>/dev/null > ../docs/api/openapi.yamlcd pangolin
# Generate JSON
cargo run -p pangolin_api --bin export_openapi json 2>/dev/null > ../docs/api/openapi.json
# Generate YAML
cargo run -p pangolin_api --bin export_openapi yaml 2>/dev/null > ../docs/api/openapi.yamlAfter regeneration, verify the output:
wc -l docs/api/openapi.json docs/api/openapi.yamlExpected output (approximate):
4734 docs/api/openapi.json
3051 docs/api/openapi.yaml
7785 total
cat docs/api/openapi.json | jq . > /dev/null && echo "✓ Valid JSON"head -20 docs/api/openapi.yamlShould show:
openapi: 3.0.3
info:
title: Pangolin Catalog API
description: Multi-tenant Apache Iceberg REST Catalog...
...After regeneration, test the Swagger UI to ensure all endpoints appear correctly:
-
Start the API server:
cd pangolin cargo run -p pangolin_api -
Open Swagger UI in your browser:
http://localhost:8080/swagger-ui -
Verify:
- All 13 API tags are present
- All 67 endpoints are listed
- Schemas are properly rendered
- "Try it out" functionality works
Symptom: Generated file is empty or contains compilation errors
Solution: Check that stderr is redirected properly:
# Correct (redirects stderr to /dev/null)
cargo run -p pangolin_api --bin export_openapi json 2>/dev/null > ../docs/api/openapi.json
# Incorrect (stderr mixed with stdout)
cargo run -p pangolin_api --bin export_openapi json > ../docs/api/openapi.jsonSymptom: Export command fails with compilation errors
Solution:
-
Ensure all code compiles:
cargo check -p pangolin_api
-
Fix any compilation errors before regenerating
-
Ensure all models referenced in handlers have
#[derive(ToSchema)]
Symptom: Some endpoints don't appear in the generated spec
Solution:
- Verify the handler has
#[utoipa::path(...)]annotation - Ensure the handler is imported in
openapi.rs:use crate::your_handlers::*;
- Add the handler function name to the
paths(...)list inopenapi.rs
Symptom: Request/response types show as empty objects
Solution:
-
Add
#[derive(ToSchema)]to the struct:use utoipa::ToSchema; #[derive(Serialize, Deserialize, ToSchema)] pub struct YourStruct { // fields... }
-
Import the type in
openapi.rs:use crate::your_module::YourStruct;
-
Add it to the
components(schemas(...))list inopenapi.rs
When adding a new API endpoint, follow these steps:
Add #[utoipa::path(...)] above your handler function:
use utoipa::ToSchema;
#[derive(Deserialize, ToSchema)]
pub struct CreateItemRequest {
pub name: String,
}
#[derive(Serialize, ToSchema)]
pub struct ItemResponse {
pub id: Uuid,
pub name: String,
}
#[utoipa::path(
post,
path = "/api/v1/items",
tag = "Items",
request_body = CreateItemRequest,
responses(
(status = 201, description = "Item created", body = ItemResponse),
(status = 400, description = "Bad request"),
(status = 500, description = "Internal server error")
),
security(("bearer_auth" = []))
)]
pub async fn create_item(
State(store): State<AppState>,
Json(payload): Json<CreateItemRequest>,
) -> impl IntoResponse {
// implementation...
}Add imports:
use crate::item_handlers::*;Add to paths:
#[openapi(
paths(
// ... existing paths ...
create_item,
list_items,
// etc.
),Add schemas:
components(
schemas(
// ... existing schemas ...
CreateItemRequest,
ItemResponse,
)
)Add tag (if new category):
tags(
// ... existing tags ...
(name = "Items", description = "Item management endpoints"),
)cd pangolin
cargo run -p pangolin_api --bin export_openapi json 2>/dev/null > ../docs/api/openapi.json
cargo run -p pangolin_api --bin export_openapi yaml 2>/dev/null > ../docs/api/openapi.yamlCheck Swagger UI to ensure your new endpoint appears correctly.
The export utility is located at:
pangolin/pangolin_api/src/bin/export_openapi.rs
It supports both JSON and YAML formats via command-line argument:
cargo run --bin export_openapi json- Outputs JSONcargo run --bin export_openapi yaml- Outputs YAML
- Total Handlers: 67
- API Tags: 13
- Core Models: 35+
- OpenAPI Version: 3.0.3
- JSON Size: ~4734 lines
- YAML Size: ~3051 lines
Consider adding these commands to your CI/CD pipeline or Makefile:
# Makefile
.PHONY: openapi-json openapi-yaml openapi-all
openapi-json:
cd pangolin && cargo run -p pangolin_api --bin export_openapi json 2>/dev/null > ../docs/api/openapi.json
openapi-yaml:
cd pangolin && cargo run -p pangolin_api --bin export_openapi yaml 2>/dev/null > ../docs/api/openapi.yaml
openapi-all: openapi-json openapi-yaml
@echo "✓ OpenAPI documentation regenerated"Then simply run:
make openapi-all