Go

  1. We follow the rule of handling an error only once, which means you either return the error or log it, but never do both.
  2. If more context is needed, wrap the error using fmt.Errorf("...: %w").
  3. Services must always return system errors (from system/errors.go). The handler then unwraps the error and, based on the type, applies the proper logic.
  4. If it’s a validation error that we want to display on forms, we must return system.ErrValidation with the appropriate error code:
    • HTTP - Unprocessed Entity 422
    • gRPC - Invalid Argument 3
  5. ALL ERRORS are checked.

SvelteKit / NextJS

  1. We use a wrapper around try / catch that forces you to handle errors. It aims to mimic what Go or Rust do (tries, JS error handling sucks).
  2. The wrapper is called Safe, and you can find its logic in safe.ts file. For more information, please check the article: Typescript with Go/Rust Errors: No Try/Catch Heresy
  3. For form validation, there are two approaches to handling validations:
  • Basic Native HTML + Full Server-Side: This is the recommended approach as it removes as much unnecessary logic from the frontend as possible. Basic validation is performed using native HTML methods (e.g., required, maxlength), while more advanced validations are handled by the server. When the server sends a proper status code, the frontend unwraps the message, assigns it to the fields object, and passes it down to the pages. The page then extracts the errors and displays it on the form.
  • Full Client-Side + Full Server-Side: This approach uses the zod library for client-side validation. The Edit Note page is the only one that showcases this flow. It works similarly to the Full Server-Side approach, with the exception that zod handles input validation first and then passes the fields object down. Of course, the server must still perform its full validation.