Abort()
& shield()
Reading Recommendation: Overview > Tour.
As we have seen in the Telefunc tour, telefunctions are public and need protection. We use throw Abort()
and shield()
to protect our telefunctions.
throw Abort()
By using throw Abort()
we essentially cancel forbidden telefunction calls:
// TodoList.telefunc.js
// Environment: Node.js
// `getTodoList()` is a public: it can be called not only by our frontend but really by anyone
export { getTodoList }
import { getContext } from 'telefunc'
async function getTodoList() {
const { user } = getContext()
// We forbid `getTodoList()` to be called by a user that is not not logged-in
if (!user) {
throw Abort()
}
const todoList = await Todo.findMany({ authorId: user.id })
return todoList
}
Here, in essence, we use
throw Abort()
to implement a permission: only a logged-in user can fetch its to-do items. We talk more about permissions at Guides > Permissions.
In principle, we could also
throw new Error()
instead ofthrow Abort()
as it also achieves the job of canceling the telefunction, butAbort()
comes with many conveniences and we therefore recommend usingthrow Abort()
.
If, upon canceling a telefunction call, we want to pass information to the frontend then we use return someValue
or throw Abort(someValue)
, which we talk more about at Guides > Permissions.
shield()
We can also use throw Abort()
to ensure the type of telefunction arguments:
// CreateTodo.telefunc.js
// Environment: Node.js
export async function onNewTodo(args) {
if (
args?.constructor !== Object ||
typeof args.text !== 'string' ||
typeof args.isCompleted !== 'boolean'
) {
throw Abort()
}
const { text, isCompleted } = args
/* ... */
}
But, for more convenience, we can use shield()
instead:
// CreateTodo.telefunc.js
// Environment: Node.js
import { shield } from 'telefunc'
const t = shield.type
shield(onNewTodo, [{ text: t.string, isCompleted: t.boolean }])
export async function onNewTodo({ text, isCompleted }) {
// ...
}
Not only does shield()
call throw Abort()
on our behalf, but it also infers the arguments' type for IntelliSense and TypeScript.
See also:
Because telefunctions are public, any of our telefunction can be called by anyone at any time with any arguments.
One way to think about it is that any random telefunction call can happen at any time.
This means we should always protect our telefunctions, even if know our frontend to call a telefunction only in a certain way. For example:
// Comment.jsx
// Environment: Browser
import { onCommentDelete } from './Comment.telefunc.js'
function Comment({ id, text, context }) {
const deleteButton =
// Note how we only show the delete button to admins
context.user.isAdmin ?
<button onClick={() => onCommentDelete(id)}>Delete</button> :
null
return <>
<p>{ text }</p>
{ deleteButton }
</>
}
Because our frontend shows the delete button only to admins, we can assume the user to be an admin whenever our frontend calls onCommentDelete()
.
But we still use throw Abort()
to protect our telefunction against calls not originating from our frontend.
// Comment.telefunc.js
// Environment: Node.js
import { getContext, Abort, shield } from 'telefunc'
shield(onCommentDelete, [shield.type.number])
export async function onCommentDelete(id) {
const { user } = getContext()
// `onCommentDelete()` is public and anyone can call it while not being an admin.
// If that happens, we make to sure to cancel the telefunction call.
if (!user.isAdmin) {
throw Abort()
}
// ...
}