Error handling
Effective error handling is crucial when implementing feature gating in your app. Proper error management ensures merchants understand why they can't access certain features and provides clear paths to resolve these limitations.
Common error scenarios
When implementing feature controls, you may want to anticipate and handle these types of error scenarios in your app:
These examples are not explicit errors returned from Mantle's API. They are common cases you may encounter based on your own app's business logic or implementation. Your code should handle them appropriately based on your use case.
- Feature not available - The feature isn't included in the merchant's current plan
- Usage limit reached - The merchant has hit the maximum allowed usage for a feature
- API errors - Issues connecting to Mantle's API or retrieving feature status
- Invalid feature keys - Attempting to check features that don't exist
Implementing error handling
Try-catch pattern
Use try-catch blocks to handle feature-related errors:
try {
await createProduct(data);
} catch (error) {
if (error.message.includes('not available')) {
// Feature not on plan - show upgrade prompt
showUpgradeModal('products');
} else if (error.message.includes('limit reached')) {
// Usage limit hit - show limit reached message
showLimitReachedMessage('products');
} else {
// Handle other errors
handleError(error);
}
}Error classification
Create helper functions to classify and handle different error types:
function isFeatureNotAvailableError(error) {
return error.message.includes('not available') ||
error.message.includes('not enabled');
}
function isLimitReachedError(error) {
return error.message.includes('limit reached') ||
error.message.includes('exceeded');
}
function handleFeatureError(error, featureKey) {
if (isFeatureNotAvailableError(error)) {
return showUpgradeModal(featureKey);
}
if (isLimitReachedError(error)) {
return showLimitReachedMessage(featureKey);
}
// Default error handling
logError(error);
showGenericErrorMessage();
}Custom error types
Define custom error classes for better error handling:
class FeatureNotAvailableError extends Error {
constructor(featureKey) {
super(`Feature '${featureKey}' is not available on your current plan`);
this.name = 'FeatureNotAvailableError';
this.featureKey = featureKey;
}
}
class FeatureLimitReachedError extends Error {
constructor(featureKey, limit, used) {
super(`Limit reached for feature '${featureKey}' (${used}/${limit})`);
this.name = 'FeatureLimitReachedError';
this.featureKey = featureKey;
this.limit = limit;
this.used = used;
}
}
// Using custom errors
async function createProduct(productData) {
const hasFeature = await mantleClient.isFeatureEnabled({
featureKey: 'products'
});
if (!hasFeature) {
throw new FeatureNotAvailableError('products');
}
const limit = await mantleClient.limitForFeature({
featureKey: 'product_limit'
});
const { customer } = await mantleClient.getCustomer();
const used = customer.usage['Product limit']?.currentValue || 0;
if (used >= limit) {
throw new FeatureLimitReachedError('product_limit', limit, used);
}
// Proceed with product creation
}Error response patterns
Frontend error handling
Present user-friendly error messages in your UI:
import { useMantle } from '@heymantle/react';
function ProductCreationForm() {
const [error, setError] = useState(null);
const { plans } = useMantle();
const handleSubmit = async (data) => {
try {
setError(null);
await createProduct(data);
showSuccess('Product created successfully');
} catch (error) {
if (error instanceof FeatureNotAvailableError) {
setError({
type: 'upgrade',
message: 'This feature requires a plan upgrade.',
feature: error.featureKey,
plans: getPlansWithFeature(plans, error.featureKey)
});
} else if (error instanceof FeatureLimitReachedError) {
setError({
type: 'limit',
message: `You've reached your limit of ${error.limit} products.`,
feature: error.featureKey,
used: error.used,
limit: error.limit
});
} else {
setError({
type: 'generic',
message: 'An error occurred. Please try again.'
});
}
}
};
return (
<div>
{error && <ErrorBanner error={error} />}
<ProductForm onSubmit={handleSubmit} />
</div>
);
}API endpoint error responses
Return informative error responses from your API:
app.post('/api/products', async (req, res) => {
try {
const product = await createProduct(req.body);
res.json({ success: true, product });
} catch (error) {
if (error instanceof FeatureNotAvailableError) {
return res.status(403).json({
code: 'FEATURE_NOT_AVAILABLE',
message: error.message,
feature: error.featureKey,
resolution: 'Upgrade your plan to access this feature'
});
} else if (error instanceof FeatureLimitReachedError) {
return res.status(403).json({
code: 'FEATURE_LIMIT_REACHED',
message: error.message,
feature: error.featureKey,
limit: error.limit,
used: error.used,
resolution: 'Upgrade your plan for a higher limit'
});
} else {
return res.status(500).json({
code: 'INTERNAL_ERROR',
message: 'An unexpected error occurred'
});
}
}
});Error recovery strategies
Graceful degradation
Provide alternative functionality when premium features aren't available:
function AnalyticsReport() {
const { isFeatureEnabled } = useMantle();
const hasAdvancedAnalytics = isFeatureEnabled({
featureKey: 'advanced_analytics'
});
return (
<div>
<h2>Analytics Report</h2>
{hasAdvancedAnalytics ? (
<AdvancedAnalyticsReport />
) : (
<>
<BasicAnalyticsReport />
<Banner status="info">
Upgrade to access advanced analytics with deeper insights.
<Button onClick={showUpgradeModal}>View Plans</Button>
</Banner>
</>
)}
</div>
);
}Retry logic
Implement retries for transient API errors:
async function checkFeatureWithRetry(featureKey, maxRetries = 3) {
let retries = 0;
while (retries < maxRetries) {
try {
return await mantleClient.isFeatureEnabled({ featureKey });
} catch (error) {
if (error.message.includes('network') && retries < maxRetries - 1) {
// Wait before retrying (with exponential backoff)
await new Promise(r => setTimeout(r, 1000 * Math.pow(2, retries)));
retries++;
} else {
throw error;
}
}
}
}Best practices
- Be specific - Provide clear, actionable error messages
- Offer solutions - Show upgrade paths or alternatives when features aren't available
- Gracefully degrade - Provide limited functionality instead of complete failure when possible
- Log errors - Track feature-related errors to identify patterns and issues
- Handle edge cases - Account for network issues and API failures
- Keep error handling consistent - Use similar patterns throughout your application
Next steps
Updated over 1 year ago