Feature Flag in React Native using Amplify Authentication

Feature Flag in React Native using Amplify Authentication

This guide explains how to implement feature flags in a React Native application using Amplify Authentication.

Dependencies

To begin, install the following dependencies in your project:

"@aws-amplify/auth": "6.10.0",
"@aws-amplify/react-native": "1.1.6",
"@aws-sdk/client-appconfigdata": "3.731.1",
"aws-amplify": "6.12.1",
"react-native-get-random-values": "1.11.0",
"readable-stream": "4.7.0",
"text-encoding": "0.7.0",
"web-streams-polyfill": "4.1.0",
"@babel/plugin-transform-class-static-block": "7.26.0",

Babel Configuration

In babel.config.js, include @babel/plugin-transform-class-static-block in the plugins array. The configuration should look like this:

module.exports = function (api) {
  api.cache(true);
  return {
    presets: [
      ["babel-preset-expo", { jsxImportSource: "nativewind" }],
      "nativewind/babel",
    ],
    plugins: [
      "react-native-reanimated/plugin",
      "@babel/plugin-transform-class-static-block",
    ],
  };
};

Key Setup

Set up the keys for both authentication and AppConfig.

For Authentication:

Amplify.configure({
  Auth: {
    Cognito: {
      userPoolClientId: "YOUR_USER_POOL_CLIENT_ID",
      userPoolId: "YOUR_USER_POOL_ID",
      identityPoolId: "YOUR_IDENTITY_POOL_ID",
    },
  },
});

For AppConfig:

ApplicationIdentifier: "YOUR_APPLICATION_IDENTIFIER",
EnvironmentIdentifier: "YOUR_ENVIRONMENT_IDENTIFIER",
ConfigurationProfileIdentifier: "YOUR_CONFIG_PROFILE_IDENTIFIER",

User Authentication

Sign in with the existing user credentials:

const { isSignedIn } = await signIn({
  username: "your@email.com",
  password: "password",
});

Fetching User Credentials

After successful sign-in, fetch the user credentials:

import { fetchAuthSession } from "aws-amplify/auth";

// Get credentials after successful sign-in
const credentials = (await fetchAuthSession()).credentials;

AppConfig Setup

Using the fetched credentials, configure the AppConfig client:

const appConfig = new AppConfigDataClient({
  credentials: {
    accessKeyId: credentials.accessKeyId,
    secretAccessKey: credentials.secretAccessKey,
    sessionToken: credentials.sessionToken,
  },
  region: "YOUR_AWS_REGION",
});

Start a configuration session:

const startSessionCommand = new StartConfigurationSessionCommand({
  ApplicationIdentifier: "YOUR_APPLICATION_IDENTIFIER",
  EnvironmentIdentifier: "YOUR_ENVIRONMENT_IDENTIFIER",
  ConfigurationProfileIdentifier: "YOUR_CONFIG_PROFILE_IDENTIFIER",
});

const session = await appConfig.send(startSessionCommand);

Retrieve the configuration:

const getConfigCommand = new GetLatestConfigurationCommand({
  ConfigurationToken: session.InitialConfigurationToken,
});

const configData = await appConfig.send(getConfigCommand);
const configString = new TextDecoder("utf-8").decode(configData.Configuration);
const parsedFlags = JSON.parse(configString);

Example feature flag log:

{"zp-1": {"enabled": true}}

Redux Implementation

Call the feature flag only once when the app loads and pass it across screens using Redux.

Redux Slice for Feature Flags:

import {
  AppConfigDataClient,
  GetLatestConfigurationCommand,
  StartConfigurationSessionCommand,
} from "@aws-sdk/client-appconfigdata";
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import { fetchAuthSession } from "aws-amplify/auth";

export const fetchFeatureFlags = createAsyncThunk(
  "featureFlags/fetch",
  async (_, { rejectWithValue }) => {
    try {
      const credentials = (await fetchAuthSession()).credentials;

      const appConfig = new AppConfigDataClient({
        credentials: {
          accessKeyId: credentials.accessKeyId,
          secretAccessKey: credentials.secretAccessKey,
          sessionToken: credentials.sessionToken,
        },
        region: "YOUR_AWS_REGION",
      });

      const startSessionCommand = new StartConfigurationSessionCommand({
        ApplicationIdentifier: "YOUR_APPLICATION_IDENTIFIER",
        EnvironmentIdentifier: "YOUR_ENVIRONMENT_IDENTIFIER",
        ConfigurationProfileIdentifier: "YOUR_CONFIG_PROFILE_IDENTIFIER",
      });

      const session = await appConfig.send(startSessionCommand);

      const getConfigCommand = new GetLatestConfigurationCommand({
        ConfigurationToken: session.InitialConfigurationToken,
      });

      const configData = await appConfig.send(getConfigCommand);
      const configString = new TextDecoder("utf-8").decode(
        configData.Configuration,
      );
      return JSON.parse(configString);
    } catch (error) {
      return rejectWithValue(String(error));
    }
  },
);

const featureFlagsSlice = createSlice({
  name: "featureFlags",
  initialState: {
    flags: null,
    loading: false,
    error: null,
  },
  reducers: {},
  extraReducers: (builder) => {
    builder
      .addCase(fetchFeatureFlags.pending, (state) => {
        state.loading = true;
        state.error = null;
      })
      .addCase(fetchFeatureFlags.fulfilled, (state, action) => {
        state.loading = false;
        state.flags = action.payload;
        state.error = null;
      })
      .addCase(fetchFeatureFlags.rejected, (state, action) => {
        state.loading = false;
        state.error = action.payload;
        state.flags = null;
      });
  },
});
export default featureFlagsSlice.reducer;

Using the Feature Flag

Here’s a simple example of how to use the feature flag in a React Native component:

const App = () => {
  const { flags, loading, error } = useSelector((state) => state.featureFlags);
  const [enabled, setEnabled] = useState(true);

  useEffect(() => {
    setEnabled(flags["zp-1"]?.enabled);
  }, [flags]);

  if (loading) return <ActivityIndicator size="large" color="black" />;
  if (error) return <ErrorMessage />;

  return enabled ? (
    <View>
      <Text>Feature Enabled</Text>
    </View>
  ) : (
    <View>
      <Text>Feature Disabled</Text>
    </View>
  );
};
4 Likes

Good Job Keep exploring new technologies . It will help us for progressive delivery. But I have some questions on this implementation. Consider we have application that can accessible publicly without logging in. Then how can we achive feature flag on this scenario

3 Likes

Great Work Prathish :raised_hands:

1 Like