featbit

featbit-python-sdk

3
0
# Install this skill:
npx skills add featbit/featbit-skills --skill "featbit-python-sdk"

Install specific skill from multi-skill repository

# Description

Expert guidance for integrating FeatBit Python Server-Side SDK in backend applications. Use for Flask, Django, FastAPI, or any Python server application. Not for client-side use.

# SKILL.md


name: featbit-python-sdk
description: Expert guidance for integrating FeatBit Python Server-Side SDK in backend applications. Use for Flask, Django, FastAPI, or any Python server application. Not for client-side use.
appliesTo:
- "/*.py"
- "
/app.py"
- "/main.py"
- "
/server.py"
- "/views.py"
- "
/api//*.py"
- "
/backend/*/.py"


FeatBit Python Server SDK Expert

Expert knowledge for integrating FeatBit Server-Side SDK in Python applications.

Source: https://github.com/featbit/featbit-python-sdk

⚠️ Important: This is a server-side SDK for multi-user systems (web servers, APIs). Not for client-side use.

Data Synchronization

  • WebSocket Streaming (recommended) or Polling for real-time sync
  • Data stored in memory by default
  • Changes pushed to SDK in <100ms average
  • Auto-reconnects after internet outage

Installation

pip install featbit-python-sdk

Prerequisites

Quick Start

from fbclient import get, set_config
from fbclient.config import Config
from fbclient.common_types import FBUser

# Configure SDK
env_secret = 'your_env_secret'
streaming_url = 'wss://app-eval.featbit.co'
events_url = 'https://app-eval.featbit.co'

config = Config(env_secret, streaming_url, events_url)
set_config(config)

# Get client instance
client = get()

# Wait for initialization
if client.initialize():
    # Create user
    user = FBUser.new_user('user-key-123')
    user.with_name('John Doe')
    user.with_custom_property('role', 'admin')
    user.with_custom_property('age', 25)

    # Evaluate flag
    flag_value = client.variation('flag-key', user, 'default')
    print(f'Flag value: {flag_value}')

    # Close client
    client.close()
else:
    print('SDK failed to initialize')

FBUser Builder

from fbclient.common_types import FBUser

# Basic user
user = FBUser.new_user('user-123')
user.with_name('Jane Doe')

# User with custom properties
user = (FBUser.new_user('user-456')
    .with_name('Bob Wilson')
    .with_custom_property('role', 'admin')
    .with_custom_property('country', 'US')
    .with_custom_property('subscription', 'premium')
    .with_custom_property('age', 30))

Flask Integration

from flask import Flask, request, jsonify, g
from fbclient import get, set_config
from fbclient.config import Config
from fbclient.common_types import FBUser

app = Flask(__name__)

# Initialize FeatBit
env_secret = 'your_env_secret'
config = Config(env_secret, 'wss://app-eval.featbit.co', 'https://app-eval.featbit.co')
set_config(config)
fb_client = get()

# Wait for initialization
@app.before_first_request
def initialize_sdk():
    if not fb_client.initialize():
        app.logger.error('FeatBit SDK failed to initialize')

# Middleware to create user context
@app.before_request
def create_user_context():
    user_id = request.headers.get('X-User-Id') or 'anonymous'
    g.fb_user = (FBUser.new_user(user_id)
        .with_name(request.headers.get('X-User-Name'))
        .with_custom_property('role', request.headers.get('X-User-Role')))

# Route with feature flag
@app.route('/api/feature')
def feature_endpoint():
    is_enabled = fb_client.variation('new-feature', g.fb_user, False)

    if not is_enabled:
        return jsonify({'error': 'Feature not available'}), 403

    return jsonify({'data': 'feature data'})

# Gradual rollout example
@app.route('/api/checkout', methods=['POST'])
def checkout():
    use_new_checkout = fb_client.variation('new-checkout-flow', g.fb_user, False)

    if use_new_checkout:
        result = process_new_checkout(request.json)
    else:
        result = process_old_checkout(request.json)

    return jsonify(result)

# A/B testing example
@app.route('/api/purchase', methods=['POST'])
def purchase():
    variant = fb_client.variation('pricing-strategy', g.fb_user, 'standard')

    price = calculate_price(request.json, variant)
    result = process_purchase(price)

    if result['success']:
        # Track conversion
        fb_client.track_metric(g.fb_user, 'purchase-completed', price)

    return jsonify(result)

# Graceful shutdown
@app.teardown_appcontext
def shutdown_sdk(exception=None):
    fb_client.close()

if __name__ == '__main__':
    app.run(debug=True)

Django Integration

# settings.py
FEATBIT_CONFIG = {
    'env_secret': 'your_env_secret',
    'streaming_url': 'wss://app-eval.featbit.co',
    'events_url': 'https://app-eval.featbit.co'
}

# apps.py
from django.apps import AppConfig
from fbclient import get, set_config
from fbclient.config import Config

class MyAppConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'myapp'

    def ready(self):
        from django.conf import settings

        config = Config(
            settings.FEATBIT_CONFIG['env_secret'],
            settings.FEATBIT_CONFIG['streaming_url'],
            settings.FEATBIT_CONFIG['events_url']
        )
        set_config(config)

        fb_client = get()
        if not fb_client.initialize():
            print('FeatBit SDK failed to initialize')

# middleware.py
from fbclient import get
from fbclient.common_types import FBUser

class FeatBitMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
        self.fb_client = get()

    def __call__(self, request):
        # Create user context
        user_id = request.user.id if request.user.is_authenticated else 'anonymous'
        request.fb_user = (FBUser.new_user(str(user_id))
            .with_name(request.user.username if request.user.is_authenticated else 'Anonymous')
            .with_custom_property('is_staff', request.user.is_staff if request.user.is_authenticated else False))

        response = self.get_response(request)
        return response

# views.py
from django.http import JsonResponse
from fbclient import get

def feature_view(request):
    fb_client = get()
    is_enabled = fb_client.variation('new-feature', request.fb_user, False)

    if not is_enabled:
        return JsonResponse({'error': 'Feature not available'}, status=403)

    return JsonResponse({'data': 'feature data'})

# Add middleware to settings.py
MIDDLEWARE = [
    # ... other middleware
    'myapp.middleware.FeatBitMiddleware',
]

FastAPI Integration

from fastapi import FastAPI, Request, HTTPException, Depends
from fbclient import get, set_config
from fbclient.config import Config
from fbclient.common_types import FBUser

app = FastAPI()

# Initialize FeatBit
@app.on_event("startup")
async def startup_event():
    env_secret = 'your_env_secret'
    config = Config(env_secret, 'wss://app-eval.featbit.co', 'https://app-eval.featbit.co')
    set_config(config)

    fb_client = get()
    if not fb_client.initialize():
        raise RuntimeError('FeatBit SDK failed to initialize')

@app.on_event("shutdown")
async def shutdown_event():
    fb_client = get()
    fb_client.close()

# Dependency to get user context
def get_fb_user(request: Request) -> FBUser:
    user_id = request.headers.get('x-user-id', 'anonymous')
    user = (FBUser.new_user(user_id)
        .with_name(request.headers.get('x-user-name', 'Anonymous'))
        .with_custom_property('role', request.headers.get('x-user-role', 'user')))
    return user

# Route with feature flag
@app.get("/api/feature")
async def feature_endpoint(fb_user: FBUser = Depends(get_fb_user)):
    fb_client = get()
    is_enabled = fb_client.variation('new-feature', fb_user, False)

    if not is_enabled:
        raise HTTPException(status_code=403, detail='Feature not available')

    return {'data': 'feature data'}

# A/B testing example
@app.post("/api/recommendations")
async def get_recommendations(fb_user: FBUser = Depends(get_fb_user)):
    fb_client = get()
    algorithm = fb_client.variation('recommendation-algorithm', fb_user, 'default')

    recommendations = generate_recommendations(algorithm)

    # Track which algorithm was used
    fb_client.track_metric(fb_user, f'algorithm-{algorithm}')

    return {'recommendations': recommendations, 'algorithm': algorithm}

Flag Evaluation

Boolean Variation

is_enabled = client.variation('flag-key', user, False)

String Variation

theme = client.variation('theme', user, 'default')

Number Variation

max_retries = client.variation('max-retries', user, 3)

JSON Variation

import json

config_json = client.variation('config', user, '{}')
config = json.loads(config_json)

With Evaluation Detail

detail = client.variation_detail('flag-key', user, 'default')
print(f'Value: {detail.value}')
print(f'Reason: {detail.reason}')
print(f'Kind: {detail.kind}')

Get All Flags

all_flags = client.get_all_latest_flag_variations(user)
print(all_flags)

Event Tracking

# Simple event
client.track_metric(user, 'button-clicked')

# Event with numeric value
client.track_metric(user, 'purchase-completed', 99.99)

# For A/B testing
client.track_metric(user, 'conversion-event')

# Flush events manually
client.flush()

Configuration Options

from fbclient.config import Config

config = Config(
    env_secret='your_env_secret',
    event_url='https://app-eval.featbit.co',
    streaming_url='wss://app-eval.featbit.co',

    # Optional settings
    start_wait=5,              # Wait time for initialization in seconds
    max_retries=3,             # Max retry attempts
    use_streaming=True,        # Use WebSocket streaming (recommended)
    offline=False              # Set to True for offline mode
)

Offline Mode

from fbclient.config import Config
import json

# Enable offline mode
config = Config(
    env_secret='your_env_secret',
    offline=True
)

# Load flags from file
with open('feature_flags.json', 'r') as f:
    flags_data = json.load(f)

# Use flags locally
# Note: In offline mode, flags won't update from server

Best Practices

1. Single Client Instance

# ✅ Good: Create once at app startup
from fbclient import get, set_config

config = Config(env_secret, streaming_url, events_url)
set_config(config)
fb_client = get()  # Reuse this instance

# ❌ Bad: Creating per request
def handle_request():
    client = get()  # Don't do this repeatedly without proper setup

2. Wait for Initialization

# ✅ Good: Wait before serving traffic
fb_client = get()
if not fb_client.initialize():
    raise RuntimeError('SDK initialization failed')

# Start server after initialization
app.run()

# ❌ Bad: Start immediately
fb_client = get()
app.run()  # Flags may return defaults

3. Graceful Shutdown

import atexit

def cleanup():
    fb_client = get()
    fb_client.close()  # Flush events

atexit.register(cleanup)

4. Error Handling

try:
    is_enabled = fb_client.variation('flag-key', user, False)
    # Use feature
except Exception as e:
    print(f'Flag evaluation failed: {e}')
    # Fall back to default
    is_enabled = False

5. User Context

# ✅ Good: Use stable identifiers
user_id = current_user.id  # From auth system
user = FBUser.new_user(str(user_id))

# ✅ Good: Add relevant properties
user = (FBUser.new_user(str(user_id))
    .with_name(current_user.name)
    .with_custom_property('role', current_user.role)
    .with_custom_property('plan', current_user.subscription))

# ❌ Bad: Random IDs
import random
user = FBUser.new_user(str(random.random()))  # Don't do this

Common Use Cases

Progressive Rollout

@app.route('/api/new-feature')
def new_feature():
    use_new_version = fb_client.variation('new-api-version', g.fb_user, False)

    if use_new_version:
        return jsonify(new_version_handler())
    else:
        return jsonify(old_version_handler())

A/B Testing

@app.route('/api/recommendations')
def recommendations():
    algorithm = fb_client.variation('recommendation-algorithm', g.fb_user, 'default')

    recommendations = get_recommendations(g.fb_user, algorithm)

    # Track which algorithm was used
    fb_client.track_metric(g.fb_user, f'algorithm-{algorithm}')

    return jsonify(recommendations)

Remote Configuration

import json

config_json = fb_client.variation('api-config', user, json.dumps({
    'timeout': 30,
    'retries': 3,
    'rate_limit': 1000
}))

config = json.loads(config_json)

# Use configuration
timeout = config['timeout']
retries = config['retries']

Feature Toggle

@app.before_request
def check_maintenance():
    system_user = FBUser.new_user('system')
    maintenance_mode = fb_client.variation('maintenance-mode', system_user, False)

    if maintenance_mode and request.path != '/health':
        return jsonify({'error': 'Service temporarily unavailable'}), 503

Threading and Async

Threading Support

import threading
from fbclient import get

def worker_thread():
    fb_client = get()  # Thread-safe
    user = FBUser.new_user('worker-1')

    flag_value = fb_client.variation('worker-feature', user, False)
    print(f'Worker flag: {flag_value}')

thread = threading.Thread(target=worker_thread)
thread.start()

Async/Await (Python 3.7+)

import asyncio
from fbclient import get

async def async_handler():
    fb_client = get()
    user = FBUser.new_user('async-user')

    # Note: SDK operations are synchronous
    # Run in executor if needed
    loop = asyncio.get_event_loop()
    flag_value = await loop.run_in_executor(
        None,
        fb_client.variation,
        'async-feature',
        user,
        False
    )

    return flag_value

Troubleshooting

SDK Not Initializing

fb_client = get()
if not fb_client.initialize():
    # Check:
    # 1. env_secret is correct
    # 2. Network connectivity
    # 3. Firewall rules for WebSocket
    print('SDK initialization failed')

Connection Issues

# Increase timeout
config = Config(
    env_secret=env_secret,
    streaming_url=streaming_url,
    events_url=events_url,
    start_wait=10  # 10 seconds
)

Events Not Sent

import atexit

def flush_events():
    fb_client = get()
    fb_client.flush()

# Register cleanup
atexit.register(flush_events)

Performance Optimization

Caching User Context

from functools import lru_cache

@lru_cache(maxsize=1000)
def get_cached_user(user_id):
    return FBUser.new_user(user_id)

Batch Operations

def get_feature_config(user):
    fb_client = get()

    return {
        'feature1': fb_client.variation('feature-1', user, False),
        'feature2': fb_client.variation('feature-2', user, False),
        'feature3': fb_client.variation('feature-3', user, 'default')
    }

Testing

# test_features.py
import unittest
from fbclient import get, set_config
from fbclient.config import Config
from fbclient.common_types import FBUser

class TestFeatureFlags(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        # Use test environment
        config = Config(
            'test-env-secret',
            'wss://test-eval.featbit.co',
            'https://test-eval.featbit.co'
        )
        set_config(config)
        cls.fb_client = get()
        cls.fb_client.initialize()

    def test_feature_enabled(self):
        user = FBUser.new_user('test-user')
        is_enabled = self.fb_client.variation('test-feature', user, False)
        self.assertIsInstance(is_enabled, bool)

    @classmethod
    def tearDownClass(cls):
        cls.fb_client.close()

Additional Resources

  • GitHub: https://github.com/featbit/featbit-python-sdk
  • PyPI: https://pypi.org/project/featbit-python-sdk/
  • Examples: https://github.com/featbit/featbit-samples
  • Documentation: https://docs.featbit.co/sdk-docs/server-side-sdks/python

# Supported AI Coding Agents

This skill is compatible with the SKILL.md standard and works with all major AI coding agents:

Learn more about the SKILL.md standard and how to use these skills with your preferred AI coding agent.