FastAPI Interactive Docs

Form Data

In the last post, Response Status Code, we set the number that rides with a response. Every request body we have read so far arrived as JSON. But classic HTML forms, the kind behind a login page, do not send JSON. They send form-encoded data. FastAPI has a dedicated tool for that: Form.

Reading form fields

Import Form from fastapi and use it inside Annotated, the same shape as Body and Query.

from typing import Annotatedfrom fastapi import FastAPI, Formapp = FastAPI()@app.post("/login")async def login(username: Annotated[str, Form()], password: Annotated[str, Form()]):    return {"username": username}

Let's review:

  1. Form() tells FastAPI to read each field from form-encoded data, not from JSON.

  2. Without it, FastAPI would treat username as a query parameter or a JSON body field. The marker is required.

  3. Form accepts the same extras as Body, like validation and an alias, since it inherits from Body.

One setup note: forms need the python-multipart package. Install it once with pip install python-multipart, or FastAPI will remind you.

Why a separate tool

HTML forms encode their data differently from JSON, using a media type called application/x-www-form-urlencoded. Form simply tells FastAPI to look in that spot. This is also exactly how the OAuth2 password flow expects a username and password, so you will see Form again when you reach security.

⚠️You cannot mix Form fields and a JSON Body in the same endpoint. A request body is encoded one way or the other, form or JSON, never both at once. That is an HTTP rule, not a FastAPI limit. Pick one per endpoint. 😁

Practice Lab: