Guides
Invite-Only Registration
Restrict sign-up so new users can only join via invitation.
1. Disable open registration
// dyrected.config.ts (same for both frameworks)
{
slug: 'users',
auth: true,
access: {
create: () => false, // no self-registration
read: ({ user }) => !!user,
update: ({ user, doc }) => user?.id === doc.id || user?.role === 'admin',
delete: ({ user }) => user?.role === 'admin',
},
fields: [
{ name: 'name', type: 'text' },
{ name: 'role', type: 'select', options: ['member', 'admin'], defaultValue: 'member' },
],
}With create: () => false, POST /api/collections/users without a valid invite token returns 403.
2. Send an invitation
import { createClient } from '@dyrected/sdk'
const client = createClient({ baseUrl: '/dyrected' })
client.setToken(adminToken)
await client.collection('users').invite('[email protected]')// app/admin/invite/actions.ts
'use server'
import { createClient } from '@dyrected/sdk'
const client = createClient({ baseUrl: process.env.NEXT_PUBLIC_DYRECTED_URL! })
export async function inviteUser(email: string, adminToken: string) {
client.setToken(adminToken)
return client.collection('users').invite(email)
}// Using the auth composable in a component or server action
const { token } = useDyrectedAuth('users')
const client = useDyrected()
async function inviteUser(email: string) {
client.setToken(token.value)
return client.collection('users').invite(email)
}Dyrected signs a 7-day JWT with purpose: 'invite' and emails it. In development the link is logged to the console via Ethereal — no email config needed.
3. Accept the invitation
// app/accept-invite/page.tsx
'use client'
import { useSearchParams, useRouter } from 'next/navigation'
import { createClient } from '@dyrected/sdk'
const client = createClient({ baseUrl: '/dyrected' })
export default function AcceptInvitePage() {
const params = useSearchParams()
const router = useRouter()
const inviteToken = params.get('token') ?? ''
async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault()
const form = e.currentTarget
const password = (form.elements.namedItem('password') as HTMLInputElement).value
const name = (form.elements.namedItem('name') as HTMLInputElement).value
const { token } = await client.collection('users').acceptInvite(inviteToken, password, { name })
document.cookie = `dyrected-token=${token}; path=/`
router.push('/dashboard')
}
return (
<form onSubmit={handleSubmit}>
<input name="name" type="text" placeholder="Your name" required />
<input name="password" type="password" placeholder="Choose a password" required />
<button type="submit">Join</button>
</form>
)
}<!-- pages/accept-invite.vue -->
<script setup lang="ts">
const route = useRoute()
const router = useRouter()
const inviteToken = route.query.token as string
const name = ref('')
const password = ref('')
async function submit() {
const { token } = await useDyrected().collection('users').acceptInvite(
inviteToken,
password.value,
{ name: name.value }
)
useDyrectedAuth('users').setToken(token)
router.push('/dashboard')
}
</script>
<template>
<form @submit.prevent="submit">
<input v-model="name" type="text" placeholder="Your name" required />
<input v-model="password" type="password" placeholder="Choose a password" required />
<button type="submit">Join</button>
</form>
</template>const { token, user } = await client.collection('users').acceptInvite(
tokenFromEmailLink,
'their-chosen-password',
{ name: 'Jane Smith' }
)4. Customise the invite email
// dyrected.config.ts (same for both frameworks)
export default defineConfig({
email: {
from: '[email protected]',
send: async ({ to, subject, html }) => { /* your provider */ },
templates: {
invite: ({ token, invitedByEmail }) => ({
subject: `You've been invited to MyApp`,
html: `
<p>${invitedByEmail ?? 'Someone'} has invited you to join MyApp.</p>
<p><a href="https://myapp.com/accept-invite?token=${token}">Accept your invitation</a></p>
<p>This link expires in 7 days.</p>
`,
}),
},
},
})See Email for all template options and production provider examples.