[{"data":1,"prerenderedAt":110},["ShallowReactive",2],{"qa-\u002Ffastapi\u002Ffundamentals\u002Fpath-operations":3},{"page":4,"siblings":93,"blog":85},{"id":5,"title":6,"body":7,"description":11,"difficulty":14,"extension":15,"framework":16,"frameworkSlug":17,"meta":18,"navigation":19,"order":20,"path":21,"questions":22,"questionsCount":84,"related":85,"seo":86,"seoDescription":87,"stem":88,"subtopic":6,"topic":89,"topicSlug":90,"updated":91,"__hash__":92},"qa\u002Ffastapi\u002Ffundamentals\u002Fpath-operations.md","Path Operations",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"easy","md","FastAPI","fastapi",{},true,3,"\u002Ffastapi\u002Ffundamentals\u002Fpath-operations",[23,27,31,36,40,44,48,52,56,60,64,68,72,76,80],{"id":24,"difficulty":14,"q":25,"a":26},"what-is-path-operation","What is a \"path operation\" in FastAPI?","A **path operation** is the combination of an HTTP method and a URL path bound\nto a Python function via a decorator. FastAPI registers it as a route and\nauto-generates the OpenAPI schema entry for it.\n\n```python\n@app.get(\"\u002Fitems\")           # GET  \u002Fitems  → list_items\nasync def list_items():\n    return [{\"id\": 1}]\n\n@app.post(\"\u002Fitems\")          # POST \u002Fitems  → create_item\nasync def create_item(item: Item):\n    return item\n```\n\nEach decorator (`@app.get`, `@app.post`, `@app.put`, `@app.patch`,\n`@app.delete`, `@app.head`, `@app.options`, `@app.trace`) corresponds to\nan HTTP method. The same path can be registered for multiple methods.\n\nRule of thumb: one decorator = one path operation = one OpenAPI endpoint entry.\n",{"id":28,"difficulty":14,"q":29,"a":30},"http-methods","Which HTTP methods does FastAPI support as route decorators?","FastAPI mirrors standard HTTP methods:\n\n| Decorator | Method | Typical use |\n|-----------|--------|-------------|\n| `@app.get` | GET | Read resource |\n| `@app.post` | POST | Create resource |\n| `@app.put` | PUT | Full update\u002Freplace |\n| `@app.patch` | PATCH | Partial update |\n| `@app.delete` | DELETE | Remove resource |\n| `@app.head` | HEAD | Like GET but no body |\n| `@app.options` | OPTIONS | CORS preflight \u002F introspection |\n| `@app.trace` | TRACE | Diagnostic loopback |\n\n```python\n@app.patch(\"\u002Fitems\u002F{id}\")\nasync def update_partial(id: int, patch: ItemPatch):\n    ...\n```\n\nRule of thumb: use PUT for full replacement, PATCH for partial updates — FastAPI\ndoesn't enforce semantics, but your OpenAPI schema and clients will reflect the choice.\n",{"id":32,"difficulty":33,"q":34,"a":35},"operation-id","medium","What is an `operation_id` and why does it matter?","The `operation_id` is a **unique string identifier** for an OpenAPI operation.\nCode generators (openapi-generator, orval) use it as the function name in\ngenerated clients. FastAPI auto-generates it from the handler function name\nand the path, but you can override it.\n\n```python\n@app.get(\n    \"\u002Fitems\u002F{item_id}\",\n    operation_id=\"retrieve_item\",   # generated client function: retrieve_item()\n)\nasync def get_item(item_id: int):\n    ...\n```\n\nWithout an explicit `operation_id`, FastAPI generates something like\n`get_item_items__item_id__get` — verbose and brittle across renames.\n\nRule of thumb: set explicit `operation_id` values for any endpoint consumed by\ngenerated client code to keep client method names stable across refactors.\n",{"id":37,"difficulty":14,"q":38,"a":39},"tags-and-grouping","How do `tags` work in FastAPI route decorators?","`tags` is a list of strings that group related operations in the Swagger UI and\nReDoc docs. They have no effect on routing — they're purely for documentation.\n\n```python\n@app.get(\"\u002Fusers\", tags=[\"users\"])\nasync def list_users(): ...\n\n@app.post(\"\u002Fusers\", tags=[\"users\"])\nasync def create_user(): ...\n\n@app.get(\"\u002Forders\", tags=[\"orders\"])\nasync def list_orders(): ...\n```\n\nYou can also set tags at the `APIRouter` level so every route in the router\ninherits them:\n\n```python\nrouter = APIRouter(prefix=\"\u002Fusers\", tags=[\"users\"])\napp.include_router(router)\n```\n\nRule of thumb: set tags at the router level for consistency; override per-route\nonly when a route genuinely belongs to a different OpenAPI group.\n",{"id":41,"difficulty":14,"q":42,"a":43},"deprecated-routes","How do you mark a route as deprecated in FastAPI?","Pass `deprecated=True` to the route decorator. FastAPI adds the OpenAPI\n`deprecated: true` flag, which Swagger UI displays with a strikethrough.\n\n```python\n@app.get(\"\u002Fold-endpoint\", deprecated=True)\nasync def old_endpoint():\n    return RedirectResponse(\"\u002Fnew-endpoint\", status_code=301)\n```\n\nDeprecating in OpenAPI doesn't prevent the route from being called — you still\nneed to keep the handler working. For hard removal, emit a `Deprecation` header\nwith a sunset date, then delete the route in a future release.\n\nRule of thumb: always mark deprecated routes in OpenAPI before removing them\nso generated clients surface the warning during their build step.\n",{"id":45,"difficulty":14,"q":46,"a":47},"response-description","How do you document response codes and descriptions in FastAPI?","Use `responses` dict in the route decorator to document non-default status codes.\nThe `summary` and `description` parameters document the operation itself.\n\n```python\n@app.get(\n    \"\u002Fitems\u002F{id}\",\n    summary=\"Retrieve an item\",\n    description=\"Returns the full item record. Returns 404 if the item does not exist.\",\n    responses={\n        404: {\"description\": \"Item not found\"},\n        200: {\"description\": \"The item\", \"model\": Item},\n    },\n)\nasync def get_item(id: int):\n    ...\n```\n\nFastAPI also reads the handler's docstring as the operation description:\n\n```python\n@app.get(\"\u002Fitems\u002F{id}\")\nasync def get_item(id: int):\n    \"\"\"Return a single item by its numeric ID.\"\"\"\n    ...\n```\n\nRule of thumb: use the docstring for prose descriptions; use the `responses` dict\nto document error codes and their schemas.\n",{"id":49,"difficulty":33,"q":50,"a":51},"multiple-responses","How do you declare that a route can return different response models for different status codes?","Use the `responses` parameter with a dict keyed by status code. For typed bodies\npass `\"model\": PydanticModel`.\n\n```python\nfrom fastapi.responses import JSONResponse\nfrom typing import Union\n\nclass Item(BaseModel): id: int; name: str\nclass Message(BaseModel): message: str\n\n@app.get(\n    \"\u002Fitems\u002F{id}\",\n    response_model=Item,                      # 200 default\n    responses={404: {\"model\": Message}},      # 404 documented\n)\nasync def get_item(id: int) -> Union[Item, JSONResponse]:\n    item = await db.get(id)\n    if not item:\n        return JSONResponse(\n            status_code=404,\n            content={\"message\": \"not found\"},\n        )\n    return item\n```\n\nNote: FastAPI only **validates** the `response_model`; additional entries in\n`responses` are documentation-only.\n\nRule of thumb: document all realistic non-200 responses in `responses={}` even if\nFastAPI won't validate them — clients and SDK generators rely on the schema.\n",{"id":53,"difficulty":33,"q":54,"a":55},"path-order-matters","Why does route order matter in FastAPI, and what is the common pitfall?","FastAPI matches routes **in the order they are registered**. The first matching\nroute wins. If a fixed path like `\u002Fitems\u002Fexport` is registered after a\nparameterised path like `\u002Fitems\u002F{id}`, the `{id}` route captures the string\n`\"export\"` as the value of `id`.\n\n```python\n# WRONG order — \u002Fitems\u002Fexport matches the first route with id=\"export\"\n@app.get(\"\u002Fitems\u002F{id}\")\nasync def get_item(id: int): ...   # FastAPI raises 422 because \"export\" is not int\n\n@app.get(\"\u002Fitems\u002Fexport\")\nasync def export_items(): ...      # never reached\n\n# CORRECT order — fixed paths before parameterised\n@app.get(\"\u002Fitems\u002Fexport\")\nasync def export_items(): ...\n\n@app.get(\"\u002Fitems\u002F{id}\")\nasync def get_item(id: int): ...\n```\n\nRule of thumb: register fixed\u002Fstatic paths before parameterised ones at the same\ndepth — or use type constraints (`{id:int}`) to let FastAPI discriminate automatically.\n",{"id":57,"difficulty":33,"q":58,"a":59},"path-converters","How do you constrain a path parameter to a specific type in the URL pattern?","FastAPI uses Starlette's **path converters** in the URL string to constrain path\nparameters without Pydantic at the routing layer:\n\n```python\n@app.get(\"\u002Fitems\u002F{item_id:int}\")    # only matches if segment is an integer\nasync def get_item(item_id: int): ...\n\n@app.get(\"\u002Ffiles\u002F{file_path:path}\") # matches any path, including slashes\nasync def get_file(file_path: str): ...\n```\n\nConverters: `str` (default), `int`, `float`, `uuid`, `path`.\n\nCombining with Pydantic type annotations provides two-level validation:\nStarlette rejects non-int URLs at routing time; Pydantic further validates range\nor constraints in the handler.\n\nRule of thumb: use `:int` or `:uuid` converters in paths to prevent wrong-type\nsegments from even reaching the handler — cleaner 404 instead of 422.\n",{"id":61,"difficulty":33,"q":62,"a":63},"route-include-router","What is `APIRouter` and why should you use it instead of `app` directly?","`APIRouter` is FastAPI's mechanism for splitting route definitions across multiple\nfiles. Routes are registered on the router, then the router is mounted onto the\napp (or another router) with a prefix and shared config.\n\n```python\n# routers\u002Fusers.py\nfrom fastapi import APIRouter\nrouter = APIRouter(prefix=\"\u002Fusers\", tags=[\"users\"])\n\n@router.get(\"\u002F\")\nasync def list_users(): ...\n\n@router.get(\"\u002F{id}\")\nasync def get_user(id: int): ...\n```\n\n```python\n# main.py\nfrom routers import users\napp.include_router(users.router)\n# registers: GET \u002Fusers\u002F  and  GET \u002Fusers\u002F{id}\n```\n\nBenefits: logical grouping, reusable prefix\u002Ftags\u002Fdependencies, testable in\nisolation, avoids a 1000-line main.py.\n\nRule of thumb: one `APIRouter` per resource or domain concept; mount all routers\nin `main.py`.\n",{"id":65,"difficulty":33,"q":66,"a":67},"include-router-dependencies","How do you apply a dependency to every route in an `APIRouter`?","Pass `dependencies=[Depends(fn)]` to `APIRouter()` or `include_router()`. Every\nroute in that router runs the dependency before the handler — useful for auth or\nrate-limiting.\n\n```python\nfrom fastapi import APIRouter, Depends\nfrom .auth import verify_token\n\n# Option A: on the router itself\nrouter = APIRouter(\n    prefix=\"\u002Fadmin\",\n    dependencies=[Depends(verify_token)],\n)\n\n# Option B: at mount time (non-destructive)\napp.include_router(router, dependencies=[Depends(verify_token)])\n```\n\nBoth options stack — if you set deps in both places, both run.\nRouter-level deps run before route-level deps.\n\nRule of thumb: put auth\u002Frate-limiting deps at the router level so you can't\naccidentally forget them on a new route; put business-logic deps at the route level.\n",{"id":69,"difficulty":33,"q":70,"a":71},"return-type-annotation","What is the effect of adding a return type annotation to a FastAPI handler?","FastAPI uses the return type annotation in two ways:\n1. **OpenAPI schema** — the annotation becomes the documented response schema.\n2. **Response validation** — if `response_model` is also set, it overrides; if not,\n   FastAPI uses the return annotation as the implicit `response_model`.\n\n```python\nclass UserOut(BaseModel):\n    id: int\n    name: str\n\n@app.get(\"\u002Fusers\u002F{id}\")\nasync def get_user(id: int) -> UserOut:   # annotation drives the schema\n    return await db.get(User, id)\n```\n\nSince Pydantic v2 + FastAPI 0.100+, you can return an ORM object that matches\n`UserOut` and FastAPI will serialise it. Using `response_model=UserOut` in the\ndecorator is equivalent but takes precedence.\n\nRule of thumb: use return type annotations as the primary way to document response\nschemas — it's more Pythonic and keeps the decorator line shorter.\n",{"id":73,"difficulty":14,"q":74,"a":75},"no-content-response","How do you return a 204 No Content response from FastAPI?","Set `status_code=204` in the decorator and return `None` (or nothing). FastAPI\nsends no body for 204 responses.\n\n```python\nfrom fastapi import status\nfrom fastapi.responses import Response\n\n@app.delete(\"\u002Fitems\u002F{id}\", status_code=status.HTTP_204_NO_CONTENT)\nasync def delete_item(id: int):\n    await db.delete(id)\n    # implicit return None → no body sent\n```\n\nAlternatively, return `Response(status_code=204)` explicitly when you need to\navoid FastAPI trying to serialise `None`.\n\nRule of thumb: always use 204 for successful DELETE operations that return no\nbody — clients rely on the status code to know there's nothing to parse.\n",{"id":77,"difficulty":33,"q":78,"a":79},"websocket-route","How do you define a WebSocket route in FastAPI?","Use `@app.websocket(\"\u002Fpath\")` and declare the handler with a `WebSocket`\nparameter. The handler stays alive in a loop, sending and receiving messages\nuntil the connection closes.\n\n```python\nfrom fastapi import WebSocket\n\n@app.websocket(\"\u002Fws\")\nasync def websocket_endpoint(ws: WebSocket):\n    await ws.accept()\n    try:\n        while True:\n            data = await ws.receive_text()\n            await ws.send_text(f\"Echo: {data}\")\n    except WebSocketDisconnect:\n        pass   # client closed the connection\n```\n\nWebSocket handlers can use `Depends()` just like HTTP handlers, making it easy\nto authenticate the upgrade request before `ws.accept()`.\n\nRule of thumb: always call `await ws.accept()` before sending; catch\n`WebSocketDisconnect` to clean up resources when the client leaves.\n",{"id":81,"difficulty":14,"q":82,"a":83},"static-files","How do you serve static files in FastAPI?","Mount Starlette's `StaticFiles` on a path:\n\n```python\nfrom fastapi.staticfiles import StaticFiles\n\napp.mount(\"\u002Fstatic\", StaticFiles(directory=\"static\"), name=\"static\")\n```\n\nRequests to `\u002Fstatic\u002Flogo.png` serve `.\u002Fstatic\u002Flogo.png` directly without going\nthrough FastAPI's routing. It's handled at the ASGI mount level, so it bypasses\nmiddleware that is added _after_ the mount.\n\nFor a single-page app (SPA) fallback:\n```python\napp.mount(\"\u002F\", StaticFiles(directory=\"dist\", html=True), name=\"spa\")\n```\n`html=True` serves `index.html` for any path not found in the directory.\n\nRule of thumb: use `StaticFiles` for assets; for dynamically generated file\ndownloads use `FileResponse` inside a regular route handler.\n",15,null,{"description":11},"FastAPI path operations interview questions — decorators, HTTP methods, status codes, operation metadata, tags, deprecated routes and include_router.","fastapi\u002Ffundamentals\u002Fpath-operations","Fundamentals","fundamentals","2026-06-20","9QLxXiT9OhguZvSQ91Ol9hHDan8TJJBNIKJvImgvmSc",[94,98,101,102,106],{"subtopic":95,"path":96,"order":97},"Async Basics","\u002Ffastapi\u002Ffundamentals\u002Fasync-basics",1,{"subtopic":99,"path":100,"order":12},"Request Lifecycle","\u002Ffastapi\u002Ffundamentals\u002Frequest-lifecycle",{"subtopic":6,"path":21,"order":20},{"subtopic":103,"path":104,"order":105},"Type Hints & FastAPI","\u002Ffastapi\u002Ffundamentals\u002Ftype-hints",4,{"subtopic":107,"path":108,"order":109},"OpenAPI & Docs","\u002Ffastapi\u002Ffundamentals\u002Fopenapi-docs",5,1782244112572]