Use cases
Role-Based Access Control (RBAC)
RBAC is a way to manage access to resources in an application based on the roles of the identified users. In this guide we will go through the process of setting up RBAC using Featurevisor complimenting the backend's role management.
Why use RBAC?#
Most teams reach for Role-Based Access Control (RBAC) as a backend concern, and rightly so. The backend owns authentication, session management, and information about what roles a user holds. That's non-negotiable.
But there's a gap that often goes unaddressed: how does the frontend know which features to show, hide, or constrain based on the current user's roles?
The naive answer is to scatter if (user.role === 'admin') conditionals throughout our UI code. The slightly better answer is to centralize those checks into a permissions utility. But neither approach gives us the flexibility, auditability, and separation of concerns that a mature product team needs.
This guide presents a third path: using Featurevisor's segment system to declare role-specific feature visibility in a Git-managed, reviewable, auditable way while keeping the backend firmly in control of what roles a user actually has.
Who owns what#
Before declaring any segments or features, it's worth being precise about responsibilities.
| Concern | Owner |
|---|---|
| Authenticating the user | Backend |
| Determining which roles the user has | Backend |
| Communicating roles to the client | Backend (via API response / JWT) |
| Declaring which features require which roles | Featurevisor project (Git) |
| Evaluating feature visibility at runtime | Featurevisor SDK (client) |
| Enforcing access on sensitive operations | Backend (always) |
The key insight:
- Featurevisor never decides who a user is.
- It only decides what a user with a given set of attributes, including their roles, should see.
- The roles themselves are always sourced from the backend.
This means:
- Frontend engineers stop writing role-checking logic, and they only ask "is this feature enabled?"
- Backend engineers retain full control over role assignment and enforcement
- Product managers can see exactly which roles unlock which features, all in one reviewable place
Note: while this guide treats frontend as the layer where the role-checking logic is implemented, it is entirely possible to implement it on yet another backend service as well.
How it works#
Featurevisor supports attributes with an array type, which can expect an array of strings in the context in your application runtime. This is a good fit for roles, as roles are inherently a set of strings.
The pattern has three parts:
- A
rolesattribute: an array attribute representing the current user's roles - Role-specific segments: one segment per role, with a condition checking if the array contains that role
- Feature rules that target those segments: features target the appropriate role segments
When our application initializes the Featurevisor SDK, it passes the roles (fetched from the backend) into the evaluation context. The SDK does the rest.
Define roles attribute#
description: | The roles assigned to the currently authenticated user, as provided by the backend.type: array# we optionally make use of the `enum` property# to provide a list of allowed roles valuesitems: type: string enum: - viewer - editor - manager - admin - super-adminThe items and enum properties are here for linting purposes. It won't break evaluation if additional roles appear, but it makes our Featurevisor project self-documenting and help catch issues early.
Role specific segments#
Each segment encodes a single, reusable question: does this user have this role?
description: Users who have the super-admin roleconditions: - attribute: roles operator: includes value: super-admindescription: Users who have the admin roleconditions: - attribute: roles operator: includes value: adminAnd the same for other roles like:
manager=>segments/role-manager.ymleditor=>segments/role-editor.ymlviewer=>segments/role-viewer.yml
These segments are reusable across any number of features. Create them once, use them everywhere.
Define features#
Now we can start defining features that target those segments.
Because of how composable things are with Featurevisor, we can combine role segments with and, or, and not operators to express any access policy.
Some examples below:
Admin-only#
description: | Advanced settings panel visible only to admins and super adminstags: - webbucketBy: userIdrules: production: - key: admins segments: or: - role-admin - role-super-admin percentage: 100 - key: everyone-else segments: "*" percentage: 0Managers and above#
description: | Billing dashboard visible to managers, admins, and super adminstags: - webbucketBy: userIdrules: production: - key: billing_roles segments: or: - role-manager - role-admin - role-super-admin percentage: 100 - key: everyone-else segments: "*" percentage: 0Everyone except viewers#
description: | Bulk data export available to all roles except read-only viewerstags: - webbucketBy: userIdrules: production: - key: non-viewers segments: not: - role-viewer percentage: 100 - key: everyone-else segments: "*" percentage: 0Roles with other segments#
We can also combine role requirements with other segments. Here, a new analytics featture is being rolled out to managers in the Netherlands first:
description: | Advanced analytics gradual rollout starting with managers in the Netherlandstags: - webbucketBy: userIdrules: production: - key: nl-managers-early-access segments: and: - role-manager - netherlands # referencing segments/netherlands.yml percentage: 100 - key: everyone-else segments: "*" percentage: 0This is where Featurevisor truly shines, as RBAC and progressive delivery are not separate systems. They are composable rules in the same declarative file.
Setting context in SDK#
The backend provides the roles for the current user, and application code passes them into the SDK context in runtime:
import { createInstance } from '@featurevisor/sdk'const session = await fetch('/api/me').then((res) => res.json())const datafileContent = await fetch(DATAFILE_URL).then((res) => res.json())const f = createInstance({ context: { userId: session.userId, roles: session.roles, // e.g. ['admin', 'super-admin'] country: 'nl', }, datafile: datafileContent,})Learn more about building datafiles here.
Evaluating features#
Now we can use the SDK instance to evaluate features wherever we please:
const canSeeAdminSettings = f.isEnabled('admin-settings-panel')const canSeeBillingDashboard = f.isEnabled('billing-dashboard')const canSeeBulkExport = f.isEnabled('bulk-export')const canSeeAdvancedAnalytics = f.isEnabled('advanced-analytics')Featurevisor offers additional SDK support covering React, Go, Python, Java, Swift, and more.
Benefits by audience#
Product Managers#
Every feature's access policy is a readable file in Git:
- You can see which roles unlock which capabilities
- Changes go through Pull Requests with reviews and approvals
- No more digging through application code scattered across multiple repositories/teams why a certain feature is accessible for some users and not others
Backend Engineers#
Your API remains the authoritative source of roles for the current logged in user, and you are not losing any control.
The pattern suggested in this guide means the frontend application(s) stops hard-coding role names in its code everywhere, which was always a maintenance liability and a potential source of inconsistency with what the backend actually enforces.
The SDK reads the roles you provide, and does not invent them.
Frontend Engineers#
You write f.isEnabled('my_feature_key'), and that's it.
You become free from maintaining all the conditions and role-checking logic in your codebase, and you only ask "is this feature enabled?"
When a role requirement changes, it's a feature definition change outside of your application codebase, not resulting in any code changes or refactors scattered across multiple components.
The context object is populated once, against which all evaluations are run, and you don't have to keep passing it around for each and every evaluation which may happen from 20 different places.
The same is true for any application (including backend services) that needs to evaluate features based on roles.

