|
| 1 | +# flask-msgspec |
| 2 | +[msgspec](https://github.com/jcrist/msgspec) integration for [Flask](https://github.com/pallets/flask) |
| 3 | + |
| 4 | +This project was inspired by the [flask-pydantic](https://github.com/bauerji/flask-pydantic) package created by [bauerji](https://github.com/bauerji) and the [Litestar](https://github.com/litestar-org/litestar) framework, however while the `validate` decorator appears similar to the one found in `flask-pydantic` there are many differences. |
| 5 | + |
| 6 | +## Usage |
| 7 | +Consider this simple example: |
| 8 | +```py |
| 9 | +class BodyStructWithConstraints(msgspec.Struct): |
| 10 | + foo: Annotated[int, msgspec.Meta(gt=0, le=100)] |
| 11 | + bar: Annotated[list[str | int], msgspec.Meta(min_length=1)] |
| 12 | + |
| 13 | + |
| 14 | +@app.post(rule="/test/<uuid:uuid_value>") |
| 15 | +@validate() |
| 16 | +def test_handler( |
| 17 | + uuid_value: UUID, |
| 18 | + query1: float, |
| 19 | + body: BodyStructWithConstraints, |
| 20 | + optional_query: str = "default_value", |
| 21 | +) -> dict: |
| 22 | + return locals() |
| 23 | +``` |
| 24 | +Here we have a UUID path parameter, a required query parameter of `float` type, a body of type `BodyStructWithConstraints`, and an optional query parameter which is a `string`, the endpoint will return a `dictionary` of unknown types. |
| 25 | + |
| 26 | +Currently there is only one reserved keyword; `body`. This tries to convert either `request.data` or `request.form` to the specified type. |
| 27 | + |
| 28 | +Similar to how `Litestar` works, keywords that are neither path parameters or reserved keywords are considered query parameters. |
| 29 | + |
| 30 | +The return type can be set either: |
| 31 | +- via the `return_model` keyword in `validate` decorator, or |
| 32 | +- by annotating the function return type. (`return_model` keyword takes priority) |
| 33 | + |
| 34 | +Sequences/iterables can also be used for return type, e.g.; `list[ResponseModel]`. |
| 35 | + |
| 36 | +The successful response status code can also be changed in two ways: |
| 37 | +- via setting the `status_code` keyword in `validate` decorator, or |
| 38 | +- by using the standard Flask syntax of returning a tuple. |
| 39 | + |
| 40 | +Returning a tuple with a status code will override the value set by the `status_code` keyword. |
| 41 | + |
| 42 | +As you might have noticed mixing these together most likely will cause issues or just going to be annoying to annotate.\ |
| 43 | +***Avoid using the standard Flask tuple return syntax to change the status code if you are also using the function return type to annotate the return model. This will cause issues; first with a static type checker, then with the code handling return values and conversion.***\ |
| 44 | +In my opinion the response model should be set via annotating the return type of the function and if the status code needs to be changed use the `status_code` keyword. Additionally, if you need to set headers, then set both, the return type and status code using the keywords of the `validate` decorator. |
| 45 | + |
| 46 | +You can use `msgspec.Struct`, `dataclass`, and most built-in types natively supported by `msgspec`. |
| 47 | + |
| 48 | +*More examples and others stuff will be added soon:tm:.* |
0 commit comments