Skip to content

Conversation

@tjb9dc
Copy link
Collaborator

@tjb9dc tjb9dc commented Dec 18, 2025

Description

Refs: Request from @tjb9dc via Slack

Adjusts the FastAPI generator to use FastAPI's temp_pydantic_v1_params module for parameter markers (Path, Query, Header, Body, File) when in pydantic v1 or v1_on_v2 mode. Additionally, uses typing.get_type_hints() to resolve forward references before applying parameter annotations for ALL pydantic versions.

Reference: https://github.com/fastapi/fastapi/blob/master/fastapi/temp_pydantic_v1_params.py
Related issue: fastapi/fastapi#13056

Changes Made

  • Added new FastAPIParams class that generates version-aware imports based on pydantic compatibility mode
  • Updated FastApiGeneratorContext to expose a fastapi_params instance
  • Updated all endpoint parameter classes to use self._context.fastapi_params.X() instead of static FastAPI.X() methods
  • Modified endpoint_generator.py to use typing.get_type_hints() for forward reference resolution (cleaner than per-parameter evaluate_forwardref calls)
  • Set response_model=None for all pydantic model endpoints to disable response model validation/serialization (per https://fastapi.tiangolo.com/tutorial/response-model/#disable-response-model)
  • Updated seed test outputs for all fixtures
  • Added changelog entry for version 2.1.0

Testing

  • Pre-commit hooks pass
  • Seed tests pass (7/7 for exhaustive fixture)
  • Verified pydantic-v1 fixture generates fastapi.temp_pydantic_v1_params.Body() etc.
  • All CI checks pass (97 passed)

Human Review Checklist

  • Verify response_model=None doesn't affect OpenAPI schema generation negatively (FastAPI should still infer from wrapper's return type annotation)
  • Confirm typing.get_type_hints() correctly resolves forward references in all edge cases
  • Verify seed test outputs are up to date with the latest generator changes

Updates Since Last Revision

  • Changed forward ref resolution from per-parameter evaluate_forwardref() calls to using typing.get_type_hints() once per endpoint (per reviewer feedback with diff from Candid API)
  • Changed response_model handling: now explicitly sets response_model=None for all pydantic model endpoints instead of omitting it (per reviewer feedback citing https://fastapi.tiangolo.com/tutorial/response-model/#disable-response-model)

Link to Devin run: https://app.devin.ai/sessions/75b0c5ac25314f3d8bec4af9136950d4
Requested by: thomas@buildwithfern.com (@tjb9dc)

…odes

Co-Authored-By: thomas@buildwithfern.com <tjb9dcshop@gmail.com>
@devin-ai-integration
Copy link
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

@github-actions
Copy link
Contributor

🌱 Seed Test Selector

Select languages to run seed tests for:

  • Python
  • TypeScript
  • Java
  • Go
  • Ruby
  • C#
  • PHP
  • Swift
  • Rust
  • OpenAPI
  • Postman

How to use: Click the ⋯ menu above → "Edit" → check the boxes you want → click "Update comment". Tests will run automatically and snapshots will be committed to this PR.

Co-Authored-By: thomas@buildwithfern.com <tjb9dcshop@gmail.com>
if uses_pydantic_v1_params:
EVALUATED_VARIABLE_NAME = "evaluated"
writer.write(f"{EVALUATED_VARIABLE_NAME} = ")
writer.write_reference(FastAPI.evaluate_forwardref)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is correct, but you also need to evaluate forward refs in the else block below for pydantic v2. forward refs need to be evaluated for all fastapi params that use Annotated, regardless of pydantic version.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the feedback! I've addressed both comments:

  1. evaluate_forwardref for all params: Now applied to ALL FastAPI params that use Annotated, not just v1/v1_on_v2 mode. The pydantic-v2 fixture now also includes the evaluate_forwardref call.

  2. Comment linking to FastAPI issue: Added a comment in the generated code that links to Can't use Annotated with ForwardRef fastapi/fastapi#13056

See commit 5d7ebbe for the changes.

writer.write("]")
if uses_pydantic_v1_params:
EVALUATED_VARIABLE_NAME = "evaluated"
writer.write(f"{EVALUATED_VARIABLE_NAME} = ")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you also write a comment into the generated code that links to the fastapi issue that this works around? here is the link: fastapi/fastapi#13056

…to FastAPI issue

Co-Authored-By: thomas@buildwithfern.com <tjb9dcshop@gmail.com>
Copy link
Collaborator Author

@tjb9dc tjb9dc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While we're here, let's remove response_model specification from our generated stubs. It is not necessary when the type is the same as the return type. See: https://docs.astral.sh/ruff/rules/fast-api-redundant-response-model/

@devin-ai-integration
Copy link
Contributor

Done! I've removed the redundant response_model specification from generated FastAPI stubs per the Ruff rule FAST001.

The change only removes response_model when it's a pydantic model (where FastAPI can infer from the return type annotation). response_class is still used for non-pydantic models (file downloads, bytes).

See commit 01eff13.

@tjb9dc
Copy link
Collaborator Author

tjb9dc commented Dec 23, 2025

While we're here, let's remove response_model specification from our generated stubs. It is not necessary when the type is the same as the return type. See: https://docs.astral.sh/ruff/rules/fast-api-redundant-response-model/

Actually, the proper thing is to include them but type them as “None”

@devin-ai-integration
Copy link
Contributor

Thanks for the feedback! I have two questions:

  1. Forward refs fix: I can't access the Candid repo (it appears to be private). Could you share the relevant code snippet or describe what the correct approach is? Currently I'm calling evaluate_forwardref per-parameter. Should I be using a different approach like typing.get_type_hints or FastAPI's typed-signature helpers?

  2. response_model=None: Just to clarify - should I emit response_model=None for all endpoints, or only for void/204 endpoints? The original code emitted response_model=<return_type> for pydantic models and response_model=None for void responses.

devin-ai-integration bot and others added 2 commits December 23, 2025 03:22
Co-Authored-By: thomas@buildwithfern.com <tjb9dcshop@gmail.com>
…f evaluate_forwardref

Co-Authored-By: thomas@buildwithfern.com <tjb9dcshop@gmail.com>
@tjb9dc tjb9dc enabled auto-merge (squash) December 23, 2025 16:27
@tjb9dc tjb9dc merged commit 09152f0 into main Dec 23, 2025
127 checks passed
@tjb9dc tjb9dc deleted the devin/1766079445-fastapi-pydantic-v1-params branch December 23, 2025 16:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

4 participants