Skip to content

Providers

Flaggy supports multiple storage providers, allowing you to choose the best backend for your application. Each provider implements the IFeatureFlagProvider interface and comes with automatic migration support for seamless database schema management.

Overview

Provider Best For Package Auto-Migration
InMemory Development, Testing, Small Apps Flaggy (Core) N/A (JSON file)
MySQL Production MySQL databases Flaggy.Provider.MySQL Yes
PostgreSQL Production PostgreSQL databases Flaggy.Provider.PostgreSQL Yes
MS SQL Server Production SQL Server databases Flaggy.Provider.MsSql Yes

InMemory Provider

The InMemory provider stores feature flags in memory with JSON file persistence for durability across application restarts. Perfect for development, testing, and small applications.

Features

  • No database required
  • JSON file persistence
  • Fast read/write operations
  • Ideal for local development
  • Perfect for unit/integration testing

Installation

The InMemory provider is included in the core Flaggy package:

dotnet add package Flaggy

Configuration

Default Configuration

By default, flags are persisted to flaggy-flags.json in the current directory:

using Flaggy.Extensions;
using Flaggy.Providers;

var builder = WebApplication.CreateBuilder(args);

// Use default file path (./flaggy-flags.json)
builder.Services.AddFlaggy(new InMemoryFeatureFlagProvider());

var app = builder.Build();
app.Run();

Custom File Path

Specify a custom path for the JSON file:

using Flaggy.Extensions;
using Flaggy.Providers;

// Custom file path
builder.Services.AddFlaggy(
    new InMemoryFeatureFlagProvider("./data/my-flags.json")
);

// Relative path
builder.Services.AddFlaggy(
    new InMemoryFeatureFlagProvider("./config/flags.json")
);

// Absolute path
builder.Services.AddFlaggy(
    new InMemoryFeatureFlagProvider("/var/app/flags/features.json")
);

JSON File Format

The JSON file stores flags in a simple array format:

[
  {
    "Key": "new-feature",
    "IsEnabled": true,
    "Value": "v2",
    "Description": "New feature flag",
    "CreatedAt": "2025-01-15T10:30:00Z",
    "UpdatedAt": "2025-01-15T12:45:00Z"
  },
  {
    "Key": "beta-mode",
    "IsEnabled": false,
    "Value": null,
    "Description": "Beta features",
    "CreatedAt": "2025-01-10T08:00:00Z",
    "UpdatedAt": "2025-01-10T08:00:00Z"
  }
]

Best Practices

  1. Development: Use InMemory provider for rapid development without database setup
  2. Testing: Perfect for unit tests and integration tests
  3. File Location: Store the JSON file in a persistent location for production-like scenarios
  4. Version Control: Consider adding the JSON file to .gitignore to avoid conflicts
  5. Migration Path: Start with InMemory, then migrate to a database provider when needed

MySQL Provider

The MySQL provider offers production-ready storage with automatic schema migration and version tracking.

Features

  • Production-ready MySQL support
  • Automatic database migration
  • Version tracking with flaggy_migrations table
  • User management with BCrypt password hashing
  • Optimized queries with proper indexing

Installation

dotnet add package Flaggy
dotnet add package Flaggy.Provider.MySQL

Configuration

using Flaggy.Extensions;

var builder = WebApplication.CreateBuilder(args);

// Basic configuration
builder.Services.AddFlaggy(options =>
{
    options.UseMySQL(
        connectionString: "Server=localhost;Database=myapp;User=root;Password=pass;"
    );
});

// Full configuration with all options
builder.Services.AddFlaggy(options =>
{
    options.UseMySQL(
        connectionString: "Server=localhost;Database=myapp;User=root;Password=pass;",
        tableName: "feature_flags",        // optional
        userTableName: "users",            // optional
        autoMigrate: true                  // optional
    );
    options.UseMemoryCache(TimeSpan.FromMinutes(5));
});

var app = builder.Build();
app.Run();

Connection String Examples

// Basic connection
"Server=localhost;Database=myapp;User=root;Password=pass;"

// With port
"Server=localhost;Port=3306;Database=myapp;User=root;Password=pass;"

// SSL connection
"Server=localhost;Database=myapp;User=root;Password=pass;SslMode=Required;"

// Connection pooling
"Server=localhost;Database=myapp;User=root;Password=pass;Pooling=true;MinimumPoolSize=5;MaximumPoolSize=20;"

// From configuration
builder.Services.AddFlaggy(options =>
{
    options.UseMySQL(
        connectionString: builder.Configuration.GetConnectionString("FlaggyDb")
    );
});

Database Schema

The following tables are created automatically:

feature_flags table

CREATE TABLE feature_flags (
    `key` VARCHAR(255) PRIMARY KEY,
    is_enabled BOOLEAN NOT NULL DEFAULT FALSE,
    `value` TEXT NULL,
    description TEXT NULL,
    created_at DATETIME NULL,
    updated_at DATETIME NULL
);

users table

CREATE TABLE users (
    username VARCHAR(255) PRIMARY KEY,
    password_hash VARCHAR(255) NOT NULL,
    email VARCHAR(255) NULL,
    created_at DATETIME NULL
);

flaggy_migrations table

CREATE TABLE flaggy_migrations (
    id INT AUTO_INCREMENT PRIMARY KEY,
    version INT NOT NULL UNIQUE,
    description VARCHAR(500) NOT NULL,
    applied_at DATETIME NOT NULL
);

See Auto Migration for more details on schema management.

PostgreSQL Provider

The PostgreSQL provider provides robust storage with automatic migration support and PostgreSQL-specific optimizations.

Features

  • Production-ready PostgreSQL support
  • Automatic database migration
  • Case-sensitive naming conventions
  • User management with BCrypt password hashing
  • Transaction support

Installation

dotnet add package Flaggy
dotnet add package Flaggy.Provider.PostgreSQL

Configuration

using Flaggy.Extensions;

var builder = WebApplication.CreateBuilder(args);

// Basic configuration
builder.Services.AddFlaggy(options =>
{
    options.UsePostgreSQL(
        connectionString: "Host=localhost;Database=myapp;Username=postgres;Password=pass"
    );
});

// Full configuration with all options
builder.Services.AddFlaggy(options =>
{
    options.UsePostgreSQL(
        connectionString: "Host=localhost;Database=myapp;Username=postgres;Password=pass",
        tableName: "feature_flags",        // optional
        userTableName: "users",            // optional
        autoMigrate: true                  // optional
    );
    options.UseMemoryCache(TimeSpan.FromMinutes(5));
});

var app = builder.Build();
app.Run();

Connection String Examples

// Basic connection
"Host=localhost;Database=myapp;Username=postgres;Password=pass"

// With port
"Host=localhost;Port=5432;Database=myapp;Username=postgres;Password=pass"

// SSL connection
"Host=localhost;Database=myapp;Username=postgres;Password=pass;SSL Mode=Require"

// Connection pooling
"Host=localhost;Database=myapp;Username=postgres;Password=pass;Pooling=true;Minimum Pool Size=5;Maximum Pool Size=20"

// From configuration
builder.Services.AddFlaggy(options =>
{
    options.UsePostgreSQL(
        connectionString: builder.Configuration.GetConnectionString("FlaggyDb")
    );
});

Database Schema

The following tables are created automatically:

feature_flags table

CREATE TABLE feature_flags (
    key VARCHAR(255) PRIMARY KEY,
    is_enabled BOOLEAN NOT NULL DEFAULT FALSE,
    value TEXT NULL,
    description TEXT NULL,
    created_at TIMESTAMP NULL,
    updated_at TIMESTAMP NULL
);

users table

CREATE TABLE users (
    username VARCHAR(255) PRIMARY KEY,
    password_hash VARCHAR(255) NOT NULL,
    email VARCHAR(255) NULL,
    created_at TIMESTAMP NULL
);

flaggy_migrations table

CREATE TABLE flaggy_migrations (
    id SERIAL PRIMARY KEY,
    version INT NOT NULL UNIQUE,
    description VARCHAR(500) NOT NULL,
    applied_at TIMESTAMP NOT NULL
);

Note: PostgreSQL uses lowercase, snake_case naming conventions for columns.

MS SQL Server Provider

The MS SQL Server provider offers enterprise-grade storage with automatic migration and full SQL Server feature support.

Features

  • Production-ready SQL Server support
  • Automatic database migration
  • User management with BCrypt password hashing
  • Transaction support
  • SQL Server-specific optimizations

Installation

dotnet add package Flaggy
dotnet add package Flaggy.Provider.MsSql

Configuration

using Flaggy.Extensions;

var builder = WebApplication.CreateBuilder(args);

// Basic configuration
builder.Services.AddFlaggy(options =>
{
    options.UseMsSql(
        connectionString: "Server=localhost;Database=myapp;User Id=sa;Password=pass;TrustServerCertificate=True"
    );
});

// Full configuration with all options
builder.Services.AddFlaggy(options =>
{
    options.UseMsSql(
        connectionString: "Server=localhost;Database=myapp;User Id=sa;Password=pass;TrustServerCertificate=True",
        tableName: "feature_flags",        // optional
        userTableName: "users",            // optional
        autoMigrate: true                  // optional
    );
    options.UseMemoryCache(TimeSpan.FromMinutes(5));
});

var app = builder.Build();
app.Run();

Connection String Examples

// SQL Server Authentication
"Server=localhost;Database=myapp;User Id=sa;Password=pass;TrustServerCertificate=True"

// Windows Authentication
"Server=localhost;Database=myapp;Integrated Security=True;TrustServerCertificate=True"

// Named instance
"Server=localhost\\SQLEXPRESS;Database=myapp;User Id=sa;Password=pass;TrustServerCertificate=True"

// With encryption
"Server=localhost;Database=myapp;User Id=sa;Password=pass;Encrypt=True;TrustServerCertificate=False"

// Azure SQL Database
"Server=tcp:myserver.database.windows.net,1433;Database=myapp;User ID=admin;Password=pass;Encrypt=True"

// From configuration
builder.Services.AddFlaggy(options =>
{
    options.UseMsSql(
        connectionString: builder.Configuration.GetConnectionString("FlaggyDb")
    );
});

Database Schema

The following tables are created automatically:

feature_flags table

CREATE TABLE feature_flags (
    [key] NVARCHAR(255) PRIMARY KEY,
    is_enabled BIT NOT NULL DEFAULT 0,
    [value] NVARCHAR(MAX) NULL,
    description NVARCHAR(MAX) NULL,
    created_at DATETIME NULL,
    updated_at DATETIME NULL
);

users table

CREATE TABLE users (
    username NVARCHAR(255) PRIMARY KEY,
    password_hash NVARCHAR(255) NOT NULL,
    email NVARCHAR(255) NULL,
    created_at DATETIME NULL
);

flaggy_migrations table

CREATE TABLE flaggy_migrations (
    id INT IDENTITY(1,1) PRIMARY KEY,
    version INT NOT NULL UNIQUE,
    description NVARCHAR(500) NOT NULL,
    applied_at DATETIME NOT NULL
);

Custom Provider

You can create your own provider by implementing the IFeatureFlagProvider interface.

IFeatureFlagProvider Interface

using Flaggy.Abstractions;
using Flaggy.Models;

public interface IFeatureFlagProvider
{
    Task<FeatureFlag?> GetFlagAsync(string key, CancellationToken cancellationToken = default);
    Task<IEnumerable<FeatureFlag>> GetAllFlagsAsync(CancellationToken cancellationToken = default);
    Task<bool> CreateFlagAsync(FeatureFlag flag, CancellationToken cancellationToken = default);
    Task<bool> UpdateFlagAsync(FeatureFlag flag, CancellationToken cancellationToken = default);
    Task<bool> DeleteFlagAsync(string key, CancellationToken cancellationToken = default);
}

Example: MongoDB Provider

using Flaggy.Abstractions;
using Flaggy.Models;
using MongoDB.Driver;

public class MongoDbFeatureFlagProvider : IFeatureFlagProvider
{
    private readonly IMongoCollection<FeatureFlag> _collection;

    public MongoDbFeatureFlagProvider(string connectionString, string databaseName)
    {
        var client = new MongoClient(connectionString);
        var database = client.GetDatabase(databaseName);
        _collection = database.GetCollection<FeatureFlag>("feature_flags");
    }

    public async Task<FeatureFlag?> GetFlagAsync(string key, CancellationToken cancellationToken = default)
    {
        var filter = Builders<FeatureFlag>.Filter.Eq(f => f.Key, key);
        return await _collection.Find(filter).FirstOrDefaultAsync(cancellationToken);
    }

    public async Task<IEnumerable<FeatureFlag>> GetAllFlagsAsync(CancellationToken cancellationToken = default)
    {
        return await _collection.Find(_ => true).ToListAsync(cancellationToken);
    }

    public async Task<bool> CreateFlagAsync(FeatureFlag flag, CancellationToken cancellationToken = default)
    {
        try
        {
            flag.CreatedAt = DateTime.UtcNow;
            flag.UpdatedAt = DateTime.UtcNow;
            await _collection.InsertOneAsync(flag, cancellationToken: cancellationToken);
            return true;
        }
        catch
        {
            return false;
        }
    }

    public async Task<bool> UpdateFlagAsync(FeatureFlag flag, CancellationToken cancellationToken = default)
    {
        try
        {
            flag.UpdatedAt = DateTime.UtcNow;
            var filter = Builders<FeatureFlag>.Filter.Eq(f => f.Key, flag.Key);
            var result = await _collection.ReplaceOneAsync(filter, flag, cancellationToken: cancellationToken);
            return result.ModifiedCount > 0;
        }
        catch
        {
            return false;
        }
    }

    public async Task<bool> DeleteFlagAsync(string key, CancellationToken cancellationToken = default)
    {
        try
        {
            var filter = Builders<FeatureFlag>.Filter.Eq(f => f.Key, key);
            var result = await _collection.DeleteOneAsync(filter, cancellationToken);
            return result.DeletedCount > 0;
        }
        catch
        {
            return false;
        }
    }
}

Usage

using Flaggy.Extensions;

builder.Services.AddFlaggy(
    new MongoDbFeatureFlagProvider(
        connectionString: "mongodb://localhost:27017",
        databaseName: "myapp"
    )
);

Provider Comparison

Performance

Provider Read Speed Write Speed Scalability Best Use Case
InMemory Fastest Fast Single instance Development, Testing
MySQL Fast Fast High Production, Web apps
PostgreSQL Fast Fast Very High Production, Enterprise
MS SQL Server Fast Fast Very High Production, Enterprise

Features Comparison

Feature InMemory MySQL PostgreSQL MS SQL
Auto-Migration N/A Yes Yes Yes
User Management Yes Yes Yes Yes
Transactions No Yes Yes Yes
JSON Persistence Yes N/A N/A N/A
Distributed Cache No Yes (with Redis) Yes (with Redis) Yes (with Redis)
Production Ready No Yes Yes Yes

Switching Providers

Switching between providers is straightforward. Here's how to migrate from InMemory to MySQL:

Before (InMemory)

using Flaggy.Extensions;
using Flaggy.Providers;

builder.Services.AddFlaggy(new InMemoryFeatureFlagProvider());

After (MySQL)

using Flaggy.Extensions;

builder.Services.AddFlaggy(options =>
{
    options.UseMySQL(
        connectionString: "Server=localhost;Database=myapp;User=root;Password=pass;"
    );
});

Migration Steps

  1. Export flags from InMemory provider (JSON file)
  2. Install the new provider package
  3. Update service configuration
  4. Import flags to the new provider (optional - use seeding)

Example migration script:

// Read from JSON file
var json = File.ReadAllText("flaggy-flags.json");
var flags = JsonSerializer.Deserialize<List<FeatureFlag>>(json);

// Seed to new provider
await app.SeedFeatureFlagsAsync(flags.ToArray());

Configuration Best Practices

1. Use Connection Strings from Configuration

// appsettings.json
{
  "ConnectionStrings": {
    "FlaggyDb": "Server=localhost;Database=myapp;User=root;Password=pass;"
  }
}

// Program.cs
builder.Services.AddFlaggy(options =>
{
    options.UseMySQL(
        connectionString: builder.Configuration.GetConnectionString("FlaggyDb")
    );
});

2. Environment-Specific Providers

if (builder.Environment.IsDevelopment())
{
    // Use InMemory for development
    builder.Services.AddFlaggy(new InMemoryFeatureFlagProvider());
}
else
{
    // Use database for production
    builder.Services.AddFlaggy(options =>
{
    options.UseMySQL(
        connectionString: builder.Configuration.GetConnectionString("FlaggyDb")
    );
});
}

3. Custom Table Names

// Use custom table names to avoid conflicts
builder.Services.AddFlaggy(options =>
{
    options.UsePostgreSQL(
        connectionString: connectionString,
        tableName: "app_feature_flags",
        userTableName: "app_flaggy_users"
    );
});

4. Disable Auto-Migration for Production

// Disable auto-migration and run migrations manually
builder.Services.AddFlaggy(options =>
{
    options.UseMySQL(
        connectionString: connectionString,
        autoMigrate: false
    );
});
  • Caching - Configure MemoryCache or Redis for better performance
  • Auto Migration - Understanding automatic database schema management
  • User Management - Managing dashboard users with providers
  • Dashboard - Using the web UI with different providers