Skip to main content

Server Side SDKs

tip

Server Side SDKs can run in 2 different modes: Local Evaluation and Remote Evaluation. We recommend reading up about the differences first before integrating the SDKS into your applications.

SDK Overview

Add the Flagsmith package

pip install flagsmith

Initialise the SDK

tip

Server-side SDKs must be initialised with Server-side Environment keys. These can be created in the Environment settings area and should be considered secret.

from flagsmith import Flagsmith

flagsmith = Flagsmith(
environment_key = "<FLAGSMITH_SERVER_SIDE_ENVIRONMENT_KEY>"
)

Get Flags for an Environment

# The method below triggers a network request
flags = flagsmith.get_environment_flags()
show_button = flags.is_feature_enabled("secret_button")
button_data = json.loads(flags.get_feature_value("secret_button"))

Get Flags for an Identity

identifier = "delboy@trotterstraders.co.uk"
traits = {"car_type": "robin_reliant"}

# The method below triggers a network request
identity_flags = flagsmith.get_identity_flags(identifier=identifier, traits=traits)
show_button = identity_flags.is_feature_enabled("secret_button")
button_data = json.loads(identity_flags.get_feature_value("secret_button"))

When running in Remote Evaluation mode

  • When requesting Flags for an Identity, all the Traits defined in the SDK will automatically be persisted against the Identity within the Flagsmith API.
  • Traits passed to the SDK will be added to all the other previously persisted Traits associated with that Identity.
  • This full set of Traits are then used to evaluate the Flag values for the Identity.
  • This all happens in a single request/response.

When running in Local Evaluation mode

  • Only the Traits provided to the SDK at runtime will be used. Local Evaluation mode, by design, does not make any network requests to the Flagsmith API when evaluating Flags for an Identity.
    • When running in Local Evaluation Mode, the SDK requests the Environment Document from the Flagsmith API. This contains all the information required to make Flag Evaluations, but it does not contain any Trait data.

Managing Default Flags

Default Flags are configured by passing in a function that is called when a Flag cannot be found or if the network request to the API fails when retrieving flags.

from flagsmith import Flagsmith
from flagsmith.models import DefaultFlag

def default_flag_handler(feature_name: str) -> DefaultFlag:
"""
Function that will be used if the API doesn't respond, or an unknown
feature is requested
"""
if feature_name == "secret_button":
return DefaultFlag(
enabled=False,
value=json.dumps({"colour": "#b8b8b8"}),
feature_name="secret_button",
)
],
return DefaultFlag(False, None)

flagsmith = Flagsmith(
environment_key="<FLAGSMITH_SERVER_SIDE_ENVIRONMENT_KEY>",
default_flag_handler=default_flag_handler,
)

Using an Offline Handler

info

Offline handlers are still in active development. We are building them for all our SDKs; those that are production ready are listed below.

Progress on the remaining SDKs can be seen here.

Flagsmith SDKs can be configured to include an offline handler which has 2 functions:

  1. It can be used alongside Offline Mode to evaluate flags in environments with no network access
  2. It can be used as a means of defining the behaviour for evaluating default flags, when something goes wrong with the regular evaluation process. To do this, simply set the offline handler initialisation parameter without enabling offline mode.

To use it as a default handler, we recommend using the flagsmith CLI to generate the environment document and use our LocalFileHandler class, but you can also create your own offline handlers, by extending the base class.

# Using the built-in local file handler

local_file_handler = LocalFileHandler(environment_document_path="/app/environment.json")
flagsmith = Flagsmith(..., offline_handler=local_file_handler)

# Defining a custom offline handler

class MyCustomOfflineHandler(BaseOfflineHandler):
def get_environment(self) -> EnvironmentModel:
return some_function_to_get_the_environment()

Network Behaviour

The Server Side SDKS share the same network behaviour across the different languages:

Remote Evaluation Mode Network Behaviour

  • A blocking network request is made every time you make a call to get an Environment Flags. In Python, for example, flagsmith.get_environment_flags() will trigger this request.
  • A blocking network request is made every time you make a call to get an Identities Flags. In Python, for example, flagsmith.get_identity_flags(identifier=identifier, traits=traits) will trigger this request.

Local Evaluation Mode Network Behaviour

info

When using Local Evaluation, it's important to read up on the Pros, Cons and Caveats.

To use Local Evaluation mode, you must use a Server Side key.

  • When the SDK is initialised, it will make an asynchronous network request to retrieve details about the Environment.
  • Every 60 seconds (by default), it will repeat this aysnchronous request to ensure that the Environment information it has is up to date.

To achieve Local Evaluation, in most languages, the SDK spawns a separate thread (or equivalent) to poll the API for changes to the Environment. In certain languages, you may be required to terminate this thread before cleaning up the instance of the Flagsmith client. Languages in which this is necessary are provided below.

// available from v5.0.5
flagsmith.close();

Offline Mode

info

Offline mode is still in active development for some SDKs. We are building it for all our SDKs; those that are production ready are listed below.

Progress on the remaining SDKs can be seen here.

To run the SDK in a fully offline mode, you can set the client to offline mode. This will prevent the SDK from making any calls to the Flagsmith API. To use offline mode, you must also provide an offline handler. See Configuring the SDK for more details on initialising the SDK in offline mode.

Configuring the SDK

You can modify the behaviour of the SDK during initialisation. Full configuration options are shown below.

flagsmith = Flagsmith(
# Your API Token.
# Note that this is either the `Environment API` key or the `Server Side SDK Token`
# depending on if you are using Local or Remote Evaluation
# Required.
environment_key = "<FLAGSMITH_SERVER_SIDE_ENVIRONMENT_KEY>",

# Controls which mode to run in; local or remote evaluation.
# See the `SDKs Overview Page` for more info
# Optional.
# Defaults to False.
enable_local_evaluation = False,

# Override the default Flagsmith API URL if you are self-hosting.
# Optional.
# Defaults to https://edge.api.flagsmith.com/api/v1/
api_url = "https://api.yourselfhostedflagsmith.com/api/v1/",

# The network timeout in seconds.
# Optional.
# Defaults to 10 seconds
request_timeout_seconds = 10,

# When running in local evaluation mode, defines
# how often to request an updated Environment document in seconds
# Optional
# Defaults to 60 seconds
environment_refresh_interval_seconds: int = 60,

# A `urllib3` Retries object to control network retry policy
# See https://urllib3.readthedocs.io/en/latest/reference/urllib3.util.html#urllib3.util.Retry
# Optional
# Defaults to None
retries: Retry = None,

# Controls whether Flag Analytics data is sent to the Flagsmith API
# See https://docs.flagsmith.com/advanced-use/flag-analytics
# Optional
# Defaults to False
enable_analytics: bool = False,

# You can pass custom headers to the Flagsmith API with this Dictionary.
# This can be helpful, for example, when sending request IDs to help trace requests.
# Optional
# Defaults to None
custom_headers: typing.Dict[str, typing.Any] = None,

# You can specify a function to handle returning defaults in the case that
# the request to flagsmith fails or the flag requested is not included in the
# response
# Optional
default_flag_handler = lambda feature_name: return DefaultFlag(enabled=False, value=None),

# (Available in 3.2.0+) Pass a mapping of protocol to proxy URL as per
# https://requests.readthedocs.io/en/latest/api/#requests.Session.proxies
# Optional
proxies: typing.Dict[str, str] = None,

# (Available in 3.4.0+) Set the SDK into offline mode.
# Optional
# Defaults to False
offline_mode: bool = False,

# (Available in 3.4.0+) Provide an offline handler to use with offline mode, or
# as a means of returning default flags.
# Optional
# Defaults to None
offline_handler: BaseOfflineHander = None,
)

Caching

The following SDKs have code and functionality related to caching flags.

If you would like to use in-memory caching, you will need to enable it (it is disabled by default). The main advantage of using in-memory caching is that you can reduce the number of HTTP calls performed to fetch flags.

Flagsmith uses Caffeine, a high performance, near optimal caching library.

If you enable caching on the Flagsmith client without setting any values (as shown below), the following default values will be set for you:

  • maxSize(10)
  • expireAfterWrite(5, TimeUnit.MINUTES)
  • project level caching will be disabled by default (i.e. only enabled if you configure a caching key)
// use in-memory caching with Flagsmith defaults as described above
final FlagsmithClient flagsmithClient = FlagsmithClient.newBuilder()
.setApiKey("YOUR_ENV_API_KEY")
.withConfiguration(FlagsmithConfig
.newBuilder()
.baseURI("http://yoururl.com")
.build())
.withCache(FlagsmithCacheConfig
.newBuilder()
.build())
.build();

If you would like to change the default settings, you can overwrite them by using the available builder methods:

// use in-memory caching with custom configuration
final FlagsmithClient flagsmithClient = FlagsmithClient.newBuilder()
.setApiKey("YOUR_ENV_API_KEY")
.withConfiguration(FlagsmithConfig
.newBuilder()
.baseURI("http://yoururl.com")
.build())
.withCache(FlagsmithCacheConfig
.newBuilder()
.maxSize(100)
.expireAfterWrite(10, TimeUnit.MINUTES)
.recordStats()
.enableEnvLevelCaching("some-key-to-avoid-clashing-with-user-identifiers")
.build())
.build();

The user identifier is used as the cache key, this provides granular control over the cache should you require it. If you would like to manipulate the cache:

// this will return null if caching is disabled
final FlagsmithCache cache = flagsmithClient.getCache();
// you can now discard a single or all entries in the cache
cache.invalidate("user-identifier");
// or
cache.invalidateAll();
// get stats (if you have enabled them in the cache configuration, otherwise all values will be zero)
final CacheStats stats = cache.stats();
// check if flags for a user identifier are cached
final FlagsAndTraits flags = cache.getIfPresent("user-identifier");

Since the user identifier is used as the cache key, you need to configure a cache key to enable project level caching. Make sure you select a project level cache key that will never be a user identifier.

// use in-memory caching with Flagsmith defaults and project level caching enabled
final String projectLevelCacheKey = "some-key-to-avoid-clashing-with-user-identifiers";
final FlagsmithClient flagsmithClient = FlagsmithClient.newBuilder()
.setApiKey("YOUR_ENV_API_KEY")
.withConfiguration(FlagsmithConfig
.newBuilder()
.baseURI("http://yoururl.com")
.build())
.withCache(FlagsmithCacheConfig
.newBuilder()
.enableEnvLevelCaching(projectLevelCacheKey)
.build())
.build();

// if you need to access the cache directly, you can do this:
final FlagsmithCache cache = flagsmithClient.getCache();
// invalidate project level cache
cache.invalidate(projectLevelCacheKey);
// check if project level flags have been cached
final FlagsAndTraits flags = cache.getIfPresent(projectLevelCacheKey);

Logging

The following SDKs have code and functionality related to logging.

Logging is disabled by default. If you would like to enable it then call .enableLogging() on the client builder:

FlagsmithClient flagsmithClient = FlagsmithClient.newBuilder()
// other configuration as shown above
.enableLogging()
.build();

Flagsmith uses SLF4J and we only implement its API. If your project does not already have SLF4J, then include an implementation, i.e.:

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>${slf4j.version}</version>
</dependency>

Contribute to the SDKs

All our SDKs are Open Source.