Teams & Roles
Multi-tenancy, team management, and invitation flows
Teams & Roles
Build multi-tenant applications with team management and role-based permissions.
Features
- Team creation and management
- Member invitations
- Role-based access control
- Team switching
- Resource isolation
Team Structure
interface Team {
id: string
name: string
slug: string
ownerId: string
createdAt: Date
}
interface TeamMember {
id: string
teamId: string
userId: string
role: 'owner' | 'admin' | 'member'
joinedAt: Date
}
Creating Teams
// Create a new team
const team = await $fetch('/api/teams', {
method: 'POST',
body: {
name: 'Acme Inc',
slug: 'acme-inc'
}
})
Inviting Members
// Send team invitation
await $fetch(`/api/teams/${teamId}/invitations`, {
method: 'POST',
body: {
email: '[email protected]',
role: 'member'
}
})
Accepting Invitations
// Accept invitation
await $fetch(`/api/teams/invitations/${token}/accept`, {
method: 'POST'
})
Team Permissions
// Check team permission
function hasPermission(user: User, team: Team, permission: string) {
const member = team.members.find(m => m.userId === user.id)
if (!member) return false
const permissions = {
owner: ['*'],
admin: ['read', 'write', 'invite', 'remove_member'],
member: ['read']
}
const rolePermissions = permissions[member.role]
return rolePermissions.includes('*') || rolePermissions.includes(permission)
}
Role Middleware
// server/middleware/team-role.ts
export default defineEventHandler(async (event) => {
const teamId = getRouterParam(event, 'teamId')
const user = event.context.user
const member = await getTeamMember(teamId, user.id)
if (!member || !hasRequiredRole(member.role, 'admin')) {
throw createError({
statusCode: 403,
message: 'Insufficient permissions'
})
}
})
Team Switching
<script setup>
const currentTeam = ref(null)
async function switchTeam(teamId: string) {
await $fetch('/api/teams/switch', {
method: 'POST',
body: { teamId }
})
currentTeam.value = teamId
await navigateTo('/dashboard')
}
</script>
Resource Isolation
Ensure resources are scoped to teams:
// Always filter by team
const projects = await db
.select()
.from(projects)
.where(eq(projects.teamId, currentTeamId))
Best Practices
- Always validate team membership
- Implement proper role checks
- Log team actions for audit
- Limit team size if needed
- Handle team deletion carefully