AroraQL 1.0.0
dotnet add package AroraQL --version 1.0.0
NuGet\Install-Package AroraQL -Version 1.0.0
<PackageReference Include="AroraQL" Version="1.0.0" />
<PackageVersion Include="AroraQL" Version="1.0.0" />
<PackageReference Include="AroraQL" />
paket add AroraQL --version 1.0.0
#r "nuget: AroraQL, 1.0.0"
#:package AroraQL@1.0.0
#addin nuget:?package=AroraQL&version=1.0.0
#tool nuget:?package=AroraQL&version=1.0.0
AroraQL
AroraQL turns plain JSON payloads into LINQ queries. A client describes what it wants — filter, sort, project, page — in a single JSON body, and one dynamic generic endpoint executes it against any data source using expression trees. No per-entity filter endpoints, no query-string gymnastics.
Features
- Dynamic generic endpoints — a single
POST /queryendpoint serves every data source; the payload'sfromfield selects the target at runtime. - JSON-first query model —
JsonQuerybinds straight from the request body via minimal API model binding. - Expression-tree execution —
whereandorderBycompile to real LINQ expressions (Where,OrderBy/OrderByDescending), including nested paths likeDepartment.Name. - Field projection —
selectreturns lightweight dictionaries containing only the requested fields, nested dot-paths included. - Scheme discovery — mark models with
IJsonQueryable<T>andMapJsonQuery()auto-registers aPOST /{typename}scheme endpoint for each.
Installation
dotnet add package AroraQL
Requires .NET 10 and the Microsoft.AspNetCore.App shared framework (i.e. an ASP.NET Core app).
Quick start: one endpoint, every source
1. Define your models
using AroraQL;
public sealed class Employee : IJsonQueryable<Employee>
{
public int Id { get; set; }
public string Name { get; set; } = "";
public int Age { get; set; }
public string Country { get; set; } = "";
public Department Department { get; set; } = new();
}
public sealed class Department : IJsonQueryable<Department>
{
public int Id { get; set; }
public string Name { get; set; } = "";
}
2. Map the dynamic generic endpoint
This is the core pattern: one endpoint, dispatched dynamically on the payload's from field. The switch is your explicit, type-safe routing table — adding a new queryable source is one line.
app.MapPost("/query",
(JsonQuery query) => query.From?.ToLowerInvariant() switch
{
"books" => Results.Ok(JsonQueryExtensions.Execute(books, query)),
"authors" => Results.Ok(JsonQueryExtensions.Execute(authors, query)),
"employees" => Results.Ok(JsonQueryExtensions.Execute(employees, query)),
_ => Results.BadRequest($"Unknown source '{query.From}'.")
})
.WithTags("AroraQL");
Executeitself is source-agnostic — it never looks atfrom. Routing the payload to the right collection (or repository, or EFDbSet) is deliberately the host's job, which keeps the library free of reflection-based source lookup and gives you full control over what is exposed.
3. Query it
POST /query
Content-Type: application/json
{
"from": "Employees",
"select": ["Id", "Name", "Department.Name"],
"where": [
{ "field": "Age", "op": ">", "value": 18 },
{ "field": "Country", "op": "=", "value": "Egypt" }
],
"orderBy": [{ "field": "Name", "dir": "asc" }],
"take": 50
}
Response:
[
{ "Id": 1, "Name": "Ahmed", "Department.Name": "Engineering" },
{ "Id": 2, "Name": "Mona", "Department.Name": "HR" },
{ "Id": 4, "Name": "Sara", "Department.Name": "Engineering" }
]
Filtering, ordering, paging, and projection all happen server-side from that single payload. When select is empty the full entities are returned; otherwise each row is a dictionary containing exactly the requested fields.
You can also pin an endpoint to a single source when you don't want dynamic dispatch:
app.MapPost("/query/books",
(JsonQuery query) => JsonQueryExtensions.Execute(books, query))
.WithTags("AroraQL");
Scheme discovery endpoints
MapJsonQuery() scans loaded assemblies for IJsonQueryable<T> implementations and maps a POST /{typename} scheme endpoint per model (handy for exercising model shapes from Scalar/OpenAPI):
app.MapJsonQuery(); // all IJsonQueryable<T> models
app.MapType<Employee>(); // or one explicit type
Query payload reference
JsonQuery
| JSON field | Type | Description |
|---|---|---|
from |
string |
Source name used by your dispatch switch. |
select |
string[] |
Fields to project; dot notation for nested paths. |
where |
WhereClause[] |
Filter clauses (combined with AND). |
orderBy |
OrderByClause[] |
Sort clauses, applied in order. |
skip |
int? |
Number of items to skip. |
take |
int? |
Maximum number of items to return. |
page |
int? |
Page number (reserved). |
pageSize |
int? |
Page size (reserved). |
distinct |
bool |
Distinct flag (reserved). |
groupBy |
string[] |
Grouping fields (reserved). |
include |
IncludeClause[] |
Related data to include (reserved). |
where clause
{ "field": "Age", "op": ">", "value": 18 }
op |
Meaning | Executed |
|---|---|---|
= |
Equal | ✅ |
!= |
Not equal | ✅ |
> |
Greater than | ✅ |
>= |
Greater or equal | ✅ |
< |
Less than | ✅ |
<= |
Less or equal | ✅ |
like |
Pattern match | planned |
in |
Set membership | planned |
not in |
Set exclusion | planned |
between |
Range | planned |
value is any JSON literal; it is converted to the target property's CLR type at execution time. Unsupported operators throw NotSupportedException.
orderBy clause
{ "field": "Name", "dir": "asc" }
dir is "asc" (default) or "desc". Nested paths like Department.Name are supported in field, select, where, and orderBy alike.
Executing outside HTTP
Execute works against any IEnumerable<T>, so the same payload can drive background jobs, tests, or gRPC handlers:
var result = JsonQueryExtensions.Execute(employees, query);
A fluent QueryBuilder is also available for composing a JsonQuery in C# when the caller isn't sending JSON.
Sample
A runnable sample (minimal API + Scalar UI, in-memory data) lives in samples/AroraQL.Sample:
dotnet run --project samples/AroraQL.Sample --launch-profile http
# then open http://localhost:5199 (redirects to /scalar/v1)
Building from source
dotnet build
dotnet pack src/AroraQL/AroraQL.csproj -c Release
The NuGet package is produced automatically on build (GeneratePackageOnBuild).
License
MIT
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0 | 114 | 7/4/2026 |