Guides
Role-Based Access Control
Restrict what different users can read, create, edit, and delete based on their role.
1. Add a role field to your users collection
// dyrected.config.ts (same for both frameworks)
{
slug: 'users',
auth: true,
fields: [
{ name: 'name', type: 'text', required: true },
{
name: 'role',
type: 'select',
options: ['admin', 'editor', 'viewer'],
defaultValue: 'viewer',
access: {
update: ({ user }) => user?.role === 'admin', // only admins can change roles
},
},
],
}2. Lock down the users collection
// dyrected.config.ts (same for both frameworks)
{
slug: 'users',
auth: true,
access: {
read: ({ user }) => !!user,
create: () => false, // use invite instead
update: ({ user, id }) => user?.role === 'admin' || user?.id === id,
delete: ({ user }) => user?.role === 'admin',
},
}3. Apply roles to a content collection
// dyrected.config.ts (same for both frameworks)
{
slug: 'posts',
access: {
read: ({ user, doc }) => doc?.status === 'published' || !!user,
create: ({ user }) => user?.role === 'admin' || user?.role === 'editor',
update: ({ user, doc }) => {
if (user?.role === 'admin') return true
return user?.role === 'editor' && doc?.createdBy === user?.id
},
delete: ({ user }) => user?.role === 'admin',
},
hooks: {
beforeChange: [
({ data, operation, user }) => {
if (operation === 'create') return { ...data, createdBy: user?.id }
return data
},
],
},
fields: [
{ name: 'title', type: 'text' },
{ name: 'content', type: 'richtext' },
{ name: 'status', type: 'select', options: ['draft', 'published'], defaultValue: 'draft' },
{ name: 'createdBy', type: 'relationship', relationTo: 'users' },
],
}4. Hide sensitive fields from lower roles
// dyrected.config.ts (same for both frameworks)
{
name: 'internalNotes',
type: 'textarea',
access: {
read: ({ user }) => user?.role === 'admin' || user?.role === 'editor',
update: ({ user }) => user?.role === 'admin',
},
}Fields stripped by read access never appear in API responses for that user — they're also hidden in the Admin UI.
5. Check the role on the frontend
import { createClient } from '@dyrected/sdk'
const client = createClient({ baseUrl: '/dyrected' })
client.setToken(userToken)
const user = await client.collection('users').me()
// user.role === 'admin' | 'editor' | 'viewer'// In a Server Component
import { createClient } from '@dyrected/sdk'
const client = createClient({ baseUrl: process.env.NEXT_PUBLIC_DYRECTED_URL! })
async function getCurrentUser() {
const token = cookies().get('dyrected-token')?.value
if (!token) return null
client.setToken(token)
try {
return await client.collection('users').me()
} catch {
return null
}
}
// In your component:
const user = await getCurrentUser()
if (user?.role === 'admin') { /* show admin controls */ }<script setup lang="ts">
const { user } = useDyrectedAuth('users')
</script>
<template>
<AdminControls v-if="user?.role === 'admin'" />
<EditorControls v-else-if="user?.role === 'editor'" />
</template>Summary
| Role | Read posts | Create | Edit own | Edit any | Delete | Manage users |
|---|---|---|---|---|---|---|
admin | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
editor | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ |
viewer | ✅ (published) | ❌ | ❌ | ❌ | ❌ | ❌ |
| Anonymous | ✅ (published) | ❌ | ❌ | ❌ | ❌ | ❌ |
See Access Control for the full reference on access function signatures.