2

I have a simple application below:

from typing import Annotated

import uvicorn
from fastapi import FastAPI, Query, Depends
from pydantic import BaseModel

app = FastAPI()


class Input(BaseModel):
    a: Annotated[str, Query(..., alias="your_name")]


@app.get("/")
def test(inp: Annotated[Input, Depends()]):
    return f"Hello {inp.a}"


def main():
    uvicorn.run("run:app", host="0.0.0.0", reload=True, port=8001)


if __name__ == "__main__":
    main()

curl "http://127.0.0.1:8001/?your_name=amin" returns "Hello amin"


I now change the alias from your_name to your-name.

from typing import Annotated

import uvicorn
from fastapi import FastAPI, Query, Depends
from pydantic import BaseModel

app = FastAPI()


class Input(BaseModel):
    a: Annotated[str, Query(..., alias="your-name")]


@app.get("/")
def test(inp: Annotated[Input, Depends()]):
    return f"Hello {inp.a}"


def main():
    uvicorn.run("run:app", host="0.0.0.0", reload=True, port=8001)


if __name__ == "__main__":
    main()

Then curl "http://127.0.0.1:8001/?your-name=amin" returns:

{"detail":[{"loc":["query","extra_data"],"msg":"field required","type":"value_error.missing"}]}

However, hyphened alias in a simpler application is allowed.

from typing import Annotated

import uvicorn
from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/")
def test(a: Annotated[str, Query(..., alias="your-name")]):
    return f"Hello {a}"


def main():
    uvicorn.run("run:app", host="0.0.0.0", reload=True, port=8001)


if __name__ == "__main__":
    main()

curl "http://127.0.0.1:8001/?your-name=amin" returns "Hello Amin"



Is this a bug? what is the problem here?

Chris
  • 18,724
  • 6
  • 46
  • 80
Amin Ba
  • 1,603
  • 1
  • 13
  • 38

1 Answers1

4

Problem Overview

As explained here by @JarroVGIT, this is a behaviour coming from Pydantic, not FastAPI. If you looked closely at the error you posted:

{"detail":[{"loc":["query","extra_data"],"msg":"field required","type":"value_error.missing"}]}

it talks about a missing value for a query parameter named extra_data—however, there is no such a parameter defined in your BaseModel. If, in fact, you used the Swagger UI autodocs at /docs, you would see that your endpoint is expecting a required parameter named extra_data. As noted by @JarroVGIT in the linked discussion above:

Whenever you give a 'non-pythonic' alias (like with dashes/hyphens), the signature that inspect.signature() will get from that BaseModel subclass will be grouped in the kwargs extra_data.

Hence the error about the missing value for extra_data query parameter.

Solution 1

You could wrap the Query() in a Field(), as demonstrated in this answer. Example:

from fastapi import Query, Depends
from pydantic import BaseModel, Field

class Input(BaseModel):
    a: str = Field(Query(..., alias="your-name"))


@app.get("/")
def main(i: Input = Depends()):
    pass

Solution 2

As shown in this answer and this answer, you could use a separate dependency class, instead of a BaseModel. Example:

from fastapi import Query, Depends
from dataclasses import dataclass

@dataclass
class Input:
    a: str = Query(..., alias="your-name")


@app.get("/")
def main(i: Input = Depends()):
    pass

Solution 3

Declare the query parameter directly in the endpoint, instead of using a Pydantic BaseModel. Example:

from fastapi import Query

@app.get("/")
def main(a: str = Query(..., alias="your-name")):
    pass
Chris
  • 18,724
  • 6
  • 46
  • 80
  • In your first solution, could you write it with `Annotated`? – Amin Ba Apr 29 '23 at 23:15
  • the question is can I have Annotated and Field and Query in the same line? – Amin Ba Apr 29 '23 at 23:35
  • `a: Annotated[str, Field(Query(..., alias=` in fastapi version 0.95.1 – Amin Ba Apr 29 '23 at 23:38
  • The question is about how to use hyphens in query parameters, when these are defined within a Pydantic model. [_"Python itself doesn't do anything with this `Annotated`"_](https://fastapi.tiangolo.com/python-types/#type-hints-with-metadata-annotations), and there is nothing preventing one from using `=` instead (regardless of the FastAPI version one might be using) - it serves the same purpose. In fact, there are cases, just like in Solution 1, where one needs to use `=` instead, as there stiil seem to be issues with `Annotated`. – Chris Apr 30 '23 at 05:38
  • Also, please [_"have in mind that when using `Query` inside of `Annotated` you cannot use the default parameter for `Query`"_](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#query-as-the-default-value-or-in-annotated), as shown in your question. – Chris Apr 30 '23 at 05:39
  • I submitted an issue in the fastapi github issues https://github.com/tiangolo/fastapi/discussions/9472 – Amin Ba Apr 30 '23 at 14:49