throw Abort() vs throw new Error()
Reading Recommendation: Overview > RPC
You might wonder: why not just use throw new Error() instead of throw Abort()?
Let's have a look at following example:
// TodoList.telefunc.js
// Environment: server
import { Abort, getContext } from 'telefunc'
export async function getTodoList() {
const { user } = getContext()
if (!user) {
// ❌ Wrong usae
throw new Error()
// ✅ Correct usage
throw Abort()
}
// ...
}// TodoList.telefunc.ts
// Environment: server
import { Abort, getContext } from 'telefunc'
export async function getTodoList() {
const { user } = getContext()
if (!user) {
// ❌ Wrong usae
throw new Error()
// ✅ Correct usage
throw Abort()
}
// ...
}It's expected that the user might not be logged in — using throw Abort() tells Telefunc that the error is expected.
Telefunc interprets
throw new Error()as unexpected. Telefunc interprets any error that isn'tthrow Abort()as a bug in your backend code.
Therefore:
- Use
throw Abort()for wrong public usage of your telefunction. - Use
throw new Error()for wrong internal usage (i.e. a bug).
Because:
throw Abort()doesn't trigger theonBug()hook (whereasthrow new Error()does)throw Abort()isn't logged (whereas Telefunc logsthrow new Error()viaconsole.error())throw Abort('some error message')=> Telefunc sendssome error messageto the clientthrow new Error('some error message')=> Telefunc doesn't sendsome error messageto the client.To prevent leaking sensitive information, Telefunc never forwards unexpected server errors to the client.
Example
// auth/getUser.js
// Environment: server
// Note that `auth/getUser.js` isn't a `.telefunc.js` file.
// `getUser()` isn't a telefunction, it's what we call a `getContext()` wrapper.
export { getUser }
import { Abort, getContext } from 'telefunc'
/**
* Retrieve the current user from context, and abort if the user doesn't have the right permission
*/
function getUser({ permission }) {
// Wrong internal usage (`getUser()` isn't a telefunction)
if (!permission) {
throw new Error('[Wrong getUser() usage] Missing `permission` argument')
}
// Prevent typos
if (!['public', 'admin'].includes(permission)) {
throw new Error('[Wrong getUser() usage] Unknown `permission` value: ' + permission)
}
const { user } = getContext()
// User isn't logged in
if (!user) {
throw Abort()
}
// User doesn't have the right permission
if (permission === 'admin' && !user.isAdmin) {
throw Abort()
}
return user
}// auth/getUser.ts
// Environment: server
// Note that `auth/getUser.ts` isn't a `.telefunc.ts` file.
// `getUser()` isn't a telefunction, it's what we call a `getContext()` wrapper.
export { getUser }
import { Abort, getContext } from 'telefunc'
/**
* Retrieve the current user from context, and abort if the user doesn't have the right permission
*/
function getUser({ permission }: { permission: string }) {
// Wrong internal usage (`getUser()` isn't a telefunction)
if (!permission) {
throw new Error('[Wrong getUser() usage] Missing `permission` argument')
}
// Prevent typos
if (!['public', 'admin'].includes(permission)) {
throw new Error('[Wrong getUser() usage] Unknown `permission` value: ' + permission)
}
const { user } = getContext()
// User isn't logged in
if (!user) {
throw Abort()
}
// User doesn't have the right permission
if (permission === 'admin' && !user.isAdmin) {
throw Abort()
}
return user
}Such
getContext()wrapper is a common Telefunc technique explained at Guides > Permissions >getContext()wrapping.
// components/Comment.telefunc.js
// Environment: server
import { getUser } from '../auth/getUser'
export async function onCommentDelete(id) {
// Only admins are allowed to delete a comment
const user = getUser({ permission: 'admin' })
const comment = await Comment.findOne({ id })
await comment.delete()
}// components/Comment.telefunc.ts
// Environment: server
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] ...') should never occur: if you make a typo and call getUser({ permission: 'admni' }) then it's an internal bug in your backend code that should be fixed.