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¶
- Development: Use InMemory provider for rapid development without database setup
- Testing: Perfect for unit tests and integration tests
- File Location: Store the JSON file in a persistent location for production-like scenarios
- Version Control: Consider adding the JSON file to
.gitignoreto avoid conflicts - 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_migrationstable - 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¶
- Export flags from InMemory provider (JSON file)
- Install the new provider package
- Update service configuration
- 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
);
});
Related Topics¶
- 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