WeGotWork supports multi-organization workflows, allowing users to belong to multiple organizations and switch between them seamlessly.
Organization membership
Users can be members of multiple organizations through the Member model (or OrganizationUser in some implementations):
model Member {
id String @id @default ( cuid ())
organizationId String
organization Organization @relation ( fields : [ organizationId ], references : [ id ], onDelete : Cascade )
userId String
user User @relation ( fields : [ userId ], references : [ id ], onDelete : Cascade )
role String @default ( "member" )
createdAt DateTime @default ( now ())
@@index ( [ organizationId ] )
@@index ( [ userId ] )
@@map ( "member" )
}
Current organization tracking
Users can set an active organization using currentOrganizationId (stored on the User model) or activeOrganizationId (stored on the Session model in some implementations).
User-based tracking
model User {
id String @id
name String
email String
currentOrganizationId String ?
currentOrganization Organization ? @relation ( fields : [ currentOrganizationId ], references : [ id ], onDelete : SetNull )
// ... other fields
}
Session-based tracking
model Session {
id String @id
activeOrganizationId String ?
// ... other fields
}
Switching organizations
Implement organization switching to allow users to change their active context:
export async function SetCurrentOrganization ( organizationId : string ) {
const session = await auth . api . getSession ({
headers: await headers (),
});
if ( ! session ) {
return {
error: true ,
message: "user not logged in."
};
}
// Verify user is a member of the organization
const isMember = await prisma . organizationUser . findFirst ({
where: {
userId: session . user . id ,
organizationId: organizationId
}
});
if ( ! isMember ) {
return {
error: true ,
message: "user not member of organization"
};
}
// Update current organization
await prisma . user . update ({
where: { id: session . user . id },
data: { currentOrganizationId: organizationId }
});
revalidatePath ( "/overview" );
return {
error: false ,
message: "Updated Organization Successfully"
};
}
Always verify that a user is a member of an organization before allowing them to switch to it.
Listing user organizations
Retrieve all organizations a user belongs to:
export async function UserOrganizations () {
const session = await auth . api . getSession ({
headers: await headers ()
});
if ( ! session ) {
redirect ( "/" );
}
const organizations = await prisma . user . findUnique ({
where: { id: session . user . id },
include: {
userOrganizations: {
include: {
organization: true
}
}
}
});
return {
userOrganizations: organizations ?. userOrganizations
};
}
Filtering by organization
When querying data, always filter by the current organization to ensure users only see data they have access to:
Jobs scoped to organization
const jobs = await prisma . job . findMany ({
where: {
organizationId: currentOrganizationId
},
include: {
category: true ,
applicants: true
}
});
Verifying organization access
const job = await prisma . job . findFirst ({
where: {
id: jobId ,
organization: {
members: {
some: {
userId: session . user . id
}
}
}
}
});
if ( ! job ) {
throw new Error ( "You are not authorized to access this job" );
}
Organization roles
Differentiate between organization owners and members:
enum Role {
owner
member
}
Check user role before allowing sensitive operations:
const membership = await prisma . organizationUser . findFirst ({
where: {
userId: session . user . id ,
organizationId: organizationId
}
});
if ( membership ?. role !== "owner" ) {
throw new Error ( "Only organization owners can perform this action" );
}
Creating organizations
When a user creates an organization, automatically make them an owner:
const organization = await prisma . organization . create ({
data: {
name: "Acme Corp" ,
slug: "acme-corp" ,
members: {
create: {
userId: session . user . id ,
role: "owner"
}
}
}
});
// Set as current organization
await prisma . user . update ({
where: { id: session . user . id },
data: { currentOrganizationId: organization . id }
});
Automatically set newly created organizations as the user’s current organization for a smoother experience.
Inviting users to organizations
Allow organization members to invite others:
const invitation = await prisma . organizationInvite . create ({
data: {
email: inviteeEmail ,
role: "member" ,
organizationId: organizationId ,
createdAt: new Date ()
}
});
// Send invitation email
await sendInvitationEmail ( inviteeEmail , invitation . id );
Organization context in UI
Display the current organization and provide a switcher:
export function OrganizationSwitcher () {
const { userOrganizations } = await UserOrganizations ();
const currentOrgId = session . user . currentOrganizationId ;
return (
< Select
value = { currentOrgId }
onValueChange = { SetCurrentOrganization }
>
{ userOrganizations ?. map (( membership ) => (
< SelectItem
key = { membership . organization . id }
value = { membership . organization . id }
>
{ membership . organization . name }
</ SelectItem >
)) }
</ Select >
);
}
Best practices
Always scope queries by organization
Never query jobs, applicants, or other organization-specific data without filtering by organization ID to prevent data leaks.
Verify membership before switching
Always check that a user is actually a member of an organization before allowing them to switch to it.
Handle deleted organizations gracefully
Use onDelete: SetNull for currentOrganizationId so users aren’t left in an invalid state if their current organization is deleted.
Show organization context
Make it clear which organization the user is currently working in, especially in the navigation or header.
Default to first organization
If a user doesn’t have a current organization set, automatically select their first organization or prompt them to choose one.