How to allow any arbitrary query parameters using FastAPI and Swagger?

You could use an Optional string parameter (book_params in the case below) to pass the query parameters as a single string through OpenAPI (Swagger UI) e.g., include=author&sort=name,zip&sort=-lname&fields=name,phone,street. You can then parse the query data (using urllib.parse.parse_qs) to get a dictionary, as shown below.

The below example also utilises the method described here, in order to fix the part where parse_qs parses single values into lists (e.g., 'foo=bar' would be parsed into foo = ['bar']), while also preserving all the values for keys that the user passes a list. For example, if the user passed the same key multiple times in the URL, that is, for instance, 'foo=2&bar=7&foo=10', using dict(request.query_params) to retrieve the query parameters would result in {"foo":"10","bar":"7"} instead of {"foo":["2","10"],"bar":"7"}. The approach, however, demonstrated in the example below (using the aforementioned method) takes care of that as well, by parsing the query string (which can be retieved using request.url.query) and makes sure that actual lists are preserved.

You can check whether this optional parameter, i.e., book_params, is empty or not to decide whether to read the query params using book_params (meaning that the request is sent through Swagger) or using the Request object directly (meaning that the request is sent through typing the URL into the address bar of the browser e.g., http://127.0.0.1:8000/api/books?include=author&sort=name,zip&sort=-lname&fields=name,phone,street, or using some other client app). Please make sure to name that optional parameter (i.e., book_params) something unique, which wouldn’t also be part of the actual parameters.

from fastapi import FastAPI, Request
from typing import Optional
from urllib.parse import parse_qs

app = FastAPI()

@app.get("/api/books")
def books(request: Request, book_params: Optional[str] = None):
    q_params = {}
    
    if book_params is not None:
        q_params = parse_qs(book_params, keep_blank_values=True)
    else:   
        q_params = parse_qs(request.url.query, keep_blank_values=True)
        
    d = dict((k, v if len(v)>1 else v[0]) 
                for k, v in q_params.items())

    return d

Leave a Comment