Add a custom javascript to the FastAPI Swagger UI docs webpage in Python

Question:

I want to load my custom javascript file or code to the FastAPI Swagger UI webpage, to add some dynamic interaction when I create a FastAPI object.

For example, in Swagger UI on docs webpage I would like to

<script src="custom_script.js"></script> 

or

<script> alert('worked!') </script>

I tried:

api = FastAPI(docs_url=None)

api.mount("/static", StaticFiles(directory="static"), name="static")

@api.get("/docs", include_in_schema=False)
async def custom_swagger_ui_html():
    return get_swagger_ui_html(
        openapi_url=api.openapi_url,
        title=api.title + " - Swagger UI",
        oauth2_redirect_url=api.swagger_ui_oauth2_redirect_url,
        swagger_js_url="/static/sample.js",
        swagger_css_url="/static/sample.css",
    )

but it is not working. Is there a way just to insert my custom javascript code on docs webpage of FastAPI Swagger UI with Python ?

Asked By: Fynn

||

Answers:

Finally I made it working. This is what I did:

from fastapi.openapi.docs import (
    get_redoc_html,
    get_swagger_ui_html,
    get_swagger_ui_oauth2_redirect_html,
)
from fastapi.staticfiles import StaticFiles

api = FastAPI(docs_url=None) 

path_to_static = os.path.join(os.path.dirname(__file__), 'static')
logger.info(f"path_to_static: {path_to_static}")
api.mount("/static", StaticFiles(directory=path_to_static), name="static")

@api.get("/docs", include_in_schema=False)
        async def custom_swagger_ui_html():
            return get_swagger_ui_html(
                openapi_url=api.openapi_url,
                title="My API",
                oauth2_redirect_url=api.swagger_ui_oauth2_redirect_url,
                swagger_js_url="/static/custom_script.js",
                # swagger_css_url="/static/swagger-ui.css",
                # swagger_favicon_url="/static/favicon-32x32.png",
            )

Important notes:

  1. Make sure the static path is correct and all your files are in the static folder, by default the static folder should be in the same folder with the script that created the FastAPI object.

For example:

 -parent_folder
     Build_FastAPI.py
     -static_folder
         custom_script.js
         custom_css.css
  1. Find the swagger-ui-bundle.js on internet and copy-paste all its content to custom_script.js, then add your custom javascript code at the beginning or at the end of custom_script.js.

For example:

setTimeout(function(){alert('My custom script is working!')}, 5000);
...
.....
/*! For license information please see swagger-ui-bundle.js.LICENSE.txt */
            !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.SwaggerUIBundle=t():e.SwaggerUIBundle=t()}
...
.....
  1. Save and refresh your browser, you are all way up!

IF SOMEBODY KNOWS A BETTER ANSWER YOUR ARE WELCOME, THE BEST ONE WILL BE ACCEPTED!

Answered By: Fynn

If you take a look at the get_swagger_ui_html function that is imported from fastapi.openapi.docs, you will see that the HTML for the docs page is constructed manually via string interpolation/concatenation. It would be trivial to modify this function to include an additional script element, as shown below:

# custom_swagger.py

import json
from typing import Any, Dict, Optional

from fastapi.encoders import jsonable_encoder
from fastapi.openapi.docs import swagger_ui_default_parameters
from starlette.responses import HTMLResponse

def get_swagger_ui_html(
    *,
    openapi_url: str,
    title: str,
    swagger_js_url: str = "https://cdn.jsdelivr.net/npm/swagger-ui-dist@4/swagger-ui-bundle.js",
    swagger_css_url: str = "https://cdn.jsdelivr.net/npm/swagger-ui-dist@4/swagger-ui.css",
    swagger_favicon_url: str = "https://fastapi.tiangolo.com/img/favicon.png",
    oauth2_redirect_url: Optional[str] = None,
    init_oauth: Optional[Dict[str, Any]] = None,
    swagger_ui_parameters: Optional[Dict[str, Any]] = None,
    custom_js_url: Optional[str] = None,
) -> HTMLResponse:
    current_swagger_ui_parameters = swagger_ui_default_parameters.copy()
    if swagger_ui_parameters:
        current_swagger_ui_parameters.update(swagger_ui_parameters)

    html = f"""
    <!DOCTYPE html>
    <html>
    <head>
    <link type="text/css" rel="stylesheet" href="{swagger_css_url}">
    <link rel="shortcut icon" href="{swagger_favicon_url}">
    <title>{title}</title>
    </head>
    <body>
    <div id="swagger-ui">
    </div>
    """
    
    if custom_js_url:
        html += f"""
        <script src="{custom_js_url}"></script>
        """

    html += f"""
    <script src="{swagger_js_url}"></script>
    <!-- `SwaggerUIBundle` is now available on the page -->
    <script>
    const ui = SwaggerUIBundle({{
        url: '{openapi_url}',
    """

    for key, value in current_swagger_ui_parameters.items():
        html += f"{json.dumps(key)}: {json.dumps(jsonable_encoder(value))},n"

    if oauth2_redirect_url:
        html += f"oauth2RedirectUrl: window.location.origin + '{oauth2_redirect_url}',"

    html += """
    presets: [
        SwaggerUIBundle.presets.apis,
        SwaggerUIBundle.SwaggerUIStandalonePreset
        ],
    })"""

    if init_oauth:
        html += f"""
        ui.initOAuth({json.dumps(jsonable_encoder(init_oauth))})
        """

    html += """
    </script>
    </body>
    </html>
    """
    return HTMLResponse(html)

A new, optional parameter named custom_js_url is added:

    custom_js_url: Optional[str] = None,

If a value is provided for this parameter, a script element is inserted into the DOM directly before the script element for swagger_js_url (this is an arbitrary choice, you can change the location of the custom script element based on your needs).

    if custom_js_url:
        html += f"""
        <script src="{custom_js_url}"></script>
        """

If no value is provided, the HTML produced is the same as the original function.

Remember to update your import statements for get_swagger_ui_html and update your function for the /docs endpoint as shown below:

from fastapi.openapi.docs import (
    get_redoc_html,
    get_swagger_ui_oauth2_redirect_html,
)
from fastapi.staticfiles import StaticFiles

from custom_swagger import get_swagger_ui_html

api = FastAPI(docs_url=None) 

path_to_static = os.path.join(os.path.dirname(__file__), 'static')
logger.info(f"path_to_static: {path_to_static}")
api.mount("/static", StaticFiles(directory=path_to_static), name="static")

@api.get("/docs", include_in_schema=False)
        async def custom_swagger_ui_html():
            return get_swagger_ui_html(
                openapi_url=api.openapi_url,
                title="My API",
                oauth2_redirect_url=api.swagger_ui_oauth2_redirect_url,
                swagger_js_url="/static/swagger-ui-bundle.js",
                swagger_css_url="/static/swagger-ui.css",
                # swagger_favicon_url="/static/favicon-32x32.png",
                custom_js_url="/static/custom_script.js",
            )

This is still a pretty hacky solution, but I think it is much cleaner and more maintainable than putting a bunch of custom javascript inside the swagger-ui-bundle.js file.

Answered By: lunaa
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.