[{"data":1,"prerenderedAt":1083},["ShallowReactive",2],{"nav":3,"page-\u002Fasync-background-tasks-observability\u002Fbackground-task-processing\u002F":310,"surround-\u002Fasync-background-tasks-observability\u002Fbackground-task-processing\u002F":1081},[4,96,212],{"title":5,"path":6,"stem":7,"children":8},"Advanced Pydantic Validation Serialization","\u002Fadvanced-pydantic-validation-serialization","advanced-pydantic-validation-serialization",[9,12,30,42,54,66,90],{"title":10,"path":6,"stem":11},"Advanced Pydantic Validation and Serialization","advanced-pydantic-validation-serialization\u002Findex",{"title":13,"path":14,"stem":15,"children":16},"Custom Validators and Field Constraints in Pydantic","\u002Fadvanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints","advanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Findex",[17,18,24],{"title":13,"path":14,"stem":15},{"title":19,"path":20,"stem":21,"children":22},"Creating Reusable Custom Validators in Pydantic","\u002Fadvanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fcreating-reusable-custom-validators-in-pydantic","advanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fcreating-reusable-custom-validators-in-pydantic\u002Findex",[23],{"title":19,"path":20,"stem":21},{"title":25,"path":26,"stem":27,"children":28},"Pydantic v2 Async Custom Validator: What to Do Instead","\u002Fadvanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fpydantic-v2-async-custom-validator","advanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fpydantic-v2-async-custom-validator\u002Findex",[29],{"title":25,"path":26,"stem":27},{"title":31,"path":32,"stem":33,"children":34},"JSON Schema Customization in Pydantic and FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fjson-schema-customization","advanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Findex",[35,36],{"title":31,"path":32,"stem":33},{"title":37,"path":38,"stem":39,"children":40},"Customizing OpenAPI Schema Generation in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Fcustomizing-openapi-schema-generation-in-fastapi","advanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Fcustomizing-openapi-schema-generation-in-fastapi\u002Findex",[41],{"title":37,"path":38,"stem":39},{"title":43,"path":44,"stem":45,"children":46},"Nested Model Serialization in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fnested-model-serialization","advanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Findex",[47,48],{"title":43,"path":44,"stem":45},{"title":49,"path":50,"stem":51,"children":52},"Handling Deeply Nested JSON Models Efficiently","\u002Fadvanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Fhandling-deeply-nested-json-models-efficiently","advanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Fhandling-deeply-nested-json-models-efficiently\u002Findex",[53],{"title":49,"path":50,"stem":51},{"title":55,"path":56,"stem":57,"children":58},"Performance Optimization for Pydantic Models in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models","advanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Findex",[59,60],{"title":55,"path":56,"stem":57},{"title":61,"path":62,"stem":63,"children":64},"Pydantic Model Serialization Performance in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Fpydantic-model-serialization-performance","advanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Fpydantic-model-serialization-performance\u002Findex",[65],{"title":61,"path":62,"stem":63},{"title":67,"path":68,"stem":69,"children":70},"Pydantic V2 Migration Guide for FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Findex",[71,72,78,84],{"title":67,"path":68,"stem":69},{"title":73,"path":74,"stem":75,"children":76},"Migrate @validator to @field_validator in Pydantic v2","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmigrate-validator-to-field-validator","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmigrate-validator-to-field-validator\u002Findex",[77],{"title":73,"path":74,"stem":75},{"title":79,"path":80,"stem":81,"children":82},"Migrating from Pydantic v1 to v2 Without Breaking APIs","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmigrating-from-pydantic-v1-to-v2-without-breaking-apis","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmigrating-from-pydantic-v1-to-v2-without-breaking-apis\u002Findex",[83],{"title":79,"path":80,"stem":81},{"title":85,"path":86,"stem":87,"children":88},"model_config vs class Config in Pydantic v2","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmodel-config-vs-class-config","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmodel-config-vs-class-config\u002Findex",[89],{"title":85,"path":86,"stem":87},{"title":91,"path":92,"stem":93,"children":94},"Type Hinting and IDE Integration in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Ftype-hinting-ide-integration","advanced-pydantic-validation-serialization\u002Ftype-hinting-ide-integration\u002Findex",[95],{"title":91,"path":92,"stem":93},{"title":97,"path":98,"stem":99,"children":100},"Async Background Tasks Observability","\u002Fasync-background-tasks-observability","async-background-tasks-observability",[101,104,122,140,158,176,194],{"title":102,"path":98,"stem":103},"Async, Background Tasks, and Observability in FastAPI","async-background-tasks-observability\u002Findex",{"title":105,"path":106,"stem":107,"children":108},"Async Correctness and Concurrency in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-correctness-concurrency","async-background-tasks-observability\u002Fasync-correctness-concurrency\u002Findex",[109,110,116],{"title":105,"path":106,"stem":107},{"title":111,"path":112,"stem":113,"children":114},"FastAPI async def vs def: Performance and When to Use Each","\u002Fasync-background-tasks-observability\u002Fasync-correctness-concurrency\u002Ffastapi-async-def-vs-def-performance","async-background-tasks-observability\u002Fasync-correctness-concurrency\u002Ffastapi-async-def-vs-def-performance\u002Findex",[115],{"title":111,"path":112,"stem":113},{"title":117,"path":118,"stem":119,"children":120},"Fixing Blocking Calls in Async FastAPI Routes","\u002Fasync-background-tasks-observability\u002Fasync-correctness-concurrency\u002Ffixing-blocking-calls-in-async-routes","async-background-tasks-observability\u002Fasync-correctness-concurrency\u002Ffixing-blocking-calls-in-async-routes\u002Findex",[121],{"title":117,"path":118,"stem":119},{"title":123,"path":124,"stem":125,"children":126},"Async Database Sessions in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-database-sessions","async-background-tasks-observability\u002Fasync-database-sessions\u002Findex",[127,128,134],{"title":123,"path":124,"stem":125},{"title":129,"path":130,"stem":131,"children":132},"Async SQLAlchemy Session per Request in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002Fasync-sqlalchemy-session-per-request","async-background-tasks-observability\u002Fasync-database-sessions\u002Fasync-sqlalchemy-session-per-request\u002Findex",[133],{"title":129,"path":130,"stem":131},{"title":135,"path":136,"stem":137,"children":138},"Fixing asyncpg Connection Pool Exhaustion in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002Ffixing-asyncpg-pool-exhaustion","async-background-tasks-observability\u002Fasync-database-sessions\u002Ffixing-asyncpg-pool-exhaustion\u002Findex",[139],{"title":135,"path":136,"stem":137},{"title":141,"path":142,"stem":143,"children":144},"Background Task Processing in FastAPI","\u002Fasync-background-tasks-observability\u002Fbackground-task-processing","async-background-tasks-observability\u002Fbackground-task-processing\u002Findex",[145,146,152],{"title":141,"path":142,"stem":143},{"title":147,"path":148,"stem":149,"children":150},"FastAPI BackgroundTasks vs Celery vs ARQ","\u002Fasync-background-tasks-observability\u002Fbackground-task-processing\u002Ffastapi-backgroundtasks-vs-celery-vs-arq","async-background-tasks-observability\u002Fbackground-task-processing\u002Ffastapi-backgroundtasks-vs-celery-vs-arq\u002Findex",[151],{"title":147,"path":148,"stem":149},{"title":153,"path":154,"stem":155,"children":156},"Running ARQ Workers with FastAPI","\u002Fasync-background-tasks-observability\u002Fbackground-task-processing\u002Frunning-arq-workers-with-fastapi","async-background-tasks-observability\u002Fbackground-task-processing\u002Frunning-arq-workers-with-fastapi\u002Findex",[157],{"title":153,"path":154,"stem":155},{"title":159,"path":160,"stem":161,"children":162},"Caching Strategies in FastAPI","\u002Fasync-background-tasks-observability\u002Fcaching-strategies","async-background-tasks-observability\u002Fcaching-strategies\u002Findex",[163,164,170],{"title":159,"path":160,"stem":161},{"title":165,"path":166,"stem":167,"children":168},"Cache Invalidation Patterns in FastAPI","\u002Fasync-background-tasks-observability\u002Fcaching-strategies\u002Fcache-invalidation-patterns-in-fastapi","async-background-tasks-observability\u002Fcaching-strategies\u002Fcache-invalidation-patterns-in-fastapi\u002Findex",[169],{"title":165,"path":166,"stem":167},{"title":171,"path":172,"stem":173,"children":174},"Redis Response Caching in FastAPI","\u002Fasync-background-tasks-observability\u002Fcaching-strategies\u002Fredis-response-caching-in-fastapi","async-background-tasks-observability\u002Fcaching-strategies\u002Fredis-response-caching-in-fastapi\u002Findex",[175],{"title":171,"path":172,"stem":173},{"title":177,"path":178,"stem":179,"children":180},"Observability and Tracing in FastAPI","\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing","async-background-tasks-observability\u002Fobservability-and-tracing\u002Findex",[181,182,188],{"title":177,"path":178,"stem":179},{"title":183,"path":184,"stem":185,"children":186},"Instrumenting FastAPI with OpenTelemetry","\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing\u002Finstrumenting-fastapi-with-opentelemetry","async-background-tasks-observability\u002Fobservability-and-tracing\u002Finstrumenting-fastapi-with-opentelemetry\u002Findex",[187],{"title":183,"path":184,"stem":185},{"title":189,"path":190,"stem":191,"children":192},"Structured JSON Logging with Request IDs in FastAPI","\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing\u002Fstructured-json-logging-with-request-ids","async-background-tasks-observability\u002Fobservability-and-tracing\u002Fstructured-json-logging-with-request-ids\u002Findex",[193],{"title":189,"path":190,"stem":191},{"title":195,"path":196,"stem":197,"children":198},"Rate Limiting and Throttling in FastAPI","\u002Fasync-background-tasks-observability\u002Frate-limiting-throttling","async-background-tasks-observability\u002Frate-limiting-throttling\u002Findex",[199,200,206],{"title":195,"path":196,"stem":197},{"title":201,"path":202,"stem":203,"children":204},"FastAPI Rate Limiting with Redis and SlowAPI","\u002Fasync-background-tasks-observability\u002Frate-limiting-throttling\u002Ffastapi-rate-limiting-with-redis-slowapi","async-background-tasks-observability\u002Frate-limiting-throttling\u002Ffastapi-rate-limiting-with-redis-slowapi\u002Findex",[205],{"title":201,"path":202,"stem":203},{"title":207,"path":208,"stem":209,"children":210},"Per-User Token Bucket Throttling in FastAPI","\u002Fasync-background-tasks-observability\u002Frate-limiting-throttling\u002Fper-user-token-bucket-throttling","async-background-tasks-observability\u002Frate-limiting-throttling\u002Fper-user-token-bucket-throttling\u002Findex",[211],{"title":207,"path":208,"stem":209},{"title":213,"path":214,"stem":215,"children":216},"Core Architecture Routing Patterns","\u002Fcore-architecture-routing-patterns","core-architecture-routing-patterns",[217,220,232,250,268,280,292],{"title":218,"path":214,"stem":219},"FastAPI Core Architecture and Routing Patterns","core-architecture-routing-patterns\u002Findex",{"title":221,"path":222,"stem":223,"children":224},"Application Factory Patterns in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fapplication-factory-patterns","core-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Findex",[225,226],{"title":221,"path":222,"stem":223},{"title":227,"path":228,"stem":229,"children":230},"FastAPI App Factory Pattern for Testing and Deployment","\u002Fcore-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Ffastapi-app-factory-pattern-for-testing-and-deployment","core-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Ffastapi-app-factory-pattern-for-testing-and-deployment\u002Findex",[231],{"title":227,"path":228,"stem":229},{"title":233,"path":234,"stem":235,"children":236},"Configuration Management in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fconfiguration-management","core-architecture-routing-patterns\u002Fconfiguration-management\u002Findex",[237,238,244],{"title":233,"path":234,"stem":235},{"title":239,"path":240,"stem":241,"children":242},"Managing Environment Variables with Pydantic Settings","\u002Fcore-architecture-routing-patterns\u002Fconfiguration-management\u002Fmanaging-environment-variables-with-pydantic-settings","core-architecture-routing-patterns\u002Fconfiguration-management\u002Fmanaging-environment-variables-with-pydantic-settings\u002Findex",[243],{"title":239,"path":240,"stem":241},{"title":245,"path":246,"stem":247,"children":248},"Pydantic Settings vs Dynaconf vs python-decouple","\u002Fcore-architecture-routing-patterns\u002Fconfiguration-management\u002Fpydantic-settings-vs-dynaconf-vs-python-decouple","core-architecture-routing-patterns\u002Fconfiguration-management\u002Fpydantic-settings-vs-dynaconf-vs-python-decouple\u002Findex",[249],{"title":245,"path":246,"stem":247},{"title":251,"path":252,"stem":253,"children":254},"Dependency Injection Strategies in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies","core-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Findex",[255,256,262],{"title":251,"path":252,"stem":253},{"title":257,"path":258,"stem":259,"children":260},"Best Practices for FastAPI Dependency Injection","\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Fbest-practices-for-fastapi-dependency-injection","core-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Fbest-practices-for-fastapi-dependency-injection\u002Findex",[261],{"title":257,"path":258,"stem":259},{"title":263,"path":264,"stem":265,"children":266},"Fixing FastAPI Dependency Injection Circular Imports","\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Ffastapi-dependency-injection-circular-import-fix","core-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Ffastapi-dependency-injection-circular-import-fix\u002Findex",[267],{"title":263,"path":264,"stem":265},{"title":269,"path":270,"stem":271,"children":272},"Error Handling and Global Exceptions in FastAPI","\u002Fcore-architecture-routing-patterns\u002Ferror-handling-global-exceptions","core-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Findex",[273,274],{"title":269,"path":270,"stem":271},{"title":275,"path":276,"stem":277,"children":278},"Global Exception Handlers for Consistent API Responses","\u002Fcore-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Fglobal-exception-handlers-for-consistent-api-responses","core-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Fglobal-exception-handlers-for-consistent-api-responses\u002Findex",[279],{"title":275,"path":276,"stem":277},{"title":281,"path":282,"stem":283,"children":284},"Middleware Implementation in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation","core-architecture-routing-patterns\u002Fmiddleware-implementation\u002Findex",[285,286],{"title":281,"path":282,"stem":283},{"title":287,"path":288,"stem":289,"children":290},"Implementing Custom Middleware for Request Tracing","\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fimplementing-custom-middleware-for-request-tracing","core-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fimplementing-custom-middleware-for-request-tracing\u002Findex",[291],{"title":287,"path":288,"stem":289},{"title":293,"path":294,"stem":295,"children":296},"Modular Router Organization in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmodular-router-organization","core-architecture-routing-patterns\u002Fmodular-router-organization\u002Findex",[297,298,304],{"title":293,"path":294,"stem":295},{"title":299,"path":300,"stem":301,"children":302},"APIRouter Prefix vs Sub-Application Mounting in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmodular-router-organization\u002Fapirouter-prefix-vs-sub-application-mounting","core-architecture-routing-patterns\u002Fmodular-router-organization\u002Fapirouter-prefix-vs-sub-application-mounting\u002Findex",[303],{"title":299,"path":300,"stem":301},{"title":305,"path":306,"stem":307,"children":308},"How to Structure Large FastAPI Projects for Scale","\u002Fcore-architecture-routing-patterns\u002Fmodular-router-organization\u002Fhow-to-structure-large-fastapi-projects-for-scale","core-architecture-routing-patterns\u002Fmodular-router-organization\u002Fhow-to-structure-large-fastapi-projects-for-scale\u002Findex",[309],{"title":305,"path":306,"stem":307},{"id":311,"title":141,"body":312,"description":1050,"extension":1051,"meta":1052,"navigation":518,"path":142,"seo":1079,"stem":143,"__hash__":1080},"content\u002Fasync-background-tasks-observability\u002Fbackground-task-processing\u002Findex.md",{"type":313,"value":314,"toc":1042},"minimark",[315,319,328,342,476,481,486,612,616,619,759,871,878,882,887,891,894,966,970,1006,1010,1038],[316,317,141],"h1",{"id":318},"background-task-processing-in-fastapi",[320,321,322,323,327],"p",{},"Background task processing is moving work the client does not need for its response out of the request — using FastAPI's ",[324,325,326],"code",{},"BackgroundTasks"," for short fire-and-forget jobs and a real queue such as Celery or ARQ for durable, retryable work.",[320,329,330,331,336,337,341],{},"This topic is part of ",[332,333,335],"a",{"href":334},"\u002Fasync-background-tasks-observability\u002F","Async, Background Tasks and Observability",". It is the relief valve for ",[332,338,340],{"href":339},"\u002Fasync-background-tasks-observability\u002Fasync-correctness-concurrency\u002F","async correctness",": work that is slow or blocking is best removed from the request entirely, and durable jobs keep the hot path fast.",[343,344,345,472],"figure",{},[346,347,355,356,355,360,355,364,355,373,355,380,355,385,355,393,355,399,355,404,355,412,355,417,355,421,355,426,355,429,355,434,355,438,355,443,355,447,355,452,355,455,355,460,355,463,355,468],"svg",{"viewBox":348,"role":349,"ariaLabelledBy":350,"xmlns":353,"style":354},"0 0 760 240","img",[351,352],"bg-title","bg-desc","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;height:auto;font-family:Inter,system-ui,sans-serif","\n  ",[357,358,359],"title",{"id":351},"In-process background tasks versus a durable queue",[361,362,363],"desc",{"id":352},"The top path shows BackgroundTasks running in the same process after the response, with no durability. The bottom path shows the request enqueuing a job to a broker, which a separate worker pool consumes with retries and durability.",[365,366],"rect",{"x":367,"y":368,"width":369,"height":370,"rx":371,"style":372},"24","40","150","46","8","fill:#E0F2F1;stroke:#009688;stroke-width:1.6px",[374,375,379],"text",{"x":376,"y":377,"style":378},"99","62","fill:#00695C;font-size:11.5px;font-weight:600;text-anchor:middle","Request",[374,381,384],{"x":376,"y":382,"style":383},"78","fill:#6B7280;font-size:10px;text-anchor:middle","responds fast",[365,386],{"x":387,"y":388,"width":389,"height":390,"rx":391,"style":392},"220","32","180","30","7","fill:#FFFFFF;stroke:#4DB6AC;stroke-width:1.5px",[374,394,398],{"x":395,"y":396,"style":397},"310","52","fill:#00695C;font-size:11px;text-anchor:middle","BackgroundTasks (in-process)",[374,400,403],{"x":395,"y":401,"style":402},"80","fill:#9A6B12;font-size:10.5px;text-anchor:middle","fire-and-forget · lost on crash",[405,406],"line",{"x1":407,"y1":408,"x2":409,"y2":410,"style":411},"174","55","218","48","stroke:#00796B;stroke-width:1.5px",[413,414],"polygon",{"points":415,"style":416},"214,44 220,47 215,53","fill:#00796B",[365,418],{"x":387,"y":369,"width":419,"height":420,"rx":371,"style":392},"120","44",[374,422,425],{"x":423,"y":424,"style":397},"280","177","Broker",[365,427],{"x":428,"y":369,"width":369,"height":420,"rx":371,"style":392},"380",[374,430,433],{"x":431,"y":432,"style":397},"455","172","Worker pool",[374,435,437],{"x":431,"y":436,"style":383},"186","retries · durable",[365,439],{"x":440,"y":369,"width":441,"height":420,"rx":371,"style":442},"560","170","fill:#E0F2F1;stroke:#009688;stroke-width:1.5px",[374,444,446],{"x":445,"y":424,"style":397},"645","Celery · ARQ",[405,448],{"x1":376,"y1":449,"x2":450,"y2":451,"style":411},"86","270","148",[413,453],{"points":454,"style":416},"266,143 272,148 263,151",[405,456],{"x1":457,"y1":432,"x2":458,"y2":432,"style":459},"340","378","stroke:#00796B;stroke-width:1.6px",[413,461],{"points":462,"style":416},"378,168 386,172 378,176",[405,464],{"x1":465,"y1":432,"x2":466,"y2":432,"style":467},"530","558","stroke:#4DB6AC;stroke-width:1.5px",[413,469],{"points":470,"style":471},"558,168 566,172 558,176","fill:#4DB6AC",[473,474,475],"figcaption",{},"BackgroundTasks runs in-process with no durability; a broker-backed queue survives restarts and retries failed jobs on dedicated workers.",[477,478,480],"h2",{"id":479},"core-mechanics-backgroundtasks","Core Mechanics: BackgroundTasks",[320,482,483,485],{},[324,484,326],{}," schedules a callable to run after the response is sent, in the same process. It is ideal for short, non-critical side effects where losing the occasional job on a restart is acceptable.",[487,488,493],"pre",{"className":489,"code":490,"language":491,"meta":492,"style":492},"language-python shiki shiki-themes github-light","from fastapi import BackgroundTasks\n\n\n@app.post(\"\u002Fcomments\")\nasync def add_comment(body: CommentIn, tasks: BackgroundTasks) -> dict[str, str]:\n    comment = await save_comment(body)\n    # Notify after responding; if the process dies, a missed notification is fine.\n    tasks.add_task(notify_subscribers, comment.id)\n    return {\"id\": comment.id}\n","python","",[324,494,495,513,520,525,542,569,584,591,597],{"__ignoreMap":492},[496,497,499,503,507,510],"span",{"class":405,"line":498},1,[496,500,502],{"class":501},"sD7c4","from",[496,504,506],{"class":505},"sgsFI"," fastapi ",[496,508,509],{"class":501},"import",[496,511,512],{"class":505}," BackgroundTasks\n",[496,514,516],{"class":405,"line":515},2,[496,517,519],{"emptyLinePlaceholder":518},true,"\n",[496,521,523],{"class":405,"line":522},3,[496,524,519],{"emptyLinePlaceholder":518},[496,526,528,532,535,539],{"class":405,"line":527},4,[496,529,531],{"class":530},"s7eDp","@app.post",[496,533,534],{"class":505},"(",[496,536,538],{"class":537},"sYBdl","\"\u002Fcomments\"",[496,540,541],{"class":505},")\n",[496,543,545,548,551,554,557,561,564,566],{"class":405,"line":544},5,[496,546,547],{"class":501},"async",[496,549,550],{"class":501}," def",[496,552,553],{"class":530}," add_comment",[496,555,556],{"class":505},"(body: CommentIn, tasks: BackgroundTasks) -> dict[",[496,558,560],{"class":559},"sYu0t","str",[496,562,563],{"class":505},", ",[496,565,560],{"class":559},[496,567,568],{"class":505},"]:\n",[496,570,572,575,578,581],{"class":405,"line":571},6,[496,573,574],{"class":505},"    comment ",[496,576,577],{"class":501},"=",[496,579,580],{"class":501}," await",[496,582,583],{"class":505}," save_comment(body)\n",[496,585,587],{"class":405,"line":586},7,[496,588,590],{"class":589},"sAwPA","    # Notify after responding; if the process dies, a missed notification is fine.\n",[496,592,594],{"class":405,"line":593},8,[496,595,596],{"class":505},"    tasks.add_task(notify_subscribers, comment.id)\n",[496,598,600,603,606,609],{"class":405,"line":599},9,[496,601,602],{"class":501},"    return",[496,604,605],{"class":505}," {",[496,607,608],{"class":537},"\"id\"",[496,610,611],{"class":505},": comment.id}\n",[477,613,615],{"id":614},"production-implementation-durable-queues","Production Implementation: Durable Queues",[320,617,618],{},"When work must not be lost — a payment capture, a report that takes minutes, anything that needs retries — use a broker-backed queue. The request enqueues a job and returns; a separate worker pool consumes it with retry semantics.",[487,620,622],{"className":489,"code":621,"language":491,"meta":492,"style":492},"# ARQ worker: an async-native queue that fits FastAPI's async model.\nfrom arq import create_pool\nfrom arq.connections import RedisSettings\n\n\nasync def generate_report(ctx: dict, report_id: str) -> None:\n    # Idempotent: safe to retry because it upserts by report_id.\n    await build_and_store_report(report_id)\n\n\nclass WorkerSettings:\n    functions = [generate_report]\n    redis_settings = RedisSettings()\n    max_tries = 5            # The broker retries on failure with backoff.\n",[324,623,624,629,641,653,657,661,690,695,703,707,712,723,734,745],{"__ignoreMap":492},[496,625,626],{"class":405,"line":498},[496,627,628],{"class":589},"# ARQ worker: an async-native queue that fits FastAPI's async model.\n",[496,630,631,633,636,638],{"class":405,"line":515},[496,632,502],{"class":501},[496,634,635],{"class":505}," arq ",[496,637,509],{"class":501},[496,639,640],{"class":505}," create_pool\n",[496,642,643,645,648,650],{"class":405,"line":522},[496,644,502],{"class":501},[496,646,647],{"class":505}," arq.connections ",[496,649,509],{"class":501},[496,651,652],{"class":505}," RedisSettings\n",[496,654,655],{"class":405,"line":527},[496,656,519],{"emptyLinePlaceholder":518},[496,658,659],{"class":405,"line":544},[496,660,519],{"emptyLinePlaceholder":518},[496,662,663,665,667,670,673,676,679,681,684,687],{"class":405,"line":571},[496,664,547],{"class":501},[496,666,550],{"class":501},[496,668,669],{"class":530}," generate_report",[496,671,672],{"class":505},"(ctx: ",[496,674,675],{"class":559},"dict",[496,677,678],{"class":505},", report_id: ",[496,680,560],{"class":559},[496,682,683],{"class":505},") -> ",[496,685,686],{"class":559},"None",[496,688,689],{"class":505},":\n",[496,691,692],{"class":405,"line":586},[496,693,694],{"class":589},"    # Idempotent: safe to retry because it upserts by report_id.\n",[496,696,697,700],{"class":405,"line":593},[496,698,699],{"class":501},"    await",[496,701,702],{"class":505}," build_and_store_report(report_id)\n",[496,704,705],{"class":405,"line":599},[496,706,519],{"emptyLinePlaceholder":518},[496,708,710],{"class":405,"line":709},10,[496,711,519],{"emptyLinePlaceholder":518},[496,713,715,718,721],{"class":405,"line":714},11,[496,716,717],{"class":501},"class",[496,719,720],{"class":530}," WorkerSettings",[496,722,689],{"class":505},[496,724,726,729,731],{"class":405,"line":725},12,[496,727,728],{"class":505},"    functions ",[496,730,577],{"class":501},[496,732,733],{"class":505}," [generate_report]\n",[496,735,737,740,742],{"class":405,"line":736},13,[496,738,739],{"class":505},"    redis_settings ",[496,741,577],{"class":501},[496,743,744],{"class":505}," RedisSettings()\n",[496,746,748,751,753,756],{"class":405,"line":747},14,[496,749,750],{"class":505},"    max_tries ",[496,752,577],{"class":501},[496,754,755],{"class":559}," 5",[496,757,758],{"class":589},"            # The broker retries on failure with backoff.\n",[487,760,762],{"className":489,"code":761,"language":491,"meta":492,"style":492},"# Enqueue from a route, forwarding the correlation ID for traceability.\n@app.post(\"\u002Freports\")\nasync def create_report(req: Request) -> dict[str, str]:\n    pool = req.app.state.arq_pool\n    report_id = new_id()\n    await pool.enqueue_job(\"generate_report\", report_id,\n                           _job_id=report_id)   # Dedupe key prevents duplicates.\n    return {\"report_id\": report_id, \"status\": \"queued\"}\n",[324,763,764,769,780,800,810,820,833,847],{"__ignoreMap":492},[496,765,766],{"class":405,"line":498},[496,767,768],{"class":589},"# Enqueue from a route, forwarding the correlation ID for traceability.\n",[496,770,771,773,775,778],{"class":405,"line":515},[496,772,531],{"class":530},[496,774,534],{"class":505},[496,776,777],{"class":537},"\"\u002Freports\"",[496,779,541],{"class":505},[496,781,782,784,786,789,792,794,796,798],{"class":405,"line":522},[496,783,547],{"class":501},[496,785,550],{"class":501},[496,787,788],{"class":530}," create_report",[496,790,791],{"class":505},"(req: Request) -> dict[",[496,793,560],{"class":559},[496,795,563],{"class":505},[496,797,560],{"class":559},[496,799,568],{"class":505},[496,801,802,805,807],{"class":405,"line":527},[496,803,804],{"class":505},"    pool ",[496,806,577],{"class":501},[496,808,809],{"class":505}," req.app.state.arq_pool\n",[496,811,812,815,817],{"class":405,"line":544},[496,813,814],{"class":505},"    report_id ",[496,816,577],{"class":501},[496,818,819],{"class":505}," new_id()\n",[496,821,822,824,827,830],{"class":405,"line":571},[496,823,699],{"class":501},[496,825,826],{"class":505}," pool.enqueue_job(",[496,828,829],{"class":537},"\"generate_report\"",[496,831,832],{"class":505},", report_id,\n",[496,834,835,839,841,844],{"class":405,"line":586},[496,836,838],{"class":837},"sqxcx","                           _job_id",[496,840,577],{"class":501},[496,842,843],{"class":505},"report_id)   ",[496,845,846],{"class":589},"# Dedupe key prevents duplicates.\n",[496,848,849,851,853,856,859,862,865,868],{"class":405,"line":593},[496,850,602],{"class":501},[496,852,605],{"class":505},[496,854,855],{"class":537},"\"report_id\"",[496,857,858],{"class":505},": report_id, ",[496,860,861],{"class":537},"\"status\"",[496,863,864],{"class":505},": ",[496,866,867],{"class":537},"\"queued\"",[496,869,870],{"class":505},"}\n",[320,872,873,874,877],{},"The end-to-end comparison and worker deployment are detailed in ",[332,875,147],{"href":876},"\u002Fasync-background-tasks-observability\u002Fbackground-task-processing\u002Ffastapi-backgroundtasks-vs-celery-vs-arq\u002F",".",[477,879,881],{"id":880},"async-and-performance-notes","Async and Performance Notes",[320,883,884,886],{},[324,885,326],{}," runs on the same event loop after the response, so a slow task still consumes that worker's capacity — it defers work but does not move it off the machine. A real queue moves work to separate workers, which is what lets you scale CPU-heavy jobs independently of request traffic. Choose ARQ when your codebase is async-first and Celery when you need its mature ecosystem and scheduling.",[477,888,890],{"id":889},"testing-strategy","Testing Strategy",[320,892,893],{},"Assert the response does not wait on the deferred work, and that jobs are idempotent:",[487,895,897],{"className":489,"code":896,"language":491,"meta":492,"style":492},"def test_signup_responds_before_email(client, fake_mailer):\n    resp = client.post(\"\u002Fsignup\", json={\"email\": \"ada@example.com\"})\n    assert resp.status_code == 200          # Returns without awaiting delivery.\n    # The mailer is invoked by the background task, not during the request.\n",[324,898,899,910,944,961],{"__ignoreMap":492},[496,900,901,904,907],{"class":405,"line":498},[496,902,903],{"class":501},"def",[496,905,906],{"class":530}," test_signup_responds_before_email",[496,908,909],{"class":505},"(client, fake_mailer):\n",[496,911,912,915,917,920,923,925,928,930,933,936,938,941],{"class":405,"line":515},[496,913,914],{"class":505},"    resp ",[496,916,577],{"class":501},[496,918,919],{"class":505}," client.post(",[496,921,922],{"class":537},"\"\u002Fsignup\"",[496,924,563],{"class":505},[496,926,927],{"class":837},"json",[496,929,577],{"class":501},[496,931,932],{"class":505},"{",[496,934,935],{"class":537},"\"email\"",[496,937,864],{"class":505},[496,939,940],{"class":537},"\"ada@example.com\"",[496,942,943],{"class":505},"})\n",[496,945,946,949,952,955,958],{"class":405,"line":522},[496,947,948],{"class":501},"    assert",[496,950,951],{"class":505}," resp.status_code ",[496,953,954],{"class":501},"==",[496,956,957],{"class":559}," 200",[496,959,960],{"class":589},"          # Returns without awaiting delivery.\n",[496,962,963],{"class":405,"line":527},[496,964,965],{"class":589},"    # The mailer is invoked by the background task, not during the request.\n",[477,967,969],{"id":968},"failure-modes-and-debugging","Failure Modes and Debugging",[971,972,973,984,990,1000],"ul",{},[974,975,976,980,981,983],"li",{},[977,978,979],"strong",{},"Lost work on restart."," Using ",[324,982,326],{}," for critical jobs drops them on deploy; move to a durable queue.",[974,985,986,989],{},[977,987,988],{},"Non-idempotent retries."," A retried job that is not idempotent double-charges or double-sends; key it.",[974,991,992,995,996,877],{},[977,993,994],{},"Lost context."," The correlation ID is gone in the worker unless forwarded; pass it as an argument, per ",[332,997,999],{"href":998},"\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing\u002F","Observability and Tracing",[974,1001,1002,1005],{},[977,1003,1004],{},"Unbounded queues."," A queue with no limits hides a failing worker; monitor depth and set alerts.",[477,1007,1009],{"id":1008},"related-reading","Related Reading",[971,1011,1012,1020,1027],{},[974,1013,1014,1017,1018,877],{},[977,1015,1016],{},"Up to the section:"," ",[332,1019,335],{"href":334},[974,1021,1022,1017,1025,877],{},[977,1023,1024],{},"Hands-on guide:",[332,1026,147],{"href":876},[974,1028,1029,1017,1032,1035,1036,877],{},[977,1030,1031],{},"Composes with:",[332,1033,1034],{"href":339},"Async Correctness and Concurrency"," and ",[332,1037,999],{"href":998},[1039,1040,1041],"style",{},"html pre.shiki code .sD7c4, html code.shiki .sD7c4{--shiki-default:#D73A49}html pre.shiki code .sgsFI, html code.shiki .sgsFI{--shiki-default:#24292E}html pre.shiki code .s7eDp, html code.shiki .s7eDp{--shiki-default:#6F42C1}html pre.shiki code .sYBdl, html code.shiki .sYBdl{--shiki-default:#032F62}html pre.shiki code .sYu0t, html code.shiki .sYu0t{--shiki-default:#005CC5}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sqxcx, html code.shiki .sqxcx{--shiki-default:#E36209}",{"title":492,"searchDepth":515,"depth":515,"links":1043},[1044,1045,1046,1047,1048,1049],{"id":479,"depth":515,"text":480},{"id":614,"depth":515,"text":615},{"id":880,"depth":515,"text":881},{"id":889,"depth":515,"text":890},{"id":968,"depth":515,"text":969},{"id":1008,"depth":515,"text":1009},"Run deferred work in FastAPI: BackgroundTasks for fire-and-forget jobs versus Celery and ARQ for durable, retryable queues, plus idempotency, retries, and passing request context.","md",{"slug":1053,"type":1054,"breadcrumb":1055,"datePublished":1064,"dateModified":1065,"faq":1066},"background-task-processing","cluster",[1056,1059,1061],{"label":1057,"path":1058},"Home","\u002F",{"label":1060,"path":334},"Async, Background Tasks & Observability",{"label":1062,"path":1063},"Background Task Processing","\u002Fasync-background-tasks-observability\u002Fbackground-task-processing\u002F","2026-02-12","2026-06-18",[1067,1070,1073,1076],{"q":1068,"a":1069},"When should I use FastAPI BackgroundTasks versus Celery or ARQ?","Use BackgroundTasks for short, fire-and-forget work that can be lost on a crash without consequence — sending a notification, writing an audit line. Use Celery or ARQ when the work must survive restarts, be retried on failure, run on a schedule, or scale on dedicated workers. The dividing line is durability and retry, not duration alone.",{"q":1071,"a":1072},"Do FastAPI BackgroundTasks run after the response is sent?","Yes. Tasks added to BackgroundTasks run after the response is returned to the client, in the same process. They share the process lifecycle, so if the worker restarts before they finish, they are lost. That is acceptable for non-critical work and unacceptable for anything that must not be dropped.",{"q":1074,"a":1075},"How do I make background jobs safe to retry?","Make them idempotent: design each job so running it twice has the same effect as running it once, using a unique job key or a database upsert. Because durable queues retry on failure, a non-idempotent job can double-charge or double-send when a retry follows a partial success.",{"q":1077,"a":1078},"How do I keep the correlation ID in a background job?","Capture the request's correlation ID when you enqueue the job and pass it as an argument, then bind it inside the worker. Background work runs outside the request context, so the contextvar set by tracing middleware is not available unless you forward the value explicitly.",{"title":141,"description":1050},"xJ2U4TEisaZdtZACmZa1_ehJmAwx_MJETAIhhDsaj1I",[1082,1082],null,1781809863387]