Abort() vs new Error()

Reading Recommendation: RPC

You may wonder, why not using throw new Error() instead of throw Abort()?

// TodoList.telefunc.js
// Environment: Node.js
 
import { getContext } from 'telefunc'
 
export async function getTodoList() {
  const { user } = getContext()
  if (!user) {
    // Instead of `throw Abort()`
    throw new Error()
  }
 
  // ...
}

By using throw Abort(), we are telling Telefunc that this is an error that is expected to happen (when a third-party makes an invalid or unprivileged telefunction call). Whereas throw new Error() is unexpected: it should never happen and, if it does happen, then it's a bug in our backend code.

For example, throw Abort() doesn't trigger onBug(), see Guides > Error handling > Error tracking.

Rule

The rule for a correct usage is:

  • We use throw Abort() for errors originating from a wrong public usage of a telefunction.
  • We use throw new Error() for errors originating from a wrong internal usage of a function.

Example

// auth/getUser.ts
// Environment: Node.js
 
// Note that `auth/getUser.ts` is not a `.telefunc.js` file (`getUser()` is
// not a telefunction but a so-called `getContext()` wrapper).
export { getUser }
 
import { Abort, getContext } from 'telefunc'
 
function getUser({ permission }) {
  if (!permission) {
    throw new Error('Wrong getUser() usage: missing permission')
  }
  // We avoid typos (a typo like `admni` could have dramatic consequences)
  if (!['public', 'admin'].includes(permission)) {
    throw new Error('Wrong getUser() usage: unknown permission ' + permission)
  }
 
  const { user } = getContext()
 
  if (!user) {
    throw Abort()
  }
 
  if (permission === 'admin') {
    if (!user.isAdmin) {
      throw Abort()
    }
    return user
  }
 
  if (permission === 'public') {
    return user
  }
}

Such getContext() wrapper is a common Telefunc technique explained at Guides > Permissions > getContext() wrapping.

// components/Comment.telefunc.ts
// Environment: Node.js
 
import { getUser } from '../auth/getUser'
 
export async function onCommentDelete(id: number) {
  // Only admins are allowed to delete a comment
  const user = getUser({ permission: 'admin' })
  const comment = await Comment.findOne({ id })
  await comment.delete()
}

It is expected that throw Abort() can occur, since onCommentDelete() is a public function anyone can call while not being an admin.

Whereas throw new Error('Wrong getUser() usage') is expected to not occur: if we make a typo and call getUser({ permission: 'admni' }) then it's an internal bug in our backend that we should fix.