Erlang SDK reference
This topic describes how to use the Harness Feature Flags Erlang SDK for your Erlang or Elixir based application.
For getting started quickly:
- For Erlang based applications, you can clone our sample Erlang application in GitHub.
- For Elixir based applications, you can clone our sample Elixir application in GitHub.
Before you begin
Make sure you read and understand:
- Feature Flags Overview
- Getting Started with Feature Flags
- Client-Side and Server-Side SDKs
- Communication Strategy Between SDKs and Harness Feature Flags
Version
The current version of this SDK is 1.0.0.
Requirements
For Erlang applications, install:
- Erlang/OTP 24 or later
- Rebar3
For Elixir applications, install:
- Elixir version 1.11.4 or later
To follow along with our test code sample, make sure you:
- Create a Feature Flag on the Harness Platform called
harnessappdemodarkmode
. - Create a server SDK key and made a copy of it.
Install the SDK
For Erlang applications
To install the SDK for Erlang based applications:
Add the SDK as a dependency to your
rebar.config
file:{deps, [{cfclient, "1.0.0", {pkg, harness_ff_erlang_server_sdk}}]}.
Add the dependency to your project's
app.src
.{applications,
[kernel, stdlib, cfclient]
},
For Elixir applications
To install the SDK for Elixir based applications:
Add the SDK as a dependency to
mix.exs
deps()
:defp deps do
[
{:cfclient, "~> 1.0.0", hex: :harness_ff_erlang_server_sdk}
]
Initialize the SDK
To initialize the Erlang SDK:
- Add your server SDK key to connect to your Harness environment.
- Add a target that you want to evaluate against a Feature Flag.
- (Optional) Configure the SDK.
- Complete the initialization with the SDK using the server SDK key, target, and configuration parameters you set.
Add the server SDK key
Add the server SDK key for Erlang applications
To connect to the correct environment that you set up on the Harness Platform, add the server SDK key from that environment. Input the server SDK key into the
api_key
tuple, for example:Provide your API key in
config/sys.config
using an environment variable:[
{cfclient, [
{api_key, {environment_variable, "YOUR_API_KEY_ENV_VARIABLE"},
]}
].Or you may provide the API key directly if required:
[
{cfclient, [
{api_key, "YOUR_API_KEY"},
]}
].Add the server SDK key for Elixir applications
To connect to the correct environment that you set up on the Harness Platform, add the server SDK key from that environment. Input the server SDK key into the
api_key
key, for example:Provide your API key in
config/config.exs
using an environment variable:config :cfclient,
api_key: System.get_env("YOUR_API_KEY_ENVIRONMENT_VARIABLE")Or you may provide the API key directly if required:
config :cfclient,
api_key: "YOUR_API_KEY"
Add a target for Erlang and Elixir applications
What is a target?
For more information about targets, go to Target Users With Flags.
To create a target, create a map and add the following keys:
Parameter | Description | Required? | Example |
---|---|---|---|
Identifier | Unique ID for the Target Key: atom Value: bitstring / atom / string | Required | identifier => <<"HT_1">> |
Name | Name for this target. This does not have to be unique. Note: If you don’t provide a value, Harness uses the ID as the name. Value: bitstring / atom / string | Optional | name => <<"Harness_Target_1">> |
Attributes | Additional data you can store for a target, such as email addresses or location. Key: atom Value: bitstring / atom / list of bitstrings or atom Note: lists of strings are not supported. See Erlang SDK GitHub Repository for more information. | Optional | %% Bitstring attributes => #{beta => <<"beta_group_1">>} %% Atom attributes => #{alpha => alpha_group_1} %% Atom in Elixir syntax attributes => #{alpha => :alpha_group_1} %% List attributes => #{beta => [<<"beta_group_1">>, beta_group_2}]} |
Configure the SDK
When initializing the SDK, you have the option of providing alternative configuration.
Configure Erlang applications
Configure Erlang applications using the
config/sys.config
file, for example:[{cfclient, [
{api_key, {envrionment_variable, "YOUR_API_KEY_ENV_VARIABLE"},
{config, [
{config_url, "https://config.ff.harness.io/api/1.0"},
{events_url, "https://config.ff.harness.io/api/1.0"},
{poll_interval, 60},
{analytics_enabled, true},
]},
]}]Configure Elixir applications
Configure Elixir applications using the
config/config.exs
file, for example:import Config
config :cfclient,
[api_key: System.get_env("FF_API_KEY_0"),
# For additional config you can pass in, see Erlang SDK docs: https://github.com/harness/ff-erlang-server-sdk/blob/main/docs/further_reading.md#further-reading
# we are just using the main config url here as an example.
config: [
config_url: "https://config.ff.harness.io/api/1.0",
events_url: "https://events.ff.harness.io/api/1.0",
poll_interval: 60000,
analytics_enabled: true
]]
You can configure the following base options of the SDK:
Name | Erlang Example | Elixir Example | Description | Default value |
---|---|---|---|---|
ConfigURL | {config_url, "https://config.ff.harness.io/api/1.0"} | config_url: "https://config.ff.harness.io/api/1.0" | The URL used to fetch Feature Flag Evaluations. When using the Relay Proxy, change this to: http://localhost:7000 | https://config.ff.harness.io/api/1.0 |
EventUrl | {events_url, "https://events.ff.harness.io/api/1.0"} | events_url: "https://events.ff.harness.io/api/1.0" | The URL for posting metrics data to the Feature Flag service. When using the Relay Proxy, change this to: http://localhost:7000 | https://events.ff.harness.io/api/1.0 |
PollingInterval | {poll_interval, 60000} | poll_interval: 60000 | The interval in seconds that we poll for changes when you are using stream mode. | 60 (seconds) |
AnalyticsEnabled | {analytics_enabled, true} | analytics_enabled: true | Set to true to enable analytics. Set to false to disable analytics. Note: When enabled, analytics data is posted every 60 seconds. | true |
Complete the initialization
To complete the initialization, provide the api_key
and any optional configuration options. The SDK will then boot along with your application at run time.
Evaluate a flag
Evaluating a flag is when the SDK processes all flag rules and returns the correct variation of that flag for the target you provide. If a matching flag can’t be found, or the SDK can’t remotely fetch flags, the default value is returned.
There are different methods for the different variation types and for each method you need to pass in:
- Identifier of the flag you want to evaluate
- The target object you want to evaluate against
- The default variation
Below are examples of evaluating different types of flag variations for Erlang and Elixir applications.
Examples for Erlang applications
Boolean variation
cfclient:bool_variation(FlagIdentifier, Target, false).
Number variation:
cfclient:number_variation(FlagIdentifier, Target, -1).
String variation:
cfclient:string_variation(FlagIdentifier, Target, "default_string").
JSON variation:
cfclient:json_variation(FlagIdentifier, Target, #{dark_mode => <<”false”>>}).
Examples for Elixir applications
Boolean variation
:cfclient:bool_variation(FlagIdentifier, Target, false)
Number variation:
:cfclient:number_variation(FlagIdentifier, Target, -1)
String variation:
:cfclient:string_variation(FlagIdentifier, Target, "default_string")
JSON variation:
:cfclient:json_variation(FlagIdentifier, Target, #{dark_mode => <<”false”>>})
Test your app is connected to Harness
When you receive a response showing the current status of your Feature Flag, go to the Harness Platform and toggle the flag on and off. The SDK polls for changes every 60 seconds by default. Check your app after this interval to verify if the flag variation displayed is updated with the variation you toggled.
The SDK must run for at least 60 seconds before it sends metrics. Please ensure metrics have not been disabled in the SDK.
Close the SDK
To help prevent memory leaks, we recommend closing the SDK when it’s not in use.
To close the SDK, run one of the following commands.
For Erlang applications:
%% Close the default instance
cfclient:close().
%% Close a named instance
cfclient:close(instance_name_1).For Elixir applications:
# Close the default instance
:cfclient.close()
# Close a named instance
:cfclient.close(:instance_name_1)
Additional options
Run multiple instances of the SDK
The SDK by default starts up a single instance called default
which is configured with your project API key.
If different parts of your application need to use
specific projects, you can start up additional client instances using by defining additional configuration for each unique project.
If the default instance fails to start, for example, due to an authentication error with the API key, then the SDK fails to boot and any additional instances do not start. To prevent the default instance from starting, go to Erlang project config and Elixir project config.
Erlang Project Config
Create project configurations for each new instance you would like to start in your
config/sys.config
file. The additional project config is defined insys.config
The following
sys.config
snippet starts up two additional instances along with the default instance:[
%% Project config name: This is an arbitrary identifier, but it must be unique per project config you define.
{harness_project_1_config, [
{cfclient, [
{config, [
%% Instance name: This must be unique across all of the project configs. E.g. it cannot be the same as an instance name
%% in another project config.
%% It will be the name you use when calling SDK API functions like `bool_variation/4`,
{name, instance_name_1}
]},
%% The API key for the Harness project you want to use with this SDK instance.
{api_key, {environment_variable, "PROJECT_1_API_KEY"}}]
}
]},
{harness_project_2_config, [
{cfclient, [
{config, [
{name, instance_name_2}
]},
{api_key, {environment_variable, "PROJECT_2_API_KEY"}}]
}
]},
{cfclient, [
{api_key, {environment_variable, "FF_API_KEY"}},
{config, [
{config_url, "https://config.ff.harness.io/api/1.0"},
{events_url, "https://config.ff.harness.io/api/1.0"}
]},
{analytics_push_interval, 60000}
]
}].If you don't require the default instance to be started up, you can do:
% ... additional project config
{cfclient, [
{start_default_instance, false},
%% The remaining tuples will be ignored, so you can choose to include or omit them.
{api_key, {environment_variable, "FF_API_KEY"}},
{config, [
{config_url, "https://config.ff.harness.io/api/1.0"},
{events_url, "https://config.ff.harness.io/api/1.0"}
]},
{analytics_push_interval, 60000}
]
},In your application supervisor, for example,
src/myapp_sup.erl
, start up acfclient_instance
for each of the project configurations you provided above:init(Args) ->
HarnessProject1Args = application:get_env(harness_project_1_config, cfclient, []),
HarnessProject2Args = application:get_env(harness_project_2_config, cfclient, []),
ChildSpec1 = #{id => project1_cfclient_instance, start => {cfclient_instance, start_link, [HarnessProject1Args]}},
ChildSpec2 = #{id => project2_cfclient_instance, start => {cfclient_instance, start_link, [HarnessProject2Args]}},
MaxRestarts = 1000,
MaxSecondsBetweenRestarts = 3600,
SupFlags = #{strategy => one_for_one,
intensity => MaxRestarts,
period => MaxSecondsBetweenRestarts},
{ok, {SupFlags, [ChildSpec1, ChildSpec2]}}.To use a specific SDK instance, provide the instance name to the public function you are calling. For example, use
bool_variation/4
instead ofbool_variation/3
- see the following code sample:-module(multi_instance_example).
-export([multi_instance_evaluations/0]).
multi_instance_evaluations() ->
Target = #{
identifier => "Harness_Target_1",
name => "HT_1",
attributes => #{email => <<"demo@harness.io">>}
},
%% Instance 1
Project1Flag = <<"harnessappdemodarkmodeproject1">>,
Project1Result = cfclient:bool_variation(instance_name_1, Project1Flag, Target, false),
logger:info("Instance Name 1 : Variation for Flag ~p with Target ~p is: ~p~n",
[Project1Flag, maps:get(identifier, Target), Project1Result]),
%% Instance 2
Project2Flag = <<"harnessappdemodarkmodeproject2">>,
Project2Result = cfclient:bool_variation(instance_name_2, Project2Flag, Target, false),
logger:info("Instance name 2 Variation for Flag ~p with Target ~p is: ~p~n",
[Project2Flag, maps:get(identifier, Target), Project2Result]).
Elixir project config
Create project configurations for each new instance you would like to start in your
config/config.exs
file:# Config for "project 1"
config :elixirsample, project1:
[
api_key: System.get_env("FF_API_KEY_1"),
config: [name: :project1]
]
# Config for "project 2"
config :elixirsample, project2:
[
api_key: System.get_env("FF_API_KEY_2"),
config: [name: :project2]
]If you don't require the default instance to be started up, you can do:
# Config for "project 1"
config :elixirsample, project1:
[
start_default_instance: false,
# The remaining tuples will be ignored, so you can choose to include or omit them.
api_key: System.get_env("FF_API_KEY_1"),
config: [name: :project1]
]In your application supervisor, for example,
lib/myapp/supervisor.ex
, start upcfclient_instance
for each of the project configurations you provided above:def init(_opts) do
project_1_config = Application.get_env(:elixirsample, :project1, [])
project_2_config = Application.get_env(:elixirsample, :project2, [])
children = [
%{
id: :project1_cfclient_instance,
start: {:cfclient_instance, :start_link, [project_1_config]},
type: :supervisor
},
%{
id: :project2_cfclient_instance,
start: {:cfclient_instance, :start_link, [project_2_config]},
type: :supervisor
},
]
Supervisor.init(children, strategy: :one_for_one)
endTo use a specific SDK instance, you provide the instance name to the public function you are calling. For example, use
bool_variation/4
instead ofbool_variation/3
- see the following code sample:defmodule ElixirSample.EvaluationSample do
require Logger
def getFlagLoop() do
target = %{
identifier: "harness",
name: "Harness",
anonymous: false,
attributes: %{}
}
# Default instance
flag = "projectflag"
result = :cfclient.bool_variation(flag, target, false)
Logger.info(
"SVariation for Flag #{flag} with Target #{inspect(target)} is: #{result}"
)
# Instance 1
project_1_flag = "project1flag"
project_1_result = :cfclient.number_variation(:project1, project_1_flag, target, 3)
Logger.info(
"SDK instance 1: Variation for Flag #{project_1_flag} with Target #{inspect(target)} is: #{project_1_result}"
)
# Instance 2
project_2_flag = "project2flag"
project_2_result = :cfclient.bool_variation(:project2, project_2_flag, target, false)
Logger.info(
"SDK instance 2: Variation for Flag #{project_2_flag} with Target #{inspect(target)} is: #{project_2_result}"
)
Process.sleep(10000)
getFlagLoop()
# Default instance
default_project_flag = "defaultflag"
default_project_result = :cfclient.bool_variation(default_project_flag, target, false)
Logger.info(
"Default instance: Variation for Flag #{default_project_flag} with Target #{inspect(target)} is: #{default_project_result}"
)
Process.sleep(10000)
getFlagLoop()
end
end
Use the Relay Proxy
When using your Feature Flag SDKs with a Harness Relay Proxy you must change the default URL and events URL to http://localhost:7000
when initializing the SDK.
To do this, pass the new URLs in when initializing the SDK, as shown below.
Erlang exmaple:
[{cfclient, [
{api_key, {envrionment_variable, "YOUR_API_KEY_ENV_VARIABLE"},
{config, [
{config_url, "http://localhost:7000"},
{events_url, "http://localhost:7000"},
]},
]}]Elixir exmaple:
config :cfclient,
[api_key: System.get_env("FF_API_KEY_0"),
# For additional config you can pass in, see Erlang SDK docs: https://github.com/harness/ff-erlang-server-sdk/blob/main/docs/further_reading.md#further-reading
# we are just using the main config url here as an example.
config: [
config_url: "http://localhost:7000",
events_url: "http://localhost:7000",
]]
Sample code
Sample code for Erlang applications
Ensure you have configured your application by following the steps in Initialize the SDK above.
-module(getting_started).
get_flag_loop() ->
Target = #{identifier => "Harness_Target_1",
name => "HT_1",
%% Attribute keys must be atoms.
%% Values must be either bitstrings, atoms, or a list of bitstrings/atoms - see Targets with custom attributes section below.
attributes => #{email => <<"demo@harness.io">>}
},
FlagIdentifier = "harnessappdemodarkmode",
Result = cfclient:bool_variation(FlagIdentifier, Target, false),
logger:info("Varaion for Flag ~p witih Target ~p is: ~p~n", [FlagIdentifier, maps:get(identifier, Target), Result]),
timer:sleep(10000),
get_flag_loop().You can also find a sample Erlang apllication that uses the Erlang SDK in GitHub.
Sample code for Elixir applications
You can find a sample Elixir application that uses the Erlang SDK in GitHub.