How to Show XML Responses in FastAPI Docs
In the dynamic landscape of modern software development, Application Programming Interfaces (APIs) serve as the fundamental backbone for communication between disparate systems. FastAPI, a high-performance web framework for building APIs with Python 3.7+ based on standard Python type hints, has rapidly gained traction due to its incredible speed, ease of use, and automatic interactive API documentation provided by Swagger UI and ReDoc. These documentation interfaces are derived directly from the OpenAPI Specification, offering developers and consumers an intuitive way to explore and interact with an api.
However, while FastAPI excels at generating documentation for JSON responses—its default and most common output format—the world of APIs is far more diverse. Many enterprise systems, legacy applications, and specialized industry standards still rely heavily on XML (Extensible Markup Language) for data exchange. For developers building new APIs with FastAPI that need to interface with such systems, or for those modernizing existing XML-based services, the challenge arises: how do you effectively represent and document XML responses within FastAPI's otherwise excellent documentation interface? This article delves deep into this crucial topic, providing comprehensive strategies, detailed code examples, and best practices to ensure your FastAPI APIs, even those returning XML, are impeccably documented. We will explore the nuances of the OpenAPI Specification, FastAPI's powerful features, and practical approaches to gracefully display XML response schemas and examples, ensuring clarity and usability for all API consumers.
The Foundation: Understanding FastAPI's Documentation Mechanism
Before we tackle the specifics of XML, it's essential to grasp how FastAPI generates its impressive interactive documentation. FastAPI leverages Python's type hints and Pydantic models to automatically infer the structure of your API requests and responses. This inference is then translated into an OpenAPI Specification (formerly known as Swagger Specification) document. This JSON or YAML file acts as a universal, language-agnostic description of your API, outlining its endpoints, parameters, authentication methods, and, crucially, its response structures.
Swagger UI and ReDoc are powerful tools that consume this OpenAPI Specification. They parse the machine-readable API definition and render it into user-friendly, interactive web pages. For instance, if your FastAPI endpoint is defined with a Pydantic response_model, FastAPI knows precisely what JSON schema to generate. When a client requests that endpoint through the documentation interface, Swagger UI will display a clear, collapsible schema defining each field, its type, and any validation rules. It even provides an "Example Value" section, often generated from the Pydantic model's default values or a simple example, demonstrating what a typical JSON response might look like.
The core of this mechanism lies in the content object within the OpenAPI Specification's responses section. For a typical JSON response, this would look something like:
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/MyPydanticModel'
Here, application/json is the media_type, indicating the format of the response body. The schema then references a component where the detailed structure of MyPydanticModel is defined using JSON Schema. This seamless integration makes documenting JSON responses trivial in FastAPI. However, when the expected media_type shifts from application/json to application/xml, the standard Pydantic-to-JSON Schema mapping no longer applies directly for the content itself, presenting the core challenge we aim to solve.
The Core Challenge: Representing XML in OpenAPI
The fundamental hurdle in documenting XML responses in FastAPI's auto-generated docs stems from OpenAPI's native schema definition language: JSON Schema. While JSON Schema is incredibly powerful for describing JSON structures, it doesn't natively understand or validate XML-specific constructs like attributes, namespaces, or mixed content as an XML Schema Definition (XSD) would.
When you specify application/xml as the media_type in your OpenAPI content object, OpenAPI still expects a schema property. However, instead of referencing a JSON Schema for an XML structure, which can be conceptually awkward, the most straightforward and universally supported way to show XML in the documentation is through the example or examples fields directly within the content object. These fields allow you to provide literal string examples of the response body, which the Swagger UI and ReDoc parsers can then display directly.
Let's break down the relevant parts of the OpenAPI Specification:
The content Object and media_type
As seen before, the content object is a map of media_type to a MediaType Object. The MediaType Object describes the content of a body. When dealing with XML, you explicitly set the media_type to application/xml.
The schema Property for XML
When using application/xml, the schema property is still available. You can technically describe an XML structure using JSON Schema, but it often leads to complex and less intuitive definitions that don't fully capture the nuances of XML. For instance, attributes might be represented as fields prefixed with @, and text content as #text. This approach requires a specific convention that is not universally adopted or easily understood by all API consumers just by looking at the schema. Furthermore, FastAPI's automatic schema generation from Pydantic models is tailored for JSON serialization, making it difficult to automatically generate a JSON Schema that accurately reflects an XML structure for display in the documentation.
A more practical use of schema for XML responses in OpenAPI might involve referencing an external XML Schema Definition (XSD). While OpenAPI allows for this by providing a format of xml and potentially a schema with an externalDocs link to the XSD, most interactive documentation UIs like Swagger UI do not natively render or validate against external XSDs. They primarily focus on displaying the schema described in the OpenAPI document itself. Therefore, relying solely on schema for complex XML definitions within OpenAPI for display purposes in Swagger UI is generally not the most effective strategy.
The Crucial example and examples Fields
This is where the magic happens for displaying XML. Instead of, or in addition to, attempting to define a JSON Schema for your XML, you can directly embed an example of the XML response.
exampleField: This field allows you to provide a single, literal string example of the response body. When Swagger UI renders this, it will simply display the provided string under an "Example Value" tab for theapplication/xmlmedia type. This is incredibly useful for straightforward, common response scenarios.yaml content: application/xml: example: | <root> <item id="1"> <name>Example Item 1</name> <value>100</value> </item> </root>The|(pipe) character in YAML indicates a multi-line string, which is perfect for embedding formatted XML.examplesField: For more complex APIs where different scenarios might lead to varied XML responses (e.g., success, error, different data structures), theexamplesfield is indispensable. It's a map where each key is a descriptive name for an example, and its value is anExample Object. AnExample Objectcan contain avalue(the literal example string), asummary, and an optionaldescription. This allows for a richer and more informative documentation experience.yaml content: application/xml: examples: successfulResponse: summary: A successful response value: | <response status="success"> <data> <message>Operation completed successfully.</message> </data> </response> errorResponse: summary: An error response value: | <response status="error"> <error code="400"> <message>Invalid input parameters.</message> </error> </response>When rendered in Swagger UI, theseexampleswill appear as a dropdown menu, allowing users to select and view different XML response scenarios. This dramatically enhances the clarity and completeness of your API documentation, especially for APIs that have complex business logic with multiple possible outcomes.
The key takeaway is that for demonstrating the actual structure and content of an XML response within FastAPI's OpenAPI-driven documentation, leveraging the example or examples fields within the content object for the application/xml media type is the most robust and widely supported method. It directly presents the XML payload as intended, sidestepping the complexities of trying to force an XML structure into a JSON Schema representation for visual documentation purposes. The following sections will illustrate how to implement these concepts effectively within your FastAPI applications.
Practical Solutions in FastAPI for XML Responses
Now that we understand the underlying OpenAPI mechanisms, let's explore practical, code-driven solutions for displaying XML responses in your FastAPI documentation. We will progress from simpler approaches suitable for basic needs to more advanced strategies that integrate better with Python's XML handling capabilities.
Solution 1: Using the responses Argument with example (Simplest Approach)
FastAPI provides a powerful responses argument in its path operation decorators (e.g., @app.get, @app.post). This argument allows you to explicitly define the OpenAPI responses object for a specific endpoint, giving you granular control over how different HTTP status codes and media types are documented. This is the most straightforward way to add an XML example.
First, ensure you have FastAPI and Uvicorn installed:
pip install fastapi "uvicorn[standard]"
Consider a simple FastAPI application that returns an XML string. To ensure the client knows it's receiving XML, you should use FastAPI's Response class and specify media_type="application/xml".
from fastapi import FastAPI, Response
from typing import Dict, Any
app = FastAPI(title="XML Response API Example")
@app.get(
"/techblog/en/xml-data-simple",
summary="Get simple XML data with example in docs",
response_description="A simple XML string response",
responses={
200: {
"description": "Successful retrieval of XML data",
"content": {
"application/xml": {
"example": "<data><message>Hello, XML World!</message></data>"
}
},
}
}
)
async def get_simple_xml_data() -> Response:
"""
Returns a very basic XML message.
The documentation for this endpoint will include an XML example.
"""
xml_content = "<data><message>Hello, XML World!</message></data>"
return Response(content=xml_content, media_type="application/xml")
# To run this: uvicorn your_file_name:app --reload
# Then navigate to http://127.0.0.1:8000/docs
Detailed Explanation:
from fastapi import FastAPI, Response: We importFastAPIfor our application instance andResponsefor manually constructing HTTP responses.@app.get(...): This decorator defines a GET endpoint.response_description="A simple XML string response": This provides a general description for the response section in the docs.responses={...}: This is the core part.200: { ... }: We specify the documentation for an HTTP 200 OK response."description": "Successful retrieval of XML data": A specific description for the 200 response."content": { ... }: This dictionary specifies the media types the response can take."application/xml": { ... }: Here, we explicitly state that for theapplication/xmlmedia type."example": "<data><message>Hello, XML World!</message></data>": This is the crucial part. We provide a literal string of XML that will be displayed in the "Example Value" section of the Swagger UI for theapplication/xmlresponse.
async def get_simple_xml_data() -> Response:: The asynchronous path operation function.xml_content = ...: We define our XML string.return Response(content=xml_content, media_type="application/xml"): We return aResponseobject. It's vital to setmedia_type="application/xml"so that FastAPI sends the correctContent-Typeheader in the actual HTTP response.
When you run this application and navigate to /docs, you will see the /xml-data-simple endpoint. Expanding it will show a 200 OK response with application/xml as a selectable media type. Under application/xml, you'll find the "Example Value" displaying the XML string exactly as you defined it.
Advantages: * Extremely simple to implement for static or predictable XML responses. * Directly shows the desired XML structure to API consumers. * No complex schema mapping required.
Limitations: * The example is a static string; it doesn't dynamically update with changes in your XML generation logic unless you manually change it in the responses dictionary. * Doesn't provide any schema validation for the XML itself within the OpenAPI document (as discussed, OpenAPI's native schema is JSON Schema).
Solution 2: Using the responses Argument with examples (More Flexible)
For APIs with more varied XML responses, such as different success scenarios, error conditions, or alternative data structures, the examples field is a superior choice. It allows you to define multiple named examples, each with its own summary and value.
Let's modify the previous example to show a successful response and an error response.
from fastapi import FastAPI, Response, HTTPException, status
from typing import Optional
app = FastAPI(title="XML Response API with Multiple Examples")
@app.get(
"/techblog/en/xml-data-complex/{item_id}",
summary="Get complex XML data with multiple examples in docs",
response_description="Detailed XML response based on item ID",
responses={
200: {
"description": "Successful retrieval of item details in XML",
"content": {
"application/xml": {
"examples": {
"successfulItem": {
"summary": "Successful response for a valid item",
"value": """<item>
<id>123</id>
<name>Example Widget</name>
<description>A highly functional widget for demonstrations.</description>
<price currency="USD">99.99</price>
</item>"""
},
"anotherSuccessfulItem": {
"summary": "Successful response for a different valid item",
"value": """<item>
<id>456</id>
<name>Super Gadget</name>
<description>An advanced gadget with many features.</description>
<price currency="EUR">149.00</price>
</item>"""
}
}
}
},
},
404: {
"description": "Item not found in XML format",
"content": {
"application/xml": {
"examples": {
"itemNotFound": {
"summary": "Error response when item is not found",
"value": """<error>
<code>404</code>
<message>Item with ID 'xyz' not found.</message>
</error>"""
}
}
}
},
},
500: {
"description": "Server error in XML format",
"content": {
"application/xml": {
"examples": {
"internalServerError": {
"summary": "General internal server error",
"value": """<error>
<code>500</code>
<message>An unexpected server error occurred.</message>
</error>"""
}
}
}
},
}
}
)
async def get_complex_xml_data(item_id: str) -> Response:
"""
Retrieves detailed XML data for a given item ID.
Demonstrates different XML responses for success and error scenarios.
"""
if item_id == "123":
xml_content = """<item>
<id>123</id>
<name>Example Widget</name>
<description>A highly functional widget for demonstrations.</description>
<price currency="USD">99.99</price>
</item>"""
return Response(content=xml_content, media_type="application/xml")
elif item_id == "456":
xml_content = """<item>
<id>456</id>
<name>Super Gadget</name>
<description>An advanced gadget with many features.</description>
<price currency="EUR">149.00</price>
</item>"""
return Response(content=xml_content, media_type="application/xml")
else:
# For simplicity, we'll return a 404 response with XML directly here.
# In a real application, you might raise HTTPException and catch it
# with custom exception handlers to return XML errors.
error_xml = f"""<error>
<code>404</code>
<message>Item with ID '{item_id}' not found.</message>
</error>"""
return Response(content=error_xml, media_type="application/xml", status_code=status.HTTP_404_NOT_FOUND)
# To run this: uvicorn your_file_name:app --reload
# Then navigate to http://127.0.0.1:8000/docs
Detailed Explanation:
- Multiple
responsesentries: We now have documentation for200,404, and500HTTP status codes. "content": { "application/xml": { "examples": { ... } } }: Inside thecontentforapplication/xml, we use the"examples"dictionary.- Named Examples:
"successfulItem"and"anotherSuccessfulItem"are keys for different successful XML responses."itemNotFound"for a 404 error."internalServerError"for a 500 error.- Each named example has a
"summary"(a short description) and a"value"(the actual XML string). Using triple quotes"""..."""for multi-line strings is often more readable for larger XML blocks.
get_complex_xml_datafunction: The logic within the function demonstrates how you might dynamically generate different XML based on input, and how it maps to the documented examples. The 404 response is returned directly usingResponsewithstatus_code. For more robust error handling, FastAPI'sHTTPExceptioncan be used, potentially with custom exception handlers to format errors as XML.
When you view this in Swagger UI, for the /xml-data-complex/{item_id} endpoint, the 200 OK response will have a dropdown menu under the application/xml media type, allowing users to switch between "Successful response for a valid item" and "Successful response for a different valid item". Similarly, the 404 and 500 responses will show their respective error XML examples. This provides a much richer and more illustrative documentation experience, crucial for complex APIs.
Advantages: * Clearly documents multiple possible XML response scenarios for various HTTP status codes. * Improves clarity for API consumers by providing concrete examples of typical and edge-case responses. * Highly flexible and descriptive.
Limitations: * Still requires manual upkeep of the XML example strings within the responses dictionary. If your XML generation logic changes significantly, you'll need to update these examples.
Solution 3: Custom XMLResponse Class for Encapsulation and Reusability
FastAPI offers a base Response class that is highly flexible. For specific media types, it also provides pre-built response classes like JSONResponse or HTMLResponse. While XMLResponse is not a standard pre-built class in fastapi.responses (as of my last update, it might be in some community extensions or easily implemented), creating one is straightforward and can greatly improve code cleanliness and reusability when dealing with XML. This custom class ensures the Content-Type header is always correctly set to application/xml.
from fastapi import FastAPI, Response, status
from starlette.responses import PlainTextResponse
# Define a custom XMLResponse class for cleaner code
class XMLResponse(Response):
media_type = "application/xml"
app = FastAPI(title="XML Response API with Custom XMLResponse")
@app.get(
"/techblog/en/item-details/{item_id}",
summary="Get item details using custom XMLResponse",
response_description="Detailed item information in XML format",
responses={
200: {
"description": "Successful retrieval of item details",
"content": {
"application/xml": {
"examples": {
"foundItem": {
"summary": "Example of a found item",
"value": """<item id="item-A">
<name>Alpha Device</name>
<description>First generation testing device.</description>
<manufacturer>TechCorp</manufacturer>
<status>Active</status>
</item>"""
}
}
}
},
},
404: {
"description": "Item not found",
"content": {
"application/xml": {
"examples": {
"notFoundError": {
"summary": "Error when item is not found",
"value": """<error>
<code>ITEM_NOT_FOUND</code>
<message>The requested item could not be located.</message>
</error>"""
}
}
}
},
}
}
)
async def get_item_details(item_id: str) -> XMLResponse:
"""
Retrieves item details and returns them as XML, using a custom XMLResponse class.
"""
if item_id == "item-A":
xml_content = """<item id="item-A">
<name>Alpha Device</name>
<description>First generation testing device.</description>
<manufacturer>TechCorp</manufacturer>
<status>Active</status>
</item>"""
return XMLResponse(content=xml_content)
else:
error_xml = """<error>
<code>ITEM_NOT_FOUND</code>
<message>The requested item could not be located.</message>
</error>"""
# Note: XMLResponse inherits from Response, so status_code can be passed
return XMLResponse(content=error_xml, status_code=status.HTTP_404_NOT_FOUND)
# To run this: uvicorn your_file_name:app --reload
# Then navigate to http://127.0.0.1:8000/docs
Detailed Explanation:
class XMLResponse(Response): media_type = "application/xml": This simple class inherits fromfastapi.Response(orstarlette.responses.Responsewhichfastapi.Responseinherits from) and overrides themedia_typeattribute. Now, any instance ofXMLResponsewill automatically send theContent-Type: application/xmlheader.-> XMLResponse: The path operation function's return type hint is nowXMLResponse, clearly indicating what type of response is expected.return XMLResponse(content=xml_content): When returning the XML, you simply instantiateXMLResponsewith your XML string. You don't need to specifymedia_typeevery time.- Documentation
responses: The documentation part remains the same as in Solution 2, usingexampleswithin thecontentforapplication/xml. The customXMLResponseclass primarily cleans up the code that generates the actual HTTP response, not the documentation definition.
Advantages: * Encapsulation: Keeps the application/xml media type definition in one place. * Reusability: Avoids repeating media_type="application/xml" across multiple endpoints. * Readability: Makes the path operation function cleaner and easier to understand. * Consistency: Ensures all XML responses uniformly carry the correct Content-Type header.
Solution 4: Integrating with Python XML Libraries for Dynamic XML Generation
In real-world scenarios, your XML responses are rarely static strings. They are typically generated dynamically from data retrieved from databases, other services, or calculated results. While FastAPI's responses argument provides static documentation examples, your application logic will often use Python's built-in xml.etree.ElementTree or the more powerful lxml library to construct XML.
This solution combines dynamic XML generation in your application logic with the static examples in your documentation. The challenge here is ensuring your static documentation examples accurately reflect the structure of your dynamically generated XML.
First, install lxml if you plan to use it (it's often preferred for performance and features over ElementTree):
pip install lxml
Now, let's create an example that generates XML from some structured data using lxml and still documents it using responses with examples.
from fastapi import FastAPI, Response, status
from typing import Dict, Any
from lxml import etree # Using lxml for robust XML generation
# Custom XMLResponse class from Solution 3
class XMLResponse(Response):
media_type = "application/xml"
app = FastAPI(title="Dynamic XML Generation & Documentation")
def generate_product_xml(product_data: Dict[str, Any]) -> str:
"""Helper function to generate XML from a dictionary."""
root = etree.Element("product")
root.set("id", str(product_data["id"]))
name = etree.SubElement(root, "name")
name.text = product_data["name"]
description = etree.SubElement(root, "description")
description.text = product_data["description"]
price = etree.SubElement(root, "price")
price.text = str(product_data["price"])
price.set("currency", product_data["currency"])
# Add features as a list
features_element = etree.SubElement(root, "features")
for feature_text in product_data.get("features", []):
feature_elem = etree.SubElement(features_element, "feature")
feature_elem.text = feature_text
return etree.tostring(root, pretty_print=True, encoding='utf-8').decode()
@app.get(
"/techblog/en/products/{product_id}",
summary="Get product details with dynamic XML generation",
response_description="Dynamically generated product details in XML",
responses={
200: {
"description": "Product details successfully retrieved",
"content": {
"application/xml": {
"examples": {
"productFound": {
"summary": "Example of a successfully retrieved product",
"value": """<product id="PROD-001">
<name>Smartwatch X1</name>
<description>Latest model smartwatch with health tracking.</description>
<price currency="USD">299.99</price>
<features>
<feature>Heart Rate Monitor</feature>
<feature>GPS Tracking</feature>
<feature>Waterproof</feature>
</features>
</product>"""
}
}
}
},
},
404: {
"description": "Product not found",
"content": {
"application/xml": {
"examples": {
"productNotFound": {
"summary": "Error when product is not found",
"value": """<error>
<code>PRODUCT_NOT_FOUND</code>
<message>Product with the specified ID does not exist.</message>
</error>"""
}
}
}
},
}
}
)
async def get_product_details(product_id: str) -> XMLResponse:
"""
Retrieves product details dynamically and returns them as XML.
"""
# Simulate database or external API call
products_db = {
"PROD-001": {
"id": "PROD-001",
"name": "Smartwatch X1",
"description": "Latest model smartwatch with health tracking.",
"price": 299.99,
"currency": "USD",
"features": ["Heart Rate Monitor", "GPS Tracking", "Waterproof"]
},
"PROD-002": {
"id": "PROD-002",
"name": "Wireless Earbuds Z",
"description": "High-fidelity audio with noise cancellation.",
"price": 149.00,
"currency": "EUR",
"features": ["Noise Cancellation", "Long Battery Life"]
}
}
product = products_db.get(product_id)
if product:
xml_content = generate_product_xml(product)
return XMLResponse(content=xml_content)
else:
error_xml = """<error>
<code>PRODUCT_NOT_FOUND</code>
<message>Product with the specified ID does not exist.</message>
</error>"""
return XMLResponse(content=error_xml, status_code=status.HTTP_404_NOT_FOUND)
# To run this: uvicorn your_file_name:app --reload
# Then navigate to http://127.0.0.1:8000/docs
Detailed Explanation:
generate_product_xml(product_data: Dict[str, Any]) -> str: This helper function takes a Python dictionary and useslxml.etreeto construct an XML document.etree.Element("product"): Creates the root element.root.set("id", ...): Sets an attribute on the root element.etree.SubElement(root, "name"): Creates a child element.name.text = ...: Sets the text content of an element.etree.tostring(root, pretty_print=True, encoding='utf-8').decode(): Serializes thelxmlelement tree to a nicely formatted (pretty-printed), UTF-8 encoded string.
get_product_details: This path operation function simulates fetching product data.- If a product is found, it calls
generate_product_xmlto construct the XML. - It then returns an
XMLResponsewith the dynamically generated content.
- If a product is found, it calls
- Documentation
responses: Crucially, the documentation examples (productFound,productNotFound) are still hardcoded strings. It is the developer's responsibility to ensure these examples accurately reflect the XML structure generated bygenerate_product_xml. This often means running thegenerate_product_xmlfunction once with sample data, capturing its output, and pasting it into theexamplesfield.
Advantages: * Dynamic Content: Your API can generate complex, dynamic XML based on real data. * Structured Generation: Using XML libraries ensures well-formed XML and allows for programmatic control over structure, attributes, and namespaces.
Limitations: * Manual Doc Sync: The documentation examples (examples in responses) are static and must be manually synchronized with your dynamic XML generation logic. Any change in the XML structure within generate_product_xml requires a corresponding manual update in the responses dictionary to keep the documentation accurate. * No automatic schema generation for the XML structure from the Python code for application/xml media type.
Mentioning APIPark for Enhanced API Management
In a scenario where you're building a diverse set of APIs—some returning JSON, others XML, and perhaps even integrating with AI models—managing their lifecycle, documentation, and access can become complex. This is where advanced API management platforms like APIPark become invaluable. APIPark, an open-source AI gateway and API developer portal, is designed to help developers and enterprises manage, integrate, and deploy AI and REST services with ease.
For APIs that return XML, APIPark can act as a central gateway, providing a unified management system. It can standardize the api invocation format for diverse backend services, including those delivering XML payloads, making them easier to consume and manage. By offering end-to-end API lifecycle management, APIPark ensures that your APIs, regardless of their underlying response format (JSON, XML, etc.), are consistently documented, secured, and discoverable through a centralized API service sharing mechanism within teams. This means that while FastAPI handles the immediate documentation of XML examples, a platform like APIPark takes that a step further by offering enterprise-grade governance, traffic management, and detailed call logging for all your API resources, including those with custom XML responses. It bridges the gap between individual service documentation and a coherent, organization-wide OpenAPI ecosystem.
Solution 5: Using response_model with response_class (A Niche Case for XML)
FastAPI's response_model argument is primarily designed for Pydantic models to define JSON response schemas. When combined with response_class, it can be conceptually challenging for XML because Pydantic's serialization mechanism defaults to JSON. However, if you have a Pydantic model that represents the data you want to serialize into XML, and you handle the XML conversion manually within a custom response_class, you can still leverage response_model for the documentation of the data structure, even if the example shown for application/xml is still hardcoded.
This approach is less about automatic XML schema generation in the docs and more about: 1. Using Pydantic for input validation/output structure definition. 2. Providing an XML example string for application/xml in responses. 3. Using a custom response_class to serialize the Pydantic model into XML before sending.
Let's illustrate how this might work. We'll need a custom XMLResponse that knows how to convert a Pydantic model into XML. This requires a custom serializer, which can be somewhat involved. For simplicity in demonstrating the response_model and response_class concept together, we'll demonstrate a custom XMLResponse that converts a dictionary (which a Pydantic model can easily become) into XML.
from fastapi import FastAPI, Response, status
from pydantic import BaseModel, Field
from typing import List, Optional, Dict, Any
from lxml import etree # For XML generation
# 1. Define a Pydantic model for your data
class Feature(BaseModel):
name: str
class ProductDetail(BaseModel):
id: str = Field(..., example="PROD-001")
name: str = Field(..., example="Smartwatch X1")
description: Optional[str] = Field(None, example="Latest model smartwatch with health tracking.")
price: float = Field(..., example=299.99)
currency: str = Field(..., example="USD")
features: List[Feature] = Field(default_factory=list)
# 2. Custom XMLResponse that serializes a Pydantic model (or dict) to XML
class PydanticToXMLResponse(Response):
media_type = "application/xml"
def render(self, content: Any) -> bytes:
if isinstance(content, BaseModel):
data = content.model_dump(mode="json") # Convert Pydantic model to dictionary
elif isinstance(content, dict):
data = content
else:
# Fallback for non-Pydantic/dict content, e.g., raw XML string if passed
return super().render(content)
# Simplified XML conversion from dictionary to demonstrate
root = etree.Element("product")
root.set("id", data.get("id", "unknown"))
etree.SubElement(root, "name").text = data.get("name")
if data.get("description"):
etree.SubElement(root, "description").text = data["description"]
price_elem = etree.SubElement(root, "price")
price_elem.text = str(data.get("price"))
price_elem.set("currency", data.get("currency", "N/A"))
features_elem = etree.SubElement(root, "features")
for feature in data.get("features", []):
if isinstance(feature, dict): # Pydantic model's List[Feature] might become List[Dict]
etree.SubElement(features_elem, "feature").text = feature.get("name")
else: # If it's a Feature object, access .name
etree.SubElement(features_elem, "feature").text = feature.name
return etree.tostring(root, pretty_print=True, encoding='utf-8')
app = FastAPI(title="Pydantic Model to XML Response")
@app.get(
"/techblog/en/product-data/{product_id}",
summary="Get product data, defined by Pydantic, returned as XML",
response_model=ProductDetail, # Pydantic model for JSON schema docs
response_class=PydanticToXMLResponse, # Custom response class for actual XML output
responses={
200: {
"description": "Product details successfully retrieved as XML",
"content": {
"application/xml": {
"examples": {
"productXMLResponse": {
"summary": "XML response for a product",
"value": """<product id="PROD-001">
<name>Smartwatch X1</name>
<description>Latest model smartwatch with health tracking.</description>
<price currency="USD">299.99</price>
<features>
<feature>Heart Rate Monitor</feature>
<feature>GPS Tracking</feature>
<feature>Waterproof</feature>
</features>
</product>"""
}
}
}
},
},
404: {
"description": "Product not found (XML error)",
"content": {
"application/xml": {
"examples": {
"productNotFoundXML": {
"summary": "XML error response for not found product",
"value": """<error>
<code>PRODUCT_NOT_FOUND</code>
<message>Product with the specified ID does not exist.</message>
</error>"""
}
}
}
}
}
}
)
async def get_product_data(product_id: str) -> ProductDetail:
"""
Retrieves product details. The response_model is for internal Pydantic validation
and JSON schema generation for other media types. Actual XML output via custom response_class.
"""
products_db = {
"PROD-001": ProductDetail(
id="PROD-001",
name="Smartwatch X1",
description="Latest model smartwatch with health tracking.",
price=299.99,
currency="USD",
features=[Feature(name="Heart Rate Monitor"), Feature(name="GPS Tracking"), Feature(name="Waterproof")]
)
}
product = products_db.get(product_id)
if product:
return product # FastAPI will pass this Pydantic model to PydanticToXMLResponse.render()
else:
# For error, we manually return XML response for simplicity, bypassing response_model
error_xml = """<error>
<code>PRODUCT_NOT_FOUND</code>
<message>Product with the specified ID does not exist.</message>
</error>"""
return PydanticToXMLResponse(content=error_xml, status_code=status.HTTP_404_NOT_FOUND)
# To run this: uvicorn your_file_name:app --reload
# Then navigate to http://127.0.0.1:8000/docs
Detailed Explanation:
ProductDetailPydantic Model: We define a Pydantic model as usual. This model will be used byresponse_modelto generate a JSON schema in the OpenAPI documentation.PydanticToXMLResponseCustom Class:- It inherits from
Response. - It overrides the
rendermethod. This method is called by FastAPI just before sending the response, with the return value of your path operation function as itscontentargument. - Inside
render, we check ifcontentis a Pydantic model. If so, we convert it to a dictionary usingcontent.model_dump(mode="json"). - Then, we use
lxml.etreeto construct XML from this dictionary. This is a manual conversion logic that maps dictionary keys/values to XML elements/attributes. This logic needs to match your desired XML structure.
- It inherits from
@app.get(..., response_model=ProductDetail, response_class=PydanticToXMLResponse, ...):response_model=ProductDetail: This tells FastAPI to useProductDetailto validate the outgoing response and to generate a JSON schema for it in the OpenAPI documentation. This schema will not be forapplication/xml, but rather forapplication/jsonif that media type were supported. It's useful for showing the underlying data structure in a JSON-centric way even if the actual output is XML.response_class=PydanticToXMLResponse: This tells FastAPI to use our customPydanticToXMLResponseclass to render the actual HTTP response. The value returned byget_product_data(aProductDetailinstance) will be passed toPydanticToXMLResponse.render().
responsesArgument: Crucially, the XML documentation still relies on theexamplesfield within theapplication/xmlcontent type. Theresponse_modelhere helps document the data structure in a Pydantic/JSON Schema way, which might be visible in other parts of the docs or for othermedia_types if you were to define them. It does not automatically convert your Pydantic model into an XML schema for display underapplication/xmlin Swagger UI. You still provide the literal XML example string.
Advantages: * Pydantic Validation: You still get the benefits of Pydantic for defining and validating your data structure internally. * Decoupled Serialization: The XML serialization logic is encapsulated within the PydanticToXMLResponse class, making your path operation functions cleaner. * Clearer Data Model: response_model provides a clear, JSON-schema-based definition of your data for those who prefer to understand the core data structure separate from its XML representation.
Limitations: * No Automatic XML Schema in Docs: This approach does not magically generate a detailed, interactive XML schema (like an XSD) for application/xml in Swagger UI based on your Pydantic model. The responses dictionary with examples is still the primary way to show the XML. * Manual XML Conversion Logic: The render method requires you to manually write the logic to convert your Pydantic model (or dictionary) into the desired XML structure. This can become complex for very intricate XML. * Potential Confusion: Developers might see a response_model and expect application/json output or an XML schema derived from it, which isn't the case here for application/xml in documentation.
This table provides a concise comparison of the discussed strategies:
| Strategy | Primary Mechanism | Pros | Cons | Best Use Case |
|---|---|---|---|---|
1. responses with example |
Hardcoded XML string in responses |
Simple to implement, direct visual representation. | Static example, no dynamic updates. | Basic endpoints with predictable, unchanging XML responses. |
2. responses with examples |
Multiple hardcoded XML strings in responses |
Documents various scenarios (success, error, different data). Richer documentation. | Static examples, requires manual synchronization if XML generation logic changes. | APIs with multiple distinct XML response types for different HTTP statuses or business logic outcomes. |
3. Custom XMLResponse class |
Custom Response subclass overriding media_type |
Encapsulates XML media type, cleaner path operations, reusable. | Still relies on responses for doc examples; doesn't automate doc generation. |
Any API that frequently returns XML, enhancing code hygiene. |
4. Dynamic XML + examples |
Python XML libs (e.g., lxml) for generation; responses for docs |
Real-world dynamic content, structured XML generation. | Documentation examples are static and require manual synchronization with dynamic generation logic. | APIs where XML content is derived from dynamic data, ensuring the API itself works correctly, and documentation is maintained manually. |
5. response_model + response_class |
Pydantic response_model for data structure; custom Response for XML serialization; responses for XML examples. |
Benefits of Pydantic for data definition/validation; decoupled serialization. | No automatic XML schema generation in docs; complex custom render logic; doc examples still manual. |
When you want to define the data structure using Pydantic, but the output format must be XML via custom serialization. |
Summary of XML Response in FastAPI Docs
To summarize, the most effective and universally understood way to show XML responses in FastAPI's auto-generated documentation (Swagger UI/ReDoc) is to leverage the responses argument in your path operation decorators, specifying application/xml as the media type, and then providing concrete XML examples using the example or, more flexibly, the examples field. While FastAPI's response_model is fantastic for JSON schema generation, it does not automatically translate into interactive XML schema documentation for application/xml media types. Your Python code will handle the actual XML generation, and you manually provide representative examples for the documentation.
APIPark is a high-performance AI gateway that allows you to securely access the most comprehensive LLM APIs globally on the APIPark platform, including OpenAI, Anthropic, Mistral, Llama2, Google Gemini, and more.Try APIPark now! 👇👇👇
Best Practices and Considerations
Effectively documenting XML responses in FastAPI goes beyond just technical implementation; it involves strategic considerations to ensure your APIs are robust, maintainable, and developer-friendly.
1. Accuracy and Consistency of XML Examples
The most critical best practice is to ensure that your XML examples in the responses argument are always accurate and consistent with the actual XML generated by your FastAPI application. Outdated or incorrect examples are worse than no examples, as they mislead API consumers and lead to frustration.
- Automated Example Generation (where possible): For complex XML, consider writing a small script or a test case that generates a sample XML payload from your data generation logic. You can then copy-paste this generated XML into your
responsesdictionary. This reduces human error and helps keep examples aligned with code changes. - Version Control: Treat your
responsesexamples as part of your codebase. Any changes to the XML structure in your application should ideally trigger a review or update of the corresponding documentation examples. - Clear Summaries and Descriptions: When using
examples, provide concisesummaryfields and, if necessary, more detaileddescriptionfor each XML example. Explain when that particular XML structure would be returned and what each part of the XML represents.
2. XML Schema Definition (XSD) and External Documentation
For truly complex or rigorously defined XML formats, an XML Schema Definition (XSD) is the industry standard for formal validation and documentation. While OpenAPI's built-in schema for application/xml does not directly render an XSD in Swagger UI, you can still link to it:
- Separate Documentation: For highly intricate XML, you might maintain separate, dedicated XML documentation alongside your API documentation. The OpenAPI docs would then serve as a high-level overview with examples, while the external docs delve into XSDs, business rules, and specific XML processing instructions.
Reference XSDs: In your responses definition for application/xml, you can include an externalDocs field pointing to your XSD. This provides a clear path for developers who need the full, formal schema definition.```python
... inside responses for application/xml content type
"application/xml": { "schema": { "type": "string", # As OpenAPI schema is JSON Schema-based, use 'string' "format": "xml", # Hint that it's XML "externalDocs": { "description": "Formal XML Schema Definition for product data", "url": "https://your-domain.com/schemas/product_v1.0.xsd" } }, "examples": { # ... your XML examples here ... } } ``` This approach provides a direct link to the formal XSD, ensuring that clients can validate their XML payloads against a precise specification.
3. Content Negotiation
Modern APIs often support multiple response formats (e.g., JSON and XML) through content negotiation. Clients indicate their preferred format using the Accept HTTP header. FastAPI can handle this:
from fastapi import FastAPI, Request, Response, status
from typing import Dict, Any
from lxml import etree # For XML generation
class XMLResponse(Response):
media_type = "application/xml"
app = FastAPI(title="Content Negotiation Example")
def generate_simple_xml() -> str:
root = etree.Element("data")
etree.SubElement(root, "message").text = "Hello from XML!"
return etree.tostring(root, pretty_print=True, encoding='utf-8').decode()
@app.get(
"/techblog/en/negotiated-data",
summary="Get data in JSON or XML based on Accept header",
response_description="Response changes based on Accept header",
responses={
200: {
"description": "Data retrieved successfully.",
"content": {
"application/json": {
"example": {"message": "Hello from JSON!"}
},
"application/xml": {
"example": "<data><message>Hello from XML!</message></data>"
}
}
}
}
)
async def get_negotiated_data(request: Request):
accept_header = request.headers.get("Accept", "application/json") # Default to JSON
if "application/xml" in accept_header:
xml_content = generate_simple_xml()
return XMLResponse(content=xml_content)
else: # Default or if "application/json" is preferred
return {"message": "Hello from JSON!"}
In this example, the responses dictionary now includes both application/json and application/xml media types with their respective examples. The API logic then inspects the Accept header to return the appropriate format. This robustly documents and supports multiple formats.
4. Error Handling and Documentation for XML Errors
Just as with successful responses, consistently documenting error responses in XML is crucial. Use the responses argument to define common error codes (e.g., 400 Bad Request, 401 Unauthorized, 404 Not Found, 500 Internal Server Error) and provide illustrative XML error payloads.
- Standardized Error Format: Define a consistent XML structure for your error messages across your API. This makes it easier for clients to parse and handle errors programmatically.
- Meaningful Error Codes and Messages: Ensure your XML error messages include relevant details like an internal error code, a human-readable message, and potentially a link to more detailed error documentation.
<error>
<code>INVALID_INPUT</code>
<message>The provided 'item_id' parameter is invalid.</message>
<details>
<field>item_id</field>
<problem>Must be a positive integer.</problem>
</details>
</error>
5. API Versioning and XML Changes
When your API evolves, the structure of your XML responses might change. Proper API versioning is essential to manage these changes gracefully.
- Clear Versioning Strategy: Implement a clear API versioning strategy (e.g., URL path versioning
/v1/products,/v2/products, or header versioningX-API-Version). - Document Version-Specific XML: Ensure your documentation for each API version accurately reflects the XML structures and examples pertinent to that specific version. If a field is added, removed, or changed in
/v2/productsXML, its documentation must show that.
6. Security Considerations for XML
While primarily a runtime concern rather than a documentation one, being aware of XML security vulnerabilities is important if your API also consumes XML. For responses, ensure you are not accidentally exposing sensitive information within your XML payloads.
- Information Disclosure: Carefully review your XML response structures to prevent unintended exposure of internal system details, sensitive data, or debugging information in production environments.
- XML Signature and Encryption: For highly sensitive data exchange via XML, consider XML Digital Signature (XML-DSig) and XML Encryption standards, though implementing and documenting these goes beyond simple response examples.
7. Readability and Maintainability of Documentation
Maintaining large blocks of XML as string literals in your Python code can impact readability.
- Comments and Structure: Use Python comments liberally to explain complex parts of your
responsesdictionary. Keep your XML examples well-formatted with indentation for better readability.
Separate XML Files: For very large or frequently updated XML examples, consider storing them in separate .xml files and reading them into your Python code when defining the responses dictionary. This can clean up your Python file and allow XML tools to work with the examples directly.```python
Example of reading from file
def load_xml_example(filename: str) -> str: with open(filename, 'r', encoding='utf-8') as f: return f.read()
Then in your path operation:
"example": load_xml_example("examples/product_success.xml")
```
8. The Role of API Gateways (Revisited)
As APIs grow in number and complexity, an OpenAPI-driven gateway and management platform like APIPark becomes indispensable. While FastAPI handles the micro-level documentation, APIPark provides the macro-level governance.
- Centralized Documentation Portal: APIPark can aggregate documentation from various FastAPI services (some XML, some JSON) into a single developer portal, offering a unified view of your entire api ecosystem. This simplifies discovery and consumption for internal teams and external partners.
- Unified API Format and Orchestration: APIPark can standardize API requests and responses, potentially transforming XML from a backend service into JSON for a client, or vice-versa, offering flexibility without burdening individual services. This allows your FastAPI service to simply return XML, while APIPark handles client-specific format requirements.
- Lifecycle Management: From design to deprecation, APIPark assists with managing the entire API lifecycle. This includes managing traffic forwarding, load balancing, and versioning of published APIs, ensuring robust operations for all response types.
- Security and Access Control: APIPark offers features like subscription approval and independent API/access permissions for each tenant, adding an extra layer of security and controlled access to your APIs, regardless of their content type. This protects your XML-serving endpoints from unauthorized access and potential data breaches.
By integrating FastAPI's strengths in building high-performance APIs with APIPark's comprehensive management capabilities, organizations can create a resilient, scalable, and well-documented API infrastructure that seamlessly supports diverse data formats like XML and JSON.
Advanced Customization and Limitations
While the strategies above cover most common scenarios, there are advanced use cases and inherent limitations to consider when working with XML in FastAPI's documentation.
Custom Swagger UI/ReDoc Templates
If the default rendering of XML examples is not sufficient for your needs, the OpenAPI ecosystem is extensible. You can fork or customize the Swagger UI or ReDoc templates. This is a significant undertaking, requiring expertise in JavaScript, React/Angular/Vue (depending on the UI framework), and potentially direct manipulation of the OpenAPI specification client-side.
- Use Cases:
- Interactive XML Editor: Integrating a custom component that allows users to edit and validate the XML examples directly within the docs.
- XSD Visualization: Developing a plugin that takes an
externalDocslink to an XSD and renders a navigable tree view of the XML schema. - Custom Syntax Highlighting: Implementing specialized highlighting beyond generic XML syntax.
This level of customization typically falls outside the scope of a standard FastAPI application and would be considered an enhancement to your overall API developer portal.
Limitations of Built-in Documentation for XML
It's important to set realistic expectations regarding FastAPI's auto-generated documentation for XML:
- No Native XML Schema Interpretation: As repeatedly emphasized, Swagger UI/ReDoc primarily interpret JSON Schema. They do not natively understand, validate, or generate interactive forms based on XML Schema Definitions (XSDs) provided for
application/xmlcontent. Theexample/examplesfields provide static string representations, not dynamic schema-driven interactions. - Lack of XML Model Inference: FastAPI's automatic schema generation (
response_model) works by serializing Pydantic models to JSON Schema. There's no built-in mechanism to infer an XML structure (including attributes, namespaces, mixed content) from a Python model and translate that into aschemaproperty suitable forapplication/xmlthat Swagger UI would intelligently render beyond a simple string. - Manual Effort for Synchronization: Keeping the
responsesexamples in sync with your actual XML generation logic requires diligent manual effort. There's no automatic mechanism to regenerate these example strings directly from your Python XML generation code into the OpenAPI specification within FastAPI itself.
Using OpenAPI Extensions (x-)
The OpenAPI Specification allows for custom extensions using x- fields (e.g., x-my-custom-field). While these won't directly change how XML is rendered by default, you could use them to embed additional metadata about your XML responses that a custom Swagger UI plugin or an external tool might consume.
For example, you could include x-xml-namespaces or x-xml-root-element to provide more context about your XML. However, these would require custom frontend logic to be useful in the UI.
# ... inside responses for application/xml content type
"application/xml": {
"x-xml-root-element": "product",
"x-xml-namespaces": {
"prefix": "ns",
"uri": "http://your-domain.com/schemas/product"
},
"examples": {
# ... your XML examples here ...
}
}
This is a path for highly specific, tailored documentation requirements where you control both the OpenAPI generation and the UI consumption.
In essence, while FastAPI and OpenAPI provide robust tools for API documentation, the unique nature of XML requires a more deliberate, example-driven approach within the OpenAPI specification, often complemented by external documentation or specialized tooling for full schema validation and interactive exploration.
Conclusion
FastAPI stands as a remarkable framework for building high-performance, asynchronous APIs, largely due to its tight integration with Pydantic and its automatic generation of OpenAPI-compliant documentation. However, the modern API landscape is diverse, and the necessity to interact with or serve XML responses remains a common requirement, particularly in enterprise and legacy integration contexts. While FastAPI's documentation tools naturally lean towards JSON, bridging the gap to effectively show XML responses in Swagger UI and ReDoc is entirely achievable through strategic application of the OpenAPI Specification's responses argument, specifically by leveraging the example and examples fields for the application/xml media type.
Throughout this comprehensive guide, we've dissected the underlying mechanisms of FastAPI's documentation generation, explored the challenges posed by XML's unique structure compared to JSON Schema, and provided a series of practical, progressively sophisticated solutions. From the simplicity of directly embedding a static XML example string to the elegance of custom XMLResponse classes for cleaner code and the power of dynamic XML generation with Python libraries like lxml, we've demonstrated how to ensure your api documentation remains clear, accurate, and truly representative of your service's behavior.
We also emphasized crucial best practices: maintaining the accuracy and consistency of your XML examples, thoughtfully linking to formal XSDs for rigorous validation, implementing robust content negotiation, and providing clear error handling documentation. Furthermore, we acknowledged the inherent limitations of relying solely on built-in documentation for complex XML schemas, suggesting avenues for advanced customization or complementary external documentation.
Finally, we highlighted the broader context of API management, underscoring how platforms like APIPark can elevate your entire API ecosystem. By centralizing API governance, facilitating unified access, and providing end-to-end lifecycle management, APIPark complements FastAPI's capabilities, ensuring that your diverse APIs—whether serving JSON or XML—are discoverable, secure, and operationally sound across your organization.
In conclusion, while the path to impeccably documenting XML responses in FastAPI requires a deliberate approach that deviates slightly from the JSON-centric defaults, the tools and strategies are well-defined and highly effective. By carefully crafting your responses definitions and providing illustrative XML examples, you empower API consumers with the clarity and information they need, fostering better integration and accelerating development cycles, regardless of the underlying data format.
Frequently Asked Questions (FAQs)
1. Why don't FastAPI docs automatically show an interactive XML schema from my Pydantic model?
FastAPI leverages Pydantic models to automatically generate JSON Schema definitions for your API responses. The OpenAPI Specification, which FastAPI uses for documentation, natively supports JSON Schema. While you can specify application/xml as a media_type, OpenAPI (and thus Swagger UI/ReDoc) does not automatically convert a Pydantic model's structure into an interactive XML schema definition. Instead, it relies on the example or examples fields to display a literal XML string. Showing a full, interactive XML schema (like one derived from an XSD) would require complex schema translation and dedicated rendering capabilities not typically built into generic OpenAPI UIs.
2. What's the best way to keep my XML examples in FastAPI docs synchronized with my dynamic XML generation code?
The most robust approach involves programmatically generating your XML examples. You can write a helper function (or even a test script) that uses your Python XML generation logic (e.g., using lxml or xml.etree.ElementTree) with sample data. Capture the output of this function, and then paste that generated XML string into the example or examples field within your FastAPI responses dictionary. This reduces human error and ensures the examples reflect your actual code. For very large examples, you could store them in separate .xml files and load them into your responses dictionary at application startup.
3. Can I use OpenAPI to link to an external XML Schema Definition (XSD) for my XML responses?
Yes, you can! Within the content object for application/xml, you can include a schema property that points to external documentation. While Swagger UI/ReDoc won't natively render or validate against the XSD, you can use the externalDocs field within the schema to provide a direct URL to your XSD. This allows developers to access the formal, rigorous definition of your XML structure when needed.
content:
application/xml:
schema:
type: string # This tells OpenAPI it's a string, not a JSON structure
format: xml # Hint that it's XML content
externalDocs:
description: Formal XML Schema Definition for this response
url: https://your-domain.com/schemas/my_response_v1.xsd
examples:
# ... your XML examples here ...
4. How can I handle both JSON and XML responses for the same endpoint using content negotiation in FastAPI?
You can handle both JSON and XML responses by inspecting the Accept HTTP header in your FastAPI path operation function. The request: Request object gives you access to request.headers. Based on the Accept header (e.g., application/json or application/xml), you can return either a Python dictionary (which FastAPI automatically serializes to JSON) or an instance of your custom XMLResponse class. In your API documentation, you would define both application/json and application/xml media types within the content object of your responses dictionary, providing appropriate examples for each.
5. Where does a platform like APIPark fit into managing APIs that return XML?
APIPark serves as an AI gateway and API management platform that can significantly enhance the governance of diverse APIs, including those returning XML. While FastAPI handles the specific documentation of XML examples at the service level, APIPark provides a centralized platform for: * Unified Documentation: Aggregating API documentation from various services into a single developer portal. * API Lifecycle Management: Overseeing the design, publication, invocation, and deprecation of all APIs, regardless of their data format. * Traffic Management: Handling load balancing, routing, and versioning for XML-based APIs. * Security & Access Control: Implementing centralized authentication, authorization, and subscription approvals for all API consumers. * Unified API Format: Potentially offering data transformation capabilities to standardize response formats for clients, even if backend services return XML.
APIPark essentially provides an enterprise-grade layer of management over the individual FastAPI services, streamlining the overall OpenAPI ecosystem.
🚀You can securely and efficiently call the OpenAI API on APIPark in just two steps:
Step 1: Deploy the APIPark AI gateway in 5 minutes.
APIPark is developed based on Golang, offering strong product performance and low development and maintenance costs. You can deploy APIPark with a single command line.
curl -sSO https://download.apipark.com/install/quick-start.sh; bash quick-start.sh

In my experience, you can see the successful deployment interface within 5 to 10 minutes. Then, you can log in to APIPark using your account.

Step 2: Call the OpenAI API.

