Promptly-Technologies-LLC / fastapi-jinja2-postgres-webapp

A template webapp with a pure-Python FastAPI backend, frontend templating with Jinja2, and a Postgres database to power user auth
https://promptlytechnologies.com/fastapi-jinja2-postgres-webapp/
MIT License
0 stars 0 forks source link

Fix middleware error handling #4

Closed chriscarrollsmith closed 2 weeks ago

chriscarrollsmith commented 2 weeks ago

Design notes for this version:

The RequestValidationError mostly shouldn't happen unless we've made a programming error (mismatch between the frontend form and the backend Pydantic request model). If it does happen, we are rendering the "validation_error.html" file.

The PasswordValidationError is a bit more mission critical, because users may occasionally encounter this, and we want the logic to be re-usable for validation of inputs to other forms. As much as possible in all such cases, we should prevent invalid inputs with HTML and Javascript pattern-matching on the frontend so that users don't reach "validation_error.html".

Previously I was manually handling this validation in the body of the API endpoint. Now I am handling validation with a Pydantic-powered dependency in the function signature. I like how this separates concerns, and it allows me to handle the redirect behavior centrally from middleware.

In the previous version, I was redirecting back to the page with an error toast. The good thing about that pattern is that the user doesn't have to navigate back to the page. The bad thing is that it clears the form data and doesn't separate out validation from other concerns. Now I am rendering a separate error page, "validation_error.html". The disadvantage of this is that the user has to click "Go Back" to return to the page and try again. The advantage is that this back-navigation preserves the user's form inputs.

I experimented with some approaches to using error toasts in a way that would preserve form data. It's possible, but unfortunately you can't pass context in a redirect. If you are redirecting from the body of the API endpoint, you could pass the form inputs as query parameters. But this will expose the user's password inputs in the URL, which is bad. And you can't use this approach from our Pydantic field validators anyway. A better approach would be to use session middleware to get the form data from the session request object. But that requires an additional dependency, so I skipped it for now.