Permissions Basics
Permissions are implemented by using throw Abort()
and return
:
// TodoItem.telefunc.ts
// Environment: server
export { onTextChange }
import { getContext, Abort } from 'telefunc'
function onTextChange ( id : string , text : string ) {
const { user } = getContext ()
if ( ! user) {
// We return `notLoggedIn: true` so that the frontend can redirect the user to the login page
return { notLoggedIn: true }
}
const todoItem = await Todo. findOne ({ id })
if ( ! todoItem) {
// `throw Abort()` corresponds to "403 Forbidden" of classical APIs
throw Abort ()
}
// We can easily programmatically implement advanced permissions such
// as "only allow the author or admins to modify a to-do item".
if (todoItem.authorId !== user.id && ! user.isAdmin) {
throw Abort ()
}
await todoItem. update ({ text })
}
In general, we use throw Abort()
upon permission denials but, sometimes, the frontend needs to know why the the telefunction call failed:
in this example we return { notLoggedIn: true }
instead of throw Abort()
so that the frontend can perform a redirection:
// TodoItem.tsx
// Environment: client
import { onTextChange } from './TodoItem.telefunc'
function onChange ( id : string , text : string ) {
const res = await onTextChange (id, text)
if (res?.notLoggedIn) {
// Redirect user to login page
window.location.href = '/login'
}
}
function TodoItem ({ id , text } : { id : string ; text : string }) {
return < input input = "text" value = {text} onChange = {( ev ) => onChange (id, ev.target.value)} />
}
getContext()
wrapping
To implement permission logic once and re-use it, we can define a getContext()
wrapper:
// components/TodoItem.telefunc.ts
// Environment: server
export { onTextChange }
import { getUser } from '../auth/getUser'
function onTextChange ( id : string , text : string ) {
const user = getUser ()
/* ... */
}
// auth/getUser.ts
// Environment: server
// Note that getUser() isn't a telefunction: it's a wrapper around getContext()
export { getUser }
import { getContext, Abort } from 'telefunc'
function getUser () {
const { user } = getContext ()
if ( ! user) {
throw Abort ({ notLoggedIn: true })
}
return user
}
// Environment: client
import { onAbort } from 'telefunc/client'
onAbort ( err => {
if (err.abortValue.notLoggedIn) {
// Redirect user to login page
window.location.href = '/login'
}
})
See also