-
Notifications
You must be signed in to change notification settings - Fork 32
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
feat: make error handling configurable #592
Conversation
hey @routmoute ! i like the idea behind this, but the current implementation is part of Drash's "core" code as opposed to being a separate service that can be plugged in through the This Pull Requestconst server = new Server({
protocol: "http",
hostname: "localhost",
port: 3000,
resources: [],
error_service: new MyErrorService() // <--- should be in services config
}); Expected Implementationconst server = new Drash.Server({
protocol: "http",
hostname: "localhost",
port: 3000,
resources: [],
services: [ new MyErrorService() ] // <--- see here
}); reason we don't introduce much code into Drash's core is because we don't know for sure that developers want "built-in" features. the way Drash is currently built is so that developers are free to create their own services as well as a custom error service that meets their needs. however, we do feel that Drash should have an error handling layer that can be used easily, which is what you're introducing in this pull request. also, thank you for writing the documentation! much appreciated :). so, all this being said, could you refactor your error service so that it can be plugged into the |
I think I see, don't create another class but add the runOnError function directly on the services. I will refactor that, it will be better, thank you. |
hey @routmoute, so i had a talk with the core team and basically we think drash should have a built-in error handler and not an "error service". so your approach was mostly correct. sorry for misguiding you. the end goal would be a Drash.ErrorHandler class or something similar. that class would be instantiated and executed in server.ts' getHandler() method inside the catch block. |
Arf, but, I think the second approach is better because developers can work the errors differently if they are thrown on a particular method, path and then on the server. |
true. maybe we stick with the error service for now and see how that goes and if it needs to be changed we can always do that later and make another release |
Actualy, I've just add a function |
hey @routmoute , so i pulled down your branch locally and tested it -- keeping developer user experience and maintainability in mind -- and during my testing, i realized using an "error service" will get us back to what we had in v1 (and we don't want to go back to what we had in v1). in v1, the error handling got really messy and was hard to maintain because we had to run middleware during the error handling process. every time we came back to the code, we forgot how it worked and there were edge cases with it that we kept finding. in your branch, you basically have what we had in v1 (https://github.com/routmoute/drash/blob/errorService/src/http/server.ts#L354-L385). your code isn't wrong. it's exactly what's needed to have an "error service" in drash, but it's also something that we removed when going from v1 to v2. @Guergeiro suggested that there be an error handling layer in drash and that it be a built-in feature that can be extended by developers and i disagreed because i thought having an "error service" would be best suited for configurability. after testing your code locally, i feel that @Guergeiro is correct and we should not use an "error service," but instead have an error handling layer that can be extended and configured by developers (if they choose to do so). |
feel free to hop in discord to discuss btw. we're pretty active in it and respond faster there. here's the link in case you're interested: https://discord.gg/DmjhWJt5 |
Revert "refactoring serviceError" This reverts commit 2ea504c. Revert "oops: clean" This reverts commit ab734e6. Revert "add basic ErrorsToJsonService with test" This reverts commit 16f603a. Revert "rename service_error to service_runOnError tests" This reverts commit fcd6765. Revert "add runBeforeResource() and runAfterResource() errors tests" This reverts commit 7a2fb22.
I will retry something |
I've created an ExceptionLayer class can be exetended and declared on server 'exception' var. what do you think about that ? |
src/http/server.ts
Outdated
|
||
if (exception) { | ||
try { | ||
await exception.catch(e, originalRequest, response); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this isn't documented in the codebase, but we try to instantiate things during compile time so that
- the user doesn't have to do it
- saves the server from having to build a new object for every request
could you make it so that the exception doesn't have to be instantiated by the user? that's the only feedback i have. id like to get thoughts on the naming of the class from @ebebbington and @Guergeiro
as far as implementation, LGTM. thanks @routmoute !
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mean the new Drash.ExceptionLayer().catch(...)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yea, but we instantiate it once and reuse it just like we do with resources
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with it. Let's avoid generating garbage for the garbage collector to collect 👍🏿
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, last commit instantiate ExceptionLayer on server.ts
src/interfaces.ts
Outdated
@@ -141,6 +141,7 @@ export interface IServerOptions { | |||
protocol: "http" | "https"; | |||
resources: typeof Resource[]; | |||
services?: Service[]; | |||
exception?: ExceptionLayer; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be the IExceptionLayer
. With that, I can create my own exception layer that does not extend the Drash layer, but rather completely overrides it.
A simple test with this would be a nice touch 😃
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in last commit, I use typeof ExceptionLayer (like 'typeof Resource').
I added a test, I don't know if it's that you want.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Guergeiro , i tested using IExceptionLayer
as the expected type in the TS playground. seems to require new MyExceptionClass()
to be used. see playground here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. It's supposed to. Here's a link with the same code, but the correct options signature.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ahh constructor. ok
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also in regards to #592 (comment), i think eric means, instantiate the exception class in the server constructor:
#exception: CustomException|null = null
constructor(...) {
if (options.exception) {
this.#exception = new options.exception()
}
}
...
const exception = this.#exception
return function () {
exception.catch(...)
}
But that wouldn't be a problem if the catch method was static anyways
…ault error handler
…t and native Request
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general, it's good for me. Logic wise.
This is some stuff I would prefer to simplify stuff. Basically, if no option for an ErrorHandler is passed, just automatically assign a default one. Then we can be assured it exists, otherwise it would fail in server setup.
src/http/server.ts
Outdated
if (options.error_handler) { | ||
this.#error_handler = new options.error_handler(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for condition. Always apply the default if none exist.
this.#error_handler = new (options.error_handler() || Drash.ErrorHandler);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@@ -173,6 +182,9 @@ export class Server { | |||
#getHandler(): (r: Request, connInfo: ConnInfo) => Promise<Response> { | |||
const resources = this.#resources; | |||
const serverServices = this.#options.services ?? []; | |||
const errorHandler = this.#error_handler; | |||
const defaultErrorHandler = new Drash.ErrorHandler(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for this.
src/http/server.ts
Outdated
if (errorHandler) { | ||
try { | ||
await errorHandler.catch(e, originalRequest, response); | ||
} catch (e) { | ||
await defaultErrorHandler.catch(e, originalRequest, response); | ||
} | ||
} else { | ||
await defaultErrorHandler.catch(e, originalRequest, response); | ||
} | ||
return new Response(e.stack, { | ||
status: e.code, | ||
headers: response.headers, | ||
}); | ||
|
||
return new Response(response.body, response); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
try {
// stuff
} catch (e) {
await this.#error_handler.catch(e, originalRequest, response);
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we use defaultErrorHandler when this.#error_handler.catch()
or errorHandler.catch()
(it's the same const errorHandler = this.#error_handler;
) thrown an error
} | ||
response.json({ error: error.message }); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why error is Error here and is HttpError in default ErrorHandler ?
if is simple Error thrown, this ErrorHandler return 200 ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So there's two approaches here:
- Catch on every error. This means the server will never stop working, regardless of the error. If an error is thrown just send
500
. - Catch only HttpError. This can make the server crash if, for instance, a database errors and the developer doesn't worry about surrounding external calls with
try...catch
.
My opinion is that option 1 would be the most interesting. Ideally the server never goes down. If an error occurs that was not expected, and Internal Server Error Exception
shall be sent to the user.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same opinion, that's exactly what I did.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I approve the architecture of this PR. Good job for me.
thank you @routmoute :) |
add Simple ErrorService possibility
See Issue #591