Organizations
The Organization entity represents a tenant within the Wrkbelt platform. It serves as the foundation for multi-tenancy and provides isolation between different organizations' data and resources.
Schema Definition
type Organization = {
// Base Properties
_id: string; // MongoDB ObjectId
name: string; // Required
tenant_subdomain: string; // Required, Unique, Lowercase alphanumeric with hyphens
logo_file?: string; // Optional, Reference to File
memberships: string[]; // Array of Membership ObjectIds
address?: Address; // Optional, Structured address information
// Timestamps
createdAt: Date; // Auto-generated
updatedAt: Date; // Auto-updated
} & BaseEntity;
Field Descriptions
Base Properties
| Field | Type | Required | Unique | Description |
|---|---|---|---|---|
_id | ObjectId | Yes | Yes | Unique identifier for the organization |
name | string | Yes | No | Organization's display name |
tenant_subdomain | string | Yes | Yes | Unique subdomain identifier (lowercase, alphanumeric) |
logo_file | ObjectId | No | No | Reference to organization's logo file |
memberships | ObjectId[] | No | No | Array of references to associated Membership documents |
address | Address | No | No | Organization's physical address information |
Timestamps
| Field | Type | Description |
|---|---|---|
createdAt | Date | Automatically set when the organization is created |
updatedAt | Date | Automatically updated when the organization changes |
Type Variations
OrganizationPopulated
A fully populated version with resolved references:
type OrganizationPopulated = Omit<
Organization,
'memberships' | 'logo_file'
> & {
memberships: MembershipPopulated[];
logo_file?: File;
};
BaseOrganizationsForSession
A minimal version used in session management:
type BaseOrganizationsForSession = Pick<
OrganizationPopulated,
'_id' | 'name' | 'logo_file'
>;
ActiveOrganizationForSession
Extended version for active organization context:
type ActiveOrganizationForSession = BaseOrganizationsForSession & {
logo_file?: Pick<File, 'storage_location'>;
memberships: MembershipLeanWithRoleNames[];
flatPermissions: AllPermissions[];
};
Relationships
Primary Relationships
-
Organization → Memberships (One-to-Many)
- Organizations can have multiple memberships
- Each membership associates a user with roles in the organization
- Referenced via
membershipsarray field
-
Organization → Files (One-to-One, Optional)
- Organizations can have a logo file
- Referenced via
logo_filefield
Indirect Relationships
- Organization → Users (Many-to-Many through Memberships)
- Organizations have multiple users through memberships
- Users can belong to multiple organizations
- Access levels defined by membership roles
Validation
tenant_subdomain
- Required string
- Must contain only lowercase letters, numbers, and hyphens
- Cannot start or end with a hyphen
- Must be unique across all organizations
- Case-insensitive uniqueness enforced via MongoDB index
address
Optional object with the following structure:
type Address = {
street: string;
city: string;
state: string;
postal_code: string;
country: string;
};
Multi-tenancy Considerations
-
Data Isolation
- Each organization's data is logically separated
- Resources are scoped to organization context
- Cross-organization access is prevented
-
Tenant Identification
- Organizations are identified by their unique subdomain
- Subdomains are used for routing and context switching
- Case-insensitive to prevent subdomain conflicts
-
Resource Management
- Organizations own their resources (files, memberships, etc.)
- Resource permissions are organization-specific
- Users can switch between organization contexts
Usage Examples
Creating an Organization
const organization = {
name: "Acme Corp",
tenant_subdomain: "acme",
address: {
street: "123 Main St",
city: "San Francisco",
state: "CA",
postal_code: "94105",
country: "USA"
}
};
Subdomain Validation
const isValidSubdomain = (subdomain: string): boolean => {
return /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(subdomain);
};
Common Operations
-
Member Management
// Add member to organization
await Organization.updateOne(
{ _id: orgId },
{ $addToSet: { memberships: newMembershipId } }
); -
Organization Context
// Get organization with populated memberships
const org = await Organization.findById(orgId)
.populate({
path: 'memberships',
populate: {
path: 'roles'
}
});