ASP is actually very good these days and feels cleaner than Spring Boot. There's less choice but the available choices are good. It has arguably the best gRPC implementation. It's just a nice experience over all.
Especially for big backend APIs: https://typescript-is-like-csharp.chrlschn.dev/
I've been doing a writeup comparing how ORMs work in TypeScript vs ORMs in .NET and one thing that is magical is that having the `Expression` type enables a much more functional and fluent experience with ORMs.
[0] https://learn.microsoft.com/en-us/dotnet/csharp/advanced-top...
Having been working in TS with Prisma for a bit, what stands out is how a Prisma query is effectively trying to express an expression tree in structural form
An example on TS with Prisma:
const loadedAda2 = await tx.runner.findFirst({
where: { email: 'ada@example.org' },
include: {
races: {
where: {
AND: [
{ position: { lte: 10 } },
{ time: { lte: 120 } },
{
race: {
name: { contains: 'New' }
}
}
]
}
}
}
})
(Other query tools like Kysely end up using strings (though with the support of TypeScript at dev time))And the same in C# with EF and LINQ:
var loadedAda = await db.Runners
.Include(r => r.RaceResults.Where(
finish => finish.Position <= 10
&& finish.Time <= TimeSpan.FromHours(2)
&& finish.Race.Name.Contains("New")
)
)
.FirstAsync(r => r.Email == "ada@example.org");
The difference being that the C# code is actually passing an expression tree and the code itself is not evaluated, allowing C# to read the code and convert it to SQL.Proper macros operate on AST, which it to say, it is exactly like the Expression stuff in C#, except it can represent the entirety of the language instead of some subset of it (C# doesn't even fully support the entirety of System.Linq.Expressions - e.g. if you want LoopExpression, you have to spell the tree out explicitly itself).
> TS does not have macro facilities
That I'm aware; I'm curious how this works on Go or Rust.For procedural macros, what you get is not even the syntax tree but rather the token tree: https://doc.rust-lang.org/proc_macro/struct.TokenStream.html. You can then use stdlib to parse that into a Rust syntax tree (which is roughly the equivalent of Expression): https://docs.rs/syn/latest/syn/enum.Expr.html
Or you can do your own parsing, meaning that you can handle pretty much any syntax that can be lexed as Rust tokens. Which means that e.g. the C# LINQ syntactic sugar (from ... select ... where etc) can also be implemented as a macro. Or you could handle raw SQL and do typechecking on it.