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

User defined errors from server function #1657

Closed
smessmer opened this issue Sep 6, 2023 · 4 comments
Closed

User defined errors from server function #1657

smessmer opened this issue Sep 6, 2023 · 4 comments

Comments

@smessmer
Copy link

smessmer commented Sep 6, 2023

Is your feature request related to a problem? Please describe.
Currently, according to the documentation, server functions must return a ServerFnError, which contains a couple of enum variants for framework errors and then allows for user defined errors in ServerFnError::ServerError. However, that's just a string and that doesn't give much flexibility. I would rather use my own error types, e.g. ones created with thiserror.

Describe the solution you'd like
I can see two possible solutions:

  1. Make the ServerFnError::ServerError variant store an arbitrary (serializable) error type instead of just a String. This, however, would require ServerFnError to become generic.
  2. If we don't want to make ServerFnError generic, we could consider an alternative approach where server functions can return any user defined error type. That type would require a bound on From<ServerFnError> so that framework errors can be converted into it.

Additional context
I've seen ServerFnErrorErr but haven't fully understood what it is for. It looks pretty much the same as ServerFnError just with additional traits implemented? Why aren't those traits just implemented on ServerFnError?

@gbj
Copy link
Collaborator

gbj commented Sep 6, 2023

To start where you ended:

I've seen ServerFnErrorErr but haven't fully understood what it is for. It looks pretty much the same as ServerFnError just with additional traits implemented? Why aren't those traits just implemented on ServerFnError?

From the docs:

Unlike ServerFnErrorErr, this does not implement std::error::Error. This means that other error types can easily be converted into it using the ? operator.

This functions in the same way as anyhow::Error. ServerFnError implements From<T> for any T: Error, which means that ServerFnError itself can't implement Error, because it would conflict with the global impl From<T> for T. However, this does mean that you can convert any error type, including your own error type defined with thiserror, into ServerFnError automatically with ?.

As far as I can tell, making ServerFnError generic over some concrete error type E would mean that it's not possible to use the try operator in this way any more, or at least without .map_err over all other error types to convert them to E first.

It should be fairly simple for you to implement an extension trait that serializes your custom error type to a string to be used in ServerFnError::ServerError, and then deserializes it on the client side.

Alternately, if you have a proposal for your second approach (allowing server functions to return a Result with any error type) I'd be open to a PR.

@smessmer
Copy link
Author

smessmer commented Sep 6, 2023

I likely won't have the time to write up a PR for it but if somebody else wants to pick it up, here's the way I had imagined the second approach being used:

use thiserror::Error;
use serde::{Serialize, Deserialize};

#[derive(Error, Serialize, Deserialize)]
pub enum MyCustomError {
  // It implements `From<ServerFnError>` so it's able to represent framework errors
  #[from]
  ServerError(ServerFnError),

  // .. and some custom variants
}

#[server(MyServerFn, "/api")]
fn my_server_fn(arg: i32) -> Result<i32, MyCustomError> {
  arg + 1
}

In the server_fn implementation, this would likely involve some modifications to the ServerFn trait, e.g.:

pub trait ServerFn {
   // ...
   type Error: From<ServerFnError> + Serialize + Deserialize;
  // ...

  fn call_fn(
        self,
        cx: T
    ) -> Pin<Box<dyn Future<Output = Result<Self::Output, Self::Error>>>>;

  // ...
}

@gbj
Copy link
Collaborator

gbj commented Sep 8, 2023

I was also reminded recently that this is a perfectly reasonable type:

#[server]
pub async fn my_fallible_server_fn() -> Result<Result<T, MyError>, ServerFnError>

in which the other result is "did the server function work" and the inner result is "what value am I returning." So Ok(Err(_)) means "the server function successfully returned an Err," etc.

@gbj gbj mentioned this issue Jan 4, 2024
20 tasks
@gbj
Copy link
Collaborator

gbj commented Jan 20, 2024

Custom error types in server functions added in #2158.

@gbj gbj closed this as completed Jan 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants