Master vs ASP.NET Core
ASP.NET Core is Microsoft’s mature, high-performance web framework with Entity Framework and a polished tooling story. Master deliberately mirrors its enterprise toolkit — MasterController 2.3–2.11 added scoped DI, model binding and ProblemDetails, auth policies, layered configuration and user secrets, hosted services, structured logging, caching and compression, OpenAPI, minimal APIs, versioning, localization, health checks and an in-process test host — in the JavaScript ecosystem, with a Next.js frontend included.
Feature by feature
| Feature | Master | ASP.NET Core |
|---|---|---|
| Language | JavaScript (ESM) / Node 22.12+ | C# / .NET |
| Pattern | MVC API + minimal APIs + Next.js frontend | MVC / Minimal APIs + Razor / Blazor |
| Composition root | server.js (setupServer → addScoped → use* → startMVC → start) | Program.cs (builder.Services → app.Use* → MapControllers) |
| Dependency injection | addSingleton / addScoped, real per-request scope, services on this.* | Singleton / Scoped / Transient, constructor injection |
| ORM | MasterRecord (scoped AppContext, this.db) | Entity Framework Core (scoped DbContext) |
| Migrations CLI | master db new / migrate / rollback / status / script / remove | dotnet ef migrations add / database update / list / script / remove |
| Scaffolding | master g scaffold (validated REST controller + model + page) | dotnet aspnet-codegenerator / templates |
| Model binding & validation | static schemas → this.model, 400 ValidationProblemDetails | [ApiController] + DataAnnotations / FluentValidation |
| Error contract | RFC 7807 ProblemDetails | RFC 7807 ProblemDetails |
| Typed results | this.ok / created / noContent / notFound / problem / file | Ok() / Created() / NoContent() / NotFound() / Problem() / File() |
| Typed route constraints | /posts/:id(int) | /posts/{id:int} |
| Authentication | JWT bearer (HS/RS/PS/ES), signed cookies, custom schemes, password hasher | JWT bearer, cookies, Identity, external providers |
| Authorization | authorize on routes / controllers / actions, roles, policies, ClaimsPrincipal (this.user) | [Authorize], roles, policies, ClaimsPrincipal |
| Configuration | appsettings.json → appsettings.{env}.json → user secrets → env vars → CLI; options pattern validated on start | IConfiguration layering; IOptions<T> + ValidateOnStart |
| User secrets | master secrets set Auth:JwtSecret … | dotnet user-secrets set … |
| Hosted services / background work | addHostedService, BackgroundService, PeriodicTimerService, queue | IHostedService, BackgroundService, PeriodicTimer |
| Logging | this.logger / createLogger(category), levels per category, scopes, X-Request-Id, JSON provider | ILogger<T>, scopes, providers |
| Caching & compression | outputCache policies, master.cache (memory/Redis), useResponseCompression (gzip/br) | Output caching, IDistributedCache, ResponseCompression |
| OpenAPI / Swagger | useOpenApi → /openapi.json + Swagger UI, generated from routes + schemas + auth | AddOpenApi / Swashbuckle |
| Minimal APIs / route groups / named routes | master.map.get(...), router.group(...), urlFor(name) | MapGet, MapGroup, WithName + LinkGenerator |
| API versioning | useApiVersioning (query / header / media-type / url-segment) | Asp.Versioning |
| Localization | useRequestLocalization, this.localizer, Content-Language | RequestLocalization, IStringLocalizer |
| Health checks | /health/live, /health/ready, /health/report (Healthy / Degraded / Unhealthy) | AddHealthChecks / MapHealthChecks |
| Integration testing | master.createTestClient() (in-process), master test | WebApplicationFactory + HttpClient, dotnet test |
| Graceful shutdown | useGracefulShutdown (SIGTERM → drain → stop hosted services) | IHostApplicationLifetime |
| Real-time | Socket.IO socket controllers | SignalR |
| Frontend | Next.js (React) included in the scaffold | Razor Pages / Blazor / SPA templates |
| Static typing | Optional (TypeScript on the frontend; backend is plain ESM JS) | C# — compile-time everywhere |
| gRPC | ||
| Runtime | Node (V8) | .NET CLR (very fast, AOT available) |
| Deploy targets | Anywhere Node runs | Anywhere .NET runs |
The same controller, side by side
// backend/app/controllers/postsController.js (generated by master g scaffold)
export default class PostsController {
static schemas = { // [ApiController] model binding + validation
create: { body: { title: { type: 'string', required: true } } },
};
static authorize = { create: true }; // [Authorize] on one action
constructor(requestObject) { this.requestObject = requestObject; }
async index() { // this.db = scoped AppContext (DbContext)
this.ok({ data: await this.db.Post.toList() });
}
async show(obj) { // route: /posts/:id(int)
const post = await this.db.Post.find(obj.params.id);
if (!post) return this.notFound('Post not found');
this.ok({ data: post });
}
async create() { // invalid body -> 400 ValidationProblemDetails
const post = this.db.Post.new();
Object.assign(post, this.model);
await this.db.saveChanges();
this.created(`/posts/${post.id}`, { data: post });
}
}
// backend/server.js (excerpt)
master.addScoped('db', AppContext);
master.useHealthChecks({ checks: { database: { check: () => master.useScope((s) => s.db.canConnect()), tags: ['ready'] } } });
master.useOpenApi({ ui: '/docs' });
master.auth.addJwtBearer('bearer', { secret: config.get('Auth:JwtSecret') });// Controllers/PostsController.cs
[ApiController]
[Route("posts")]
public class PostsController : ControllerBase
{
private readonly AppDbContext _db; // scoped DbContext
public PostsController(AppDbContext db) => _db = db;
[HttpGet]
public async Task<IActionResult> Index() =>
Ok(new { data = await _db.Posts.ToListAsync() });
[HttpGet("{id:int}")]
public async Task<IActionResult> Show(int id)
{
var post = await _db.Posts.FindAsync(id);
return post is null ? NotFound("Post not found") : Ok(new { data = post });
}
[Authorize]
[HttpPost] // invalid body -> 400 ValidationProblemDetails
public async Task<IActionResult> Create(PostDto dto)
{
var post = new Post { Title = dto.Title };
_db.Posts.Add(post);
await _db.SaveChangesAsync();
return Created($"/posts/{post.Id}", new { data = post });
}
}
// Program.cs (excerpt)
builder.Services.AddDbContext<AppDbContext>();
builder.Services.AddHealthChecks().AddDbContextCheck<AppDbContext>(tags: new[] { "ready" });
builder.Services.AddOpenApi();
builder.Services.AddAuthentication().AddJwtBearer();Where the two genuinely differ
The programming model is now close enough that an ASP.NET developer can read a Master controller without a glossary. What does not translate one-to-one:
- Language and typing. C# is statically typed end to end; a Master backend is plain ESM JavaScript (TypeScript is optional, and the scaffold uses it only on the Next.js side). Validation happens at the boundary (
static schemas), not in the compiler. - Querying. EF Core has LINQ and
IQueryablecomposition; MasterRecord uses string/arrow-function lambdas with$$parameters plus explicit builders (include,thenInclude,groupBy().aggregate()) andctx.query()for raw SQL. See MasterRecord vs Entity Framework. - Ecosystem. .NET has Microsoft-maintained libraries for almost everything and an ecosystem of the same size; Master leans on npm — bigger, but less uniform.
- UI stacks. Razor Pages, Blazor and view components have no equivalent in Master — the UI is a Next.js app, by design. MasterController does have server-side view engines, but they are not the primary path.
- gRPC and SignalR specifics. Master has no gRPC story. Real-time is Socket.IO socket controllers, not SignalR hubs (no built-in backplane, no .NET client).
- Identity UI.The primitives are there (password hasher, tokens, cookie and bearer schemes, policies) but there is no generated user-management UI like ASP.NET Identity’s.
- Raw throughput. The CLR and AOT compilation are faster than V8 for CPU-bound work; for typical I/O-bound APIs both are more than fast enough.
Which should you choose?
Choose Master if your team is in the JavaScript/Node ecosystem, wants a React (Next.js) frontend as a first-class citizen, and still wants the ASP.NET-style structure — scoped DI, validated models, ProblemDetails, policies, configuration layers, health checks, OpenAPI and an in-process test host — without assembling it from a dozen packages.
Choose ASP.NET Coreif you are invested in C#/.NET, need compile-time typing across the whole stack, LINQ, gRPC, SignalR or Blazor, want the CLR’s raw performance, or run on Microsoft infrastructure.