Skip to content

Instantly share code, notes, and snippets.

@imankulov
Last active September 21, 2024 10:28
Show Gist options
  • Save imankulov/cef71dd5a01f9a27caeb66f7bedaf241 to your computer and use it in GitHub Desktop.
Save imankulov/cef71dd5a01f9a27caeb66f7bedaf241 to your computer and use it in GitHub Desktop.
Parse JSON-encoded parameters from request queries with FastAPI

Problem: FastAPI doesn't accept JSON-encoded pydantic models in query strings. See #884.

Solution: Use json_param() from the snippet below.

Usage example.

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class User(BaseModel):
    name: str

@app.get("/")
def root(user: User = json_param("user", User, description="User object")):
    return {"message": f"Hello, {user!r}"}

Request and response examples (with httpie)

Success:

$ http localhost:7000 user=='{"name": "Foo"}'
HTTP/1.1 200 OK

{
    "message": "Hello, User(name='Foo')"
}

Validation error:

HTTP/1.1 400 Bad Request

{
    "detail": [
        {
            "loc": [
                "name"
            ],
            "msg": "none is not an allowed value",
            "type": "type_error.none.not_allowed"
        }
    ]
}
from typing import Any
import pydantic
from fastapi import Depends, HTTPException, Query
from pydantic import Json, ValidationError
def json_param(param_name: str, model: Any, **query_kwargs):
"""Parse JSON-encoded query parameters as pydantic models.
The function returns a `Depends()` instance that takes the JSON-encoded value from
the query parameter `param_name` and converts it to a Pydantic model, defined
by the `model` attribute.
"""
def get_parsed_object(value: Json = Query(alias=param_name, **query_kwargs)):
try:
return pydantic.parse_obj_as(model, value)
except ValidationError as err:
raise HTTPException(400, detail=err.errors())
return Depends(get_parsed_object)
@dakai-wei-of-shizen
Copy link

So cool, but for pydantic v2, parse_obj_as is marked as deprecated.

maybe

    def get_parsed_object(value: Json = Query(alias=param_name, **query_kwargs)):
        try:
            ta = pydantic.TypeAdapter(model)
            return ta.validate_python(value)
        except ValidationError as err:
            raise HTTPException(400, err.errors())

@theFireProject
Copy link

theFireProject commented Dec 21, 2023

Really cool solution. I'm still learning FastAPI, can I ask some questions about your code:

  • Could you raise a RequestValidationError instead of HTTPException and get the same result? (except for the response status code, I think it should be 422 for validation errors)
  • Could you use model.parse_obj(value) instead of pydantic.parse_obj_as(mode, value) in Pydantic v1?
  • Could you also use value: str and model.parse_raw(value) instead of value: Json and pydantic.parse_obj_as(model, value) in Pydantic v1?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment