Engineering

6

mins read

Fast, Flexible Role-Based Access in the UI

Taylor's headshot

Taylor Nguyen

Jul 7, 2025

Summary

Access control is one of those backend problems that quickly becomes a frontend nightmare — especially in collaborative tools where visibility matters. In this article, I walk through how we designed Hexa’s role-based permission system to be secure, fast, and user-aware — without creating branching logic chaos in the frontend.

Summary

Access control is one of those backend problems that quickly becomes a frontend nightmare — especially in collaborative tools where visibility matters. In this article, I walk through how we designed Hexa’s role-based permission system to be secure, fast, and user-aware — without creating branching logic chaos in the frontend.

Why permissions can be dangerous and slow

In most CRMs, permissions are bolted on — after the product is shipped. That leads to:

  • Redundant checks in every component

  • Overfetching or underfetching sensitive data

  • Hard-to-debug “why can’t I see this?” tickets

  • Slow interfaces with unnecessary guards

We wanted permission logic that was:
Declarative, centralized, cache-friendly, and composable.

Our permission model in a sentence

Every object (deal, note, summary, etc.) has a defined scope, and every user has an evaluated role within that scope.

That means instead of checking user → object directly, we check user → role → scope. This gave us flexibility.

The schema

We created a simple permissions matrix:

-- permission_scope table
id | user_id | object_type | object_id | role

Roles include:

  • owner

  • collaborator

  • observer

  • manager

This schema is indexed by object + user, so reads are lightning fast.

Evaluating permissions (inline logic)

On the server:

function canEditSummary(userId, summaryId) {
  return hasRole(userId, summaryId, ['owner', 'collaborator'])
}

The frontend simply calls an endpoint like:

GET /api/permissions/summary/{id}
{ canView: true, canEdit: false }

This avoids logic duplication — and lets us toggle logic centrally.

Smart caching for fast UI

Permissions are cached per session in Redis with a key like:

user:{id}:permissions:summary:{objectId}

This lets us hydrate client UIs fast with role-based guards, without async waterfalls.

Feature toggles layered in

Since roles aren’t the only gate, we wrapped a feature toggle system around it. Some actions require both:

✅ permission level
✅ feature flag (e.g. AI Summaries enabled)

That allowed product to test features with specific cohorts without breaking the role model.

Final Thought

Access control is foundational — but it doesn’t have to be messy. With the right schema and structure, you can build a permission system that feels invisible to the user — and rock solid to the team.

Jump to

Share Article

Share Article

Share Article

Related Reads

More in

Engineering

If it’s not covered here, reach out — or just try Hexa free and see for yourself.

Start Closing Faster

Ready to close faster? Start your free trial today.

Try it free. No contracts, no credit card. Just results, from day one.

  • Used by 2,300+ teams

  • Cancel anytime, no risk

  • 97% user retention after 30 days

Start Closing Faster

Ready to close faster? Start your free trial today.

Try it free. No contracts, no credit card. Just results, from day one.

  • Used by 2,300+ teams

  • Cancel anytime, no risk

  • 97% user retention after 30 days

Start Closing Faster

Ready to close faster? Start your free trial today.

Try it free. No contracts, no credit card. Just results, from day one.

  • Used by 2,300+ teams

  • Cancel anytime, no risk

  • 97% user retention after 30 days