pythonfastapiswagger-uiopenapipydantic

How to set Swagger UI to use List fields in query parameters using FastAPI?


When making an app that uses dependency injection with a list field, the parameter automatically goes to the request body in SwaggerUI:

from fastapi import FastAPI, Query, Depends
import uvicorn
from pydantic import BaseModel, Field
from typing import List


class QueryParams(BaseModel):
    name: str = Field(...)
    ages: List[int] = Field([])


app = FastAPI()


@app.get("/test")
def test(query: QueryParams = Depends()):
    return "hi"

uvicorn.run(app)

SwaggerUI

Which means I cant test it in swagger UI. Even if I change field to query, it still doesn't work:

from fastapi import FastAPI, Query, Depends
import uvicorn
from pydantic import BaseModel, Field
from typing import List


class QueryParams(BaseModel):
    name: str = Field(...)
    ages: List[int] = Query([])  # <-- Query


app = FastAPI()


@app.get("/test")
def test(query: QueryParams = Depends()):
    return "hi"

uvicorn.run(app)

If I put it in the route function, it works:

from fastapi import FastAPI, Query, Depends
import uvicorn
from pydantic import BaseModel, Field
from typing import List


class QueryParams(BaseModel):
    name: str = Field(...)


app = FastAPI()


@app.get("/test")
def test(query: QueryParams = Depends(), ages: List[int] = Query([])):
    return "hi"

uvicorn.run(app)

Working UI

How can I get swagger UI to recognize a list query field in a basemodel with dependency injection?


Solution

  • As described in this answer, one can't use a List field inside a Pydantic model and expect it to be a query parameter. The way to do this is to implement your query parameter-parsing in a separate dependency class, as shown below:

    class QueryParams:
        def __init__(
            self,
            name: str,
            ages: List[int] = Query(...)
        ):
            self.name = name
            self.ages = ages
            
    @app.get("/test")
    def test(query: QueryParams = Depends()):
        return "hi"
    

    The above can be re-written using the @dataclass decorator, as shown below:

    from dataclasses import dataclass
    
    @dataclass
    class QueryParams:
        name: str
        ages: List[int] = Query(...)
    
    @app.get("/test")
    def test(query: QueryParams = Depends()):
        return "hi"
    

    Update

    One could wrap the Query() in a Field() that would allow them to define a List field as query parameter inside the Pydantic model. Complete working examples can be found in this answer and this answer.

    from fastapi import Query
    from pydantic import BaseModel, Field
    from typing import List
    
    class QueryParams(BaseModel):
        name: str
        ages: List[int] = Field(Query(...))