Getting Started with Subscrio Core
This tutorial walks through the minimum set of steps to bootstrap the Subscrio core library, define features/products/plans, onboard a customer, issue a subscription, and verify feature access. It is a distilled version of the full sample app—aimed at engineers who want a fast path to a working setup.
Prerequisites
- PostgreSQL connection string (same one used by the core library and admin app).
- Node.js 18+, PNPM or NPM.
- Run
pnpm install(ornpm install) at the repo root:
- Optional: copy
core.typescript/.env.exampleto.envand setDATABASE_URL.
- .NET 8.0+ SDK.
- Add the
Subscrio.CoreNuGet package to your project. - Set
DATABASE_URLenvironment variable (e.g.,Host=localhost;Port=5432;Database=subscrio;Username=postgres;Password=postgres).
Step 1 – Initialize Subscrio
Create a file such as scripts/bootstrap.ts:
import { Subscrio } from 'core.typescript';
import { loadConfig } from 'core.typescript/config';
async function main() {
const config = loadConfig();
const subscrio = new Subscrio(config);
// Check if schema exists
const schemaVersion = await subscrio.verifySchema();
if (schemaVersion === null) {
await subscrio.installSchema();
console.log('Schema installed.');
} else {
console.log(`Schema version: ${schemaVersion}`);
const migrationsApplied = await subscrio.migrate();
if (migrationsApplied > 0) {
console.log(`Applied ${migrationsApplied} migration(s).`);
}
}
console.log('Ready to use Subscrio services.');
}
main().catch((error) => {
console.error(error);
process.exit(1);
});
Run with ts-node or compile to JavaScript.
Create a bootstrap method:
using Subscrio.Core;
using Subscrio.Core.Config;
var config = ConfigLoader.LoadConfig();
await using var subscrio = new Subscrio(config);
var schemaVersion = await subscrio.VerifySchemaAsync();
if (schemaVersion == null)
{
await subscrio.InstallSchemaAsync();
Console.WriteLine("Schema installed.");
}
else
{
Console.WriteLine($"Schema version: {schemaVersion}");
var migrationsApplied = await subscrio.MigrateAsync();
if (migrationsApplied > 0)
{
Console.WriteLine($"Applied {migrationsApplied} migration(s).");
}
}
Console.WriteLine("Ready to use Subscrio services.");
Use await using (or Dispose() when done) to ensure proper cleanup.
Using dependency injection (web or host-based apps): Register Subscrio so it is created per scope (e.g. per request) and injected into controllers or minimal API handlers:
using Subscrio.Core.DependencyInjection; // and Subscrio.Core.Config for ConfigLoader
var config = ConfigLoader.Load();
builder.Services.AddSubscrio(config, ServiceLifetime.Scoped);
Then inject Subscrio via constructor (e.g. public MyController(Subscrio subscrio)) or as a parameter in minimal API (e.g. app.MapGet("/products", async (Subscrio subscrio) => ...)). Use ServiceLifetime.Scoped for web apps so each request gets its own instance and DbContext; use Transient for console or background services. You can build SubscrioConfig from ConfigLoader.Load() or from your own config (e.g. appsettings). See Core Overview for the constructor and config reference.
Running Migrations
When you update the Subscrio package, you may need to run migrations to update your database schema. The migration system tracks schema versions in the system_config table and only applies pending migrations, so it's safe to run multiple times. You can run migrations programmatically:
Step 2 – Define Features
Features are global definitions with typed defaults. Plans and subscriptions draw from them later.
Tip: Define all keys—products, plans, billing cycles, features—as constants or in a shared module so both backend and admin UI reference the same strings. This prevents typos and makes refactors safer.
const FEATURE_KEYS = {
Analytics: 'analytics-dashboard',
MaxProjects: 'max-projects'
} as const;
const analyticsFeature = await subscrio.features.createFeature({
key: FEATURE_KEYS.Analytics,
displayName: 'Analytics Dashboard',
valueType: 'toggle',
defaultValue: 'false'
});
const maxProjectsFeature = await subscrio.features.createFeature({
key: FEATURE_KEYS.MaxProjects,
displayName: 'Max Projects',
valueType: 'numeric',
defaultValue: '3'
});
const string FEATURE_ANALYTICS = "analytics-dashboard";
const string FEATURE_MAX_PROJECTS = "max-projects";
await subscrio.Features.CreateFeatureAsync(new CreateFeatureDto(
Key: FEATURE_ANALYTICS,
DisplayName: "Analytics Dashboard",
ValueType: "toggle",
DefaultValue: "false"
));
await subscrio.Features.CreateFeatureAsync(new CreateFeatureDto(
Key: FEATURE_MAX_PROJECTS,
DisplayName: "Max Projects",
ValueType: "numeric",
DefaultValue: "3"
));
Step 3 – Create Product, Plan, and Billing Cycle
Create a product (with a constant key), associate features, define a plan and billing cycle, then set plan feature values:
const PRODUCT = 'projecthub';
const product = await subscrio.products.createProduct({
key: PRODUCT,
displayName: 'ProjectHub',
description: 'Project management suite'
});
await subscrio.products.associateFeature(PRODUCT, FEATURE_KEYS.Analytics);
await subscrio.products.associateFeature(PRODUCT, FEATURE_KEYS.MaxProjects);
const PLAN = 'starter';
await subscrio.plans.createPlan({
productKey: PRODUCT,
key: PLAN,
displayName: 'Starter Plan',
description: 'Best for small teams'
});
const BILLING_CYCLE = 'starter-monthly';
await subscrio.billingCycles.createBillingCycle({
planKey: PLAN,
key: BILLING_CYCLE,
displayName: 'Monthly',
durationValue: 1,
durationUnit: 'months'
});
await subscrio.plans.setFeatureValue(PLAN, FEATURE_KEYS.Analytics, 'true');
await subscrio.plans.setFeatureValue(PLAN, FEATURE_KEYS.MaxProjects, '10');
const string PRODUCT = "projecthub";
var product = await subscrio.Products.CreateProductAsync(new CreateProductDto(
Key: PRODUCT,
DisplayName: "ProjectHub",
Description: "Project management suite"
));
await subscrio.Products.AssociateFeatureAsync(PRODUCT, FEATURE_ANALYTICS);
await subscrio.Products.AssociateFeatureAsync(PRODUCT, FEATURE_MAX_PROJECTS);
const string PLAN = "starter";
const string BILLING_CYCLE = "starter-monthly";
await subscrio.Plans.CreatePlanAsync(new CreatePlanDto(
ProductKey: PRODUCT,
Key: PLAN,
DisplayName: "Starter Plan",
Description: "Best for small teams"
));
await subscrio.BillingCycles.CreateBillingCycleAsync(new CreateBillingCycleDto(
PlanKey: PLAN,
Key: BILLING_CYCLE,
DisplayName: "Monthly",
DurationValue: 1,
DurationUnit: "months"
));
await subscrio.Plans.SetFeatureValueAsync(PLAN, FEATURE_ANALYTICS, "true");
await subscrio.Plans.SetFeatureValueAsync(PLAN, FEATURE_MAX_PROJECTS, "10");
Step 4 – Onboard a Customer
Step 5 – Issue a Subscription
Subscriptions tie the customer to a plan/billing cycle (and optionally contain overrides).
Need a temporary override? For example, bump max-projects to 20 for a month:
Step 6 – Verify Feature Access
Use the Feature Checker service to evaluate the final resolved values.
var maxProjects = await subscrio.FeatureChecker.GetValueForCustomerAsync<string>(
customer.Key,
PRODUCT,
FEATURE_MAX_PROJECTS,
"0"
);
var hasAnalytics = await subscrio.FeatureChecker.IsEnabledForCustomerAsync(
customer.Key,
PRODUCT,
FEATURE_ANALYTICS
);
Console.WriteLine($"Max projects: {maxProjects}, Has analytics: {hasAnalytics}");
Results obey the hierarchy: subscription override → plan value → feature default.
Where to Go Next
core-overview.md– service-by-service reference.products.md,plans.md,billing-cycles.md– deeper dives on catalog modeling.subscriptions.md&subscription-lifecycle.md– lifecycle rules.feature-checker.md– advanced feature resolution scenarios.sample/project – full demo covering trials, upgrades, overrides, and downgrades.
Once these steps succeed end-to-end, you can expand into Stripe integration, API key management, admin UI, and automated migrations. Happy building!