Engineering

6

mins read

Fast, Flexible Role-Based Access in the UI

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.

More copy, more conversions

Better content at the speed of write

High-converting, human content assets written by a 10-year copywriting expert. Delivered in 48 hours or less. That’s my promise to you.

More copy, more conversions

Better content at the speed of write

High-converting, human content assets written by a 10-year copywriting expert. Delivered in 48 hours or less. That’s my promise to you.

More copy, more conversions

Better content at the speed of write

High-converting, human content assets written by a 10-year copywriting expert. Delivered in 48 hours or less. That’s my promise to you.