Appearance
Working with OpenAPI in Azure API Management Manually
This section describes several challenges to consider when working with OpenAPI in Azure API Management (APIM) if you handle it manually.
Step 1: Export the OpenAPI Specification from Your App
OpenAPI Limitations in Azure API Management (APIM)
Azure API Management (APIM) supports importing APIs defined with certain versions of the OpenAPI specification. However, there are limitations and quirks you need to be aware of.
Supported OpenAPI Versions
- OpenAPI 2.0 (Swagger 2.0):
- Works reliably, though it is limited in descriptive features.
- OpenAPI 3.0.x (up to 3.0.3):
- 3.0.1: Fully supported and stable.
- 3.0.3: Automatically converted to 3.0.1 during import. Still works without issues in practice.
- OpenAPI 3.1 (import only):
- Officially listed as supported, but in practice fails.
- Typical errors include:
- “String was not recognized as a valid Boolean.”
- “The input OpenAPI file is not valid for the OpenAPI specification.”
- APIM recommends using version 3.0.1 instead.
Behavior Observed in Testing (as of September 2025)
- 2.0 → Imported and kept as 2.0.
- 3.0.1 → Imported and kept as 3.0.1.
- 3.0.3 → Imported but automatically downgraded to 3.0.1 (still functional).
- 3.1 → Import fails with parsing errors (Boolean/string mismatch).
Serving Static Files in APIM
Sometimes you need to serve static files from your app, such as images or documents. By default, APIM does not automatically support static file routing. You can use the following workaround to add static file support to APIM.
APIM Path Matching Limitations
APIM has a strict route rules that is not really friendly for static files:
/static/→/static/awill not work/static/{path}→/static/a/cwill not work either
To address this limitation, you must explicitly define the path as /static/* for URL matching to work properly.
FastAPI Static Files Support
From version 0.2.1 of haskoning_equation, static file serving is supported out of the box. Simply add an endpoint to your FastAPI app to serve files from the static directory, and use a wildcard path for proper matching in APIM.
Additional Notes
- Only one author can be specified in the description field. If multiple authors are needed, use an organization or team name instead.
- These behaviors are not dependent on the default version of the Resource Group. APIM generally uses the latest version supported in the region/location, but OpenAPI version handling remains consistent.
- Importing specifications with unsupported features or newer versions may cause deployment failures.
For official details, see Microsoft’s documentation: Restrictions and details of API formats support - Azure API Management
Generate the OpenAPI Specification from Your App
If you are not using the provided pipeline templates, you need to manually export the OpenAPI specification from your app. You can generate the OpenAPI specification file in several ways:
- For FastAPI, the OpenAPI specification is generated automatically and is accessible at
/openapi.jsonby default. - For Django REST Framework, use a package like
drf-spectacularordrf-yasgto generate the OpenAPI specification file.drf-spectacularis recommended because it supports OpenAPI 3.x and is actively maintained. Note:drf-yasgwill generate OpenAPI 2.0 and is no longer updated to the latest OpenAPI specs.
Modify the OpenAPI Specification if Needed
After generating the OpenAPI specification file, you may need to modify it to ensure compatibility with APIM. Common modifications include:
- Downgrade OpenAPI version: If your specification is in OpenAPI 3.1, downgrade it to 3.0.1 or 2.0. You can check out this tool called
openapi-down-convertto downgrade your API schema, or reference one of our schema generators. - Remove unsupported features: Check for any features or constructs not supported by APIM and remove or modify them accordingly.
- Add missing information: Ensure all required fields are filled out, such as
info,paths, andcomponents. APIM may require certain metadata to function correctly. Depending on your endpoints app, you may need to add a wildcard path likeOPTIONS /*to support CORS preflight requests for Vue.js frontend apps. These are generally ignored by the generator package.
You can then validate the generated OpenAPI schema before pushing to APIM as described here.
Step 2: Import the OpenAPI Specification into APIM
Import to APIM from a Local File
Importing routes to APIM from an OpenAPI specification file can also be handled by a pipeline template. The basic command to import an OpenAPI specification from a local file to APIM is as follows:
bash
az apim api import \
--resource-group $(apiManagementResourceGroupName) \
--service-name $(apiManagementServiceName) \
--path $(apiServiceID) \
--api-id $(apiServiceID) \
--specification-format OpenApi \
--specification-path openapi.json \
--verboseTIP
If you get an unknown error ERROR: No status found in body when importing with az apim, the role assigned to your service connection may not be sufficient. Make sure the service connection has role assigned to APIM (equation in this case).
Import to APIM from a URL
When importing an OpenAPI specification into API Management, you can either provide the specification file directly, or a URL where the specification can be found. But be aware that the URL must be publicly accessible, and we will need a way to allow the APIM to access it, this mean our app need to be run and accessible first before we can import the OpenAPI specification, due to this reason, this method is not supported with current pipeline template. But if you still want to use this method, you can follow below notes.
Make Sure Your OpenAPI Specification is Accessible
Most OpenAPI generation tools do not automatically define a route for the OpenAPI specification file itself. This will not cause problems for API Management to read the routes and apply them properly. When accessing your API through API Management however, your OpenAPI specification file cannot be reached as API Management will return a 404 Not Found for this URL instead.
If you do need direct access to your OpenAPI specification file, either make sure that your generator explicitly exposes the route, or do it manually. For example, if your specification is served at /openapi.json, add the following snippet:
hcl
resource "azurerm_api_management_api_operation" "api_operation_openapi_json" {
resource_group_name = var.resource_group_name
api_management_name = var.api_management_name
api_name = "YOUR_API_NAME"
display_name = "OpenAPI"
description = "Retrieve the OpenAPI specification file."
method = "GET"
operation_id = "openapi"
url_template = "/openapi.json"
}Re-reading the OpenAPI configuration on new API deployments
When your OpenAPI specification changes as API operations are added and removed from your API, you need to make sure that Azure API Management re-reads your specification file.
Whenever the API changes, the API deployment pipeline runs, but this does not update the API Management API instance. It does not automatically “know” that it is supposed to re-read the OpenAPI specification.
Furthermore, given that the URL to our OpenAPI specification is usually a static one, even running the Terraform deployment pipeline will still by default does not re-read the OpenAPI specification, as Terraform will think that nothing has changed (as the URL is still exactly the same as it was before).
Therefore, we need to “trick” the pipeline to force it to always re-read the OpenAPI specification. We can do so by adding a “cache buster” technique, where we simply append a fake querystring parameter to the OpenAPI url, so that it will always be different.
WARNING
While it may seem convenient to add the OpenAPI import specification to the Terraform infrastructure pipeline, this approach is not recommended. The OpenAPI specification is more closely related to the API itself rather than the infrastructure, so it is preferred to define it in the API project for better maintainability and separation of concerns.
Option 1: Defined in the API project
Set up your API pipeline to perform Terraform deployments and add the following section that contains a trick to force the pipeline into always reloading the OpenAPI specification whenever it runs.
hcl
# Define a force_reload variable that has a unique value on every run
locals {
force_reload = timestamp()
}
resource "azurerm_api_management_api" "api" {
... existing API definition
# Append this variable to the URL, so that Terraform will see it as always different
import {
content_format = "openapi+json-link"
content_value = "https://YOUR_API_BASE_URL/openapi.json?rev=${local.force_reload}"
}
}Option 2: Defined in the infrastructure project
This option requires an additional step.
First, put the snippet defined under Option 1 into the infrastructure Terraform code.
Then, include the following snippet in your pipeline definition. Replace API_PIPELINE_NAME with the name of your existing DevOps pipeline that deploys your API. This snippet will make sure your infrastructure pipeline will always run**** when the other pipeline is completed. Make sure that your API deployment pipeline also updates the running application, otherwise the OpenAPI specification may not (yet) be up to date when the infrastructure pipeline gets triggered!
yaml
resources:
pipelines:
- pipeline: api-trigger
source: API_PIPELINE_NAME
trigger: true
# Existing other pipeline definition