-
Notifications
You must be signed in to change notification settings - Fork 40
Expand file tree
/
Copy path+Page.mdx
More file actions
119 lines (90 loc) · 2.59 KB
/
+Page.mdx
File metadata and controls
119 lines (90 loc) · 2.59 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import { Link } from '@brillout/docpress'
## Basics
Permissions are implemented by using `throw Abort()` and `return`:
```ts
// TodoItem.telefunc.ts
// Environment: server
export { onTextChange }
import { getContext, Abort } from 'telefunc'
async function onTextChange(id: string, text: string) {
const { user } = getContext()
if (!user) {
// 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()
}
// You 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, use `throw Abort()` upon permission denials but, sometimes, the frontend needs to know why the telefunction call failed:
in this example `{ notLoggedIn: true }` is returned instead of `throw Abort()` so that the frontend can perform a redirection:
```tsx
// TodoItem.tsx
// Environment: client
import { onTextChange } from './TodoItem.telefunc'
async 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={async (ev) => await onChange(id, ev.target.value)}
/>
)
}
```
## `getContext()` wrapping
To implement permission logic once and re-use it, you can define a `getContext()` wrapper:
```ts
// components/TodoItem.telefunc.ts
// Environment: server
export { onTextChange }
import { getUser } from '../auth/getUser'
function onTextChange(id: string, text: string) {
const user = getUser()
/* ... */
}
```
```ts
// 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
}
```
```ts
// Environment: client
import { onAbort } from 'telefunc/client'
onAbort((err) => {
if (err.abortValue.notLoggedIn) {
// Redirect user to login page
window.location.href = '/login'
}
})
```
## See also
- <Link href="/onAbort" />
- <Link href="/Abort" />
- <Link href="/getContext" />