Core Principle: Model Your Domain, Not a Meta-Model
The most common mistake when starting with OpenFGA is creating an overly generic model that can represent “anything.” While this seems flexible, it trades clarity for abstraction and often hurts performance.The Recommended Approach
Define types and relations that mirror your application’s domain. If your app has organizations, projects, and documents, model exactly that with explicit relationships: Notice how this model has a clear hierarchy (organization → project → document) where each type and relationship directly reflects the application’s domain. Permission inheritance follows a well-defined path that’s easy to understand and audit. This approach has several advantages:- Enhanced clarity and maintainability: Authorization logic is easier to understand, debug, and maintain. Developers and security auditors can readily grasp the meaning of each type and relationship just by reading the model.
- Better performance: Models with specific types and flatter hierarchies perform better. OpenFGA processes queries more efficiently with well-defined types compared to navigating complex recursive relationships within generic types.
- Easier evolution: OpenFGA’s modeling language is designed to be adaptable. You can define numerous distinct types and relationships without significant overhead. Model changes rarely require data migrations, allowing you to evolve your model as your application grows.
- Team autonomy with modules: Resource types owned by each application team can be maintained in independent modules. You can control which application can write to specific resource types through API credentials, providing better security boundaries.
What to avoid: The overly generic model
What to avoid: The overly generic model
The model below can technically represent any organization hierarchy, any resource hierarchy, and any role hierarchy:While flexible, this approach creates problems:
- The model doesn’t communicate what your application actually does
- To understand the actual relationships you need to rely on tuples, e.g., the fact that a project can have documents.
- Generic recursive relations are slower to evaluate
- You can’t use modules to isolate different resource types
- ListObjects returns mixed results (all “resources” instead of just “documents”)
Modeling Roles
Most applications have roles. The key question is: are they built-in or user-defined?Built-in Roles
For roles that come with your application (admin, member, viewer, etc.), define them directly as relations: Adding new built-in roles is straightforward: add a relation to the model. This happens infrequently and doesn’t require data migration.Custom Roles (User-Defined)
Some applications let end-users create their own roles. In this case, combine static roles with a dynamicrole type:
This hybrid approach gives you the clarity of static roles for common cases while supporting custom roles when needed.
For more details, see Modeling Roles and Custom Roles.
Modeling Organizational Structures
B2B SaaS applications often have two distinct organizational requirements:- Super-admin access: Your company’s employees need to access customer data for support or disaster recovery
- Customer hierarchies: Your customers have their own organizational structures
Super-Admin Access
For internal support and admin access, use a dedicatedsystem type rather than making organizations recursive:
This approach:
- Clearly separates your internal access from customer access
- Avoids recursive relations (faster to evaluate)
- Makes audit and compliance reviews easier
Customer Organization Hierarchies
If customers need hierarchical organizations, prefer explicit types for each level when the structure is well-defined: This makes the hierarchy explicit: organizations contain departments, and department members inherit from the organization. If the hierarchy depth is truly dynamic (customers can nest arbitrarily), then add recursion only where needed: Now you have two distinct hierarchies:- System hierarchy: Non-recursive, for your internal super-admin access
- Organization hierarchy: Recursive only if customers truly need arbitrary nesting
Modeling Resource Types
Applications have different kinds of resources: documents, folders, projects, tickets, accounts, etc. Some have parent-child relationships (folders contain documents, projects contain tickets).Use Specific Types, Not a Generic “Resource”
Define a type for each kind of resource in your application: Benefits of specific types:- Accurate ListObjects results: Querying for documents returns only documents, not all resources
- Type-specific permissions:
can_printmakes sense for documents but not folders - Clearer model: Each type shows exactly what permissions apply to it
- Module support: Different teams can own different resource types
What to avoid: The generic resource type
What to avoid: The generic resource type
Problems with this approach:
- ListObjects returns mixed results (folders, documents, and everything else)
- You end up with a superset of all permissions, making it unclear which apply to what
- No way to use modules for team ownership
- Harder to understand and audit
Quick Reference
Related Sections
Check out these related resources for more information about modeling in OpenFGACustom Roles
Learn how to implement user-defined custom roles.
Modular Authorization Models
Learn how to break down your authorization model into modules.
Modeling Roles
Detailed guidance on role-based access control patterns.
Building Blocks
Understand the fundamental concepts for building authorization models.