Skip to main content

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

FieldTypeRequiredUniqueDescription
_idObjectIdYesYesUnique identifier for the organization
namestringYesNoOrganization's display name
tenant_subdomainstringYesYesUnique subdomain identifier (lowercase, alphanumeric)
logo_fileObjectIdNoNoReference to organization's logo file
membershipsObjectId[]NoNoArray of references to associated Membership documents
addressAddressNoNoOrganization's physical address information

Timestamps

FieldTypeDescription
createdAtDateAutomatically set when the organization is created
updatedAtDateAutomatically 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 memberships array field
  • Organization → Files (One-to-One, Optional)

    • Organizations can have a logo file
    • Referenced via logo_file field

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

  1. Data Isolation

    • Each organization's data is logically separated
    • Resources are scoped to organization context
    • Cross-organization access is prevented
  2. Tenant Identification

    • Organizations are identified by their unique subdomain
    • Subdomains are used for routing and context switching
    • Case-insensitive to prevent subdomain conflicts
  3. 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

  1. Member Management

    // Add member to organization
    await Organization.updateOne(
    { _id: orgId },
    { $addToSet: { memberships: newMembershipId } }
    );
  2. Organization Context

    // Get organization with populated memberships
    const org = await Organization.findById(orgId)
    .populate({
    path: 'memberships',
    populate: {
    path: 'roles'
    }
    });