[{"data":1,"prerenderedAt":1769},["ShallowReactive",2],{"nav":3,"page-\u002Fadvanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Fself-referencing-and-recursive-models\u002F":580,"surround-\u002Fadvanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Fself-referencing-and-recursive-models\u002F":1768},[4,186,386],{"title":5,"path":6,"stem":7,"children":8},"Advanced Pydantic Validation Serialization","\u002Fadvanced-pydantic-validation-serialization","advanced-pydantic-validation-serialization",[9,12,42,66,90,114,150,174],{"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,30,36],{"title":13,"path":14,"stem":15},{"title":19,"path":20,"stem":21,"children":22},"Before, After and Wrap Validators in Pydantic v2","\u002Fadvanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fbefore-after-and-wrap-validators","advanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fbefore-after-and-wrap-validators\u002Findex",[23],{"title":19,"path":20,"stem":21},{"title":25,"path":26,"stem":27,"children":28},"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",[29],{"title":25,"path":26,"stem":27},{"title":31,"path":32,"stem":33,"children":34},"Cross-Field Validation Patterns in Pydantic v2","\u002Fadvanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fcross-field-validation-patterns","advanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fcross-field-validation-patterns\u002Findex",[35],{"title":31,"path":32,"stem":33},{"title":37,"path":38,"stem":39,"children":40},"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",[41],{"title":37,"path":38,"stem":39},{"title":43,"path":44,"stem":45,"children":46},"JSON Schema Customization in Pydantic and FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fjson-schema-customization","advanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Findex",[47,48,54,60],{"title":43,"path":44,"stem":45},{"title":49,"path":50,"stem":51,"children":52},"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",[53],{"title":49,"path":50,"stem":51},{"title":55,"path":56,"stem":57,"children":58},"Discriminated Unions in OpenAPI with Pydantic","\u002Fadvanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Fdiscriminated-unions-in-openapi","advanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Fdiscriminated-unions-in-openapi\u002Findex",[59],{"title":55,"path":56,"stem":57},{"title":61,"path":62,"stem":63,"children":64},"Examples in the OpenAPI Schema with FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Fexamples-in-openapi-schema","advanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Fexamples-in-openapi-schema\u002Findex",[65],{"title":61,"path":62,"stem":63},{"title":67,"path":68,"stem":69,"children":70},"Nested Model Serialization in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fnested-model-serialization","advanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Findex",[71,72,78,84],{"title":67,"path":68,"stem":69},{"title":73,"path":74,"stem":75,"children":76},"Excluding Fields Per Endpoint in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Fexcluding-fields-per-endpoint","advanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Fexcluding-fields-per-endpoint\u002Findex",[77],{"title":73,"path":74,"stem":75},{"title":79,"path":80,"stem":81,"children":82},"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",[83],{"title":79,"path":80,"stem":81},{"title":85,"path":86,"stem":87,"children":88},"Self-Referencing and Recursive Models in Pydantic v2","\u002Fadvanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Fself-referencing-and-recursive-models","advanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Fself-referencing-and-recursive-models\u002Findex",[89],{"title":85,"path":86,"stem":87},{"title":91,"path":92,"stem":93,"children":94},"Performance Optimization for Pydantic Models in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models","advanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Findex",[95,96,102,108],{"title":91,"path":92,"stem":93},{"title":97,"path":98,"stem":99,"children":100},"model_construct and When to Skip Validation in Pydantic","\u002Fadvanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Fmodel-construct-when-to-skip-validation","advanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Fmodel-construct-when-to-skip-validation\u002Findex",[101],{"title":97,"path":98,"stem":99},{"title":103,"path":104,"stem":105,"children":106},"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",[107],{"title":103,"path":104,"stem":105},{"title":109,"path":110,"stem":111,"children":112},"TypeAdapter for Non-Model Types in Pydantic","\u002Fadvanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Ftypeadapter-for-non-model-types","advanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Ftypeadapter-for-non-model-types\u002Findex",[113],{"title":109,"path":110,"stem":111},{"title":115,"path":116,"stem":117,"children":118},"Pydantic V2 Migration Guide for FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Findex",[119,120,126,132,138,144],{"title":115,"path":116,"stem":117},{"title":121,"path":122,"stem":123,"children":124},"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",[125],{"title":121,"path":122,"stem":123},{"title":127,"path":128,"stem":129,"children":130},"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",[131],{"title":127,"path":128,"stem":129},{"title":133,"path":134,"stem":135,"children":136},"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",[137],{"title":133,"path":134,"stem":135},{"title":139,"path":140,"stem":141,"children":142},"Replacing json_encoders with field_serializer in Pydantic v2","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Freplacing-json-encoders-with-field-serializer","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Freplacing-json-encoders-with-field-serializer\u002Findex",[143],{"title":139,"path":140,"stem":141},{"title":145,"path":146,"stem":147,"children":148},"Migrating @root_validator to @model_validator in Pydantic v2","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Froot-validator-to-model-validator","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Froot-validator-to-model-validator\u002Findex",[149],{"title":145,"path":146,"stem":147},{"title":151,"path":152,"stem":153,"children":154},"Request Validation Patterns in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Frequest-validation-patterns","advanced-pydantic-validation-serialization\u002Frequest-validation-patterns\u002Findex",[155,156,162,168],{"title":151,"path":152,"stem":153},{"title":157,"path":158,"stem":159,"children":160},"Optional vs Nullable Fields in Pydantic and FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Frequest-validation-patterns\u002Foptional-vs-nullable-fields","advanced-pydantic-validation-serialization\u002Frequest-validation-patterns\u002Foptional-vs-nullable-fields\u002Findex",[161],{"title":157,"path":158,"stem":159},{"title":163,"path":164,"stem":165,"children":166},"Query, Path and Body Parameter Validation in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Frequest-validation-patterns\u002Fquery-path-and-body-parameter-validation","advanced-pydantic-validation-serialization\u002Frequest-validation-patterns\u002Fquery-path-and-body-parameter-validation\u002Findex",[167],{"title":163,"path":164,"stem":165},{"title":169,"path":170,"stem":171,"children":172},"Validating File Uploads and Forms in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Frequest-validation-patterns\u002Fvalidating-file-uploads-and-forms","advanced-pydantic-validation-serialization\u002Frequest-validation-patterns\u002Fvalidating-file-uploads-and-forms\u002Findex",[173],{"title":169,"path":170,"stem":171},{"title":175,"path":176,"stem":177,"children":178},"Type Hinting and IDE Integration in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Ftype-hinting-ide-integration","advanced-pydantic-validation-serialization\u002Ftype-hinting-ide-integration\u002Findex",[179,180],{"title":175,"path":176,"stem":177},{"title":181,"path":182,"stem":183,"children":184},"Annotated Dependencies and Reusable Types in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Ftype-hinting-ide-integration\u002Fannotated-dependencies-and-reusable-types","advanced-pydantic-validation-serialization\u002Ftype-hinting-ide-integration\u002Fannotated-dependencies-and-reusable-types\u002Findex",[185],{"title":181,"path":182,"stem":183},{"title":187,"path":188,"stem":189,"children":190},"Async Background Tasks Observability","\u002Fasync-background-tasks-observability","async-background-tasks-observability",[191,194,224,254,284,308,338,362],{"title":192,"path":188,"stem":193},"Async, Background Tasks, and Observability in FastAPI","async-background-tasks-observability\u002Findex",{"title":195,"path":196,"stem":197,"children":198},"Async Correctness and Concurrency in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-correctness-concurrency","async-background-tasks-observability\u002Fasync-correctness-concurrency\u002Findex",[199,200,206,212,218],{"title":195,"path":196,"stem":197},{"title":201,"path":202,"stem":203,"children":204},"Concurrent Requests with asyncio.gather in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-correctness-concurrency\u002Fconcurrent-requests-with-asyncio-gather","async-background-tasks-observability\u002Fasync-correctness-concurrency\u002Fconcurrent-requests-with-asyncio-gather\u002Findex",[205],{"title":201,"path":202,"stem":203},{"title":207,"path":208,"stem":209,"children":210},"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",[211],{"title":207,"path":208,"stem":209},{"title":213,"path":214,"stem":215,"children":216},"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",[217],{"title":213,"path":214,"stem":215},{"title":219,"path":220,"stem":221,"children":222},"Running Sync Code in a Threadpool in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-correctness-concurrency\u002Frunning-sync-code-in-a-threadpool","async-background-tasks-observability\u002Fasync-correctness-concurrency\u002Frunning-sync-code-in-a-threadpool\u002Findex",[223],{"title":219,"path":220,"stem":221},{"title":225,"path":226,"stem":227,"children":228},"Async Database Sessions in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-database-sessions","async-background-tasks-observability\u002Fasync-database-sessions\u002Findex",[229,230,236,242,248],{"title":225,"path":226,"stem":227},{"title":231,"path":232,"stem":233,"children":234},"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",[235],{"title":231,"path":232,"stem":233},{"title":237,"path":238,"stem":239,"children":240},"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",[241],{"title":237,"path":238,"stem":239},{"title":243,"path":244,"stem":245,"children":246},"Testing with Async Database Fixtures in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002Ftesting-with-async-database-fixtures","async-background-tasks-observability\u002Fasync-database-sessions\u002Ftesting-with-async-database-fixtures\u002Findex",[247],{"title":243,"path":244,"stem":245},{"title":249,"path":250,"stem":251,"children":252},"Transaction Management and Rollback in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002Ftransaction-management-and-rollback","async-background-tasks-observability\u002Fasync-database-sessions\u002Ftransaction-management-and-rollback\u002Findex",[253],{"title":249,"path":250,"stem":251},{"title":255,"path":256,"stem":257,"children":258},"Background Task Processing in FastAPI","\u002Fasync-background-tasks-observability\u002Fbackground-task-processing","async-background-tasks-observability\u002Fbackground-task-processing\u002Findex",[259,260,266,272,278],{"title":255,"path":256,"stem":257},{"title":261,"path":262,"stem":263,"children":264},"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",[265],{"title":261,"path":262,"stem":263},{"title":267,"path":268,"stem":269,"children":270},"Retry and Idempotency for FastAPI Background Tasks","\u002Fasync-background-tasks-observability\u002Fbackground-task-processing\u002Fretry-and-idempotency-for-tasks","async-background-tasks-observability\u002Fbackground-task-processing\u002Fretry-and-idempotency-for-tasks\u002Findex",[271],{"title":267,"path":268,"stem":269},{"title":273,"path":274,"stem":275,"children":276},"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",[277],{"title":273,"path":274,"stem":275},{"title":279,"path":280,"stem":281,"children":282},"When FastAPI BackgroundTasks Silently Fails","\u002Fasync-background-tasks-observability\u002Fbackground-task-processing\u002Fwhen-backgroundtasks-silently-fails","async-background-tasks-observability\u002Fbackground-task-processing\u002Fwhen-backgroundtasks-silently-fails\u002Findex",[283],{"title":279,"path":280,"stem":281},{"title":285,"path":286,"stem":287,"children":288},"Caching Strategies in FastAPI","\u002Fasync-background-tasks-observability\u002Fcaching-strategies","async-background-tasks-observability\u002Fcaching-strategies\u002Findex",[289,290,296,302],{"title":285,"path":286,"stem":287},{"title":291,"path":292,"stem":293,"children":294},"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",[295],{"title":291,"path":292,"stem":293},{"title":297,"path":298,"stem":299,"children":300},"Caching Dependency Results in FastAPI","\u002Fasync-background-tasks-observability\u002Fcaching-strategies\u002Fcaching-dependency-results","async-background-tasks-observability\u002Fcaching-strategies\u002Fcaching-dependency-results\u002Findex",[301],{"title":297,"path":298,"stem":299},{"title":303,"path":304,"stem":305,"children":306},"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",[307],{"title":303,"path":304,"stem":305},{"title":309,"path":310,"stem":311,"children":312},"Observability and Tracing in FastAPI","\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing","async-background-tasks-observability\u002Fobservability-and-tracing\u002Findex",[313,314,320,326,332],{"title":309,"path":310,"stem":311},{"title":315,"path":316,"stem":317,"children":318},"Correlating Logs, Traces and Errors in FastAPI","\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing\u002Fcorrelating-logs-traces-and-errors","async-background-tasks-observability\u002Fobservability-and-tracing\u002Fcorrelating-logs-traces-and-errors\u002Findex",[319],{"title":315,"path":316,"stem":317},{"title":321,"path":322,"stem":323,"children":324},"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",[325],{"title":321,"path":322,"stem":323},{"title":327,"path":328,"stem":329,"children":330},"Prometheus Metrics for FastAPI","\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing\u002Fprometheus-metrics-for-fastapi","async-background-tasks-observability\u002Fobservability-and-tracing\u002Fprometheus-metrics-for-fastapi\u002Findex",[331],{"title":327,"path":328,"stem":329},{"title":333,"path":334,"stem":335,"children":336},"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",[337],{"title":333,"path":334,"stem":335},{"title":339,"path":340,"stem":341,"children":342},"Rate Limiting and Throttling in FastAPI","\u002Fasync-background-tasks-observability\u002Frate-limiting-throttling","async-background-tasks-observability\u002Frate-limiting-throttling\u002Findex",[343,344,350,356],{"title":339,"path":340,"stem":341},{"title":345,"path":346,"stem":347,"children":348},"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",[349],{"title":345,"path":346,"stem":347},{"title":351,"path":352,"stem":353,"children":354},"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",[355],{"title":351,"path":352,"stem":353},{"title":357,"path":358,"stem":359,"children":360},"Rate Limit Headers and 429 Responses in FastAPI","\u002Fasync-background-tasks-observability\u002Frate-limiting-throttling\u002Frate-limit-headers-and-429-responses","async-background-tasks-observability\u002Frate-limiting-throttling\u002Frate-limit-headers-and-429-responses\u002Findex",[361],{"title":357,"path":358,"stem":359},{"title":363,"path":364,"stem":365,"children":366},"Testing FastAPI Applications","\u002Fasync-background-tasks-observability\u002Ftesting-fastapi-applications","async-background-tasks-observability\u002Ftesting-fastapi-applications\u002Findex",[367,368,374,380],{"title":363,"path":364,"stem":365},{"title":369,"path":370,"stem":371,"children":372},"Mocking External Services in FastAPI Tests","\u002Fasync-background-tasks-observability\u002Ftesting-fastapi-applications\u002Fmocking-external-services-in-tests","async-background-tasks-observability\u002Ftesting-fastapi-applications\u002Fmocking-external-services-in-tests\u002Findex",[373],{"title":369,"path":370,"stem":371},{"title":375,"path":376,"stem":377,"children":378},"TestClient vs httpx AsyncClient in FastAPI","\u002Fasync-background-tasks-observability\u002Ftesting-fastapi-applications\u002Ftestclient-vs-httpx-asyncclient","async-background-tasks-observability\u002Ftesting-fastapi-applications\u002Ftestclient-vs-httpx-asyncclient\u002Findex",[379],{"title":375,"path":376,"stem":377},{"title":381,"path":382,"stem":383,"children":384},"Testing Async FastAPI Endpoints with pytest-asyncio","\u002Fasync-background-tasks-observability\u002Ftesting-fastapi-applications\u002Ftesting-async-endpoints-with-pytest-asyncio","async-background-tasks-observability\u002Ftesting-fastapi-applications\u002Ftesting-async-endpoints-with-pytest-asyncio\u002Findex",[385],{"title":381,"path":382,"stem":383},{"title":387,"path":388,"stem":389,"children":390},"Core Architecture Routing Patterns","\u002Fcore-architecture-routing-patterns","core-architecture-routing-patterns",[391,394,412,436,472,496,526,556],{"title":392,"path":388,"stem":393},"FastAPI Core Architecture and Routing Patterns","core-architecture-routing-patterns\u002Findex",{"title":395,"path":396,"stem":397,"children":398},"Application Factory Patterns in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fapplication-factory-patterns","core-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Findex",[399,400,406],{"title":395,"path":396,"stem":397},{"title":401,"path":402,"stem":403,"children":404},"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",[405],{"title":401,"path":402,"stem":403},{"title":407,"path":408,"stem":409,"children":410},"Lifespan Events vs Startup and Shutdown in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Flifespan-events-vs-startup-shutdown","core-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Flifespan-events-vs-startup-shutdown\u002Findex",[411],{"title":407,"path":408,"stem":409},{"title":413,"path":414,"stem":415,"children":416},"Configuration Management in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fconfiguration-management","core-architecture-routing-patterns\u002Fconfiguration-management\u002Findex",[417,418,424,430],{"title":413,"path":414,"stem":415},{"title":419,"path":420,"stem":421,"children":422},"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",[423],{"title":419,"path":420,"stem":421},{"title":425,"path":426,"stem":427,"children":428},"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",[429],{"title":425,"path":426,"stem":427},{"title":431,"path":432,"stem":433,"children":434},"Secrets and .env Files Per Environment in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fconfiguration-management\u002Fsecrets-and-env-files-per-environment","core-architecture-routing-patterns\u002Fconfiguration-management\u002Fsecrets-and-env-files-per-environment\u002Findex",[435],{"title":431,"path":432,"stem":433},{"title":437,"path":438,"stem":439,"children":440},"Dependency Injection Strategies in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies","core-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Findex",[441,442,448,454,460,466],{"title":437,"path":438,"stem":439},{"title":443,"path":444,"stem":445,"children":446},"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",[447],{"title":443,"path":444,"stem":445},{"title":449,"path":450,"stem":451,"children":452},"Dependency Caching and use_cache in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Fdependency-caching-and-use-cache","core-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Fdependency-caching-and-use-cache\u002Findex",[453],{"title":449,"path":450,"stem":451},{"title":455,"path":456,"stem":457,"children":458},"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",[459],{"title":455,"path":456,"stem":457},{"title":461,"path":462,"stem":463,"children":464},"Overriding Dependencies in FastAPI Tests","\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Foverriding-dependencies-in-tests","core-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Foverriding-dependencies-in-tests\u002Findex",[465],{"title":461,"path":462,"stem":463},{"title":467,"path":468,"stem":469,"children":470},"Yield Dependencies and Cleanup Order in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Fyield-dependencies-and-cleanup-order","core-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Fyield-dependencies-and-cleanup-order\u002Findex",[471],{"title":467,"path":468,"stem":469},{"title":473,"path":474,"stem":475,"children":476},"Error Handling and Global Exceptions in FastAPI","\u002Fcore-architecture-routing-patterns\u002Ferror-handling-global-exceptions","core-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Findex",[477,478,484,490],{"title":473,"path":474,"stem":475},{"title":479,"path":480,"stem":481,"children":482},"Customising Validation Error Responses in FastAPI","\u002Fcore-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Fcustomising-validation-error-responses","core-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Fcustomising-validation-error-responses\u002Findex",[483],{"title":479,"path":480,"stem":481},{"title":485,"path":486,"stem":487,"children":488},"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",[489],{"title":485,"path":486,"stem":487},{"title":491,"path":492,"stem":493,"children":494},"HTTPException vs Custom Exception Classes in FastAPI","\u002Fcore-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Fhttpexception-vs-custom-exception-classes","core-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Fhttpexception-vs-custom-exception-classes\u002Findex",[495],{"title":491,"path":492,"stem":493},{"title":497,"path":498,"stem":499,"children":500},"Middleware Implementation in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation","core-architecture-routing-patterns\u002Fmiddleware-implementation\u002Findex",[501,502,508,514,520],{"title":497,"path":498,"stem":499},{"title":503,"path":504,"stem":505,"children":506},"CORS Middleware Configuration in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fcors-middleware-configuration","core-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fcors-middleware-configuration\u002Findex",[507],{"title":503,"path":504,"stem":505},{"title":509,"path":510,"stem":511,"children":512},"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",[513],{"title":509,"path":510,"stem":511},{"title":515,"path":516,"stem":517,"children":518},"Middleware Execution Order in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fmiddleware-execution-order","core-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fmiddleware-execution-order\u002Findex",[519],{"title":515,"path":516,"stem":517},{"title":521,"path":522,"stem":523,"children":524},"Middleware vs Dependencies: When to Use Which","\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fmiddleware-vs-dependencies-when-to-use-which","core-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fmiddleware-vs-dependencies-when-to-use-which\u002Findex",[525],{"title":521,"path":522,"stem":523},{"title":527,"path":528,"stem":529,"children":530},"Modular Router Organization in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmodular-router-organization","core-architecture-routing-patterns\u002Fmodular-router-organization\u002Findex",[531,532,538,544,550],{"title":527,"path":528,"stem":529},{"title":533,"path":534,"stem":535,"children":536},"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",[537],{"title":533,"path":534,"stem":535},{"title":539,"path":540,"stem":541,"children":542},"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",[543],{"title":539,"path":540,"stem":541},{"title":545,"path":546,"stem":547,"children":548},"Router Tags and OpenAPI Grouping in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmodular-router-organization\u002Frouter-tags-and-openapi-grouping","core-architecture-routing-patterns\u002Fmodular-router-organization\u002Frouter-tags-and-openapi-grouping\u002Findex",[549],{"title":545,"path":546,"stem":547},{"title":551,"path":552,"stem":553,"children":554},"Versioning APIs with FastAPI Routers","\u002Fcore-architecture-routing-patterns\u002Fmodular-router-organization\u002Fversioning-apis-with-routers","core-architecture-routing-patterns\u002Fmodular-router-organization\u002Fversioning-apis-with-routers\u002Findex",[555],{"title":551,"path":552,"stem":553},{"title":557,"path":558,"stem":559,"children":560},"The FastAPI Request\u002FResponse Lifecycle","\u002Fcore-architecture-routing-patterns\u002Frequest-response-lifecycle","core-architecture-routing-patterns\u002Frequest-response-lifecycle\u002Findex",[561,562,568,574],{"title":557,"path":558,"stem":559},{"title":563,"path":564,"stem":565,"children":566},"How a Request Flows Through FastAPI","\u002Fcore-architecture-routing-patterns\u002Frequest-response-lifecycle\u002Fhow-a-request-flows-through-fastapi","core-architecture-routing-patterns\u002Frequest-response-lifecycle\u002Fhow-a-request-flows-through-fastapi\u002Findex",[567],{"title":563,"path":564,"stem":565},{"title":569,"path":570,"stem":571,"children":572},"Response Model and Serialization Order","\u002Fcore-architecture-routing-patterns\u002Frequest-response-lifecycle\u002Fresponse-model-and-serialization-order","core-architecture-routing-patterns\u002Frequest-response-lifecycle\u002Fresponse-model-and-serialization-order\u002Findex",[573],{"title":569,"path":570,"stem":571},{"title":575,"path":576,"stem":577,"children":578},"Streaming and File Responses in FastAPI","\u002Fcore-architecture-routing-patterns\u002Frequest-response-lifecycle\u002Fstreaming-and-file-responses","core-architecture-routing-patterns\u002Frequest-response-lifecycle\u002Fstreaming-and-file-responses\u002Findex",[579],{"title":575,"path":576,"stem":577},{"id":581,"title":85,"body":582,"dateModified":1736,"datePublished":1736,"description":1737,"extension":1738,"faq":1739,"howto":1750,"meta":1751,"navigation":894,"path":86,"seo":1765,"stem":87,"type":1766,"__hash__":1767},"content\u002Fadvanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Fself-referencing-and-recursive-models\u002Findex.md",{"type":583,"value":584,"toc":1719},"minimark",[585,589,596,642,651,656,659,665,669,695,708,828,832,845,849,854,1178,1184,1188,1202,1209,1212,1231,1262,1267,1271,1277,1287,1293,1297,1300,1306,1315,1319,1330,1336,1347,1358,1361,1367,1372,1376,1462,1466,1469,1591,1594,1598,1601,1618,1626,1630,1641,1653,1659,1673,1679,1683,1715],[586,587,85],"h1",{"id":588},"self-referencing-and-recursive-models-in-pydantic-v2",[590,591,592],"p",{},[593,594,595],"strong",{},"Key takeaways:",[597,598,599,608,619,629,636],"ul",{},[600,601,602,603,607],"li",{},"A model can reference itself by name in a string annotation: ",[604,605,606],"code",{},"replies: list[\"Comment\"] = []",".",[600,609,610,611,614,615,618],{},"Pydantic v2 usually resolves this automatically; ",[604,612,613],{},"model_rebuild()"," returns ",[604,616,617],{},"None"," when nothing was left to do.",[600,620,621,622,626,627,607],{},"Forward references to types defined ",[623,624,625],"em",{},"later"," in the module do need an explicit ",[604,628,613],{},[600,630,631,632,635],{},"Too-deep input fails as a normal 422 with ",[604,633,634],{},"type: recursion_loop"," — it does not crash the process.",[600,637,638,639,607],{},"On Pydantic 2.13.4 \u002F CPython 3.12 at the default recursion limit of 1000, the measured ceiling was ",[593,640,641],{},"255 levels",[590,643,644,645,650],{},"This page extends ",[646,647,649],"a",{"href":648},"\u002Fadvanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002F","nested model serialization"," to the case where the nesting has no fixed depth.",[652,653,655],"h2",{"id":654},"the-problem-this-solves","The Problem This Solves",[590,657,658],{},"Comment threads, org charts, category trees, file systems, nested rule expressions — a lot of real data is a tree whose depth is decided by users, not by you. Modelling it means a class that refers to itself, which is a problem in Python because the class does not exist while its own body is executing.",[590,660,661,662,664],{},"The follow-on questions are the ones that actually cost time: does it need ",[604,663,613],{},", does serialization recurse correctly, and what happens when someone posts a thread 5,000 levels deep.",[652,666,668],{"id":667},"why-it-happens","Why It Happens",[590,670,671,674,675,678,679,682,683,686,687,690,691,694],{},[604,672,673],{},"class Comment"," binds the name ",[604,676,677],{},"Comment"," only after the class body finishes. An annotation inside the body that says ",[604,680,681],{},"list[Comment]"," therefore raises ",[604,684,685],{},"NameError",". Python's answer is a ",[623,688,689],{},"forward reference",": write the name as a string, or turn on ",[604,692,693],{},"from __future__ import annotations"," so every annotation in the module becomes a string automatically.",[590,696,697,698,701,702,704,705,707],{},"That defers the problem rather than solving it, because Pydantic must eventually resolve the string to a real type to build the validation schema. In v1 you were required to call ",[604,699,700],{},"update_forward_refs()"," yourself. In v2 Pydantic attempts resolution as soon as the class body completes, and since ",[604,703,677],{}," is bound by then, a self-reference resolves without help. ",[604,706,613],{}," remains for the cases automatic resolution cannot handle — most commonly a reference to a class defined further down the module.",[709,710,711,824],"figure",{},[712,713,721,725,729,738,744,749,753,758,763,768,773,780,786,791,797,801,806,814,820],"svg",{"viewBox":714,"role":715,"ariaLabelledBy":716,"xmlns":719,"style":720},"0 0 720 290","img",[717,718],"rec-title","rec-desc","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;height:auto;max-width:720px;margin:2rem 0",[722,723,724],"title",{"id":717},"A self-referencing comment model and the tree it validates",[726,727,728],"desc",{"id":718},"The Comment class has a replies field typed as a list of Comment, which produces a tree where each node can contain further nodes to a measured depth of 255 levels.",[730,731],"rect",{"x":732,"y":733,"width":734,"height":735,"rx":736,"style":737},"28","40","250","112","9","fill:none;stroke:#00796B;stroke-width:1.8",[739,740,673],"text",{"x":741,"y":742,"style":743},"153","66","text-anchor:middle;fill:#00796B;font:600 13px sans-serif",[739,745,748],{"x":741,"y":746,"style":747},"90","text-anchor:middle;fill:currentColor;font:400 12.5px monospace","id: int",[739,750,752],{"x":741,"y":751,"style":747},"110","body: str",[739,754,757],{"x":741,"y":755,"style":756},"130","text-anchor:middle;fill:#00796B;font:600 12.5px monospace","replies: list[Comment]",[759,760],"path",{"d":761,"style":762},"M278 96 C 320 96 320 200 153 200 C 60 200 60 160 90 154","fill:none;stroke:#00796B;stroke-width:1.5;stroke-dasharray:5 4",[764,765],"polygon",{"points":766,"style":767},"94,150 86,156 96,158","fill:#00796B",[739,769,772],{"x":741,"y":770,"style":771},"222","text-anchor:middle;fill:currentColor;font:400 11.5px sans-serif","refers to itself by name",[730,774],{"x":775,"y":733,"width":776,"height":777,"rx":778,"style":779},"470","150","34","7","fill:none;stroke:#00796B;stroke-width:1.6",[739,781,785],{"x":782,"y":783,"style":784},"545","62","text-anchor:middle;fill:#00796B;font:500 12px sans-serif","root comment",[730,787],{"x":788,"y":789,"width":776,"height":777,"rx":778,"style":790},"500","94","fill:none;stroke:currentColor;stroke-width:1.4",[739,792,796],{"x":793,"y":794,"style":795},"575","116","text-anchor:middle;fill:currentColor;font:500 12px sans-serif","reply",[730,798],{"x":799,"y":800,"width":776,"height":777,"rx":778,"style":790},"530","148",[739,802,805],{"x":803,"y":804,"style":795},"605","170","reply to reply",[807,808],"line",{"x1":809,"y1":810,"x2":811,"y2":812,"style":813},"490","74","512","92","stroke:currentColor;stroke-width:1.4",[807,815],{"x1":816,"y1":817,"x2":818,"y2":819,"style":813},"520","128","542","146",[739,821,823],{"x":793,"y":822,"style":771},"206","…measured ceiling: 255 levels",[825,826,827],"figcaption",{},"A single class describes a tree of unbounded shape, but not of unbounded depth — the validator itself has a limit.",[652,829,831],{"id":830},"prerequisites","Prerequisites",[597,833,834,837],{},[600,835,836],{},"Pydantic 2.13.4, FastAPI 0.139.2, CPython 3.12 at the default recursion limit.",[600,838,839,840,844],{},"Familiarity with ",[646,841,843],{"href":842},"\u002Fadvanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Fhandling-deeply-nested-json-models-efficiently\u002F","handling deeply nested JSON models efficiently"," for the fixed-depth case.",[652,846,848],{"id":847},"step-by-step-implementation","Step-by-Step Implementation",[850,851,853],"h3",{"id":852},"_1-declare-the-self-reference","1. Declare the self-reference",[855,856,861],"pre",{"className":857,"code":858,"language":859,"meta":860,"style":860},"language-python shiki shiki-themes github-light-high-contrast","\"\"\"Self-referencing models: a comment tree, forward references, model_rebuild, depth limits.\"\"\"\nfrom __future__ import annotations\n\nimport sys\nfrom typing import Any\n\nfrom fastapi import FastAPI\nfrom pydantic import BaseModel, ValidationError\n\napp = FastAPI()\n\n\nclass Comment(BaseModel):\n    id: int\n    body: str\n    replies: list[\"Comment\"] = []\n\n\n# With `from __future__ import annotations` every annotation is a string, so the self-reference\n# resolves at rebuild time rather than class-creation time.\nREBUILT = Comment.model_rebuild()\n\n\nclass Node(BaseModel):\n    \"\"\"A model whose forward reference points at a type defined later in the module.\"\"\"\n\n    label: str\n    child: \"Leaf | None\" = None\n\n\nclass Leaf(BaseModel):\n    value: int\n\n\nNODE_REBUILT = Node.model_rebuild()\n","python","",[604,862,863,871,889,896,905,918,923,936,949,954,966,971,976,995,1007,1016,1033,1038,1043,1050,1056,1068,1073,1078,1092,1098,1103,1111,1125,1130,1135,1149,1157,1162,1167],{"__ignoreMap":860},[864,865,867],"span",{"class":807,"line":866},1,[864,868,870],{"class":869},"sYEJz","\"\"\"Self-referencing models: a comment tree, forward references, model_rebuild, depth limits.\"\"\"\n",[864,872,874,878,882,885],{"class":807,"line":873},2,[864,875,877],{"class":876},"sTJeM","from",[864,879,881],{"class":880},"sacAq"," __future__",[864,883,884],{"class":876}," import",[864,886,888],{"class":887},"sigWx"," annotations\n",[864,890,892],{"class":807,"line":891},3,[864,893,895],{"emptyLinePlaceholder":894},true,"\n",[864,897,899,902],{"class":807,"line":898},4,[864,900,901],{"class":876},"import",[864,903,904],{"class":887}," sys\n",[864,906,908,910,913,915],{"class":807,"line":907},5,[864,909,877],{"class":876},[864,911,912],{"class":887}," typing ",[864,914,901],{"class":876},[864,916,917],{"class":887}," Any\n",[864,919,921],{"class":807,"line":920},6,[864,922,895],{"emptyLinePlaceholder":894},[864,924,926,928,931,933],{"class":807,"line":925},7,[864,927,877],{"class":876},[864,929,930],{"class":887}," fastapi ",[864,932,901],{"class":876},[864,934,935],{"class":887}," FastAPI\n",[864,937,939,941,944,946],{"class":807,"line":938},8,[864,940,877],{"class":876},[864,942,943],{"class":887}," pydantic ",[864,945,901],{"class":876},[864,947,948],{"class":887}," BaseModel, ValidationError\n",[864,950,952],{"class":807,"line":951},9,[864,953,895],{"emptyLinePlaceholder":894},[864,955,957,960,963],{"class":807,"line":956},10,[864,958,959],{"class":887},"app ",[864,961,962],{"class":876},"=",[864,964,965],{"class":887}," FastAPI()\n",[864,967,969],{"class":807,"line":968},11,[864,970,895],{"emptyLinePlaceholder":894},[864,972,974],{"class":807,"line":973},12,[864,975,895],{"emptyLinePlaceholder":894},[864,977,979,982,986,989,992],{"class":807,"line":978},13,[864,980,981],{"class":876},"class",[864,983,985],{"class":984},"sV4o_"," Comment",[864,987,988],{"class":887},"(",[864,990,991],{"class":880},"BaseModel",[864,993,994],{"class":887},"):\n",[864,996,998,1001,1004],{"class":807,"line":997},14,[864,999,1000],{"class":880},"    id",[864,1002,1003],{"class":887},": ",[864,1005,1006],{"class":880},"int\n",[864,1008,1010,1013],{"class":807,"line":1009},15,[864,1011,1012],{"class":887},"    body: ",[864,1014,1015],{"class":880},"str\n",[864,1017,1019,1022,1025,1028,1030],{"class":807,"line":1018},16,[864,1020,1021],{"class":887},"    replies: list[",[864,1023,1024],{"class":869},"\"Comment\"",[864,1026,1027],{"class":887},"] ",[864,1029,962],{"class":876},[864,1031,1032],{"class":887}," []\n",[864,1034,1036],{"class":807,"line":1035},17,[864,1037,895],{"emptyLinePlaceholder":894},[864,1039,1041],{"class":807,"line":1040},18,[864,1042,895],{"emptyLinePlaceholder":894},[864,1044,1046],{"class":807,"line":1045},19,[864,1047,1049],{"class":1048},"sFeEa","# With `from __future__ import annotations` every annotation is a string, so the self-reference\n",[864,1051,1053],{"class":807,"line":1052},20,[864,1054,1055],{"class":1048},"# resolves at rebuild time rather than class-creation time.\n",[864,1057,1059,1062,1065],{"class":807,"line":1058},21,[864,1060,1061],{"class":880},"REBUILT",[864,1063,1064],{"class":876}," =",[864,1066,1067],{"class":887}," Comment.model_rebuild()\n",[864,1069,1071],{"class":807,"line":1070},22,[864,1072,895],{"emptyLinePlaceholder":894},[864,1074,1076],{"class":807,"line":1075},23,[864,1077,895],{"emptyLinePlaceholder":894},[864,1079,1081,1083,1086,1088,1090],{"class":807,"line":1080},24,[864,1082,981],{"class":876},[864,1084,1085],{"class":984}," Node",[864,1087,988],{"class":887},[864,1089,991],{"class":880},[864,1091,994],{"class":887},[864,1093,1095],{"class":807,"line":1094},25,[864,1096,1097],{"class":869},"    \"\"\"A model whose forward reference points at a type defined later in the module.\"\"\"\n",[864,1099,1101],{"class":807,"line":1100},26,[864,1102,895],{"emptyLinePlaceholder":894},[864,1104,1106,1109],{"class":807,"line":1105},27,[864,1107,1108],{"class":887},"    label: ",[864,1110,1015],{"class":880},[864,1112,1114,1117,1120,1122],{"class":807,"line":1113},28,[864,1115,1116],{"class":887},"    child: ",[864,1118,1119],{"class":869},"\"Leaf | None\"",[864,1121,1064],{"class":876},[864,1123,1124],{"class":880}," None\n",[864,1126,1128],{"class":807,"line":1127},29,[864,1129,895],{"emptyLinePlaceholder":894},[864,1131,1133],{"class":807,"line":1132},30,[864,1134,895],{"emptyLinePlaceholder":894},[864,1136,1138,1140,1143,1145,1147],{"class":807,"line":1137},31,[864,1139,981],{"class":876},[864,1141,1142],{"class":984}," Leaf",[864,1144,988],{"class":887},[864,1146,991],{"class":880},[864,1148,994],{"class":887},[864,1150,1152,1155],{"class":807,"line":1151},32,[864,1153,1154],{"class":887},"    value: ",[864,1156,1006],{"class":880},[864,1158,1160],{"class":807,"line":1159},33,[864,1161,895],{"emptyLinePlaceholder":894},[864,1163,1165],{"class":807,"line":1164},34,[864,1166,895],{"emptyLinePlaceholder":894},[864,1168,1170,1173,1175],{"class":807,"line":1169},35,[864,1171,1172],{"class":880},"NODE_REBUILT",[864,1174,1064],{"class":876},[864,1176,1177],{"class":887}," Node.model_rebuild()\n",[590,1179,1180,1181,1183],{},"Note the mutable default ",[604,1182,606],{},". In a dataclass that would be a bug; in Pydantic it is fine, because Pydantic deep-copies defaults per instance rather than sharing them.",[850,1185,1187],{"id":1186},"_2-check-whether-the-rebuild-was-needed","2. Check whether the rebuild was needed",[590,1189,1190,1191,1194,1195,1197,1198,1201],{},"The ",[604,1192,1193],{},"\u002Frebuild-status"," endpoint reports what each ",[604,1196,613],{}," call returned. Real output from ",[604,1199,1200],{},"_verify\u002Foutput\u002Fpyd-recursive-models.txt",":",[855,1203,1207],{"className":1204,"code":1206,"language":739,"meta":860},[1205],"language-text","$ GET \u002Frebuild-status\n200 OK\n{\n  \"Comment.model_rebuild()\": null,\n  \"Node.model_rebuild()\": true,\n  \"Comment fields\": [\n    \"id\",\n    \"body\",\n    \"replies\"\n  ],\n  \"replies annotation\": \"list[example_pyd-recursive-models.Comment]\",\n  \"recursion_limit\": 1000\n}\n",[604,1208,1206],{"__ignoreMap":860},[590,1210,1211],{},"The two return values tell the whole story of when you need the call.",[590,1213,1214,1217,1218,1220,1221,1224,1225,1227,1228,1230],{},[604,1215,1216],{},"Comment.model_rebuild()"," returned ",[604,1219,617],{},", meaning ",[593,1222,1223],{},"the model was already complete",". The self-reference resolved on its own the moment the class body finished, because ",[604,1226,677],{}," was bound in the module namespace by then. The annotation, printed above, is a fully resolved ",[604,1229,681],{}," — not a dangling string.",[590,1232,1233,1217,1236,1239,1240,1243,1244,1247,1248,1251,1252,1255,1256,1258,1259,1261],{},[604,1234,1235],{},"Node.model_rebuild()",[604,1237,1238],{},"True",", meaning it ",[593,1241,1242],{},"actually rebuilt something",". ",[604,1245,1246],{},"Node.child"," refers to ",[604,1249,1250],{},"Leaf",", which is defined afterwards, so when Pydantic first tried to build ",[604,1253,1254],{},"Node","'s schema the name did not resolve. The model sat incomplete until ",[604,1257,613],{}," was called at a point where ",[604,1260,1250],{}," existed.",[590,1263,1264,1265,607],{},"That is the rule in practice: self-references are free, forward references to later definitions are not. Since the call is idempotent and cheap, leaving it in after each group of mutually-referencing models is reasonable defensive style — it just usually returns ",[604,1266,617],{},[850,1268,1270],{"id":1269},"_3-validate-and-serialize-a-tree","3. Validate and serialize a tree",[855,1272,1275],{"className":1273,"code":1274,"language":739,"meta":860},[1205],"$ POST \u002Fcomments\u002F  {\"id\": 1, \"body\": \"Does exclude_unset apply to nested models?\", \"replies\": [{\"id\": 2, \"body\": \"Yes, it recurses.\", \"replies\": [{\"id\": 4, \"body\": \"With a caveat for defaults.\", \"replies\": []}]}, {\"id\": 3, \"body\": \"See the serialization docs.\", \"replies\": []}]}\n200 OK\n{\n  \"id\": 1,\n  \"body\": \"Does exclude_unset apply to nested models?\",\n  \"replies\": [\n    {\n      \"id\": 2,\n      \"body\": \"Yes, it recurses.\",\n      \"replies\": [\n        {\n          \"id\": 4,\n          \"body\": \"With a caveat for defaults.\",\n          \"replies\": []\n        }\n      ]\n    },\n    {\n      \"id\": 3,\n      \"body\": \"See the serialization docs.\",\n      \"replies\": []\n    }\n  ]\n}\n",[604,1276,1274],{"__ignoreMap":860},[590,1278,1279,1280,1283,1284,1286],{},"Validation and serialization both recurse without any extra configuration, and ",[604,1281,1282],{},"response_model=Comment"," produces the full tree. The forward-referenced ",[604,1285,1254],{}," works the same way once rebuilt:",[855,1288,1291],{"className":1289,"code":1290,"language":739,"meta":860},[1205],"$ POST \u002Fnodes\u002F  {\"label\": \"root\", \"child\": {\"value\": 9}}\n200 OK\n{\n  \"label\": \"root\",\n  \"child\": {\n    \"value\": 9\n  }\n}\n",[604,1292,1290],{"__ignoreMap":860},[850,1294,1296],{"id":1295},"_4-read-a-nested-error-location","4. Read a nested error location",[590,1298,1299],{},"Errors deep in a tree carry a full path, which is what makes them debuggable:",[855,1301,1304],{"className":1302,"code":1303,"language":739,"meta":860},[1205],"$ POST \u002Fcomments\u002F  {\"id\": 1, \"body\": \"bad\", \"replies\": [{\"id\": \"x\", \"body\": \"y\"}]}\n422 Unprocessable Entity\n{\n  \"detail\": [\n    {\n      \"type\": \"int_parsing\",\n      \"loc\": [\n        \"body\",\n        \"replies\",\n        0,\n        \"id\"\n      ],\n      \"msg\": \"Input should be a valid integer, unable to parse string as an integer\",\n      \"input\": \"x\"\n    }\n  ]\n}\n",[604,1305,1303],{"__ignoreMap":860},[590,1307,1308,1311,1312,1314],{},[604,1309,1310],{},"loc"," interleaves field names and list indices all the way down. For a failure ten levels deep the ",[604,1313,1310],{}," is twenty-odd elements long — accurate, and worth truncating before it reaches a log aggregator.",[652,1316,1318],{"id":1317},"the-depth-limit","The Depth Limit",[590,1320,1321,1322,1325,1326,1329],{},"A recursive model accepts a tree of any ",[623,1323,1324],{},"shape",", but not of any ",[623,1327,1328],{},"depth",". The example builds a single-child chain of a requested depth and validates it:",[855,1331,1334],{"className":1332,"code":1333,"language":739,"meta":860},[1205],"$ GET \u002Fdepth\u002F50\n200 OK\n{\n  \"depth\": 50,\n  \"result\": \"ok\",\n  \"serialized_bytes\": 1889\n}\n\n$ GET \u002Fdepth\u002F400\n200 OK\n{\n  \"depth\": 400,\n  \"result\": \"ValidationError\",\n  \"type\": \"recursion_loop\",\n  \"msg\": \"Recursion error - cyclic reference detected\",\n  \"loc_length\": 510\n}\n",[604,1335,1333],{"__ignoreMap":860},[590,1337,1338,1339,1342,1343,1346],{},"Depth 50 validates and serializes. Depth 400 does not — and the failure is a well-behaved ",[604,1340,1341],{},"ValidationError",", not a ",[604,1344,1345],{},"RecursionError"," escaping into the request handler. Pydantic's core detects the runaway and converts it, so FastAPI returns 422 rather than a 500.",[590,1348,1349,1350,1353,1354,1357],{},"The message is worth reading carefully: ",[623,1351,1352],{},"\"Recursion error - cyclic reference detected\"",". The structure here contains no cycle at all; it is a plain 400-deep chain. Pydantic reports depth exhaustion using its cyclic-reference wording, so an alert triggered by this text will be misleading. The ",[604,1355,1356],{},"loc_length"," of 510 is the giveaway — the error path is 510 elements deep, which is not what a genuine cycle looks like.",[590,1359,1360],{},"A binary search for the exact ceiling:",[855,1362,1365],{"className":1363,"code":1364,"language":739,"meta":860},[1205],"$ GET \u002Fmax-depth\n200 OK\n{\n  \"deepest_validating_tree\": 255,\n  \"recursion_limit\": 1000\n}\n",[604,1366,1364],{"__ignoreMap":860},[590,1368,1369,1371],{},[593,1370,641],{}," on Pydantic 2.13.4 with CPython 3.12 at the default recursion limit of 1000. This is not a documented constant and should not be treated as one — it moves with the interpreter's recursion limit, with the shape of the model, and plausibly with the Pydantic version. Measure it in your own environment with your own model if the number matters. What you can rely on is that a ceiling exists, that it is in the low hundreds rather than the low thousands, and that exceeding it produces a 422.",[652,1373,1375],{"id":1374},"edge-cases-and-gotchas","Edge Cases and Gotchas",[597,1377,1378,1384,1394,1408,1434,1448],{},[600,1379,1380,1383],{},[593,1381,1382],{},"The limit applies to any deeply nested model, not just recursive ones."," A recursive type merely makes it reachable by user-supplied data, which turns a theoretical limit into an availability concern.",[600,1385,1386,1393],{},[593,1387,1388,1389,1392],{},"Raising ",[604,1390,1391],{},"sys.setrecursionlimit()"," is not a fix."," It converts a clean 422 into a real risk of a C-stack segfault, which takes the worker down rather than returning an error.",[600,1395,1396,1403,1404,1407],{},[593,1397,1398,1399,1402],{},"Self-references in ",[604,1400,1401],{},"Optional"," and unions need the same treatment."," ",[604,1405,1406],{},"parent: \"Comment | None\" = None"," is a forward reference, and a model whose parent and child link both ways builds an object graph the serializer will traverse repeatedly.",[600,1409,1410,1413,1414,1417,1418,1421,1422,1425,1426,1429,1430,1433],{},[593,1411,1412],{},"True cycles are a different failure."," If you construct ",[604,1415,1416],{},"a.replies = [b]"," and ",[604,1419,1420],{},"b.replies = [a]"," in Python and then serialize, you get the same ",[604,1423,1424],{},"recursion_loop"," error — this time correctly named. Recursive ",[623,1427,1428],{},"models"," are fine; recursive ",[623,1431,1432],{},"object graphs"," are not serializable to JSON.",[600,1435,1436,1443,1444,607],{},[593,1437,1438,1439,1442],{},"The OpenAPI schema uses a ",[604,1440,1441],{},"$ref"," back to the same component."," That is valid and most generators handle it, but some client generators produce awkward or non-terminating output for self-referential schemas. Check your generator before committing to the shape — see ",[646,1445,1447],{"href":1446},"\u002Fadvanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Fcustomizing-openapi-schema-generation-in-fastapi\u002F","customizing OpenAPI schema generation",[600,1449,1450,1453,1454,1457,1458,607],{},[593,1451,1452],{},"Per-endpoint exclusion works but is verbose."," Dropping a field at every level needs ",[604,1455,1456],{},"{\"replies\": {\"__all__\": {...}}}"," repeated for the depth you want to cover; see ",[646,1459,1461],{"href":1460},"\u002Fadvanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Fexcluding-fields-per-endpoint\u002F","excluding fields per endpoint",[652,1463,1465],{"id":1464},"verification","Verification",[590,1467,1468],{},"Pin the behaviour you rely on, including the failure:",[855,1470,1472],{"className":857,"code":1471,"language":859,"meta":860,"style":860},"def test_tree_round_trips():\n    tree = Comment.model_validate(TREE)\n    assert tree.replies[0].replies[0].id == 4\n\n\ndef test_excessive_depth_is_a_validation_error_not_a_crash():\n    with pytest.raises(ValidationError) as exc_info:\n        Comment.model_validate(nest(400))\n    assert exc_info.value.errors()[0][\"type\"] == \"recursion_loop\"\n",[604,1473,1474,1486,1502,1527,1531,1535,1544,1558,1569],{"__ignoreMap":860},[864,1475,1476,1479,1483],{"class":807,"line":866},[864,1477,1478],{"class":876},"def",[864,1480,1482],{"class":1481},"s3dhs"," test_tree_round_trips",[864,1484,1485],{"class":887},"():\n",[864,1487,1488,1491,1493,1496,1499],{"class":807,"line":873},[864,1489,1490],{"class":887},"    tree ",[864,1492,962],{"class":876},[864,1494,1495],{"class":887}," Comment.model_validate(",[864,1497,1498],{"class":880},"TREE",[864,1500,1501],{"class":887},")\n",[864,1503,1504,1507,1510,1513,1516,1518,1521,1524],{"class":807,"line":891},[864,1505,1506],{"class":876},"    assert",[864,1508,1509],{"class":887}," tree.replies[",[864,1511,1512],{"class":880},"0",[864,1514,1515],{"class":887},"].replies[",[864,1517,1512],{"class":880},[864,1519,1520],{"class":887},"].id ",[864,1522,1523],{"class":876},"==",[864,1525,1526],{"class":880}," 4\n",[864,1528,1529],{"class":807,"line":898},[864,1530,895],{"emptyLinePlaceholder":894},[864,1532,1533],{"class":807,"line":907},[864,1534,895],{"emptyLinePlaceholder":894},[864,1536,1537,1539,1542],{"class":807,"line":920},[864,1538,1478],{"class":876},[864,1540,1541],{"class":1481}," test_excessive_depth_is_a_validation_error_not_a_crash",[864,1543,1485],{"class":887},[864,1545,1546,1549,1552,1555],{"class":807,"line":925},[864,1547,1548],{"class":876},"    with",[864,1550,1551],{"class":887}," pytest.raises(ValidationError) ",[864,1553,1554],{"class":876},"as",[864,1556,1557],{"class":887}," exc_info:\n",[864,1559,1560,1563,1566],{"class":807,"line":938},[864,1561,1562],{"class":887},"        Comment.model_validate(nest(",[864,1564,1565],{"class":880},"400",[864,1567,1568],{"class":887},"))\n",[864,1570,1571,1573,1576,1578,1581,1584,1586,1588],{"class":807,"line":951},[864,1572,1506],{"class":876},[864,1574,1575],{"class":887}," exc_info.value.errors()[",[864,1577,1512],{"class":880},[864,1579,1580],{"class":887},"][",[864,1582,1583],{"class":869},"\"type\"",[864,1585,1027],{"class":887},[864,1587,1523],{"class":876},[864,1589,1590],{"class":869}," \"recursion_loop\"\n",[590,1592,1593],{},"The second test is the one that matters in production: it asserts that hostile input produces a 422 rather than an unhandled exception, and it will fail loudly if a future Pydantic version changes that contract.",[652,1595,1597],{"id":1596},"trade-offs-and-when-not-to","Trade-offs and When Not To",[590,1599,1600],{},"A recursive response model is elegant and it makes payload size a function of user-generated content. One popular thread produces a multi-megabyte body, serialized on the event loop, in a single response nobody can paginate. The depth ceiling then arrives as a 422 on exactly the content your users care most about.",[590,1602,1603,1604,1606,1607,1609,1610,1613,1614,1617],{},"For anything user-facing, cap it. Serve ",[604,1605,677],{}," with a bounded ",[604,1608,1328],{}," query parameter and a ",[604,1611,1612],{},"has_more_replies"," flag, or return a flat list of comments with ",[604,1615,1616],{},"parent_id"," and let the client assemble the tree — flat lists paginate, cache, and diff far better than trees. The recursive model is the right tool for configuration documents, rule expressions, and internal structures whose depth you control, and a liability for anything whose depth a stranger decides.",[590,1619,1620,1621,1625],{},"If you keep the tree, validate a maximum depth explicitly in a ",[646,1622,1624],{"href":1623},"\u002Fadvanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fcross-field-validation-patterns\u002F","model validator"," so you reject at a depth you chose, with a message you wrote, rather than at 255 with a message about cyclic references.",[652,1627,1629],{"id":1628},"faq","FAQ",[590,1631,1632,1635,1636,614,1638,1640],{},[593,1633,1634],{},"Do I still need model_rebuild() for self-referencing models in Pydantic v2?","\nUsually not. Pydantic v2 attempts to resolve forward references automatically when the class body finishes, and ",[604,1637,613],{},[604,1639,617],{}," when the model was already complete. You need it when the referenced type is defined later in the module or imported conditionally, in which case calling it is harmless and explicit.",[590,1642,1643,1646,1647,1649,1650,1652],{},[593,1644,1645],{},"What error does a too-deep recursive structure produce?","\nA ",[604,1648,1341],{}," with type ",[604,1651,1424],{}," and the message \"Recursion error - cyclic reference detected\". It is a normal validation error, so FastAPI returns 422 rather than crashing, but the message names cyclic references even when the structure is merely deep.",[590,1654,1655,1658],{},[593,1656,1657],{},"How deep can a recursive Pydantic model go?","\nOn Pydantic 2.13.4 with CPython 3.12 at the default recursion limit of 1000, a self-referencing comment tree validated to a depth of 255 and failed beyond that. The figure tracks the interpreter's recursion limit, so it is a property of the deployment rather than a documented constant.",[590,1660,1661,1664,1665,1668,1669,1672],{},[593,1662,1663],{},"Why does my forward reference fail with a class not fully defined error?","\nThe referenced name did not exist when Pydantic tried to build the schema. Call ",[604,1666,1667],{},"Model.model_rebuild()"," after every referenced class is defined, and if the type lives in another module make sure it is imported at module scope rather than inside a ",[604,1670,1671],{},"TYPE_CHECKING"," block.",[590,1674,1675,1678],{},[593,1676,1677],{},"Should an API expose an unbounded comment tree?","\nNo. An unbounded recursive response makes payload size a function of user-generated data, so a single deep thread can produce a very large body. Paginate children or cap the depth server-side and let clients request deeper levels explicitly.",[652,1680,1682],{"id":1681},"related-reading","Related Reading",[597,1684,1685,1692,1701,1706],{},[600,1686,1687,1403,1690,607],{},[593,1688,1689],{},"Up to the topic:",[646,1691,649],{"href":648},[600,1693,1694,1695,1417,1697,607],{},"For fixed-depth structures and the cost of serializing them, see ",[646,1696,843],{"href":842},[646,1698,1700],{"href":1699},"\u002Fadvanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Fpydantic-model-serialization-performance\u002F","Pydantic model serialization performance",[600,1702,1703,1704,607],{},"Trimming what a tree emits per route is covered in ",[646,1705,1461],{"href":1460},[600,1707,1708,1709,1711,1712,607],{},"Self-referential ",[604,1710,1441],{},"s in the generated document are discussed in ",[646,1713,1714],{"href":1446},"customizing OpenAPI schema generation in FastAPI",[1716,1717,1718],"style",{},"html pre.shiki code .sYEJz, html code.shiki .sYEJz{--shiki-default:#032563}html pre.shiki code .sTJeM, html code.shiki .sTJeM{--shiki-default:#A0111F}html pre.shiki code .sacAq, html code.shiki .sacAq{--shiki-default:#023B95}html pre.shiki code .sigWx, html code.shiki .sigWx{--shiki-default:#0E1116}html pre.shiki code .sV4o_, html code.shiki .sV4o_{--shiki-default:#702C00}html pre.shiki code .sFeEa, html code.shiki .sFeEa{--shiki-default:#66707B}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 .s3dhs, html code.shiki .s3dhs{--shiki-default:#622CBC}",{"title":860,"searchDepth":873,"depth":873,"links":1720},[1721,1722,1723,1724,1730,1731,1732,1733,1734,1735],{"id":654,"depth":873,"text":655},{"id":667,"depth":873,"text":668},{"id":830,"depth":873,"text":831},{"id":847,"depth":873,"text":848,"children":1725},[1726,1727,1728,1729],{"id":852,"depth":891,"text":853},{"id":1186,"depth":891,"text":1187},{"id":1269,"depth":891,"text":1270},{"id":1295,"depth":891,"text":1296},{"id":1317,"depth":873,"text":1318},{"id":1374,"depth":873,"text":1375},{"id":1464,"depth":873,"text":1465},{"id":1596,"depth":873,"text":1597},{"id":1628,"depth":873,"text":1629},{"id":1681,"depth":873,"text":1682},"2026-07-20","Build a recursive comment tree in FastAPI with self-referencing Pydantic models, forward references and model_rebuild, plus the real depth limit it fails at.","md",[1740,1742,1744,1746,1748],{"q":1634,"a":1741},"Usually not. Pydantic v2 attempts to resolve forward references automatically when the class body finishes, and model_rebuild() returns None when the model was already complete. You need it when the referenced type is defined later in the module or imported conditionally, in which case calling it is harmless and explicit.",{"q":1645,"a":1743},"A ValidationError with type recursion_loop and the message 'Recursion error - cyclic reference detected'. It is a normal validation error, so FastAPI returns 422 rather than crashing, but the message names cyclic references even when the structure is merely deep.",{"q":1657,"a":1745},"On Pydantic 2.13.4 with CPython 3.12 at the default recursion limit of 1000, a self-referencing comment tree validated to a depth of 255 and failed beyond that. The figure tracks the interpreter's recursion limit, so it is a property of the deployment rather than a documented constant.",{"q":1663,"a":1747},"The referenced name did not exist when Pydantic tried to build the schema. Call Model.model_rebuild() after every referenced class is defined, and if the type lives in another module make sure it is imported at module scope rather than inside a TYPE_CHECKING block.",{"q":1677,"a":1749},"No. An unbounded recursive response makes payload size a function of user-generated data, so a single deep thread can produce a very large body. Paginate children or cap the depth server-side and let clients request deeper levels explicitly.",null,{"slug":1752,"breadcrumb":1753},"self-referencing-and-recursive-models",[1754,1757,1760,1762],{"label":1755,"path":1756},"Home","\u002F",{"label":1758,"path":1759},"Advanced Pydantic Validation & Serialization","\u002Fadvanced-pydantic-validation-serialization\u002F",{"label":1761,"path":648},"Nested Model Serialization",{"label":1763,"path":1764},"Self-Referencing and Recursive Models","\u002Fadvanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Fself-referencing-and-recursive-models\u002F",{"title":85,"description":1737},"article","nfb7h2r59C1OGeeaq0QS6haItKyWvGK6s-cuaDID0c8",[1750,1750],1784588202620]