Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The proposed solution to problem #787 #819

Merged
merged 16 commits into from
Feb 26, 2023
Merged

The proposed solution to problem #787 #819

merged 16 commits into from
Feb 26, 2023

Conversation

RobinTail
Copy link
Owner

@RobinTail RobinTail commented Feb 14, 2023

Related to the discussion #787

Should enable the developer using zod as a tool within Endpoint's handler without considering its possible parsing/validation errors as IO related ones, leading to the status code 500 instead of 400 in that case.

@TheWisestOne

The approach

  1. I am making an InputValidationError, consistent with OutputValidationError.
  2. I separate the input parsing from the execution.
  3. Validation errors (ZodError) catched before running middlewares and Endpoint handlers are transformed into InputValidationError
  • It also contains originalError prop for the further processing if needed.
  • However the message prop is already prepared for being human readable.
  1. Thus, your ResultHandler now can be sure that any ZodError is actually NOT input validation error and can respond with 500.

Inspired by #794.

ToDo

  • needs more tests
  • need to expose error processing helpers in index ✅

@RobinTail RobinTail changed the title The proposed solution. The proposed solution to problem #787 Feb 14, 2023
@RobinTail RobinTail marked this pull request as ready for review February 14, 2023 00:24
@RobinTail
Copy link
Owner Author

@TheWisestOne , please review

Copy link
Contributor

@TheWisestOne TheWisestOne left a comment

Choose a reason for hiding this comment

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

Seems good to me!

I have 2 comments:

  1. EndpointHandlerError is a very generic name, and might be better renamed EndpointHandlerZodError or something like that to clarify that any other errors thrown by the endpoint don't get wrapped in this error class

  2. From my understanding, the flow of execution / error handling now looks like:

  • parse middleware input / run middleware -> any errors go to ResultHandler directly
  • parse handler input -> any errors go to ResultHandler directly
  • run endpoint handler -> any zod errors get wrapped in EndpointHandlerError
  • parse output -> any zod errors get wrapped in OutputValidationError

It seems a little arbitrary when we wrap up errors into these wrapper classes vs. let the result handler deal with the raw error. I am fine with the current state, just want to raise that it is not all that consistent, and it might be worth thinking about if there's a way to make it more consistent.

One proposal that comes to mind to resolve 2. above would be to sort of "merge" our two approaches:
We would do a try/catch around the parseAsync call rather than around the this.handler call, and in the catch block we would throw a new InputValidationError, which gets passed on to the ResultHandler. Then, the ResultHandler code that currently checks for a ZodError and decides to throw a 400 can instead check for an InputValidationError.
In this world, parsing of both input and output have the potential to wrap zod errors, but the endpoint handler itself always throws raw errors to the ResultHandler

src/endpoint.ts Outdated Show resolved Hide resolved
@RobinTail
Copy link
Owner Author

EndpointHandlerError is a very generic name, and might be better renamed EndpointHandlerZodError or something like that to clarify that any other errors thrown by the endpoint don't get wrapped in this error class.

@TheWisestOne, yes, agree. That need to be fixed.

@RobinTail
Copy link
Owner Author

RobinTail commented Feb 19, 2023

and in the catch block we would throw a new InputValidationError

there was a reason why I tried to refrain from making another validation error — it would be a breaking change for existing custom ResultHandlers made by developers.
Since ZodError is for a long time works as a synonym for the input validation error.

But I also agree with your point on consistency.

Need to think on it a little bit.
Sorry that it takes a bit long.

@TheWisestOne
Copy link
Contributor

TheWisestOne commented Feb 21, 2023

and in the catch block we would throw a new InputValidationError

there was a reason why I tried to refrain from making another validation error — it would be a breaking change for existing custom ResultHandlers made by developers. Since ZodError is for a long time works as a synonym for the input validation error.

But I also agree with your point on consistency.

Need to think on it a little bit. Sorry that it takes a bit long.

ah, an interesting point. One thing to note is that my attempt at this fix (#794) actually is non-breaking for people using an instanceof ZodError check in their ResultHandler.
This is because:
throw createHttpError(400, makeErrorFromAnything(e)); still creates an error that is an instance of a ZodError if e was a ZodError. I still see some of the benifits of your approach, but just wanted to point out that we might be able to do something non-breaking for existing ResultHandlers

@RobinTail
Copy link
Owner Author

I'm going to tackle this problem once again keeping in mind that the breaking change is acceptable, since I'm going to make v9 with another one.

@RobinTail
Copy link
Owner Author

I wanna go with the InputValidationError, for of consistency.

@RobinTail
Copy link
Owner Author

I wanna leave the utilization of createHttpError for the developer's decision on the final implementation and customizations.

@RobinTail RobinTail changed the base branch from master to v9-beta February 26, 2023 19:49
? new z.ZodError(
e.issues.map(({ path, ...rest }) => ({
...rest,
path: (["output"] as typeof path).concat(path),
Copy link
Owner Author

Choose a reason for hiding this comment

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

this trick is moved to the helper

@RobinTail
Copy link
Owner Author

OK. Now I'm happy with it.
I hope it will also fulfills your needs, @TheWisestOne

Anyway, I tested that throwing ZodError inside the Endpoint is not considered as IO validation issue.
So you could use zod for your database validations.

@RobinTail RobinTail merged commit bc5229c into v9-beta Feb 26, 2023
@RobinTail RobinTail deleted the problem-787 branch February 26, 2023 21:29
RobinTail added a commit that referenced this pull request Feb 28, 2023
* The proposed solution.

* Changing the name of the error.

* Adding originalError prop.

* The proposed solution.

* Changing the name of the error.

* Adding originalError prop.

* Ref: the solution based on the consistency of OutputValidationError and InputValidationError.

* Exposing getMessageFromError() and getStatusCodeFromError() helpers.

* Enabling workflows.

* Testing the edge cases.

* Testing the inheritance of InputValidationError.

* Adding the specific test for the problem #787.

* Revert "Enabling workflows."

This reverts commit c71689b.
RobinTail added a commit that referenced this pull request Mar 4, 2023
* Remove request body from `DELETE` operations in OpenAPI spec (#821)

* Remove request body from delete operations in OpenAPI spec

* Remove `DELETE` request `body` from `defaultInputSources` + revert `src/open-api.ts` change from previous commit

* Fix test name, add couple todos.

* Testing fallback input sources for unknown request methods.

* Updating the workflows to run in this branch.

---------

Co-authored-by: Robin Tail <robin_tail@me.com>

* Readme: reflecting the changes, including the config type description.

* Changelog: version 9.0.0-beta1, describing the potentially breaking changes.

* V9 is for Brianna Ghey.

* Updating the workflows to support the future branch of v8.

* Updating the Security policy.

* 9.0.0-beta1

* Solving the problem #787 (#819)

* The proposed solution.

* Changing the name of the error.

* Adding originalError prop.

* The proposed solution.

* Changing the name of the error.

* Adding originalError prop.

* Ref: the solution based on the consistency of OutputValidationError and InputValidationError.

* Exposing getMessageFromError() and getStatusCodeFromError() helpers.

* Enabling workflows.

* Testing the edge cases.

* Testing the inheritance of InputValidationError.

* Adding the specific test for the problem #787.

* Revert "Enabling workflows."

This reverts commit c71689b.

* Handling the edge case when the issues array is empty.

* Changelog: v9.0.0-beta2, describing the potenitally breaking changes.

* Readme: updating the instructions on creating a custom ResultHandler.

* 9.0.0-beta2

* Changelog: the response may actually not be json.

* Changelog: a note on base for 9.0.0-beta1.

* Changelog: upcoming v9.0.0-beta3.

* 9.0.0-beta3

* Breaking: removing createApiResponse().

* Ref: moving the array initializer for scopes and tags.

* Replacing mimeTypes parameter with hasUpload, moving mime types initialization to constructor.

* Ref: figuring out hasUpload inside the constructor.

* Ref: single method for getMimeTypes() with variants.

* Ref: moving response schema and status codes initializations into constructor.

* Ref: single method for getStatusCode() with variants.

* Ref: removing getOutputSchema() - redundant.

* Ref: single method for getSchema() with variants and overloads.

* Ref: extracting variant types.

* Fix: using ZodTypeAny where anything is implied.

* Changelog: upcoming 9.0.0-beta4.

* Minor: fix typo.

* 9.0.0-beta4

---------

Co-authored-by: Aleksandr Sidorenko <mcmerph@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants