Skip to content

Instantly share code, notes, and snippets.

@reecelucas
Last active March 28, 2020 15:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save reecelucas/a71b0cc0ebeee6da620cffc352eee1dc to your computer and use it in GitHub Desktop.
Save reecelucas/a71b0cc0ebeee6da620cffc352eee1dc to your computer and use it in GitHub Desktop.
Basic feature flag implementation
export default {
FEATURE_A: {
name: 'FEATURE_A',
description: 'Human readable description of feature A',
enabled: {
development: true,
production: false
}
},
FEATURE_B: {
name: 'FEATURE_B',
description: 'Human readable description of feature B',
enabled: {
development: false,
production: true
}
}
};
import config from "./featureConfig";
import Cookies from "js-cookie";
const getDefaultConfigForEnv = environment =>
Object.entries(config).reduce(
(obj, [key, value]) => ({
...obj,
[key]: value.enabled[environment]
}),
{}
);
const getConfigOverridesFromCookie = cookieString => {
try {
return cookieString ? JSON.parse(cookieString) : {};
} catch (error) {
throw new Error(
"Could not determine feature config overrides: error parsing cookie value"
);
}
};
const getConfig = (environment, cookieString) => {
const defaultConfig = getDefaultConfigForEnv(environment);
const overrides = getConfigOverridesFromCookie(cookieString);
return {
...defaultConfig,
...overrides
};
};
export const getFeatureUtils = ({ environment, cookieName }) => {
const cookieValueFromDocument = Cookies.get(cookieName);
const isFeatureEnabled = (featureName, cookieString) => {
// If `cookieString` is provided we use this to determine feature overrides.
// This is useful when using `isFeatureEnabled` on the server, where `cookieName` can
// be accessed on the `request` object. On the client we retrieve the cookie value from
// the document.
const cookieValue = cookieString || cookieValueFromDocument;
const config = getConfig(environment, cookieValue);
const value = config[featureName];
if (value === undefined) {
throw new Error(
`${featureName} is not defined for the ${environment} environment`
);
}
return value;
};
// `values` should be an object. E.g. setFeatureOverrides({ FEATURE_A: false })
const setFeatureOverrides = values => {
Cookies.set(cookieName, values);
};
const getFeatureConfig = cookieString => {
const cookieValue = cookieString || cookieValueFromDocument;
return getConfig(environment, cookieValue);
};
return { isFeatureEnabled, setFeatureOverrides, getFeatureConfig };
};
import { getFeatureUtils } from "./featureFlags";
const {
isFeatureEnabled,
getFeatureConfig,
setFeatureOverrides
} = getFeatureUtils({
environment: "development",
cookieName: "featureOverrides"
});
console.log(isFeatureEnabled("FEATURE_B")); // false
console.log(getFeatureConfig()); // { FEATURE_A: true, FEATURE_B: false }
setFeatureOverrides({ FEATURE_B: true });
// After refreshing page...
console.log(isFeatureEnabled("FEATURE_B")); // true
console.log(getFeatureConfig()); // { FEATURE_A: true, FEATURE_B: true }
import express from "express";
import cookieParser from "cookie-parser";
import { getFeatureUtils } from "./featureFlags";
const app = express();
const { isFeatureEnabled, getFeatureConfig } = getFeatureUtils({
environment: "development",
cookieName: "featureOverrides",
});
const ensureFeatureUtils = (req, _res, next) => {
if (!req.cookies) {
throw new Error(
"Server configuration error: ensureFeatureUtils middleware requires cookie-parser middleware upstream"
);
}
const { featureOverrides } = req.cookies;
req.isFeatureEnabled = (featureName) => isFeatureEnabled(featureName, featureOverrides);
req.getFeatureConfig = () => getFeatureConfig(featureOverrides);
next();
};
app.use(cookieParser());
app.use(ensureFeatureUtils);
app.get("/", (req, res) => {
console.log(req.getFeatureConfig()); // { FEATURE_A: true, FEATURE_B: false }
if (req.isFeatureEnabled("FEATURE_A")) {
res.send("FEATURE_A is enabled");
} else {
res.send("FEATURE_A is NOT enabled");
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment