Entitlements
Entitlements are the features and usage limits that a user has access to based on subscription plan.
I've created a simple and flexible way to manage entitlements based on the subscription plan that a user has.
Types of Entitlements
There are two types of entitlements that you can manage:
- Features: Features are the functionalities that a user has access to. For example, a user with a PRO subscription plan might have access to advanced features.
- Usage Limits: Usage limits are the maximum number of times a user can perform an action. For example, a user with a FREE subscription plan might have a limit of 100 requests per day.
Managing Entitlements
There are many different ways of managing entitlements, but I've found that the best way is to use a simple config file to define the entitlements for each subscription plan.
Some developers prefer to use a database to store entitlements, this approach is not recommended as it makes it harder to make changes to the entitlements in case of a change in the subscription plans.
Config File
Inside the packages/utils/src/constants/entitlements.ts
file, you can define the entitlements for each subscription plan. Here's an example of how you can define the entitlements:
In this example, we have defined the entitlements for the BASIC
, PRO
, and PREMIUM
subscription plans.
Each subscription plan has a set of features and usage limits that the user has access to.
Modify this file to define the entitlements for each subscription plan based on your requirements.
Usage
You can use the ensureFeatureAccess
and ensureUsageWithinLimit
helper functions to check if a user has access to a specific feature or has reached a usage limit based on subscription plan.
Do this in a server-side tRPC procedure/api endpoint to control access to features and usage limits.
Checking Feature Access
The ensureFeatureAccess
function checks if the user has access to a specific feature based on the subscription plan.
In the below example we are checking if the user has access to the analytics
feature.
- If the user has access to the
analytics
feature, we allow the user to access the feature. - If the user doesn't have access to the
analytics
feature, we throw an error.
Example usage in a tRPC procedure (packages/api
):
The canAccessFeature function takes in an object with the following properties:
subscriptionData
: The user's subscription data, which contains details about the subscription.featureId
: The ID of the feature that you want to check access for.notSubscribedErrorMessage
(optional): The custom error message to display if the user is not subscribed.noAccessErrorMessage
(optional): The custom error message to display if the user doesn't have access to the feature.
Checking Usage Limits
The ensureUsageWithinLimit
function checks if the user has reached the usage limit for a specific action.
In the below example, we are checking if the user has reached the limit for the clicks
action.
- If the user hasn't reached the limit for the
clicks
action, we allow the user to perform the action. - If the user has reached the limit for the
clicks
action, we throw an error.
Example usage in a tRPC procedure (packages/api
):
The isUsageWithinLimit function takes in an object with the following properties:
subscriptionData
: The user's subscription data, which contains details about the subscription.featureId
: The ID of the feature for which you want to check the usage limit.usageCount
: The current number of times the user has performed the action.usageIncrement
: How much the usage count should be incremented by after the performed action. Note: This is not going to actually update the database, it is just for the check.notSubscribedErrorMessage
(optional): The custom error message to display if the user is not subscribed.noAccessErrorMessage
(optional): The custom error message to display if the user doesn't have access to the feature.
Benefits
The benefits of this approach are that we can easily make changes to the entitlements based on the subscription plan. All we need to do is change one configuration file and the changes will be reflected across the application.
This allows you to easily experiment with different entitlements and subscription plans without major changes to the codebase. It also makes adjustments very simple.
Last updated on