Skip to content

Don't automatically throw from redirect and error #11404

@cdcarson

Description

@cdcarson

Describe the problem

I appreciate the thought & work that went into #11144 and #11165 but I think it's the wrong approach.

The confusion around return and throw arises from SvelteKit's original decision to stray from the common sense meanings of those words. I get there was a reason for this -- deriving action function return types, which can't be done if validation errors are thrown. But it led to a less than intuitive API...

  • return fail(400, {bah: 'humbug'}) ...ack
  • throw redirect(302, '/success') ...ack (it's a success, you should be able to return it)
  • throw redirect(302, '/login') ...fair enough
  • throw error(500) ...fair enough

Hiding the throw in the redirect and error cases doesn't really fix the problem. It just kicks the can down the road.

  • It replaces a well-understood keyword with a SvelteKit abstraction that does the same exact thing. The fact that it does the same thing has to be remembered or looked up.
  • The never works in an IDE. But you won't see the "unreachable code" lint if you're looking at something in a GitHub issue.

There's also a decent case to be made for...

const err = error(4xx, /** something complicated **/);
// throw it various places later...

Describe the proposed solution

Consider rolling it back.

Alternatives considered

Ideally, the error handling API should be re-written to be simpler and more intuitive. Stick to to the easy-to-understand paradigm of throwing Error and returning success, like so...

// I think these already exist, but making up the names...
import type {InvalidFormError, Redirect, ExpectedError} from '@svelte/kit';

// always thrown...
throw new ExpectedError(403, {message: 'You cannot do that.'});

// validation errors always thrown...
throw new InvalidFormError(400, {...});

// redirects are thrown or returned...
return new Redirect(302, '/success'); // as the return value of an action
throw new Redirect(302, '/login'); // thrown from an auth check in an action or load function

Granted, this leaves out how to type Action return values, given that validation errors are thrown. I get that this typing is a feature, but I'm not convinced that it's super valuable in practice. The shape of form (and the enhance result) on the client usually needs to narrowed anyway, between success and failure states, and between actions if a route has more than one. The generated types become cumbersome. It's just simpler to cheat...

// auth/+page.svelte
import type { ActionData } from './$types.js';
export let form: ActionData;
$: actionData = form as any; 
// passed to form components which have narrowed types...

// SignInForm.svelte
export let actionData: {data: SignInData, errors: {[k in keyof SignInData]?: string}};

// SignUpForm.svelte
export let actionData: {data: SignUpData, errors: {[k in keyof SignUpData]?: string}};

I suppose it's a matter of opinion whether generating action types is worth increasing the eccentricity and surface of the framework API. I'm fine either way. It's easy enough to wrap away the eccentricities.

Importance

nice to have

Additional Information

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    breaking changeneeds-decisionNot sure if we want to do this yet, also design work needed

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions