[{"data":1,"prerenderedAt":99},["ShallowReactive",2],{"qa-\u002Ffastapi\u002Frouting\u002Frequest-body":3},{"page":4,"siblings":85,"blog":77},{"id":5,"title":6,"body":7,"description":11,"difficulty":14,"extension":15,"framework":16,"frameworkSlug":17,"meta":18,"navigation":19,"order":12,"path":20,"questions":21,"questionsCount":76,"related":77,"seo":78,"seoDescription":79,"stem":80,"subtopic":6,"topic":81,"topicSlug":82,"updated":83,"__hash__":84},"qa\u002Ffastapi\u002Frouting\u002Frequest-body.md","Request Body",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"medium","md","FastAPI","fastapi",{},true,"\u002Ffastapi\u002Frouting\u002Frequest-body",[22,27,31,35,39,43,47,51,55,59,63,67,72],{"id":23,"difficulty":24,"q":25,"a":26},"json-body-basics","easy","How do you declare a JSON request body in FastAPI?","Declare a function parameter typed as a Pydantic `BaseModel` subclass. FastAPI\nreads the request body as JSON, parses it, and validates it against the model.\n\n```python\nfrom pydantic import BaseModel\nfrom fastapi import FastAPI\n\nclass Item(BaseModel):\n    name: str\n    price: float\n    in_stock: bool = True\n\napp = FastAPI()\n\n@app.post(\"\u002Fitems\")\nasync def create_item(item: Item):\n    return item\n# POST \u002Fitems  body: {\"name\": \"Widget\", \"price\": 9.99}\n# → Item(name='Widget', price=9.99, in_stock=True)\n```\n\nFastAPI returns 422 if the body is missing, malformed JSON, or fails validation.\n\nRule of thumb: one Pydantic model = one request body; the model name appears in\nthe OpenAPI schema as the expected input shape.\n",{"id":28,"difficulty":24,"q":29,"a":30},"optional-body","How do you make the request body optional in FastAPI?","Annotate the body parameter with `T | None = None`:\n\n```python\nclass Item(BaseModel):\n    name: str\n    price: float\n\n@app.patch(\"\u002Fitems\u002F{id}\")\nasync def update_item(id: int, item: Item | None = None):\n    if item is None:\n        return {\"updated\": False}\n    return {\"updated\": True, \"item\": item}\n```\n\nA `None` default makes the body optional; FastAPI won't error if the client\nsends an empty body. For partial updates prefer a model where all fields are\n`Optional` rather than making the whole body optional.\n\nRule of thumb: for PATCH endpoints, make individual fields optional inside the\nmodel rather than making the entire body optional.\n",{"id":32,"difficulty":14,"q":33,"a":34},"nested-models","How does FastAPI handle nested Pydantic models in the request body?","Nest a `BaseModel` inside another `BaseModel`. FastAPI validates the full\nnested structure and generates the OpenAPI schema with `$ref` references.\n\n```python\nclass Address(BaseModel):\n    street: str\n    city: str\n    postcode: str\n\nclass User(BaseModel):\n    name: str\n    email: str\n    address: Address       # nested model\n\n@app.post(\"\u002Fusers\")\nasync def create_user(user: User):\n    return user\n# body: {\"name\": \"Alice\", \"email\": \"a@b.com\",\n#         \"address\": {\"street\": \"1 Main St\", \"city\": \"London\", \"postcode\": \"SW1A\"}}\n```\n\nArbitrarily deep nesting is supported; Pydantic validates all levels.\n\nRule of thumb: prefer deep models over flat ones with `address_street`,\n`address_city` prefixes — nested models are cleaner and reusable across endpoints.\n",{"id":36,"difficulty":14,"q":37,"a":38},"multiple-body-params","How do you declare multiple Pydantic models as separate body parameters?","When you declare two Pydantic model parameters, FastAPI expects the body to be a\n**JSON object keyed by parameter name**:\n\n```python\nclass Item(BaseModel):\n    name: str\n    price: float\n\nclass Supplier(BaseModel):\n    name: str\n    contact: str\n\n@app.post(\"\u002Fitems\")\nasync def create_item(item: Item, supplier: Supplier):\n    return {\"item\": item, \"supplier\": supplier}\n# body: {\"item\": {\"name\": \"Widget\", \"price\": 9.99},\n#         \"supplier\": {\"name\": \"Acme\", \"contact\": \"info@acme.com\"}}\n```\n\nRule of thumb: multiple body models wrap themselves under their parameter names\nautomatically; use this to bundle related but distinct payloads in one request.\n",{"id":40,"difficulty":14,"q":41,"a":42},"body-singular","How do you mix a Pydantic body model with a singular body value?","Use `Body(embed=True)` on the scalar value. This forces FastAPI to wrap it under\nits parameter name in the expected JSON object.\n\n```python\nfrom fastapi import Body\nfrom typing import Annotated\n\nclass Item(BaseModel):\n    name: str\n\n@app.put(\"\u002Fitems\u002F{id}\")\nasync def update_item(\n    id: int,\n    item: Item,\n    importance: Annotated[int, Body(ge=1, le=5, embed=True)],\n):\n    return {\"item\": item, \"importance\": importance}\n# body: {\"item\": {\"name\": \"Widget\"}, \"importance\": 3}\n```\n\nWithout `embed=True`, FastAPI would expect `importance` at the top level of the\nJSON, which conflicts with `item`'s keys.\n\nRule of thumb: any singular `Body()` value alongside a Pydantic model needs\n`embed=True` so FastAPI knows to namespace it.\n",{"id":44,"difficulty":14,"q":45,"a":46},"body-field-extra","What happens when the client sends extra fields not defined in the Pydantic model?","By default (Pydantic v2), extra fields are **ignored** — they're stripped silently\nand don't reach the handler.\n\n```python\nclass Item(BaseModel):\n    name: str\n    price: float\n# body: {\"name\": \"Widget\", \"price\": 9.99, \"secret\": \"ignored\"}\n# → Item(name='Widget', price=9.99)  — secret dropped\n```\n\nYou can change this behaviour via `model_config`:\n\n```python\nfrom pydantic import BaseModel, ConfigDict\n\nclass StrictItem(BaseModel):\n    model_config = ConfigDict(extra=\"forbid\")  # 422 if extra fields sent\n    name: str\n    price: float\n```\n\nOptions: `\"ignore\"` (default), `\"allow\"` (keep in `model.model_extra`),\n`\"forbid\"` (raise validation error).\n\nRule of thumb: use `extra=\"forbid\"` for request bodies to catch client typos\nearly; use `extra=\"ignore\"` for internal models where schema drift is acceptable.\n",{"id":48,"difficulty":14,"q":49,"a":50},"list-body","How do you accept a JSON array (list) as the root of the request body?","Type-annotate the body parameter as `list[MyModel]`:\n\n```python\n@app.post(\"\u002Fitems\u002Fbulk\")\nasync def bulk_create(items: list[Item]):\n    return {\"count\": len(items)}\n# body: [{\"name\": \"A\", \"price\": 1.0}, {\"name\": \"B\", \"price\": 2.0}]\n```\n\nFastAPI validates each element against `Item`. For a top-level list, there is\nno \"outer key\" — the raw JSON array is the entire body.\n\nRule of thumb: use bulk endpoints for batch operations; add a reasonable size\nlimit (`len(items) \u003C= 100`) inside the handler to prevent abuse.\n",{"id":52,"difficulty":14,"q":53,"a":54},"form-data-body","How do you accept HTML form data instead of JSON in FastAPI?","Use `Form()` annotation and install `python-multipart`:\n\n```python\nfrom fastapi import Form\nfrom typing import Annotated\n\n@app.post(\"\u002Flogin\")\nasync def login(\n    username: Annotated[str, Form()],\n    password: Annotated[str, Form()],\n):\n    return {\"user\": username}\n# Content-Type: application\u002Fx-www-form-urlencoded\n# body: username=alice&password=secret\n```\n\nYou **cannot mix** `Form()` and a Pydantic JSON body in the same handler — HTTP\nonly allows one `Content-Type` per request.\n\nRule of thumb: use `Form()` for OAuth2 password flows and HTML `\u003Cform>` submissions;\nuse JSON body everywhere else.\n",{"id":56,"difficulty":14,"q":57,"a":58},"file-upload-body","How do you accept a file alongside form fields in FastAPI?","Use `UploadFile` for the file and `Form()` for accompanying fields. Both are\nmultipart\u002Fform-data.\n\n```python\nfrom fastapi import UploadFile, Form\nfrom typing import Annotated\n\n@app.post(\"\u002Fupload\")\nasync def upload(\n    file: UploadFile,\n    description: Annotated[str, Form()],\n):\n    content = await file.read()\n    return {\n        \"filename\": file.filename,\n        \"size\": len(content),\n        \"description\": description,\n    }\n```\n\n`UploadFile` wraps a `SpooledTemporaryFile`; use `await file.read()` for small\nfiles, iterate in chunks for large ones.\n\nRule of thumb: always `await file.seek(0)` before re-reading a file that was\nalready partially read elsewhere in the handler.\n",{"id":60,"difficulty":24,"q":61,"a":62},"body-validation-error-format","What does a FastAPI body validation error response look like?","HTTP **422 Unprocessable Entity** with a JSON body listing each failing field:\n\n```json\n{\n  \"detail\": [\n    {\n      \"type\": \"missing\",\n      \"loc\": [\"body\", \"price\"],\n      \"msg\": \"Field required\",\n      \"input\": {\"name\": \"Widget\"},\n      \"url\": \"https:\u002F\u002Ferrors.pydantic.dev\u002F2.0\u002Fv\u002Fmissing\"\n    },\n    {\n      \"type\": \"float_parsing\",\n      \"loc\": [\"body\", \"price\"],\n      \"msg\": \"Input should be a valid number\",\n      \"input\": \"not-a-number\"\n    }\n  ]\n}\n```\n\n`loc` is a breadcrumb path: `[\"body\", \"field_name\"]` for top-level fields,\n`[\"body\", \"nested\", \"field\"]` for nested models.\n\nRule of thumb: parse `detail[*].loc` in client code to map validation errors\nback to specific form fields for UX display.\n",{"id":64,"difficulty":24,"q":65,"a":66},"content-type-requirement","What Content-Type header does FastAPI expect for JSON body requests?","FastAPI expects `Content-Type: application\u002Fjson` for Pydantic body parameters.\nIf the client sends a different or missing `Content-Type`, FastAPI attempts to\nparse the body as JSON anyway — but tools like Swagger UI always send the header.\n\n```http\nPOST \u002Fitems HTTP\u002F1.1\nContent-Type: application\u002Fjson\n\n{\"name\": \"Widget\", \"price\": 9.99}\n```\n\nFor form data: `application\u002Fx-www-form-urlencoded` or `multipart\u002Fform-data`.\nFor raw file streams: `application\u002Foctet-stream` with `Request` body directly.\n\nRule of thumb: always set `Content-Type: application\u002Fjson` explicitly in REST\nclients — don't rely on default behaviour.\n",{"id":68,"difficulty":69,"q":70,"a":71},"raw-request-body","hard","How do you read the raw request body bytes in FastAPI without Pydantic parsing?","Inject the `Request` object and call `await request.body()`:\n\n```python\nfrom fastapi import Request\n\n@app.post(\"\u002Fwebhook\")\nasync def webhook(request: Request):\n    raw = await request.body()       # bytes\n    payload = json.loads(raw)\n    signature = request.headers.get(\"X-Signature\")\n    verify_signature(raw, signature) # HMAC check on raw bytes\n    return {\"ok\": True}\n```\n\nYou cannot mix `await request.body()` with a Pydantic body parameter in the same\nhandler — FastAPI reads the body stream once; `body()` consumes it before Pydantic can.\n\nRule of thumb: use raw body access for webhooks that need HMAC verification\non the exact bytes; use Pydantic models for everything else.\n",{"id":73,"difficulty":69,"q":74,"a":75},"body-max-size","How do you enforce a maximum request body size in FastAPI?","FastAPI has no built-in body size limit. Enforce it in middleware:\n\n```python\nfrom fastapi import Request\nfrom fastapi.responses import JSONResponse\nfrom starlette.middleware.base import BaseHTTPMiddleware\n\nMAX_BODY = 1 * 1024 * 1024  # 1 MB\n\nclass LimitBodyMiddleware(BaseHTTPMiddleware):\n    async def dispatch(self, request: Request, call_next):\n        if request.headers.get(\"content-length\"):\n            if int(request.headers[\"content-length\"]) > MAX_BODY:\n                return JSONResponse({\"detail\": \"Body too large\"}, status_code=413)\n        return await call_next(request)\n\napp.add_middleware(LimitBodyMiddleware)\n```\n\nFor production, configure the limit at the reverse proxy (Nginx `client_max_body_size`\nor Uvicorn `--limit-concurrency`) rather than in application code.\n\nRule of thumb: enforce body size at the reverse proxy level for efficiency;\nadd middleware as a defence-in-depth layer inside the app.\n",13,null,{"description":11},"FastAPI request body interview questions — Pydantic models as bodies, Body(), nested models, multiple bodies, form data, file uploads and embed.","fastapi\u002Frouting\u002Frequest-body","Routing & Parameters","routing","2026-06-20","Ju078x3mX7g8VqxLdOPN9YTmVhYdnoMSpKenH_qpqkU",[86,90,91,95],{"subtopic":87,"path":88,"order":89},"Path & Query Parameters","\u002Ffastapi\u002Frouting\u002Fpath-query-params",1,{"subtopic":6,"path":20,"order":12},{"subtopic":92,"path":93,"order":94},"Response Models","\u002Ffastapi\u002Frouting\u002Fresponse-models",3,{"subtopic":96,"path":97,"order":98},"Routers & Structure","\u002Ffastapi\u002Frouting\u002Frouters",4,1782244112699]