seasonedcc / composable-functions

Types and functions to make composition easy and safe
MIT License
666 stars 14 forks source link

Allow a domain function to throw custom error data #19

Closed danielweinmann closed 2 years ago

danielweinmann commented 2 years ago

Purpose

This PR allows domain functions to throw partial ErrorData to be able to return inputErrors and environmentErrors as well as errors. This is useful for implementing validations that go beyond the parsing of the schema, like "Email is already taken", for example.

With this, whenever a server-side validation fails, we can simply throw the appropriate input errors, environment errors, or global errors.

What do you guys think about this solution?

gustavoguichard commented 2 years ago

We might even call it DomainFunctionError

Updated the screenshot on prev comment

To help with the TS, we'd need to ensure at least one property of ErrorData:

type AtLeastOne<T, U = {[K in keyof T]: Pick<T, K> }> = Partial<T> & U[keyof U]

class DomainFunctionError extends Error {
  data: AtLeastOne<ErrorData>

  constructor(data: AtLeastOne<ErrorData>) {
    super(
      data.errors[0]?.message
      ?? data.environmentErrors[0]?.message
      ?? data.inputErrors[0]?.message
    )
    this.name = "DomainFunctionError"
    this.data = data
  }
}

I know I'm missing the path on the type above but just sketching a raw idea:

const domainFunction = makeDomainFunction(z.null())(async () => {
  throw new DomainFunctionError({
    errors: [{ message: 'Custom error' }],
    inputErrors: [{ message: 'Custom input error', path: ['id'] }],
    environmentErrors: [{ message: 'Custom environment error', path: ['id'] }],
  })
})