-
-
Notifications
You must be signed in to change notification settings - Fork 2k
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
Enable top-level wrapping of all user (i.e. not Kit) code #4822
Comments
Have you seen You could stick a reference to anything you need on |
Hey Ben. Thanks and ya I have. The problem with using e.g. Here's what it would look like to do the error handling in // hooks.ts
export async function handle({event, resolve}) {
// setup txn
const txn = await beginTransaction()
event.locals.txn = txn
// call endpoints/renderers
// sometimes this swallows exceptions and other times it doesn't
const resp = await resolve(event)
// commit... but only if resolve doesn't throw an error, causing this line to never execute.
// but resolve() might not throw an error and instead return status: 500 (unexpected error not handled
// by app logic), which should cause a rollback
if (resp.ok) {
await txn.commit()
} else if (resp.status === 500) {
await txn.rollback()
}
// set txn to null because if there was an exception swallowed by `resolve()` `handleError()` is going to be
// called right after this function which means we don't want to attempt to rollback twice.
event.locals.txn = null
}
export async function handleError({error, event}) {
if (event.locals.txn) {
event.locals.txn.rollback()
// set this to null just in case `handleError()` is called before `resolve(event)` returns... which I think might
// happen when loading nested nodes.
event.locals.txn = null
}
} |
I've been chatting with folks in the Discord, and they seem to think that |
Personally, I'd prefer #1 -- it would be nice to be able to be able to catch all errors via |
This behaviour may have changed since the issue was opened, but: |
Awesome. Thanks Rich |
Describe the problem
I've been struggling with a way to wrap my endpoint code in such a way that any exception thrown will be handled by a single piece of logic in order to clean up various resources after an endpoint executes.
Some things I've tried:
await resolve(event)
in hooks.tshandle()
functionFirst approach: Top-level
resolve(event)
wrapperThe first doesn't work due to strange and unsupported exception dynamics. E.g. sometimes the try/catch will catch the error and sometimes it won't. See #4801 for more details. Also, @Rich-Harris suggests to use a wrapper function - #4712 (comment). Hence the second approach.
Second approach: Endpoint wrappers
The second option isn't working because of the timing of the call to
getSession()
. This one is a bit more nuanced and I don't see a way around fixing it with the current design.Let's take a step back and describe how a request should be fulfilled:
Error
for whatever reason, which causes the endpoint wrapper to catch the error and rolls the transaction back before re-throwing the error so that SvelteKit can handle it in whatever manner it needs to.Now, the issue I mentioned with
getSession()
is that this is the thing that needs to look the user up in the DB in order to pass the user object to the browser. Which means it's the thing that needs to create the DB transaction. But since it is called before the endpoint wrapper code that has the try/catch logic, it will not be able to manage failure cases that require a rollback.Fwiw, I tried moving the logic out of
getSession()
that fetches from the DB and put it into the endpoint wrapper. But apparently thesession
object from$app/stores
is read-only on the server which means I wasn't able to modify the session before the endpoint had access to it.So, the fundamental question is - How do you wrap all of your domain logic in a try/catch? This includes
getSession()
and every line of code in your endpoints and ideally all of the rendering for templates on the server.Describe the proposed solution
Listed in order of effort I think would be required to implement (high to low.)
Option 1: Simplify
handle()
so every exception bubbles up to itIMO, the most straightforward and elegant way to achieve this is to support it in the
handle()
function. This requires that exceptions thrown by endpoints orgetSession()
, (and I'm assumingload()
as well) bubble up tohandle()
in a consistent way. (See the description above for examples of when that doesn't happen.)Option 2: Provide a wrapper method for endpoints that is called before
getSession()
or is provided a way to mutate the session before executing the wrapped endpoint.In this option, there is some sort of SvelteKit provided "wrap my endpoint" method that is called by
resolve(event)
insidehandle()
. This wrapper would be able to implement a try/catch around all endpoint +getSession()
logic. Or, it would be provided the result ofgetSession()
and be able to mutate it before calling the endpoint or rendering the template.Option 3: Enable server-side mutation of the
session
object from$app/stores
This is probably the quickest way to implement what I'm looking for. It would allow all domain logic, (i.e. the code I write) to be wrapped by endpoint wrappers, (i.e. the second approach in the description I tried and the one proposed by @Rich-Harris.) Leaving
getSession()
to almost be a no-op since the wrappers or endpoint could update thesession
store as they saw fit.I'm sure there are dragons with this approach...
Alternatives considered
await resolve(event)
in hooks.tshandle()
functionImportance
i cannot use SvelteKit without it
Additional Information
Thanks to all of the creators and maintainers on this project. Seriously, it's magical and I can't wait to get it into production!
The text was updated successfully, but these errors were encountered: