Use cases
User entitlements
As your application grows in number of features, it can lead you to offer your services via different plans to your users. Where each plan can come with its own set of entitlements (activities that users are allowed to perform, aka permissions).
Your application
Imagine you own a social media application, where you offer your users the ability to create posts, like posts, and comment on posts.
Users can sign up for free and start liking and commenting on others' posts. But to be able to create new posts themselves, they have to buy a premium plan.
Mapping entitlements against plans
We can map out the entitlements of your users (what they can do) against the different plans you intend to offer as follows:
Entitlement | Free Plan | Premium Plan (paid) |
---|---|---|
Like Posts | ✅ | ✅ |
Comment on Posts | ✅ | ✅ |
Create Posts | ❌ | ✅ |
User Profile service
For the sake of this guide, let's assume you already have a User Profile service, that allows your application to know in the runtime what plan the currently logged in user is on.
Response of the said User Profile service can be like this:
// GET /profile
{
"id": "<UUID-here>",
"name": "Erlich Bachman",
"plan": "premium", // or `free`
"country": "us"
}
Attributes
Let's start defining our Featurevisor attributes for your application.
We will use them throughout this guide at various stages.
userId
This attribute will be used to identify the user in the runtime. The id
field from the response of the User Profile service will be used for this purpose.
# attributes/userId.yml
description: User ID
type: string
capture: true
Marked as capture: true
for convenience of tracking experiment activations, which we don't need for this guide.
country
This attribute will be used to identify the country of the user in the runtime. The country
field from the response of the User Profile service will be used for this purpose.
# attributes/country.yml
description: Country codes in lowercase like us, nl, de, etc.
type: string
Feature
We will be creating a new feature called plan
that will be used to control the entitlements of your users against various different plans.
# features/plan.yml
description: Plans and their entitlements against known User
tags:
- all
bucketBy: userId
# we define a variable called `entitlements`,
# that will be an array of strings
variablesSchema:
- key: entitlements
type: array
defaultValue:
- likePosts
- commentOnPosts
# we aren't running an experiment here,
# and will rely on sticky features for users,
# therefore weight distribution of variations are not relevant
variations:
- value: free
weight: 100
- value: premium
weight: 0
variables:
- key: entitlements
value:
- likePosts
- commentOnPosts
- createPosts # extra entitlement for premium users only
# this is a core application config,
# and is recommended to be rolled out to 100% of the traffic
environments:
production:
rules:
- key: "1"
segments: "*"
percentage: 100
Evaluating entitlements with SDKs
Now that we have defined our feature, we can use Featurevisor SDKs to evaluate the entitlements of your users in the runtime.
First, initialize the SDK:
import { createInstance } from "@featurevisor/sdk";
const f = createInstance({
datafileUrl: "https://cdn.yoursite.com/datafile.json",
});
Fetch your User's ID and Plan info from your User Profile service and make it available:
const userProfile = await fetch("https://api.yoursite.com/profile")
.then((res) => res.json());
Set sticky features in the SDK for known user:
// we want our known user to be always bucketed
// into the same plan (variation) as User Profile service suggests
f.setStickyFeatures({
plan: {
enabled: true,
variation: userProfile.plan,
},
});
Get available entitlements for the known user:
const featureKey = "plan";
const variableKey = "entitlements";
const context = {
userId: userProfile.id,
country: userProfile.country,
};
const entitlements = f.getVariable(featureKey, variableKey, context);
The entitlements
variable will contain an array of all entitlements the user should have against their current plan.
const canCreatePosts = entitlements.includes("createPosts");
const canLikePosts = entitlements.includes("likePosts");
const canCommentOnPosts = entitlements.includes("commentOnPosts");
Managing entitlements in one place
As your entitlements and number of plans grow, you can use Featurevisor to manage them all declaratively in one place.
Your custom User Profile service only needs to be aware of the plan of the user, and nothing more unless you have any custom user specific overrides.
Since Featurevisor JavaScript SDK is universal and works in both Node.js and browser environments, you can use it to evaluate your users' entitlements in your backend as well as in frontend.
Always verify in backend
Please note that entitlements check in frontend is never a substitute for backend checks. You should always check entitlements in your backend before performing any action.
User overrides
It is possible in specific circumstances, that you may want to override the entitlements of a user irrespective of what plan they are on.
For example, if a user is on a free plan, but you want to give them access to create posts for free for a limited time.
We can expect our User Profile service to optionally provide the override information given this is about a specific individual user:
// GET /profile
{
"id": "<UUID-here>",
"plan": "free",
"country": "us",
// optional field for overrides
"overrideEntitlements": [
"likePosts",
"commentOnPosts",
"createPosts"
]
}
We can then use the overrideEntitlements
field from User Profile and set it as a sticky feature in Featurevisor SDK:
f.setStickyFeatures({
plan: {
enabled: true,
variation: userProfile.plan,
variables: userProfile.overrideEntitlements
// user overrides
? { entitlements: userProfile.overrideEntitlements }
// otherwise leave empty
: {},
},
});
You can now continue evaluating entitlements as before using the SDK, and the user will have the overridden entitlements.
Conditional entitlements
It is possible that you may want to offer a specific entitlement to your users based on their location. We aren't talking about running experiments here targeting one specific country, but more like an entitlement that can only ever be available in one single country only.
For the sake of this guide, let's assume your social media app can legally allow your users to upload videos in the US only in premium
plan, and nowhere else.
We can declare that config in our feature's definition as follows:
# features/plan.yml
# ...
variations:
- value: free
weight: 100
- value: premium
weight: 0
variables:
- key: entitlements
value:
- likePosts
- commentOnPosts
- createPosts
overrides:
- conditions:
- attribute: country
operator: equals
value: us
value:
- likePosts
- commentOnPosts
- createPosts
- uploadVideos # for US users only
# ...
The entitlements array may look repetitive here, but you can also take an approach of breaking down your entitlements into multiple variables instead of one as you see fit.
Separate variables per entitlement
If you do not wish to have a single variable for all entitlements, you can break them down into multiple variables as follows:
# features/plan.yml
description: Plans and their entitlements against known User
tags:
- all
bucketBy: userId
variablesSchema:
- key: canLikePosts
type: boolean
defaultValue: true
- key: canCommentOnPosts
type: boolean
defaultValue: true
- key: canCreatePosts
type: boolean
defaultValue: false
- key: canUploadVideos
type: boolean
defaultValue: false
variations:
- value: free
weight: 100
- value: premium
weight: 0
variables:
- key: canCreatePosts
value: true
- key: canUploadVideos
value: false
overrides:
- conditions:
- attribute: country
operator: equals
value: us
value: true
environments:
production:
rules:
- key: "1"
segments: "*"
percentage: 100
This will then require you to evaluate each entitlement separately in your application code using Featurevisor SDKs:
const canCreatePosts = f.getVariable(
"plan",
"canCreatePosts",
context
);
if (canCreatePosts) {
// show create post button
}
Conclusion
When your application and its architecture grows big, and you have multiple teams working and shipping in a distributed fashion, it can become hard to manage entitlements in one place.
Having them declared in one place as a single source of truth can help you manage them better, and also help you avoid any accidental entitlements leaks.