Getting Started
Create a feature, give it a value on a plan, and assign that plan to a customer. The example below returns a project limit of 10.
Install and connect
Use a development database with no conflicting example keys. Schema installation creates Subscrio's tables, but does not create the database itself. Catalog creation is not an upsert: running this example again against the same records raises duplicate-key errors.
Use Node.js 20.19 or newer and PostgreSQL. Install the library and a TypeScript runner:
Set DATABASE_URL to your private PostgreSQL connection URI. Save the example as getting-started.ts in an ESM project and run npx tsx getting-started.ts.
Use a .NET 8, 9, or 10 console application with PostgreSQL or SQL Server:
For this example, set DATABASE_URL to an Npgsql connection string, put the code in Program.cs, and run dotnet run. For SQL Server, use its connection string and set DatabaseType = "sqlserver" on DatabaseConfig.
Keep database credentials on the server. Review Schema Upgrade before running migration against an existing deployment.
Create a catalog and check a limit
import { Subscrio } from 'subscrio';
const subscrio = new Subscrio({
database: { connectionString: process.env.DATABASE_URL! }
});
try {
if (await subscrio.verifySchema() === null) await subscrio.installSchema();
else await subscrio.migrate();
await subscrio.features.createFeature({
key: 'max-projects', displayName: 'Projects', valueType: 'numeric', defaultValue: '0'
});
await subscrio.products.createProduct({ key: 'projecthub', displayName: 'ProjectHub' });
await subscrio.products.associateFeature('projecthub', 'max-projects');
await subscrio.plans.createPlan({
key: 'starter', productKey: 'projecthub', displayName: 'Starter'
});
await subscrio.plans.setFeatureValue('starter', 'max-projects', '10');
await subscrio.billingCycles.createBillingCycle({
key: 'starter-monthly', planKey: 'starter', displayName: 'Monthly',
durationUnit: 'months', durationValue: 1
});
await subscrio.customers.createCustomer({ key: 'acme', displayName: 'Acme' });
await subscrio.subscriptions.createSubscription({
key: 'acme-starter', customerKey: 'acme', billingCycleKey: 'starter-monthly'
});
const limit = await subscrio.featureChecker.getValueForCustomer(
'acme', 'projecthub', 'max-projects', 0
);
console.log(limit); // 10
} finally {
await subscrio.close();
}
using Subscrio.Core;
using Subscrio.Core.Config;
using Subscrio.Core.Application.DTOs;
using var subscrio = new Subscrio.Core.Subscrio(new SubscrioConfig
{
Database = new DatabaseConfig
{
ConnectionString = Environment.GetEnvironmentVariable("DATABASE_URL")!
}
});
if (await subscrio.VerifySchemaAsync() is null) await subscrio.InstallSchemaAsync();
else await subscrio.MigrateAsync();
await subscrio.Features.CreateFeatureAsync(new CreateFeatureDto(
Key: "max-projects", DisplayName: "Projects", ValueType: "numeric", DefaultValue: "0"));
await subscrio.Products.CreateProductAsync(new CreateProductDto("projecthub", "ProjectHub"));
await subscrio.Products.AssociateFeatureAsync("projecthub", "max-projects");
await subscrio.Plans.CreatePlanAsync(new CreatePlanDto("projecthub", "starter", "Starter"));
await subscrio.Plans.SetFeatureValueAsync("starter", "max-projects", "10");
await subscrio.BillingCycles.CreateBillingCycleAsync(new CreateBillingCycleDto(
PlanKey: "starter", Key: "starter-monthly", DisplayName: "Monthly",
DurationValue: 1, DurationUnit: "months"));
await subscrio.Customers.CreateCustomerAsync(new CreateCustomerDto("acme", "Acme"));
await subscrio.Subscriptions.CreateSubscriptionAsync(new CreateSubscriptionDto(
CustomerKey: "acme", BillingCycleKey: "starter-monthly", Key: "acme-starter"));
var limit = await subscrio.FeatureChecker.GetValueForCustomerAsync<int>(
"acme", "projecthub", "max-projects", 0);
Console.WriteLine(limit); // 10
Understand the result
The feature default is zero. The Starter plan replaces it with 10, and Acme's subscription selects that plan through its monthly billing cycle. The numeric getter returns 10. It does not count existing projects or prevent your application from creating another one.
In TypeScript, passing the numeric fallback 0 selects numeric conversion; a generic type argument alone does not change runtime conversion. In .NET, the generic target type selects the conversion. See Feature Checker for missing-record behavior and other conversions.
Billing-cycle dates describe a subscription period. Subscrio does not charge the customer or automatically advance those dates. Your billing integration maintains them.
Updating existing records
Methods that link to this convention change supplied properties while retaining omitted properties. For example, changing a feature's default value leaves its name and description unchanged. This applies only to methods that explicitly reference the convention.
Leave a property out or pass undefined to keep its current value. Explicit null may be rejected or may clear a value, depending on the method and field.
For update DTOs that use nullable properties to represent omission, leaving a property at its default null keeps the saved value. That null does not clear the value. Some methods provide explicit flags for clearing a field.
Partial updates do not imply that nested objects are merged. The owning method documents replacement or patch behavior, empty collections, and restrictions after usage or credit activity. Configuration sync has its own replacement rules.
Use dependency injection in .NET
TypeScript applications manage the instance directly as shown above. The following container registration is specific to .NET.
Register a scoped instance in an ASP.NET application's service collection. This complete container example uses the database initialized above; registration itself does not install the schema or apply configuration.
using Microsoft.Extensions.DependencyInjection;
using Subscrio.Core.DependencyInjection;
var services = new ServiceCollection();
services.AddSubscrio(new SubscrioConfig
{
Database = new DatabaseConfig
{
ConnectionString = Environment.GetEnvironmentVariable("DATABASE_URL")!
}
}, ServiceLifetime.Scoped);
using var provider = services.BuildServiceProvider();
using var scope = provider.CreateScope();
var scopedSubscrio = scope.ServiceProvider.GetRequiredService<Subscrio.Core.Subscrio>();
Console.WriteLine(await scopedSubscrio.VerifySchemaAsync());
In an ASP.NET application, register on builder.Services; the request scope handles disposal. See Subscrio for configuration properties.
Continue with your application
Keep an instance alive for the application work that needs it, and close or dispose it when finished. For the full configuration and object list, see Subscrio.
- How Subscrio Works explains the model and how to choose between quotas and credits.
- Add-ons and Overrides extends this example with reusable packages and individual exceptions.
- Subscription Lifecycle covers trials, cancellation, and plan transitions.
- Managing Configuration replaces repeated catalog-creation scripts with an explicit synchronization workflow.