Request data#

Request body#

The body of HTTP requests can be accessed using the special data parameter in a handler function.

from typing import Dict

from starlite import Starlite, post


@post(path="/")
async def index(data: Dict[str, str]) -> Dict[str, str]:
    return data


app = Starlite(route_handlers=[index])
from starlite import Starlite, post


@post(path="/")
async def index(data: dict[str, str]) -> dict[str, str]:
    return data


app = Starlite(route_handlers=[index])

The type of data an be any supported type, including

from dataclasses import dataclass

from starlite import Starlite, post


@dataclass
class User:
    id: int
    name: str


@post(path="/")
async def index(data: User) -> User:
    return data


app = Starlite(route_handlers=[index])

Validation and customizing OpenAPI documentation#

With the help of Body, you have fine-grained control over the validation of the request body, and can also customize the OpenAPI documentation:

from dataclasses import dataclass

from starlite import Body, Starlite, post


@dataclass
class User:
    id: int
    name: str


@post(path="/")
async def create_user(
    data: User = Body(title="Create User", description="Create a new user."),
) -> User:
    return data


app = Starlite(route_handlers=[create_user])

Specifying a content-type#

By default, Starlite will try to parse the request body as JSON. While this may be desired in most cases, you might want to specify a different type. You can do so by passing a RequestEncodingType to Body. This will also help to generate the correct media-type in the OpenAPI schema.

URL Encoded Form Data#

To access data sent as url-encoded form data, i.e. application/x-www-form-urlencoded Content-Type header, use Body and specify RequestEncodingType.URL_ENCODED as the media_type:

from dataclasses import dataclass

from starlite import Body, RequestEncodingType, Starlite, post


@dataclass
class User:
    id: int
    name: str


@post(path="/")
async def create_user(
    data: User = Body(media_type=RequestEncodingType.URL_ENCODED),
) -> User:
    return data


app = Starlite(route_handlers=[create_user])

Note

URL encoded data is inherently less versatile than JSON data - for example, it cannot handle complex dictionaries and deeply nested data. It should only be used for simple data structures.

MultiPart Form Data#

You can access data uploaded using a request with a multipart/form-data Content-Type header by specifying it in the Body function:

from dataclasses import dataclass

from starlite import Body, RequestEncodingType, Starlite, post


@dataclass
class User:
    id: int
    name: str


@post(path="/")
async def create_user(
    data: User = Body(media_type=RequestEncodingType.MULTI_PART),
) -> User:
    return data


app = Starlite(route_handlers=[create_user])

File uploads#

In case of files uploaded, Starlite transforms the results into an instance of UploadFile class, which offer a convenient interface for working with files. Therefore, you need to type your file uploads accordingly.

To access a single file simply type data as UploadFile:

from starlite import Body, MediaType, RequestEncodingType, Starlite, UploadFile, post


@post(path="/", media_type=MediaType.TEXT)
async def handle_file_upload(
    data: UploadFile = Body(media_type=RequestEncodingType.MULTI_PART),
) -> str:
    content = await data.read()
    filename = data.filename
    return f"{filename}, {content.decode()}"


app = Starlite(route_handlers=[handle_file_upload])
from starlite import Body, MediaType, RequestEncodingType, Starlite, UploadFile, post


@post(path="/", media_type=MediaType.TEXT)
def handle_file_upload(
    data: UploadFile = Body(media_type=RequestEncodingType.MULTI_PART),
) -> str:
    content = data.file.read()
    filename = data.filename
    return f"{filename}, {content.decode()}"


app = Starlite(route_handlers=[handle_file_upload])

Technical details

UploadFile wraps SpooledTemporaryFile so it can be used asynchronously. Inside of a synchronous function we don’t need this wrapper, so we can use SpooledTemporaryFile.read directly.

Multiple files#

To access multiple files with known filenames, you can use a pydantic model:

from typing import Dict

from pydantic import BaseConfig, BaseModel

from starlite import Body, RequestEncodingType, Starlite, UploadFile, post


class FormData(BaseModel):
    cv: UploadFile
    diploma: UploadFile

    class Config(BaseConfig):
        arbitrary_types_allowed = True


@post(path="/")
async def handle_file_upload(
    data: FormData = Body(media_type=RequestEncodingType.MULTI_PART),
) -> Dict:
    cv_content = await data.cv.read()
    diploma_content = await data.diploma.read()

    return {"cv": cv_content.decode(), "diploma": diploma_content.decode()}


app = Starlite(route_handlers=[handle_file_upload])
from pydantic import BaseConfig, BaseModel

from starlite import Body, RequestEncodingType, Starlite, UploadFile, post


class FormData(BaseModel):
    cv: UploadFile
    diploma: UploadFile

    class Config(BaseConfig):
        arbitrary_types_allowed = True


@post(path="/")
async def handle_file_upload(
    data: FormData = Body(media_type=RequestEncodingType.MULTI_PART),
) -> dict:
    cv_content = await data.cv.read()
    diploma_content = await data.diploma.read()

    return {"cv": cv_content.decode(), "diploma": diploma_content.decode()}


app = Starlite(route_handlers=[handle_file_upload])

Files as a dictionary#

If you do not care about parsing and validation and only want to access the form data as a dictionary, you can use a dict instead:

from typing import Dict

from starlite import Body, RequestEncodingType, Starlite, UploadFile, post


@post(path="/")
async def handle_file_upload(
    data: Dict[str, UploadFile] = Body(media_type=RequestEncodingType.MULTI_PART),
) -> Dict[str, str]:
    file_contents = {}
    for name, file in data.items():
        content = await file.read()
        file_contents[name] = content.decode()

    return file_contents


app = Starlite(route_handlers=[handle_file_upload])
from starlite import Body, RequestEncodingType, Starlite, UploadFile, post


@post(path="/")
async def handle_file_upload(
    data: dict[str, UploadFile] = Body(media_type=RequestEncodingType.MULTI_PART),
) -> dict[str, str]:
    file_contents = {}
    for name, file in data.items():
        content = await file.read()
        file_contents[name] = content.decode()

    return file_contents


app = Starlite(route_handlers=[handle_file_upload])

Files as a list#

Finally, you can also access the files as a list without the filenames:

from typing import Dict, List

from starlite import Body, RequestEncodingType, Starlite, UploadFile, post


@post(path="/")
async def handle_file_upload(
    data: List[UploadFile] = Body(media_type=RequestEncodingType.MULTI_PART),
) -> Dict[str, str]:
    file_contents = {}
    for file in data:
        content = await file.read()
        file_contents[file.filename] = content.decode()

    return file_contents


app = Starlite(route_handlers=[handle_file_upload])
from starlite import Body, RequestEncodingType, Starlite, UploadFile, post


@post(path="/")
async def handle_file_upload(
    data: list[UploadFile] = Body(media_type=RequestEncodingType.MULTI_PART),
) -> dict[str, str]:
    file_contents = {}
    for file in data:
        content = await file.read()
        file_contents[file.filename] = content.decode()

    return file_contents


app = Starlite(route_handlers=[handle_file_upload])

MessagePack data#

To receive MessagePack data, specify the appropriate Content-Type for Body , by using RequestEncodingType.MESSAGEPACK:

msgpack_request.py#
from typing import Dict

from starlite import Body, RequestEncodingType, Starlite, post


@post(path="/")
def msgpack_handler(data: Dict = Body(media_type=RequestEncodingType.MESSAGEPACK)) -> Dict:
    # This will try to parse the request body as `MessagePack` regardless of the
    # `Content-Type`
    return data


app = Starlite(route_handlers=[msgpack_handler])
msgpack_request.py#
from starlite import Body, RequestEncodingType, Starlite, post


@post(path="/")
def msgpack_handler(data: dict = Body(media_type=RequestEncodingType.MESSAGEPACK)) -> dict:
    # This will try to parse the request body as `MessagePack` regardless of the
    # `Content-Type`
    return data


app = Starlite(route_handlers=[msgpack_handler])