Error handling

This page is about handling bugs, expected errors, and network errors.

Bugs

If a telefunction has a bug:

// hello.telefunc.js
// Environment: server
 
export { hello }
 
function hello(name) {
  // This telefunction has a bug: it should be `name` instead of `namee`
  return 'Hello ' + namee
}

Then a telefunction call throws an error:

<!-- index.html -->
<!-- Environment: client -->
 
<html>
  <body>
    <script type="module">
      import { hello } from './hello.telefunc.js'
 
      try {
        await hello('Eva')
        console.log("I'm never printed")
      } catch(err) {
        console.log(err.message) // Prints 'Internal Server Error'
        // E.g. show a popup "Something went wrong. Try again (later)."
        // ...
      }
    </script>
  </body>
</html>

To avoid leaking sensitive information, Telefunc doesn't send the original Error object to the frontend.

Expected Errors

An error thrown by a telefunction may not be a bug but an expected error instead.

For example:

  • Some authentication libraries throw an error if the user isn't logged in.
  • Some validation libraries throw errors upon invalid data.

We can:

  1. Propagate the error to the frontend, or
  2. handle the error on the server-side.

1. Propagate the error to the frontend.

We can propagate error information to the frontend like this:

import { validate } from 'some-library'
 
function onFromSubmit(data) {
  try {
    validate(data)
  } catch(err) {
    return {
      errorMessage: `Data is invalid: ${err.message}. Please enter valid data.`
    }
  }
}

We can also use throw Abort(someValue):

import { validate } from 'some-library'
import { Abort } from 'telefunc'
 
function onFromSubmit(data) {
  try {
    validate(data)
  } catch(err) {
    throw Abort({
      errorMessage: `Data is invalid: ${err.message}. Please enter valid data.`
    })
  }
}

In general, we recommend using return { someValue } instead of throw Abort(someValue), see explanation at Guides > Form validation > throw Abort(someValue).

We need to catch the error and use throw Abort(someValue) because Telefunc doesn't send the original Error object to the frontend (in order to avoid leaking sensitive information).

Also see Guides > Permissions > getContext() wrapping.


2. Handle the error on the server-side.

We can handle the thrown error at our Telefunc server middleware:

// server.js
 
// Server (Express.js/Fastify/...)
 
import { telefunc } from 'telefunc'
 
// Telefunc middleware
app.all('/_telefunc', async (req, res) => {
  const httpResponse = await telefunc(/* ... */)
  // Telefunc exposes any error thrown by a telefunction at httpResponse.err
  if (httpResponse.err) {
    // Our error handling
  }
})

Also see API > getContext() > Provide.

Network Errors

If the user's browser can't connect to our server:

<!-- index.html -->
<!-- Environment: client -->
 
<html>
  <body>
    <script type="module">
      import { hello } from './hello.telefunc.js'
 
      try {
        await hello('Eva')
      } catch(err) {
        if (err.isConnectionError) {
          // There is a network problem:
          //  - the user isn't connected to the internet, or
          //  - our server is down.
          console.log(err.message) // Prints 'No Server Connection'
        }
      }
    </script>
  </body>
</html>