Basic Usage¶
This guide covers the fundamental operations when working with Flaggy feature flags.
Creating Your First Feature Flag¶
Via Dashboard¶
- Navigate to
https://localhost:5001/flaggy - Click "Create Flag" or "New Flag"
- Fill in the flag details:
- Key - Unique identifier (e.g., "new-checkout-flow")
- Enabled - Toggle to enable/disable
- Value - Optional value (string, number, JSON, etc.)
- Description - Human-readable description
- Click "Save"
Programmatically¶
Create flags in your application code:
using Flaggy.Extensions;
using Flaggy.Models;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddFlaggy(options =>
{
options.UseMySQL(
connectionString: connectionString,
tableName: "feature_flags", // optional
userTableName: "users", // optional
autoMigrate: true // optional
);
options.UseMemoryCache(TimeSpan.FromMinutes(5));
});
var app = builder.Build();
// Create a simple flag
await app.InitializeFeatureFlagsAsync(async flagService =>
{
await flagService.CreateFlagAsync(new FeatureFlag
{
Key = "new-checkout-flow",
IsEnabled = false,
Description = "New improved checkout experience"
});
});
app.Run();
Checking if a Flag is Enabled¶
Simple Enabled/Disabled Check¶
Check if a feature flag is enabled in your application:
using Flaggy.Abstractions;
app.MapGet("/api/checkout", async (IFeatureFlagService flagService) =>
{
var isNewCheckoutEnabled = await flagService.IsEnabledAsync("new-checkout-flow");
if (isNewCheckoutEnabled)
{
return Results.Ok(new { message = "Using new checkout flow" });
}
return Results.Ok(new { message = "Using legacy checkout" });
});
In Controllers¶
Inject the flag service into your controllers:
using Flaggy.Abstractions;
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
private readonly IFeatureFlagService _flagService;
public ProductsController(IFeatureFlagService flagService)
{
_flagService = flagService;
}
[HttpGet("{id}")]
public async Task<IActionResult> GetProduct(int id)
{
var product = await GetProductFromDb(id);
var showReviews = await _flagService.IsEnabledAsync("product-reviews");
if (showReviews)
{
product.Reviews = await GetReviews(id);
}
return Ok(product);
}
}
In Services¶
Use flags in your business logic services:
using Flaggy.Abstractions;
public class OrderService
{
private readonly IFeatureFlagService _flagService;
private readonly IPaymentService _paymentService;
public OrderService(IFeatureFlagService flagService, IPaymentService paymentService)
{
_flagService = flagService;
_paymentService = paymentService;
}
public async Task<Order> CreateOrderAsync(Order order)
{
// Use legacy payment processor
if (!await _flagService.IsEnabledAsync("new-payment-processor"))
{
return await _paymentService.ProcessWithLegacyAsync(order);
}
// Use new payment processor
return await _paymentService.ProcessWithNewAsync(order);
}
}
Getting Flag Values with Types¶
Feature flags can store typed values, not just boolean states.
String Values¶
Store and retrieve string values:
app.MapGet("/api/welcome", async (IFeatureFlagService flagService) =>
{
// Get string value with default
var message = await flagService.GetValueAsync(
key: "welcome-message",
defaultValue: "Welcome to our app!"
);
return Results.Ok(new { message });
});
Integer Values¶
Store and retrieve numeric configuration:
app.MapGet("/api/limits", async (IFeatureFlagService flagService) =>
{
// Get integer value with default
var maxUsers = await flagService.GetValueAsync<int>(
key: "max-concurrent-users",
defaultValue: 100
);
var maxRequests = await flagService.GetValueAsync<int>(
key: "max-requests-per-minute",
defaultValue: 60
);
return Results.Ok(new { maxUsers, maxRequests });
});
Double/Decimal Values¶
Store percentage-based values like discounts:
app.MapGet("/api/products/{id}/price", async (int id, IFeatureFlagService flagService) =>
{
var product = await GetProduct(id);
// Get discount rate with default
var discountRate = await flagService.GetValueAsync<double>(
key: "discount-rate",
defaultValue: 0.0
);
var finalPrice = product.Price * (1 - discountRate);
return Results.Ok(new {
originalPrice = product.Price,
discountRate,
finalPrice
});
});
Boolean Values¶
Store boolean configuration (note: different from IsEnabled):
app.MapGet("/api/features", async (IFeatureFlagService flagService) =>
{
var enableAnalytics = await flagService.GetValueAsync<bool>(
key: "enable-analytics",
defaultValue: false
);
return Results.Ok(new { enableAnalytics });
});
JSON Values¶
Store complex JSON data:
app.MapGet("/api/config", async (IFeatureFlagService flagService) =>
{
var configJson = await flagService.GetValueAsync(
key: "theme-config",
defaultValue: "{\"theme\": \"light\", \"fontSize\": 14}"
);
// Parse and use the JSON
var config = JsonSerializer.Deserialize<ThemeConfig>(configJson);
return Results.Ok(config);
});
How IsEnabled Works¶
Understanding the relationship between IsEnabled and Value:
| Scenario | IsEnabled | GetValueAsync Result |
|---|---|---|
| Flag doesn't exist | N/A | Returns defaultValue |
| IsEnabled = false | false | Returns defaultValue |
| IsEnabled = true with value | true | Returns the stored value |
| IsEnabled = true no value | true | Returns null (or defaultValue if provided) |
// Example: A flag with IsEnabled=false will always return the default
// even if it has a stored value
var result = await flagService.GetValueAsync<int>(
key: "some-flag",
defaultValue: 100
);
// Result: 100 (because IsEnabled is false)
// Example: A flag with IsEnabled=true returns its value
var result = await flagService.GetValueAsync<int>(
key: "some-flag",
defaultValue: 100
);
// Result: the actual value stored in the flag (e.g., 500)
Using Flags in Controllers and Services¶
Complete Controller Example¶
using Flaggy.Abstractions;
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
private readonly IFeatureFlagService _flagService;
private readonly IUserService _userService;
public UsersController(IFeatureFlagService flagService, IUserService userService)
{
_flagService = flagService;
_userService = userService;
}
[HttpPost]
public async Task<IActionResult> CreateUser([FromBody] CreateUserRequest request)
{
// Check if new registration flow is enabled
var useNewFlow = await _flagService.IsEnabledAsync("new-user-registration");
if (useNewFlow)
{
return Ok(await _userService.CreateWithNewFlowAsync(request));
}
else
{
return Ok(await _userService.CreateWithLegacyFlowAsync(request));
}
}
[HttpGet("{id}")]
public async Task<IActionResult> GetUser(int id)
{
var user = await _userService.GetUserAsync(id);
// Include premium features if flag is enabled
var includePremiumFeatures = await _flagService.IsEnabledAsync("premium-features");
if (includePremiumFeatures)
{
user.Features = await _userService.GetPremiumFeaturesAsync(id);
}
return Ok(user);
}
}
Complete Service Example¶
using Flaggy.Abstractions;
public class EmailService
{
private readonly IFeatureFlagService _flagService;
private readonly IEmailProvider _emailProvider;
public EmailService(IFeatureFlagService flagService, IEmailProvider emailProvider)
{
_flagService = flagService;
_emailProvider = emailProvider;
}
public async Task SendWelcomeEmailAsync(User user)
{
// Get welcome email template from flag
var template = await _flagService.GetValueAsync(
key: "welcome-email-template",
defaultValue: "default"
);
// Get email provider to use
var provider = await _flagService.GetValueAsync(
key: "email-provider",
defaultValue: "sendgrid"
);
// Get sender email
var senderEmail = await _flagService.GetValueAsync(
key: "sender-email",
defaultValue: "noreply@company.com"
);
await _emailProvider.SendAsync(new EmailMessage
{
To = user.Email,
From = senderEmail,
TemplateName = template,
TemplateData = new { user.Name }
});
}
}
Managing Flags via Dashboard¶
Dashboard Features¶
The web UI allows you to:
- View All Flags - See a list of all configured flags
- Create Flags - Add new feature flags
- Edit Flags - Modify flag properties and values
- Enable/Disable - Toggle flags on/off with a single click
- Search - Find flags by key or description
- Manage Users - Create/delete dashboard users
Dashboard Workflow¶
1. Navigate to https://localhost:5001/flaggy
2. Log in with your credentials
3. View the flag list
4. Create new flags
5. Toggle flags on/off
6. Edit flag values
7. Changes take effect immediately (cache is invalidated)
Managing Flags Programmatically¶
Basic CRUD Operations¶
using Flaggy.Extensions;
using Flaggy.Models;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddFlaggy(options =>
{
options.UseMySQL(connectionString: connectionString);
});
var app = builder.Build();
app.MapPost("/api/admin/flags", async (IFeatureFlagService flagService) =>
{
// Create a new flag
await flagService.CreateFlagAsync(new FeatureFlag
{
Key = "feature-x",
IsEnabled = false,
Value = "beta",
Description = "Feature X in beta"
});
return Results.Ok();
});
app.MapPut("/api/admin/flags/{key}", async (string key, IFeatureFlagService flagService) =>
{
// Get flag
var flag = await flagService.GetFlagAsync(key);
if (flag == null) return Results.NotFound();
// Update flag
flag.IsEnabled = true;
await flagService.UpdateFlagAsync(flag);
return Results.Ok();
});
app.MapDelete("/api/admin/flags/{key}", async (string key, IFeatureFlagService flagService) =>
{
// Delete flag
await flagService.DeleteFlagAsync(key);
return Results.Ok();
});
Extension Methods¶
Use convenient extension methods for common operations:
using Flaggy.Extensions;
// Create only if doesn't exist
await flagService.CreateFlagIfNotExistsAsync("dark-mode", isEnabled: true);
// Upsert (create or update)
await flagService.UpsertFlagAsync("beta-features", isEnabled: false);
// Enable/Disable flags
await flagService.EnableFlagAsync("dark-mode");
await flagService.DisableFlagAsync("beta-features");
// Toggle flag
await flagService.ToggleFlagAsync("dark-mode");
// Update only value or description
await flagService.UpdateFlagValueAsync("theme", "auto");
await flagService.UpdateFlagDescriptionAsync("theme", "User theme preference");
// Check if flag exists
bool exists = await flagService.FlagExistsAsync("dark-mode");
// Get enabled/disabled flags
var enabledFlags = await flagService.GetEnabledFlagsAsync();
var disabledFlags = await flagService.GetDisabledFlagsAsync();
// Get summary
var summary = await flagService.GetFlagsSummaryAsync();
Console.WriteLine($"Total: {summary.TotalCount}, Enabled: {summary.EnabledCount}");
// Delete multiple flags
await flagService.DeleteFlagsAsync(new[] { "flag1", "flag2" });
// Delete all disabled flags
await flagService.DeleteAllDisabledFlagsAsync();
Fluent API Builder¶
Use the fluent API for cleaner flag creation:
using Flaggy.Helpers;
var initializer = new FeatureFlagInitializer(flagService);
await initializer.CreateFlag("premium-features")
.Enabled()
.WithDescription("Premium features for paid users")
.WithValue("tier-1,tier-2")
.CreateIfNotExistsAsync();
await initializer.CreateFlag("maintenance-mode")
.Disabled()
.WithDescription("Enable maintenance mode")
.UpsertAsync();
Seeding Flags at Startup¶
Initialize your application with default flags:
using Flaggy.Extensions;
using Flaggy.Helpers;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddFlaggy(options =>
{
options.UsePostgreSQL(connectionString: connectionString);
});
var app = builder.Build();
// Seed flags using extension method
await app.SeedFeatureFlagsAsync(
new FeatureFlag { Key = "welcome-banner", IsEnabled = true },
new FeatureFlag { Key = "new-dashboard", IsEnabled = false },
new FeatureFlag { Key = "analytics", IsEnabled = true }
);
// Or seed using initializer for more control
await app.InitializeFeatureFlagsAsync(async flagService =>
{
var initializer = new FeatureFlagInitializer(flagService);
await initializer.CreateFlag("notifications")
.Enabled()
.WithDescription("Email notifications")
.CreateIfNotExistsAsync();
await initializer.CreateFlag("theme-selector")
.Enabled()
.WithValue("light")
.WithDescription("Default theme")
.CreateIfNotExistsAsync();
});
app.Run();
Simple Examples¶
Example 1: A/B Testing¶
app.MapGet("/api/landing", async (IFeatureFlagService flagService) =>
{
var useNewDesign = await flagService.IsEnabledAsync("new-landing-design");
if (useNewDesign)
{
return Results.Ok(new { design = "new", color = "blue" });
}
return Results.Ok(new { design = "legacy", color = "gray" });
});
Example 2: Feature Gradual Rollout¶
app.MapPost("/api/purchase", async (PurchaseRequest request, IFeatureFlagService flagService) =>
{
var newCheckoutEnabled = await flagService.IsEnabledAsync("new-checkout-v2");
if (newCheckoutEnabled)
{
// Process with new checkout system
var result = await ProcessWithNewCheckoutAsync(request);
return Results.Ok(result);
}
else
{
// Fall back to legacy checkout
var result = await ProcessWithLegacyCheckoutAsync(request);
return Results.Ok(result);
}
});
Example 3: Configuration Management¶
public class AppSettings
{
private readonly IFeatureFlagService _flagService;
public AppSettings(IFeatureFlagService flagService)
{
_flagService = flagService;
}
public async Task<int> GetMaxRetries()
{
return await _flagService.GetValueAsync<int>("max-retries", defaultValue: 3);
}
public async Task<TimeSpan> GetRequestTimeout()
{
var seconds = await _flagService.GetValueAsync<int>(
"request-timeout-seconds",
defaultValue: 30
);
return TimeSpan.FromSeconds(seconds);
}
public async Task<string> GetApiEndpoint()
{
return await _flagService.GetValueAsync(
"api-endpoint",
defaultValue: "https://api.legacy.com"
);
}
}
// Usage
var settings = new AppSettings(flagService);
var endpoint = await settings.GetApiEndpoint();
var timeout = await settings.GetRequestTimeout();
Example 4: Caching Strategy¶
app.MapGet("/api/data", async (IFeatureFlagService flagService) =>
{
var enableRedisCache = await flagService.IsEnabledAsync("redis-cache");
var cacheDuration = await flagService.GetValueAsync<int>(
"cache-duration-minutes",
defaultValue: 5
);
var data = enableRedisCache
? await GetDataWithRedis(TimeSpan.FromMinutes(cacheDuration))
: await GetDataWithMemoryCache(TimeSpan.FromMinutes(cacheDuration));
return Results.Ok(data);
});
Next Steps¶
- Installation - Configure other providers
- Advanced Usage - Production patterns
- Examples - More detailed examples
- API Reference - Complete API documentation